From f7b8a301f8a705cec6df17c7051164169cf619a6 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Wed, 3 Jul 2013 07:47:35 +0000 Subject: Fixed player moving between worlds. Fixes FS #407. Also fixes a few possible deadlocks between SocketThreads and TickThread git-svn-id: http://mc-server.googlecode.com/svn/trunk@1641 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/ClientHandle.cpp | 67 +++++++++++++++++++++++++++++++++++++++---------- source/ClientHandle.h | 10 ++++++++ source/Player.cpp | 49 ++++++++++++++++++++++-------------- source/Player.h | 2 +- source/World.cpp | 2 ++ 5 files changed, 97 insertions(+), 33 deletions(-) diff --git a/source/ClientHandle.cpp b/source/ClientHandle.cpp index 8e031a124..9a84d23f5 100644 --- a/source/ClientHandle.cpp +++ b/source/ClientHandle.cpp @@ -406,6 +406,11 @@ void cClientHandle::RemoveFromAllChunks() cCSLock Lock(m_CSChunkLists); m_LoadedChunks.clear(); m_ChunksToSend.clear(); + + // Also reset the LastStreamedChunk coords to bogus coords, + // so that all chunks are streamed in subsequent StreamChunks() call (FS #407) + m_LastStreamedChunkX = 0x7fffffff; + m_LastStreamedChunkZ = 0x7fffffff; } } @@ -898,18 +903,11 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, c void cClientHandle::HandleChat(const AString & a_Message) { - AString Message(a_Message); - if (!cRoot::Get()->GetServer()->Command(*this, Message)) - { - AString Msg; - Printf(Msg, "<%s%s%s> %s", - m_Player->GetColor().c_str(), - m_Player->GetName().c_str(), - cChatColor::White.c_str(), - Message.c_str() - ); - m_Player->GetWorld()->BroadcastChat(Msg); - } + // We need to process messages in the Tick thread, to avoid deadlocks resulting from player-commands being processed + // in the SocketThread and waiting for acquiring the ChunkMap CS with Plugin CS locked + + cCSLock Lock(m_CSMessages); + m_PendingMessages.push_back(a_Message); } @@ -1292,8 +1290,8 @@ void cClientHandle::Tick(float a_Dt) m_ShouldCheckDownloaded = false; } + // Send a ping packet: cTimer t1; - // Send ping packet if ( (m_Player != NULL) && // Is logged in? (m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime()) @@ -1324,6 +1322,9 @@ void cClientHandle::Tick(float a_Dt) m_CurrentExplosionTick = (m_CurrentExplosionTick + 1) % ARRAYCOUNT(m_NumExplosionsPerTick); m_RunningSumExplosions -= m_NumExplosionsPerTick[m_CurrentExplosionTick]; m_NumExplosionsPerTick[m_CurrentExplosionTick] = 0; + + // Process the queued messages: + ProcessPendingMessages(); } @@ -1944,6 +1945,46 @@ void cClientHandle::AddWantedChunk(int a_ChunkX, int a_ChunkZ) +void cClientHandle::ProcessPendingMessages(void) +{ + while (true) + { + AString Message; + + // Extract one message from the PendingMessages buffer: + { + cCSLock Lock(m_CSMessages); + if (m_PendingMessages.empty()) + { + // No more messages in the buffer, bail out + return; + } + Message = m_PendingMessages.front(); + m_PendingMessages.pop_front(); + } // Lock(m_CSMessages) + + // If a command, perform it: + if (cRoot::Get()->GetServer()->Command(*this, Message)) + { + continue; + } + + // Not a command, broadcast as a simple message: + AString Msg; + Printf(Msg, "<%s%s%s> %s", + m_Player->GetColor().c_str(), + m_Player->GetName().c_str(), + cChatColor::White.c_str(), + Message.c_str() + ); + m_Player->GetWorld()->BroadcastChat(Msg); + } // while (true) +} + + + + + void cClientHandle::PacketBufferFull(void) { // Too much data in the incoming queue, the server is probably too busy, kick the client: diff --git a/source/ClientHandle.h b/source/ClientHandle.h index cd0ca1ee6..c02924ebf 100644 --- a/source/ClientHandle.h +++ b/source/ClientHandle.h @@ -185,6 +185,7 @@ public: bool HandleLogin(int a_ProtocolVersion, const AString & a_Username); void SendData(const char * a_Data, int a_Size); + private: int m_ViewDistance; // Number of chunks the player can see in each direction; 4 is the minimum ( http://wiki.vg/Protocol_FAQ#.E2.80.A6all_connecting_clients_spasm_and_jerk_uncontrollably.21 ) @@ -270,6 +271,12 @@ private: /// Running sum of m_NumExplosionsPerTick[] int m_RunningSumExplosions; + /// Lock for the m_PendingMessages buffer + cCriticalSection m_CSMessages; + + /// Buffer for received messages to be processed in the Tick thread + AStringList m_PendingMessages; + /// Returns true if the rate block interactions is within a reasonable limit (bot protection) @@ -292,6 +299,9 @@ private: /// Handles the block placing packet when it is a real block placement (not block-using, item-using or eating) void HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler); + + /// Processes the messages in m_PendingMessages; called from the Tick thread + void ProcessPendingMessages(void); // cSocketThreads::cCallback overrides: virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client diff --git a/source/Player.cpp b/source/Player.cpp index 84d38f13b..865ff24d6 100644 --- a/source/Player.cpp +++ b/source/Player.cpp @@ -117,9 +117,9 @@ cPlayer::~cPlayer(void) -void cPlayer::Initialize( cWorld* a_World ) +void cPlayer::Initialize(cWorld * a_World) { - cPawn::Initialize( a_World ); + super::Initialize(a_World); GetWorld()->AddPlayer(this); } @@ -891,26 +891,37 @@ void cPlayer::TossItem( -bool cPlayer::MoveToWorld( const char* a_WorldName ) +bool cPlayer::MoveToWorld(const char * a_WorldName) { - cWorld * World = cRoot::Get()->GetWorld( a_WorldName ); - if ( World ) + cWorld * World = cRoot::Get()->GetWorld(a_WorldName); + if (World == NULL) { - /* Remove all links to the old world */ - m_World->RemovePlayer( this ); - m_ClientHandle->RemoveFromAllChunks(); - m_World->RemoveEntity(this); - - /* Add player to all the necessary parts of the new world */ - SetWorld( World ); - GetWorld()->AddPlayer(this); - - m_ClientHandle->HandleRespawn(); - m_ClientHandle->StreamChunks(); - return true; + LOG("%s: Couldn't find world \"%s\".", a_WorldName); + return false; } - - return false; + + eDimension OldDimension = m_World->GetDimension(); + + // Remove all links to the old world + m_World->RemovePlayer(this); + m_ClientHandle->RemoveFromAllChunks(); + m_World->RemoveEntity(this); + + // Add player to all the necessary parts of the new world + SetWorld(World); + World->AddEntity(this); + World->AddPlayer(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 + if (OldDimension != World->GetDimension()) + { + m_ClientHandle->SendRespawn(); + } + + // Stream the new chunks: + m_ClientHandle->StreamChunks(); + return true; } diff --git a/source/Player.h b/source/Player.h index f36545a4c..639b2595b 100644 --- a/source/Player.h +++ b/source/Player.h @@ -145,7 +145,7 @@ public: void SetVisible( bool a_bVisible ); // tolua_export bool IsVisible(void) const { return m_bVisible; } // tolua_export - bool MoveToWorld(const char * a_WorldName ); // tolua_export + bool MoveToWorld(const char * a_WorldName); // tolua_export bool SaveToDisk(void); bool LoadFromDisk(void); diff --git a/source/World.cpp b/source/World.cpp index 4333aa7a4..ac7ba42aa 100644 --- a/source/World.cpp +++ b/source/World.cpp @@ -1843,6 +1843,8 @@ void cWorld::AddPlayer(cPlayer * a_Player) m_Players.remove(a_Player); // Make sure the player is registered only once m_Players.push_back(a_Player); + + // The player has already been added to the chunkmap as the entity, do NOT add again! } -- cgit v1.2.3