1
0
Fork 0

Fixed deadlock when moving players to other worlds.

Fixes #1039, fixes #851
This commit is contained in:
Mattes D 2014-06-08 21:58:08 +02:00
parent b904223b9d
commit af4a21ea06
32 changed files with 204 additions and 96 deletions

View File

@ -790,6 +790,7 @@ enum eDimension
dimNether = -1,
dimOverworld = 0,
dimEnd = 1,
dimNotSet = 255, // For things that need an "indeterminate" state, such as cProtocol's LastSentDimension
} ;

View File

@ -327,7 +327,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID)
// Send experience
m_Player->SendExperience();
m_Player->Initialize(World);
m_Player->Initialize(*World);
m_State = csAuthenticated;
// Query player team
@ -356,7 +356,7 @@ void cClientHandle::StreamChunks(void)
}
ASSERT(m_Player != NULL);
int ChunkPosX = FAST_FLOOR_DIV((int)m_Player->GetPosX(), cChunkDef::Width);
int ChunkPosZ = FAST_FLOOR_DIV((int)m_Player->GetPosZ(), cChunkDef::Width);
if ((ChunkPosX == m_LastStreamedChunkX) && (ChunkPosZ == m_LastStreamedChunkZ))
@ -1753,18 +1753,8 @@ void cClientHandle::SendData(const char * a_Data, size_t a_Size)
void cClientHandle::MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket)
void cClientHandle::RemoveFromWorld(void)
{
UNUSED(a_World);
ASSERT(m_Player != NULL);
if (a_SendRespawnPacket)
{
SendRespawn();
}
cWorld * World = m_Player->GetWorld();
// Remove all associated chunks:
cChunkCoordsList Chunks;
{
@ -1774,7 +1764,6 @@ void cClientHandle::MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket)
}
for (cChunkCoordsList::iterator itr = Chunks.begin(), end = Chunks.end(); itr != end; ++itr)
{
World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this);
m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ);
} // for itr - Chunks[]
@ -2379,9 +2368,9 @@ void cClientHandle::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effec
void cClientHandle::SendRespawn(void)
void cClientHandle::SendRespawn(const cWorld & a_World)
{
m_Protocol->SendRespawn();
m_Protocol->SendRespawn(a_World);
}

View File

@ -149,7 +149,7 @@ public:
void SendPlayerSpawn (const cPlayer & a_Player);
void SendPluginMessage (const AString & a_Channel, const AString & a_Message); // Exported in ManualBindings.cpp
void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID);
void SendRespawn (void);
void SendRespawn (const cWorld & a_World);
void SendExperience (void);
void SendExperienceOrb (const cExpOrb & a_ExpOrb);
void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode);
@ -251,8 +251,8 @@ public:
void SendData(const char * a_Data, size_t a_Size);
/** Called when the player moves into a different world.
Locks the current world, doesn't lock the new world. */
void MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket);
Sends an UnloadChunk packet for each loaded chunk and resets the streamed chunks. */
void RemoveFromWorld(void);
/** Called when the player will enchant a Item */
void HandleEnchantItem(Byte & WindowID, Byte & Enchantment);

View File

@ -129,9 +129,9 @@ const char * cEntity::GetParentClass(void) const
bool cEntity::Initialize(cWorld * a_World)
bool cEntity::Initialize(cWorld & a_World)
{
if (cPluginManager::Get()->CallHookSpawningEntity(*a_World, *this))
if (cPluginManager::Get()->CallHookSpawningEntity(a_World, *this))
{
return false;
}
@ -144,13 +144,13 @@ bool cEntity::Initialize(cWorld * a_World)
*/
m_IsInitialized = true;
m_World = a_World;
m_World = &a_World;
m_World->AddEntity(this);
cPluginManager::Get()->CallHookSpawnedEntity(*a_World, *this);
cPluginManager::Get()->CallHookSpawnedEntity(a_World, *this);
// Spawn the entity on the clients:
a_World->BroadcastSpawnEntity(*this);
a_World.BroadcastSpawnEntity(*this);
return true;
}

View File

@ -146,8 +146,9 @@ public:
cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height);
virtual ~cEntity();
/// Spawns the entity in the world; returns true if spawned, false if not (plugin disallowed)
virtual bool Initialize(cWorld * a_World);
/** Spawns the entity in the world; returns true if spawned, false if not (plugin disallowed).
Adds the entity to the world. */
virtual bool Initialize(cWorld & a_World);
// tolua_begin

View File

@ -940,6 +940,8 @@ void cPlayer::Killed(cEntity * a_Victim)
void cPlayer::Respawn(void)
{
ASSERT(m_World != NULL);
m_Health = GetMaxHealth();
SetInvulnerableTicks(20);
@ -952,7 +954,7 @@ void cPlayer::Respawn(void)
m_LifetimeTotalXp = 0;
// ToDo: send score to client? How?
m_ClientHandle->SendRespawn();
m_ClientHandle->SendRespawn(*m_World);
// Extinguish the fire:
StopBurning();
@ -1583,19 +1585,19 @@ bool cPlayer::MoveToWorld(const char * a_WorldName)
return false;
}
eDimension OldDimension = m_World->GetDimension();
// Send the respawn packet:
if (m_ClientHandle != NULL)
{
m_ClientHandle->SendRespawn(*World);
}
// Remove all links to the old world
m_World->RemovePlayer(this);
m_ClientHandle->RemoveFromAllChunks();
m_World->RemoveEntity(this);
// If the dimension is different, we can send the respawn packet
// http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02
m_ClientHandle->MoveToWorld(*World, (OldDimension != World->GetDimension()));
// Add player to all the necessary parts of the new world
World->AddEntity(this);
// Queue adding player to the new world, including all the necessary adjustments to the object
World->AddPlayer(this);
return true;

View File

@ -75,7 +75,7 @@ public:
double z = Callbacks.m_Pos.z;
cBoat * Boat = new cBoat(x + 0.5, y + 1, z + 0.5);
Boat->Initialize(a_World);
Boat->Initialize(*a_World);
return true;
}

View File

@ -66,7 +66,7 @@ public:
{
return;
}
if (!Arrow->Initialize(a_Player->GetWorld()))
if (!Arrow->Initialize(*a_Player->GetWorld()))
{
delete Arrow;
return;

View File

@ -231,7 +231,7 @@ public:
else
{
cFloater * Floater = new cFloater(a_Player->GetPosX(), a_Player->GetStance(), a_Player->GetPosZ(), a_Player->GetLookVector() * 15, a_Player->GetUniqueID(), 100 + a_World->GetTickRandomNumber(800) - (a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchLure) * 100));
Floater->Initialize(a_World);
Floater->Initialize(*a_World);
a_Player->SetIsFishing(true, Floater->GetUniqueID());
}
return true;

View File

@ -34,7 +34,7 @@ public:
if (Block == E_BLOCK_AIR)
{
cItemFrame * ItemFrame = new cItemFrame(a_Dir, a_BlockX, a_BlockY, a_BlockZ);
if (!ItemFrame->Initialize(a_World))
if (!ItemFrame->Initialize(*a_World))
{
delete ItemFrame;
return false;

View File

@ -70,7 +70,7 @@ public:
return false;
}
} // switch (m_ItemType)
Minecart->Initialize(a_World);
Minecart->Initialize(*a_World);
if (!a_Player->IsGameModeCreative())
{

View File

@ -79,7 +79,7 @@ public:
};
cPainting * Painting = new cPainting(gPaintingTitlesList[a_World->GetTickRandomNumber(ARRAYCOUNT(gPaintingTitlesList) - 1)].Title, Dir, a_BlockX, a_BlockY, a_BlockZ);
Painting->Initialize(a_World);
Painting->Initialize(*a_World);
if (!a_Player->IsGameModeCreative())
{

View File

@ -44,7 +44,7 @@ void cBlaze::Attack(float a_Dt)
{
return;
}
if (!FireCharge->Initialize(m_World))
if (!FireCharge->Initialize(*m_World))
{
delete FireCharge;
return;

View File

@ -46,7 +46,7 @@ void cGhast::Attack(float a_Dt)
{
return;
}
if (!GhastBall->Initialize(m_World))
if (!GhastBall->Initialize(*m_World))
{
delete GhastBall;
return;

View File

@ -81,7 +81,7 @@ void cSkeleton::Attack(float a_Dt)
{
return;
}
if (!Arrow->Initialize(m_World))
if (!Arrow->Initialize(*m_World))
{
delete Arrow;
return;

View File

@ -30,7 +30,7 @@ bool cWither::IsArmored(void) const
bool cWither::Initialize(cWorld * a_World)
bool cWither::Initialize(cWorld & a_World)
{
// Set health before BroadcastSpawnEntity()
SetHealth(GetMaxHealth() / 3);

View File

@ -25,7 +25,7 @@ public:
bool IsArmored(void) const;
// cEntity overrides
virtual bool Initialize(cWorld * a_World) override;
virtual bool Initialize(cWorld & a_World) override;
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;

View File

@ -60,6 +60,9 @@ static void SetThreadName(DWORD dwThreadID, const char * threadName)
cIsThread::cIsThread(const AString & iThreadName) :
m_ShouldTerminate(false),
m_ThreadName(iThreadName),
#ifdef _WIN32
m_ThreadID(0),
#endif
m_Handle(NULL_HANDLE)
{
}
@ -83,8 +86,8 @@ bool cIsThread::Start(void)
ASSERT(m_Handle == NULL_HANDLE); // Has already started one thread?
#ifdef _WIN32
// Create the thread suspended, so that the mHandle variable is valid in the thread procedure
DWORD ThreadID = 0;
m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &ThreadID);
m_ThreadID = 0;
m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &m_ThreadID);
if (m_Handle == NULL)
{
LOGERROR("ERROR: Could not create thread \"%s\", GLE = %d!", m_ThreadName.c_str(), GetLastError());
@ -96,7 +99,7 @@ bool cIsThread::Start(void)
// Thread naming is available only in MSVC
if (!m_ThreadName.empty())
{
SetThreadName(ThreadID, m_ThreadName.c_str());
SetThreadName(m_ThreadID, m_ThreadName.c_str());
}
#endif // _DEBUG and _MSC_VER
@ -177,3 +180,15 @@ unsigned long cIsThread::GetCurrentID(void)
bool cIsThread::IsCurrentThread(void) const
{
#ifdef _WIN32
return (GetCurrentThreadId() == m_ThreadID);
#else
return (m_Handle == pthread_self());
#endif
}

View File

@ -48,6 +48,9 @@ public:
/// Returns the OS-dependent thread ID for the caller's thread
static unsigned long GetCurrentID(void);
/** Returns true if the thread calling this function is the thread contained within this object. */
bool IsCurrentThread(void) const;
protected:
AString m_ThreadName;
@ -60,6 +63,7 @@ protected:
#ifdef _WIN32
DWORD m_ThreadID;
HANDLE m_Handle;
static DWORD __stdcall thrExecute(LPVOID a_Param)

View File

@ -100,7 +100,7 @@ public:
virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0;
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) = 0;
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) = 0;
virtual void SendRespawn (void) = 0;
virtual void SendRespawn (const cWorld & a_World) = 0;
virtual void SendExperience (void) = 0;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) = 0;
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) = 0;

View File

@ -133,7 +133,8 @@ typedef unsigned char Byte;
cProtocol125::cProtocol125(cClientHandle * a_Client) :
super(a_Client),
m_ReceivedData(32 KiB)
m_ReceivedData(32 KiB),
m_LastSentDimension(dimNotSet)
{
}
@ -591,6 +592,7 @@ void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
WriteByte (0); // Unused
WriteByte (60); // Client list width or something
Flush();
m_LastSentDimension = a_World.GetDimension();
}
@ -831,16 +833,23 @@ void cProtocol125::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effect
void cProtocol125::SendRespawn(void)
void cProtocol125::SendRespawn(const cWorld & a_World)
{
cCSLock Lock(m_CSPacket);
if (m_LastSentDimension == a_World.GetDimension())
{
// Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do
return;
}
cPlayer * Player = m_Client->GetPlayer();
WriteByte (PACKET_RESPAWN);
WriteInt ((int)(Player->GetWorld()->GetDimension()));
WriteInt (a_World.GetDimension());
WriteByte (2); // TODO: Difficulty; 2 = Normal
WriteChar ((char)Player->GetGameMode());
WriteShort (256); // Current world height
WriteString("default");
Flush();
m_LastSentDimension = a_World.GetDimension();
}

View File

@ -72,7 +72,7 @@ public:
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
virtual void SendRespawn (void) override;
virtual void SendRespawn (const cWorld & a_World) override;
virtual void SendExperience (void) override;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
@ -113,6 +113,10 @@ protected:
cByteBuffer m_ReceivedData; ///< Buffer for the received data
AString m_Username; ///< Stored in ParseHandshake(), compared to Login username
/** The dimension that was last sent to a player in a Respawn or Login packet.
Used to avoid Respawning into the same dimension, which confuses the client. */
eDimension m_LastSentDimension;
virtual void SendData(const char * a_Data, size_t a_Size) override;

View File

@ -253,7 +253,7 @@ void cProtocol132::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
WriteByte (0); // Unused, used to be world height
WriteByte (8); // Client list width or something
Flush();
m_LastSentDimension = a_World.GetDimension();
SendCompass(a_World);
}

View File

@ -158,10 +158,10 @@ void cProtocol161::SendPlayerMaxSpeed(void)
void cProtocol161::SendRespawn(void)
void cProtocol161::SendRespawn(const cWorld & a_World)
{
// Besides sending the respawn, we need to also send the player max speed, otherwise the client reverts to super-fast
super::SendRespawn();
super::SendRespawn(a_World);
SendPlayerMaxSpeed();
}

View File

@ -42,7 +42,7 @@ protected:
virtual void SendGameMode (eGameMode a_GameMode) override;
virtual void SendHealth (void) override;
virtual void SendPlayerMaxSpeed(void) override;
virtual void SendRespawn (void) override;
virtual void SendRespawn (const cWorld & a_World) override;
virtual void SendWindowOpen (const cWindow & a_Window) override;
virtual int ParseEntityAction (void) override;

View File

@ -92,7 +92,8 @@ cProtocol172::cProtocol172(cClientHandle * a_Client, const AString & a_ServerAdd
m_ReceivedData(32 KiB),
m_OutPacketBuffer(64 KiB),
m_OutPacketLenBuffer(20), // 20 bytes is more than enough for one VarInt
m_IsEncrypted(false)
m_IsEncrypted(false),
m_LastSentDimension(dimNotSet)
{
// Create the comm log file, if so requested:
if (g_ShouldLogCommIn || g_ShouldLogCommOut)
@ -656,6 +657,7 @@ void cProtocol172::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
Pkt.WriteByte(std::min(Server->GetMaxPlayers(), 60));
Pkt.WriteString("default"); // Level type - wtf?
}
m_LastSentDimension = a_World.GetDimension();
// Send the spawn position:
{
@ -984,14 +986,21 @@ void cProtocol172::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effect
void cProtocol172::SendRespawn(void)
void cProtocol172::SendRespawn(const cWorld & a_World)
{
if (m_LastSentDimension == a_World.GetDimension())
{
// Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do
return;
}
cPacketizer Pkt(*this, 0x07); // Respawn packet
cPlayer * Player = m_Client->GetPlayer();
Pkt.WriteInt(Player->GetWorld()->GetDimension());
Pkt.WriteInt(a_World.GetDimension());
Pkt.WriteByte(2); // TODO: Difficulty (set to Normal)
Pkt.WriteByte((Byte)Player->GetEffectiveGameMode());
Pkt.WriteString("default");
m_LastSentDimension = a_World.GetDimension();
}

View File

@ -104,7 +104,7 @@ public:
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
virtual void SendRespawn (void) override;
virtual void SendRespawn (const cWorld & a_World) override;
virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8
virtual void SendExperience (void) override;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
@ -244,6 +244,10 @@ protected:
/** The logfile where the comm is logged, when g_ShouldLogComm is true */
cFile m_CommLogFile;
/** The dimension that was last sent to a player in a Respawn or Login packet.
Used to avoid Respawning into the same dimension, which confuses the client. */
eDimension m_LastSentDimension;
/** Adds the received (unencrypted) data to m_ReceivedData, parses complete packets */
void AddReceivedData(const char * a_Data, size_t a_Size);

View File

@ -555,10 +555,10 @@ void cProtocolRecognizer::SendRemoveEntityEffect(const cEntity & a_Entity, int a
void cProtocolRecognizer::SendRespawn(void)
void cProtocolRecognizer::SendRespawn(const cWorld & a_World)
{
ASSERT(m_Protocol != NULL);
m_Protocol->SendRespawn();
m_Protocol->SendRespawn(a_World);
}

View File

@ -107,7 +107,7 @@ public:
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
virtual void SendRespawn (void) override;
virtual void SendRespawn (const cWorld & a_World) override;
virtual void SendExperience (void) override;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;

View File

@ -60,7 +60,7 @@ void cSandSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChun
);
*/
cFallingBlock * FallingBlock = new cFallingBlock(Pos, BlockType, a_Chunk->GetMeta(itr->x, itr->y, itr->z));
FallingBlock->Initialize(&m_World);
FallingBlock->Initialize(m_World);
a_Chunk->SetBlock(itr->x, itr->y, itr->z, E_BLOCK_AIR, 0);
}
}

View File

@ -754,6 +754,9 @@ void cWorld::Tick(float a_Dt, int a_LastTickDurationMSec)
m_EntitiesToAdd.clear();
}
// Add players waiting in the queue to be added:
AddQueuedPlayers();
m_ChunkMap->Tick(a_Dt);
TickClients(a_Dt);
@ -1642,7 +1645,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double
a_BlockX, a_BlockY, a_BlockZ,
*itr, IsPlayerCreated, SpeedX, SpeedY, SpeedZ
);
Pickup->Initialize(this);
Pickup->Initialize(*this);
}
}
@ -1663,7 +1666,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double
a_BlockX, a_BlockY, a_BlockZ,
*itr, IsPlayerCreated, (float)a_SpeedX, (float)a_SpeedY, (float)a_SpeedZ
);
Pickup->Initialize(this);
Pickup->Initialize(*this);
}
}
@ -1674,7 +1677,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double
int cWorld::SpawnFallingBlock(int a_X, int a_Y, int a_Z, BLOCKTYPE BlockType, NIBBLETYPE BlockMeta)
{
cFallingBlock * FallingBlock = new cFallingBlock(Vector3i(a_X, a_Y, a_Z), BlockType, BlockMeta);
FallingBlock->Initialize(this);
FallingBlock->Initialize(*this);
return FallingBlock->GetUniqueID();
}
@ -1690,7 +1693,7 @@ int cWorld::SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward)
}
cExpOrb * ExpOrb = new cExpOrb(a_X, a_Y, a_Z, a_Reward);
ExpOrb->Initialize(this);
ExpOrb->Initialize(*this);
return ExpOrb->GetUniqueID();
}
@ -1713,7 +1716,7 @@ int cWorld::SpawnMinecart(double a_X, double a_Y, double a_Z, int a_MinecartType
return -1;
}
} // switch (a_MinecartType)
Minecart->Initialize(this);
Minecart->Initialize(*this);
return Minecart->GetUniqueID();
}
@ -1724,7 +1727,7 @@ int cWorld::SpawnMinecart(double a_X, double a_Y, double a_Z, int a_MinecartType
void cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, int a_FuseTicks, double a_InitialVelocityCoeff)
{
cTNTEntity * TNT = new cTNTEntity(a_X, a_Y, a_Z, a_FuseTicks);
TNT->Initialize(this);
TNT->Initialize(*this);
TNT->SetSpeed(
a_InitialVelocityCoeff * (GetTickRandomNumber(2) - 1), /** -1, 0, 1 */
a_InitialVelocityCoeff * 2,
@ -2240,7 +2243,7 @@ void cWorld::SetChunkData(
// Initialize the entities (outside the m_ChunkMap's CS, to fix FS #347):
for (cEntityList::iterator itr = a_Entities.begin(), end = a_Entities.end(); itr != end; ++itr)
{
(*itr)->Initialize(this);
(*itr)->Initialize(*this);
}
// If a client is requesting this chunk, send it to them:
@ -2333,23 +2336,8 @@ void cWorld::CollectPickupsByPlayer(cPlayer * a_Player)
void cWorld::AddPlayer(cPlayer * a_Player)
{
{
cCSLock Lock(m_CSPlayers);
ASSERT(std::find(m_Players.begin(), m_Players.end(), a_Player) == m_Players.end()); // Is it already in the list? HOW?
m_Players.remove(a_Player); // Make sure the player is registered only once
m_Players.push_back(a_Player);
}
// Add the player's client to the list of clients to be ticked:
if (a_Player->GetClientHandle() != NULL)
{
cCSLock Lock(m_CSClients);
m_ClientsToAdd.push_back(a_Player->GetClientHandle());
}
// The player has already been added to the chunkmap as the entity, do NOT add again!
cCSLock Lock(m_CSPlayersToAdd);
m_PlayersToAdd.push_back(a_Player);
}
@ -2358,17 +2346,26 @@ void cWorld::AddPlayer(cPlayer * a_Player)
void cWorld::RemovePlayer(cPlayer * a_Player)
{
m_ChunkMap->RemoveEntity(a_Player);
{
cCSLock Lock(m_CSPlayersToAdd);
m_PlayersToAdd.remove(a_Player);
}
{
cCSLock Lock(m_CSPlayers);
LOGD("Removing player \"%s\" from world \"%s\".", a_Player->GetName().c_str(), m_WorldName.c_str());
m_Players.remove(a_Player);
}
// Remove the player's client from the list of clients to be ticked:
if (a_Player->GetClientHandle() != NULL)
cClientHandle * Client = a_Player->GetClientHandle();
if (Client != NULL)
{
Client->RemoveFromWorld();
m_ChunkMap->RemoveClientFromChunks(Client);
cCSLock Lock(m_CSClients);
m_ClientsToRemove.push_back(a_Player->GetClientHandle());
m_ClientsToRemove.push_back(Client);
}
}
@ -2977,7 +2974,7 @@ int cWorld::SpawnMobFinalize(cMonster * a_Monster)
delete a_Monster;
return -1;
}
if (!a_Monster->Initialize(this))
if (!a_Monster->Initialize(*this))
{
delete a_Monster;
return -1;
@ -2999,7 +2996,7 @@ int cWorld::CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProje
{
return -1;
}
if (!Projectile->Initialize(this))
if (!Projectile->Initialize(*this))
{
delete Projectile;
return -1;
@ -3129,6 +3126,63 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c
void cWorld::AddQueuedPlayers(void)
{
ASSERT(m_TickThread.IsCurrentThread());
// Grab the list of players to add, it has to be locked to access it:
cPlayerList PlayersToAdd;
{
cCSLock Lock(m_CSPlayersToAdd);
std::swap(PlayersToAdd, m_PlayersToAdd);
}
// Add all the players in the grabbed list:
{
cCSLock Lock(m_CSPlayers);
for (cPlayerList::iterator itr = PlayersToAdd.begin(), end = PlayersToAdd.end(); itr != end; ++itr)
{
ASSERT(std::find(m_Players.begin(), m_Players.end(), *itr) == m_Players.end()); // Is it already in the list? HOW?
m_Players.push_back(*itr);
(*itr)->SetWorld(this);
// Add to chunkmap, if not already there (Spawn vs MoveToWorld):
if (!m_ChunkMap->HasEntity((*itr)->GetUniqueID()))
{
m_ChunkMap->AddEntity(*itr);
}
} // for itr - PlayersToAdd[]
} // Lock(m_CSPlayers)
// Add all the players' clienthandles:
{
cCSLock Lock(m_CSClients);
for (cPlayerList::iterator itr = PlayersToAdd.begin(), end = PlayersToAdd.end(); itr != end; ++itr)
{
cClientHandle * Client = (*itr)->GetClientHandle();
if (Client != NULL)
{
m_Clients.push_back(Client);
}
} // for itr - PlayersToAdd[]
} // Lock(m_CSClients)
// Stream chunks to all eligible clients:
for (cPlayerList::iterator itr = PlayersToAdd.begin(), end = PlayersToAdd.end(); itr != end; ++itr)
{
cClientHandle * Client = (*itr)->GetClientHandle();
if (Client != NULL)
{
Client->StreamChunks();
}
} // for itr - PlayersToAdd[]
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorld::cTaskSaveAllChunks:
@ -3269,4 +3323,3 @@ void cWorld::cChunkGeneratorCallbacks::CallHookChunkGenerated (cChunkDesc & a_Ch

View File

@ -284,8 +284,15 @@ public:
void CollectPickupsByPlayer(cPlayer * a_Player);
void AddPlayer( cPlayer* a_Player );
void RemovePlayer( cPlayer* a_Player );
/** Adds the player to the world.
Uses a queue to store the player object until the Tick thread processes the addition event.
Also adds the player as an entity in the chunkmap, and the player's ClientHandle, if any, for ticking. */
void AddPlayer(cPlayer * a_Player);
/** Removes the player from the world.
Removes the player from the addition queue, too, if appropriate.
If the player has a ClientHandle, the ClientHandle is removed from all chunks in the world and will not be ticked by this world anymore. */
void RemovePlayer(cPlayer * a_Player);
/** Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true */
virtual bool ForEachPlayer(cPlayerListCallback & a_Callback) override; // >> EXPORTED IN MANUALBINDINGS <<
@ -933,6 +940,12 @@ private:
/** List of entities that are scheduled for adding, waiting for the Tick thread to add them. */
cEntityList m_EntitiesToAdd;
/** Guards m_PlayersToAdd */
cCriticalSection m_CSPlayersToAdd;
/** List of players that are scheduled for adding, waiting for the Tick thread to add them. */
cPlayerList m_PlayersToAdd;
cWorld(const AString & a_WorldName);
virtual ~cWorld();
@ -970,6 +983,10 @@ private:
/** Creates a new redstone simulator.*/
cRedstoneSimulator * InitializeRedstoneSimulator(cIniFile & a_IniFile);
/** Adds the players queued in the m_PlayersToAdd queue into the m_Players list.
Assumes it is called from the Tick thread. */
void AddQueuedPlayers(void);
}; // tolua_export