1
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, dimNether = -1,
dimOverworld = 0, dimOverworld = 0,
dimEnd = 1, 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 // Send experience
m_Player->SendExperience(); m_Player->SendExperience();
m_Player->Initialize(World); m_Player->Initialize(*World);
m_State = csAuthenticated; m_State = csAuthenticated;
// Query player team // Query player team
@ -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: // Remove all associated chunks:
cChunkCoordsList 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) 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); m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ);
} // for itr - Chunks[] } // 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 SendPlayerSpawn (const cPlayer & a_Player);
void SendPluginMessage (const AString & a_Channel, const AString & a_Message); // Exported in ManualBindings.cpp void SendPluginMessage (const AString & a_Channel, const AString & a_Message); // Exported in ManualBindings.cpp
void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID); void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID);
void SendRespawn (void); void SendRespawn (const cWorld & a_World);
void SendExperience (void); void SendExperience (void);
void SendExperienceOrb (const cExpOrb & a_ExpOrb); void SendExperienceOrb (const cExpOrb & a_ExpOrb);
void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode); 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); void SendData(const char * a_Data, size_t a_Size);
/** Called when the player moves into a different world. /** Called when the player moves into a different world.
Locks the current world, doesn't lock the new world. */ Sends an UnloadChunk packet for each loaded chunk and resets the streamed chunks. */
void MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket); void RemoveFromWorld(void);
/** Called when the player will enchant a Item */ /** Called when the player will enchant a Item */
void HandleEnchantItem(Byte & WindowID, Byte & Enchantment); 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; return false;
} }
@ -144,13 +144,13 @@ bool cEntity::Initialize(cWorld * a_World)
*/ */
m_IsInitialized = true; m_IsInitialized = true;
m_World = a_World; m_World = &a_World;
m_World->AddEntity(this); m_World->AddEntity(this);
cPluginManager::Get()->CallHookSpawnedEntity(*a_World, *this); cPluginManager::Get()->CallHookSpawnedEntity(a_World, *this);
// Spawn the entity on the clients: // Spawn the entity on the clients:
a_World->BroadcastSpawnEntity(*this); a_World.BroadcastSpawnEntity(*this);
return true; 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); cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height);
virtual ~cEntity(); virtual ~cEntity();
/// Spawns the entity in the world; returns true if spawned, false if not (plugin disallowed) /** Spawns the entity in the world; returns true if spawned, false if not (plugin disallowed).
virtual bool Initialize(cWorld * a_World); Adds the entity to the world. */
virtual bool Initialize(cWorld & a_World);
// tolua_begin // tolua_begin

View File

@ -940,6 +940,8 @@ void cPlayer::Killed(cEntity * a_Victim)
void cPlayer::Respawn(void) void cPlayer::Respawn(void)
{ {
ASSERT(m_World != NULL);
m_Health = GetMaxHealth(); m_Health = GetMaxHealth();
SetInvulnerableTicks(20); SetInvulnerableTicks(20);
@ -952,7 +954,7 @@ void cPlayer::Respawn(void)
m_LifetimeTotalXp = 0; m_LifetimeTotalXp = 0;
// ToDo: send score to client? How? // ToDo: send score to client? How?
m_ClientHandle->SendRespawn(); m_ClientHandle->SendRespawn(*m_World);
// Extinguish the fire: // Extinguish the fire:
StopBurning(); StopBurning();
@ -1583,19 +1585,19 @@ bool cPlayer::MoveToWorld(const char * a_WorldName)
return false; 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 // Remove all links to the old world
m_World->RemovePlayer(this); m_World->RemovePlayer(this);
m_ClientHandle->RemoveFromAllChunks();
m_World->RemoveEntity(this);
// If the dimension is different, we can send the respawn packet // 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 // 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 // Queue adding player to the new world, including all the necessary adjustments to the object
World->AddEntity(this);
World->AddPlayer(this); World->AddPlayer(this);
return true; return true;

View File

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

View File

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

View File

@ -231,7 +231,7 @@ public:
else 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)); 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()); a_Player->SetIsFishing(true, Floater->GetUniqueID());
} }
return true; return true;

View File

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

View File

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

View File

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

View File

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

View File

@ -81,7 +81,7 @@ void cSkeleton::Attack(float a_Dt)
{ {
return; return;
} }
if (!Arrow->Initialize(m_World)) if (!Arrow->Initialize(*m_World))
{ {
delete Arrow; delete Arrow;
return; 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() // Set health before BroadcastSpawnEntity()
SetHealth(GetMaxHealth() / 3); SetHealth(GetMaxHealth() / 3);

View File

@ -25,7 +25,7 @@ public:
bool IsArmored(void) const; bool IsArmored(void) const;
// cEntity overrides // 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 void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) 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) : cIsThread::cIsThread(const AString & iThreadName) :
m_ShouldTerminate(false), m_ShouldTerminate(false),
m_ThreadName(iThreadName), m_ThreadName(iThreadName),
#ifdef _WIN32
m_ThreadID(0),
#endif
m_Handle(NULL_HANDLE) m_Handle(NULL_HANDLE)
{ {
} }
@ -83,8 +86,8 @@ bool cIsThread::Start(void)
ASSERT(m_Handle == NULL_HANDLE); // Has already started one thread? ASSERT(m_Handle == NULL_HANDLE); // Has already started one thread?
#ifdef _WIN32 #ifdef _WIN32
// Create the thread suspended, so that the mHandle variable is valid in the thread procedure // Create the thread suspended, so that the mHandle variable is valid in the thread procedure
DWORD ThreadID = 0; m_ThreadID = 0;
m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &ThreadID); m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &m_ThreadID);
if (m_Handle == NULL) if (m_Handle == NULL)
{ {
LOGERROR("ERROR: Could not create thread \"%s\", GLE = %d!", m_ThreadName.c_str(), GetLastError()); 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 // Thread naming is available only in MSVC
if (!m_ThreadName.empty()) if (!m_ThreadName.empty())
{ {
SetThreadName(ThreadID, m_ThreadName.c_str()); SetThreadName(m_ThreadID, m_ThreadName.c_str());
} }
#endif // _DEBUG and _MSC_VER #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 /// Returns the OS-dependent thread ID for the caller's thread
static unsigned long GetCurrentID(void); 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: protected:
AString m_ThreadName; AString m_ThreadName;
@ -60,6 +63,7 @@ protected:
#ifdef _WIN32 #ifdef _WIN32
DWORD m_ThreadID;
HANDLE m_Handle; HANDLE m_Handle;
static DWORD __stdcall thrExecute(LPVOID a_Param) static DWORD __stdcall thrExecute(LPVOID a_Param)

View File

@ -100,7 +100,7 @@ public:
virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0; virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0;
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) = 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 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 SendExperience (void) = 0;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) = 0; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) = 0;
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) = 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) : cProtocol125::cProtocol125(cClientHandle * a_Client) :
super(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 (0); // Unused
WriteByte (60); // Client list width or something WriteByte (60); // Client list width or something
Flush(); 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); 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(); cPlayer * Player = m_Client->GetPlayer();
WriteByte (PACKET_RESPAWN); WriteByte (PACKET_RESPAWN);
WriteInt ((int)(Player->GetWorld()->GetDimension())); WriteInt (a_World.GetDimension());
WriteByte (2); // TODO: Difficulty; 2 = Normal WriteByte (2); // TODO: Difficulty; 2 = Normal
WriteChar ((char)Player->GetGameMode()); WriteChar ((char)Player->GetGameMode());
WriteShort (256); // Current world height WriteShort (256); // Current world height
WriteString("default"); 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 SendPlayerSpawn (const cPlayer & a_Player) override;
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) 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 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 SendExperience (void) override;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
@ -114,6 +114,10 @@ protected:
AString m_Username; ///< Stored in ParseHandshake(), compared to Login username 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; virtual void SendData(const char * a_Data, size_t a_Size) override;
/// Sends the Handshake packet /// Sends the Handshake packet

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 (0); // Unused, used to be world height
WriteByte (8); // Client list width or something WriteByte (8); // Client list width or something
Flush(); Flush();
m_LastSentDimension = a_World.GetDimension();
SendCompass(a_World); 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 // 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(); SendPlayerMaxSpeed();
} }

View File

@ -42,7 +42,7 @@ protected:
virtual void SendGameMode (eGameMode a_GameMode) override; virtual void SendGameMode (eGameMode a_GameMode) override;
virtual void SendHealth (void) override; virtual void SendHealth (void) override;
virtual void SendPlayerMaxSpeed(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 void SendWindowOpen (const cWindow & a_Window) override;
virtual int ParseEntityAction (void) 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_ReceivedData(32 KiB),
m_OutPacketBuffer(64 KiB), m_OutPacketBuffer(64 KiB),
m_OutPacketLenBuffer(20), // 20 bytes is more than enough for one VarInt 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: // Create the comm log file, if so requested:
if (g_ShouldLogCommIn || g_ShouldLogCommOut) 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.WriteByte(std::min(Server->GetMaxPlayers(), 60));
Pkt.WriteString("default"); // Level type - wtf? Pkt.WriteString("default"); // Level type - wtf?
} }
m_LastSentDimension = a_World.GetDimension();
// Send the spawn position: // 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 cPacketizer Pkt(*this, 0x07); // Respawn packet
cPlayer * Player = m_Client->GetPlayer(); 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(2); // TODO: Difficulty (set to Normal)
Pkt.WriteByte((Byte)Player->GetEffectiveGameMode()); Pkt.WriteByte((Byte)Player->GetEffectiveGameMode());
Pkt.WriteString("default"); 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 SendPlayerSpawn (const cPlayer & a_Player) override;
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) 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 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 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 SendExperience (void) override;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) 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 */ /** The logfile where the comm is logged, when g_ShouldLogComm is true */
cFile m_CommLogFile; 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 */ /** Adds the received (unencrypted) data to m_ReceivedData, parses complete packets */
void AddReceivedData(const char * a_Data, size_t a_Size); 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); 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 SendPlayerSpawn (const cPlayer & a_Player) override;
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) 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 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 SendExperience (void) override;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) 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)); 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); 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(); m_EntitiesToAdd.clear();
} }
// Add players waiting in the queue to be added:
AddQueuedPlayers();
m_ChunkMap->Tick(a_Dt); m_ChunkMap->Tick(a_Dt);
TickClients(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, a_BlockX, a_BlockY, a_BlockZ,
*itr, IsPlayerCreated, SpeedX, SpeedY, SpeedZ *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, a_BlockX, a_BlockY, a_BlockZ,
*itr, IsPlayerCreated, (float)a_SpeedX, (float)a_SpeedY, (float)a_SpeedZ *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) 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); cFallingBlock * FallingBlock = new cFallingBlock(Vector3i(a_X, a_Y, a_Z), BlockType, BlockMeta);
FallingBlock->Initialize(this); FallingBlock->Initialize(*this);
return FallingBlock->GetUniqueID(); 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); cExpOrb * ExpOrb = new cExpOrb(a_X, a_Y, a_Z, a_Reward);
ExpOrb->Initialize(this); ExpOrb->Initialize(*this);
return ExpOrb->GetUniqueID(); return ExpOrb->GetUniqueID();
} }
@ -1713,7 +1716,7 @@ int cWorld::SpawnMinecart(double a_X, double a_Y, double a_Z, int a_MinecartType
return -1; return -1;
} }
} // switch (a_MinecartType) } // switch (a_MinecartType)
Minecart->Initialize(this); Minecart->Initialize(*this);
return Minecart->GetUniqueID(); 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) 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); cTNTEntity * TNT = new cTNTEntity(a_X, a_Y, a_Z, a_FuseTicks);
TNT->Initialize(this); TNT->Initialize(*this);
TNT->SetSpeed( TNT->SetSpeed(
a_InitialVelocityCoeff * (GetTickRandomNumber(2) - 1), /** -1, 0, 1 */ a_InitialVelocityCoeff * (GetTickRandomNumber(2) - 1), /** -1, 0, 1 */
a_InitialVelocityCoeff * 2, a_InitialVelocityCoeff * 2,
@ -2240,7 +2243,7 @@ void cWorld::SetChunkData(
// Initialize the entities (outside the m_ChunkMap's CS, to fix FS #347): // 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) 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: // 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) void cWorld::AddPlayer(cPlayer * a_Player)
{ {
{ cCSLock Lock(m_CSPlayersToAdd);
cCSLock Lock(m_CSPlayers); m_PlayersToAdd.push_back(a_Player);
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!
} }
@ -2358,17 +2346,26 @@ void cWorld::AddPlayer(cPlayer * a_Player)
void cWorld::RemovePlayer(cPlayer * a_Player) void cWorld::RemovePlayer(cPlayer * a_Player)
{ {
m_ChunkMap->RemoveEntity(a_Player); m_ChunkMap->RemoveEntity(a_Player);
{
cCSLock Lock(m_CSPlayersToAdd);
m_PlayersToAdd.remove(a_Player);
}
{ {
cCSLock Lock(m_CSPlayers); 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); m_Players.remove(a_Player);
} }
// Remove the player's client from the list of clients to be ticked: // 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); 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; delete a_Monster;
return -1; return -1;
} }
if (!a_Monster->Initialize(this)) if (!a_Monster->Initialize(*this))
{ {
delete a_Monster; delete a_Monster;
return -1; return -1;
@ -2999,7 +2996,7 @@ int cWorld::CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProje
{ {
return -1; return -1;
} }
if (!Projectile->Initialize(this)) if (!Projectile->Initialize(*this))
{ {
delete Projectile; delete Projectile;
return -1; 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: // cWorld::cTaskSaveAllChunks:
@ -3269,4 +3323,3 @@ void cWorld::cChunkGeneratorCallbacks::CallHookChunkGenerated (cChunkDesc & a_Ch

View File

@ -284,7 +284,14 @@ public:
void CollectPickupsByPlayer(cPlayer * a_Player); void CollectPickupsByPlayer(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); 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); 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 */ /** Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true */
@ -933,6 +940,12 @@ private:
/** List of entities that are scheduled for adding, waiting for the Tick thread to add them. */ /** List of entities that are scheduled for adding, waiting for the Tick thread to add them. */
cEntityList m_EntitiesToAdd; 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); cWorld(const AString & a_WorldName);
virtual ~cWorld(); virtual ~cWorld();
@ -970,6 +983,10 @@ private:
/** Creates a new redstone simulator.*/ /** Creates a new redstone simulator.*/
cRedstoneSimulator * InitializeRedstoneSimulator(cIniFile & a_IniFile); 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 }; // tolua_export