1
0

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
This commit is contained in:
madmaxoft@gmail.com 2013-07-03 07:47:35 +00:00
parent 2f8eebaad1
commit f7b8a301f8
5 changed files with 96 additions and 32 deletions

View File

@ -406,6 +406,11 @@ void cClientHandle::RemoveFromAllChunks()
cCSLock Lock(m_CSChunkLists); cCSLock Lock(m_CSChunkLists);
m_LoadedChunks.clear(); m_LoadedChunks.clear();
m_ChunksToSend.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) void cClientHandle::HandleChat(const AString & a_Message)
{ {
AString Message(a_Message); // We need to process messages in the Tick thread, to avoid deadlocks resulting from player-commands being processed
if (!cRoot::Get()->GetServer()->Command(*this, Message)) // in the SocketThread and waiting for acquiring the ChunkMap CS with Plugin CS locked
{
AString Msg; cCSLock Lock(m_CSMessages);
Printf(Msg, "<%s%s%s> %s", m_PendingMessages.push_back(a_Message);
m_Player->GetColor().c_str(),
m_Player->GetName().c_str(),
cChatColor::White.c_str(),
Message.c_str()
);
m_Player->GetWorld()->BroadcastChat(Msg);
}
} }
@ -1292,8 +1290,8 @@ void cClientHandle::Tick(float a_Dt)
m_ShouldCheckDownloaded = false; m_ShouldCheckDownloaded = false;
} }
// Send a ping packet:
cTimer t1; cTimer t1;
// Send ping packet
if ( if (
(m_Player != NULL) && // Is logged in? (m_Player != NULL) && // Is logged in?
(m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime()) (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_CurrentExplosionTick = (m_CurrentExplosionTick + 1) % ARRAYCOUNT(m_NumExplosionsPerTick);
m_RunningSumExplosions -= m_NumExplosionsPerTick[m_CurrentExplosionTick]; m_RunningSumExplosions -= m_NumExplosionsPerTick[m_CurrentExplosionTick];
m_NumExplosionsPerTick[m_CurrentExplosionTick] = 0; 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) void cClientHandle::PacketBufferFull(void)
{ {
// Too much data in the incoming queue, the server is probably too busy, kick the client: // Too much data in the incoming queue, the server is probably too busy, kick the client:

View File

@ -185,6 +185,7 @@ public:
bool HandleLogin(int a_ProtocolVersion, const AString & a_Username); bool HandleLogin(int a_ProtocolVersion, const AString & a_Username);
void SendData(const char * a_Data, int a_Size); void SendData(const char * a_Data, int a_Size);
private: 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 ) 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[] /// Running sum of m_NumExplosionsPerTick[]
int m_RunningSumExplosions; 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) /// Returns true if the rate block interactions is within a reasonable limit (bot protection)
@ -293,6 +300,9 @@ private:
/// Handles the block placing packet when it is a real block placement (not block-using, item-using or eating) /// 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); 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: // cSocketThreads::cCallback overrides:
virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client
virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client

View File

@ -119,7 +119,7 @@ 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); GetWorld()->AddPlayer(this);
} }
@ -894,23 +894,34 @@ void cPlayer::TossItem(
bool cPlayer::MoveToWorld(const char * a_WorldName) bool cPlayer::MoveToWorld(const char * a_WorldName)
{ {
cWorld * World = cRoot::Get()->GetWorld(a_WorldName); cWorld * World = cRoot::Get()->GetWorld(a_WorldName);
if ( World ) if (World == NULL)
{ {
/* Remove all links to the old world */ LOG("%s: Couldn't find world \"%s\".", a_WorldName);
return false;
}
eDimension OldDimension = m_World->GetDimension();
// Remove all links to the old world
m_World->RemovePlayer(this); m_World->RemovePlayer(this);
m_ClientHandle->RemoveFromAllChunks(); m_ClientHandle->RemoveFromAllChunks();
m_World->RemoveEntity(this); m_World->RemoveEntity(this);
/* Add player to all the necessary parts of the new world */ // Add player to all the necessary parts of the new world
SetWorld(World); SetWorld(World);
GetWorld()->AddPlayer(this); World->AddEntity(this);
World->AddPlayer(this);
m_ClientHandle->HandleRespawn(); // If the dimension is different, we can send the respawn packet
m_ClientHandle->StreamChunks(); // http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02
return true; if (OldDimension != World->GetDimension())
{
m_ClientHandle->SendRespawn();
} }
return false; // Stream the new chunks:
m_ClientHandle->StreamChunks();
return true;
} }

View File

@ -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.remove(a_Player); // Make sure the player is registered only once
m_Players.push_back(a_Player); m_Players.push_back(a_Player);
// The player has already been added to the chunkmap as the entity, do NOT add again!
} }