Fixed deadlock when moving players to other worlds.
Fixes #1039, fixes #851
This commit is contained in:
parent
b904223b9d
commit
af4a21ea06
@ -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
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -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
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ public:
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!Arrow->Initialize(a_Player->GetWorld()))
|
||||
if (!Arrow->Initialize(*a_Player->GetWorld()))
|
||||
{
|
||||
delete Arrow;
|
||||
return;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -70,7 +70,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
} // switch (m_ItemType)
|
||||
Minecart->Initialize(a_World);
|
||||
Minecart->Initialize(*a_World);
|
||||
|
||||
if (!a_Player->IsGameModeCreative())
|
||||
{
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -44,7 +44,7 @@ void cBlaze::Attack(float a_Dt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!FireCharge->Initialize(m_World))
|
||||
if (!FireCharge->Initialize(*m_World))
|
||||
{
|
||||
delete FireCharge;
|
||||
return;
|
||||
|
@ -46,7 +46,7 @@ void cGhast::Attack(float a_Dt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!GhastBall->Initialize(m_World))
|
||||
if (!GhastBall->Initialize(*m_World))
|
||||
{
|
||||
delete GhastBall;
|
||||
return;
|
||||
|
@ -81,7 +81,7 @@ void cSkeleton::Attack(float a_Dt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!Arrow->Initialize(m_World))
|
||||
if (!Arrow->Initialize(*m_World))
|
||||
{
|
||||
delete Arrow;
|
||||
return;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
@ -114,6 +114,10 @@ protected:
|
||||
|
||||
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;
|
||||
|
||||
/// Sends the Handshake packet
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
111
src/World.cpp
111
src/World.cpp
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
17
src/World.h
17
src/World.h
@ -284,7 +284,14 @@ public:
|
||||
|
||||
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);
|
||||
|
||||
/** 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 */
|
||||
@ -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
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user