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:
parent
2f8eebaad1
commit
f7b8a301f8
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user