summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/BlockID.h1
-rw-r--r--src/ChunkDef.h34
-rw-r--r--src/ChunkMap.cpp157
-rw-r--r--src/Defines.h9
-rw-r--r--src/Entities/Entity.cpp3
-rw-r--r--src/Entities/Player.cpp4
-rw-r--r--src/GroupManager.cpp46
-rw-r--r--src/OSSupport/SocketThreads.cpp12
-rw-r--r--src/Simulator/RedstoneSimulator.cpp25
-rw-r--r--src/Simulator/RedstoneSimulator.h2
-rw-r--r--src/World.cpp6
11 files changed, 219 insertions, 80 deletions
diff --git a/src/BlockID.h b/src/BlockID.h
index b31c589b9..f53440ec8 100644
--- a/src/BlockID.h
+++ b/src/BlockID.h
@@ -801,6 +801,7 @@ enum eDamageType
dtPotionOfHarming,
dtEnderPearl, // Thrown an ender pearl, teleported by it
dtAdmin, // Damage applied by an admin command
+ dtExplosion, // Damage applied by an explosion
// Some common synonyms:
dtPawnAttack = dtAttack,
diff --git a/src/ChunkDef.h b/src/ChunkDef.h
index 09bd766da..1c4aa6aca 100644
--- a/src/ChunkDef.h
+++ b/src/ChunkDef.h
@@ -497,7 +497,7 @@ public:
-/// Generic template that can store any kind of data together with a triplet of 3 coords:
+/** Generic template that can store any kind of data together with a triplet of 3 coords*/
template <typename X> class cCoordWithData
{
public:
@@ -517,12 +517,40 @@ public:
}
} ;
-// Illegal in C++03: typedef std::list< cCoordWithData<X> > cCoordWithDataList<X>;
typedef cCoordWithData<int> cCoordWithInt;
typedef cCoordWithData<BLOCKTYPE> cCoordWithBlock;
+
typedef std::list<cCoordWithInt> cCoordWithIntList;
typedef std::vector<cCoordWithInt> cCoordWithIntVector;
-typedef std::vector<cCoordWithBlock> cCoordWithBlockVector;
+
+
+
+
+
+/** Generic template that can store two types of any kind of data together with a triplet of 3 coords */
+template <typename X, typename Z> class cCoordWithDoubleData
+{
+public:
+ int x;
+ int y;
+ int z;
+ X Data;
+ Z DataTwo;
+
+ cCoordWithDoubleData(int a_X, int a_Y, int a_Z) :
+ x(a_X), y(a_Y), z(a_Z)
+ {
+ }
+
+ cCoordWithDoubleData(int a_X, int a_Y, int a_Z, const X & a_Data, const Z & a_DataTwo) :
+ x(a_X), y(a_Y), z(a_Z), Data(a_Data), DataTwo(a_DataTwo)
+ {
+ }
+};
+
+typedef cCoordWithDoubleData <BLOCKTYPE, bool> cCoordWithBlockAndBool;
+
+typedef std::vector<cCoordWithBlockAndBool> cCoordWithBlockAndBoolVector;
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index 049bb5fb2..1e2181f32 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -15,6 +15,9 @@
#include "Blocks/BlockHandler.h"
#include "MobCensus.h"
#include "MobSpawner.h"
+#include "BoundingBox.h"
+
+#include "Entities/Pickup.h"
#ifndef _WIN32
#include <cstdlib> // abs
@@ -1657,49 +1660,52 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
{
return;
}
-
+
+ bool ShouldDestroyBlocks = true;
+
// Don't explode if the explosion center is inside a liquid block:
- switch (m_World->GetBlock((int)floor(a_BlockX), (int)floor(a_BlockY), (int)floor(a_BlockZ)))
+ if (IsBlockLiquid(m_World->GetBlock((int)floor(a_BlockX), (int)floor(a_BlockY), (int)floor(a_BlockZ))))
{
- case E_BLOCK_WATER:
- case E_BLOCK_STATIONARY_WATER:
- case E_BLOCK_LAVA:
- case E_BLOCK_STATIONARY_LAVA:
- {
- return;
- }
+ ShouldDestroyBlocks = false;
}
-
- cBlockArea area;
+
+ int ExplosionSizeInt = (int)ceil(a_ExplosionSize);
+ int ExplosionSizeSq = ExplosionSizeInt * ExplosionSizeInt;
+
int bx = (int)floor(a_BlockX);
int by = (int)floor(a_BlockY);
int bz = (int)floor(a_BlockZ);
- int ExplosionSizeInt = (int) ceil(a_ExplosionSize);
- int ExplosionSizeSq = ExplosionSizeInt * ExplosionSizeInt;
- a_BlocksAffected.reserve(8 * ExplosionSizeInt * ExplosionSizeInt * ExplosionSizeInt);
+
int MinY = std::max((int)floor(a_BlockY - ExplosionSizeInt), 0);
int MaxY = std::min((int)ceil(a_BlockY + ExplosionSizeInt), cChunkDef::Height - 1);
- area.Read(m_World, bx - ExplosionSizeInt, (int)ceil(a_BlockX + ExplosionSizeInt), MinY, MaxY, bz - ExplosionSizeInt, (int)ceil(a_BlockZ + ExplosionSizeInt));
- for (int x = -ExplosionSizeInt; x < ExplosionSizeInt; x++)
+
+ if (ShouldDestroyBlocks)
{
- for (int y = -ExplosionSizeInt; y < ExplosionSizeInt; y++)
+ cBlockArea area;
+
+ a_BlocksAffected.reserve(8 * ExplosionSizeInt * ExplosionSizeInt * ExplosionSizeInt);
+
+ area.Read(m_World, bx - ExplosionSizeInt, (int)ceil(a_BlockX + ExplosionSizeInt), MinY, MaxY, bz - ExplosionSizeInt, (int)ceil(a_BlockZ + ExplosionSizeInt));
+ for (int x = -ExplosionSizeInt; x < ExplosionSizeInt; x++)
{
- if ((by + y >= cChunkDef::Height) || (by + y < 0))
+ for (int y = -ExplosionSizeInt; y < ExplosionSizeInt; y++)
{
- // Outside of the world
- continue;
- }
- for (int z = -ExplosionSizeInt; z < ExplosionSizeInt; z++)
- {
- if ((x * x + y * y + z * z) > ExplosionSizeSq)
+ if ((by + y >= cChunkDef::Height) || (by + y < 0))
{
- // Too far away
+ // Outside of the world
continue;
}
-
- BLOCKTYPE Block = area.GetBlockType(bx + x, by + y, bz + z);
- switch (Block)
+ for (int z = -ExplosionSizeInt; z < ExplosionSizeInt; z++)
{
+ if ((x * x + y * y + z * z) > ExplosionSizeSq)
+ {
+ // Too far away
+ continue;
+ }
+
+ BLOCKTYPE Block = area.GetBlockType(bx + x, by + y, bz + z);
+ switch (Block)
+ {
case E_BLOCK_TNT:
{
// Activate the TNT, with a random fuse between 10 to 30 game ticks
@@ -1724,20 +1730,20 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_WATER);
break;
}
-
+
case E_BLOCK_STATIONARY_LAVA:
{
// Turn into simulated lava:
area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_LAVA);
break;
}
-
+
case E_BLOCK_AIR:
{
// No pickups for air
break;
}
-
+
default:
{
if (m_World->GetTickRandomNumber(100) <= 25) // 25% chance of pickups
@@ -1751,11 +1757,90 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_AIR);
a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z));
}
- } // switch (BlockType)
- } // for z
- } // for y
- } // for x
- area.Write(m_World, bx - ExplosionSizeInt, MinY, bz - ExplosionSizeInt);
+ } // switch (BlockType)
+ } // for z
+ } // for y
+ } // for x
+ area.Write(m_World, bx - ExplosionSizeInt, MinY, bz - ExplosionSizeInt);
+ }
+
+ class cTNTDamageCallback :
+ public cEntityCallback
+ {
+ public:
+ cTNTDamageCallback(cBoundingBox & a_bbTNT, Vector3d a_ExplosionPos, int a_ExplosionSize, int a_ExplosionSizeSq) :
+ m_bbTNT(a_bbTNT),
+ m_ExplosionPos(a_ExplosionPos),
+ m_ExplosionSize(a_ExplosionSize),
+ m_ExplosionSizeSq(a_ExplosionSizeSq)
+ {
+ }
+
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ if (a_Entity->IsPickup())
+ {
+ if (((cPickup *)a_Entity)->GetAge() < 20) // If pickup age is smaller than one second, it is invincible (so we don't kill pickups that were just spawned)
+ {
+ return false;
+ }
+ }
+
+ Vector3d EntityPos = a_Entity->GetPosition();
+ cBoundingBox bbEntity(EntityPos, a_Entity->GetWidth() / 2, a_Entity->GetHeight());
+
+ if (!m_bbTNT.IsInside(bbEntity)) // IsInside actually acts like DoesSurround
+ {
+ return false;
+ }
+
+ Vector3d AbsoluteEntityPos(abs(EntityPos.x), abs(EntityPos.y), abs(EntityPos.z));
+ Vector3d MaxExplosionBoundary(m_ExplosionSizeSq, m_ExplosionSizeSq, m_ExplosionSizeSq);
+
+ // Work out how far we are from the edge of the TNT's explosive effect
+ AbsoluteEntityPos -= m_ExplosionPos;
+ AbsoluteEntityPos = MaxExplosionBoundary - AbsoluteEntityPos;
+
+ double FinalDamage = ((AbsoluteEntityPos.x + AbsoluteEntityPos.y + AbsoluteEntityPos.z) / 3) * m_ExplosionSize;
+ FinalDamage = a_Entity->GetMaxHealth() - abs(FinalDamage);
+
+ // Clip damage values
+ if (FinalDamage > a_Entity->GetMaxHealth())
+ FinalDamage = a_Entity->GetMaxHealth();
+ else if (FinalDamage < 0)
+ return false;
+
+ if (!a_Entity->IsTNT()) // Don't apply damage to other TNT entities, they should be invincible
+ {
+ a_Entity->TakeDamage(dtExplosion, NULL, (int)FinalDamage, 0);
+ }
+
+ // Apply force to entities around the explosion - code modified from World.cpp DoExplosionAt()
+ Vector3d distance_explosion = a_Entity->GetPosition() - m_ExplosionPos;
+ if (distance_explosion.SqrLength() < 4096.0)
+ {
+ distance_explosion.Normalize();
+ distance_explosion *= m_ExplosionSizeSq;
+
+ a_Entity->AddSpeed(distance_explosion);
+ }
+
+ return false;
+ }
+
+ protected:
+ cBoundingBox & m_bbTNT;
+ Vector3d m_ExplosionPos;
+ int m_ExplosionSize;
+ int m_ExplosionSizeSq;
+ };
+
+ cBoundingBox bbTNT(Vector3d(a_BlockX, a_BlockY, a_BlockZ), 0.5, 1);
+ bbTNT.Expand(ExplosionSizeInt * 2, ExplosionSizeInt * 2, ExplosionSizeInt * 2);
+
+
+ cTNTDamageCallback TNTDamageCallback(bbTNT, Vector3d(a_BlockX, a_BlockY, a_BlockZ), a_ExplosionSize, ExplosionSizeSq);
+ ForEachEntity(TNTDamageCallback);
// Wake up all simulators for the area, so that water and lava flows and sand falls into the blasted holes (FS #391):
WakeUpSimulatorsInArea(
diff --git a/src/Defines.h b/src/Defines.h
index 3a26f4be6..5b868b2e5 100644
--- a/src/Defines.h
+++ b/src/Defines.h
@@ -263,6 +263,15 @@ inline bool IsBlockWater(BLOCKTYPE a_BlockType)
+inline bool IsBlockWaterOrIce(BLOCKTYPE a_BlockType)
+{
+ return (IsBlockWater(a_BlockType) || (a_BlockType == E_BLOCK_ICE));
+}
+
+
+
+
+
inline bool IsBlockLava(BLOCKTYPE a_BlockType)
{
return ((a_BlockType == E_BLOCK_LAVA) || (a_BlockType == E_BLOCK_STATIONARY_LAVA));
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index e22f689d9..08780ca8b 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -397,6 +397,7 @@ int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_Dama
case dtPotionOfHarming:
case dtFalling:
case dtLightning:
+ case dtPlugin:
{
return 0;
}
@@ -473,7 +474,7 @@ void cEntity::KilledBy(cEntity * a_Killer)
return;
}
- // Drop loot:
+ // Drop loot:
cItems Drops;
GetDrops(Drops, a_Killer);
m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ());
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index a1c942c20..d649cacf2 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -792,11 +792,11 @@ void cPlayer::SetFlying(bool a_IsFlying)
void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
{
- if (a_TDI.DamageType != dtInVoid)
+ if ((a_TDI.DamageType != dtInVoid) && (a_TDI.DamageType != dtPlugin))
{
if (IsGameModeCreative())
{
- // No damage / health in creative mode if not void damage
+ // No damage / health in creative mode if not void or plugin damage
return;
}
}
diff --git a/src/GroupManager.cpp b/src/GroupManager.cpp
index 497f98c93..d5567d91e 100644
--- a/src/GroupManager.cpp
+++ b/src/GroupManager.cpp
@@ -69,47 +69,51 @@ cGroupManager::cGroupManager()
}
unsigned int NumKeys = IniFile.GetNumKeys();
- for( unsigned int i = 0; i < NumKeys; i++ )
+ for (size_t i = 0; i < NumKeys; i++)
{
std::string KeyName = IniFile.GetKeyName( i );
cGroup* Group = GetGroup( KeyName.c_str() );
LOGD("Loading group: %s", KeyName.c_str() );
- Group->SetName( KeyName );
- char Color = IniFile.GetValue( KeyName, "Color", "-" )[0];
- if( Color != '-' )
- Group->SetColor( cChatColor::Color + Color );
+ Group->SetName(KeyName);
+ AString Color = IniFile.GetValue(KeyName, "Color", "-");
+ if ((Color != "-") && (Color.length() >= 1))
+ {
+ Group->SetColor(cChatColor::Color + Color[0]);
+ }
else
- Group->SetColor( cChatColor::White );
+ {
+ Group->SetColor(cChatColor::White);
+ }
- AString Commands = IniFile.GetValue( KeyName, "Commands", "" );
- if( Commands.size() > 0 )
+ AString Commands = IniFile.GetValue(KeyName, "Commands", "");
+ if (!Commands.empty())
{
- AStringVector Split = StringSplit( Commands, "," );
- for( unsigned int i = 0; i < Split.size(); i++)
+ AStringVector Split = StringSplitAndTrim(Commands, ",");
+ for (size_t i = 0; i < Split.size(); i++)
{
- Group->AddCommand( Split[i] );
+ Group->AddCommand(Split[i]);
}
}
- AString Permissions = IniFile.GetValue( KeyName, "Permissions", "" );
- if( Permissions.size() > 0 )
+ AString Permissions = IniFile.GetValue(KeyName, "Permissions", "");
+ if (!Permissions.empty())
{
- AStringVector Split = StringSplit( Permissions, "," );
- for( unsigned int i = 0; i < Split.size(); i++)
+ AStringVector Split = StringSplitAndTrim(Permissions, ",");
+ for (size_t i = 0; i < Split.size(); i++)
{
- Group->AddPermission( Split[i] );
+ Group->AddPermission(Split[i]);
}
}
- std::string Groups = IniFile.GetValue( KeyName, "Inherits", "" );
- if( Groups.size() > 0 )
+ std::string Groups = IniFile.GetValue(KeyName, "Inherits", "");
+ if (!Groups.empty())
{
- AStringVector Split = StringSplit( Groups, "," );
- for( unsigned int i = 0; i < Split.size(); i++)
+ AStringVector Split = StringSplitAndTrim(Groups, ",");
+ for (size_t i = 0; i < Split.size(); i++)
{
- Group->InheritFrom( GetGroup( Split[i].c_str() ) );
+ Group->InheritFrom(GetGroup(Split[i].c_str()));
}
}
}
diff --git a/src/OSSupport/SocketThreads.cpp b/src/OSSupport/SocketThreads.cpp
index 3e2631733..a02661d2c 100644
--- a/src/OSSupport/SocketThreads.cpp
+++ b/src/OSSupport/SocketThreads.cpp
@@ -209,6 +209,10 @@ bool cSocketThreads::cSocketThread::RemoveClient(const cCallback * a_Client)
if (m_Slots[i].m_State == sSlot::ssRemoteClosed)
{
// The remote has already closed the socket, remove the slot altogether:
+ if (m_Slots[i].m_Socket.IsValid())
+ {
+ m_Slots[i].m_Socket.CloseSocket();
+ }
m_Slots[i] = m_Slots[--m_NumSlots];
}
else
@@ -489,6 +493,7 @@ void cSocketThreads::cSocketThread::ReadFromSockets(fd_set * a_Read)
// Notify the callback that the remote has closed the socket; keep the slot
m_Slots[i].m_Client->SocketClosed();
m_Slots[i].m_State = sSlot::ssRemoteClosed;
+ m_Slots[i].m_Socket.CloseSocket();
break;
}
case sSlot::ssWritingRestOut:
@@ -557,6 +562,11 @@ void cSocketThreads::cSocketThread::WriteToSockets(fd_set * a_Write)
}
} // if (outgoing data is empty)
+ if (m_Slots[i].m_State == sSlot::ssRemoteClosed)
+ {
+ continue;
+ }
+
if (!SendDataThroughSocket(m_Slots[i].m_Socket, m_Slots[i].m_Outgoing))
{
int Err = cSocket::GetLastError();
@@ -566,7 +576,7 @@ void cSocketThreads::cSocketThread::WriteToSockets(fd_set * a_Write)
{
m_Slots[i].m_Client->SocketClosed();
}
- return;
+ continue;
}
if (m_Slots[i].m_Outgoing.empty() && (m_Slots[i].m_State == sSlot::ssWritingRestOut))
diff --git a/src/Simulator/RedstoneSimulator.cpp b/src/Simulator/RedstoneSimulator.cpp
index 5749f5035..05badf0d4 100644
--- a/src/Simulator/RedstoneSimulator.cpp
+++ b/src/Simulator/RedstoneSimulator.cpp
@@ -9,7 +9,6 @@
#include "../Blocks/BlockTorch.h"
#include "../Blocks/BlockDoor.h"
#include "../Piston.h"
-#include "../Tracer.h"
@@ -170,7 +169,7 @@ void cRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChu
{
if (!IsAllowedBlock(Block))
{
- ChunkData.erase(itr); // The new blocktype is not redstone; it must be removed from this list
+ itr->DataTwo = true; // The new blocktype is not redstone; it must be queued to be removed from this list
}
else
{
@@ -185,7 +184,7 @@ void cRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChu
return;
}
- ChunkData.push_back(cCoordWithBlock(RelX, a_BlockY, RelZ, Block));
+ ChunkData.push_back(cCoordWithBlockAndBool(RelX, a_BlockY, RelZ, Block, false));
}
@@ -208,8 +207,14 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c
int BaseX = a_Chunk->GetPosX() * cChunkDef::Width;
int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width;
- for (cRedstoneSimulatorChunkData::const_iterator dataitr = ChunkData.begin(); dataitr != ChunkData.end(); ++dataitr)
+ for (cRedstoneSimulatorChunkData::iterator dataitr = ChunkData.begin(); dataitr != ChunkData.end();)
{
+ if (dataitr->DataTwo)
+ {
+ dataitr = ChunkData.erase(dataitr);
+ continue;
+ }
+
int a_X = BaseX + dataitr->x;
int a_Z = BaseZ + dataitr->z;
switch (dataitr->Data)
@@ -279,6 +284,7 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c
break;
}
}
+ ++dataitr;
}
}
@@ -919,7 +925,7 @@ void cRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_BlockY, int a_B
case E_BLOCK_STONE_PRESSURE_PLATE:
{
// MCS feature - stone pressure plates can only be triggered by players :D
- cPlayer * a_Player = m_World.FindClosestPlayer(Vector3f(a_BlockX + 0.5f, (float)a_BlockY, a_BlockZ + 0.5f), 0.5f);
+ cPlayer * a_Player = m_World.FindClosestPlayer(Vector3f(a_BlockX + 0.5f, (float)a_BlockY, a_BlockZ + 0.5f), 0.5f, false);
if (a_Player != NULL)
{
@@ -950,19 +956,14 @@ void cRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_BlockY, int a_B
virtual bool Item(cEntity * a_Entity) override
{
- cTracer LineOfSight(m_World);
-
Vector3f EntityPos = a_Entity->GetPosition();
Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f);
float Distance = (EntityPos - BlockPos).Length();
if (Distance < 0.5)
{
- if (!LineOfSight.Trace(BlockPos, (EntityPos - BlockPos), (int)(EntityPos - BlockPos).Length()))
- {
- m_Entity = a_Entity;
- return true; // Break out, we only need to know for wooden plates that at least one entity is on top
- }
+ m_Entity = a_Entity;
+ return true; // Break out, we only need to know for wooden plates that at least one entity is on top
}
return false;
}
diff --git a/src/Simulator/RedstoneSimulator.h b/src/Simulator/RedstoneSimulator.h
index bb2efeb8a..c505b2a0f 100644
--- a/src/Simulator/RedstoneSimulator.h
+++ b/src/Simulator/RedstoneSimulator.h
@@ -4,7 +4,7 @@
#include "Simulator.h"
/// Per-chunk data for the simulator, specified individual chunks to simulate; 'Data' is not used
-typedef cCoordWithBlockVector cRedstoneSimulatorChunkData;
+typedef cCoordWithBlockAndBoolVector cRedstoneSimulatorChunkData;
diff --git a/src/World.cpp b/src/World.cpp
index 78b42e810..de2002b84 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -632,7 +632,7 @@ void cWorld::GenerateRandomSpawn(void)
{
LOGD("Generating random spawnpoint...");
- while (IsBlockWater(GetBlock((int)m_SpawnX, GetHeight((int)m_SpawnX, (int)m_SpawnZ), (int)m_SpawnZ)))
+ while (IsBlockWaterOrIce(GetBlock((int)m_SpawnX, GetHeight((int)m_SpawnX, (int)m_SpawnZ), (int)m_SpawnZ)))
{
if ((GetTickRandomNumber(4) % 2) == 0) // Randomise whether to increment X or Z coords
{
@@ -1551,7 +1551,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double
a_FlyAwaySpeed /= 100; // Pre-divide, so that we don't have to divide each time inside the loop
for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr)
{
- if (!IsValidItem(itr->m_ItemType))
+ if (!IsValidItem(itr->m_ItemType) || itr->m_ItemType == E_BLOCK_AIR)
{
// Don't spawn pickup if item isn't even valid; should prevent client crashing too
continue;
@@ -1577,7 +1577,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double
{
for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr)
{
- if (!IsValidItem(itr->m_ItemType))
+ if (!IsValidItem(itr->m_ItemType) || itr->m_ItemType == E_BLOCK_AIR)
{
continue;
}