summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--source/cChunk.cpp10
-rw-r--r--source/cChunk.h26
-rw-r--r--source/cChunkGenerator.h3
-rw-r--r--source/cChunkMap.cpp144
-rw-r--r--source/cChunkMap.h17
-rw-r--r--source/cClientHandle.cpp5
-rw-r--r--source/cEntity.cpp83
-rw-r--r--source/cEntity.h4
-rw-r--r--source/cMonster.cpp36
-rw-r--r--source/cPawn.cpp10
-rw-r--r--source/cPlayer.cpp65
-rw-r--r--source/cWorld.cpp67
-rw-r--r--source/cWorld.h16
13 files changed, 372 insertions, 114 deletions
diff --git a/source/cChunk.cpp b/source/cChunk.cpp
index 827e97cb9..9f64716d0 100644
--- a/source/cChunk.cpp
+++ b/source/cChunk.cpp
@@ -1148,7 +1148,7 @@ bool cChunk::HasAnyClients(void)
-void cChunk::AddEntity( cEntity * a_Entity )
+void cChunk::AddEntity( cEntity * a_Entity)
{
cCSLock Lock(m_CSEntities);
if (a_Entity->GetEntityType() != cEntity::E_PLAYER)
@@ -1171,9 +1171,13 @@ void cChunk::RemoveEntity(cEntity * a_Entity)
m_Entities.remove(a_Entity);
SizeAfter = m_Entities.size();
}
- if ((a_Entity->GetEntityType() != cEntity::E_PLAYER) && (SizeBefore != SizeAfter))
+ if (SizeBefore != SizeAfter)
{
- MarkDirty();
+ // Mark as dirty if it was a server-generated entity:
+ if (a_Entity->GetEntityType() != cEntity::E_PLAYER)
+ {
+ MarkDirty();
+ }
}
}
diff --git a/source/cChunk.h b/source/cChunk.h
index 4453ed16a..289461f0d 100644
--- a/source/cChunk.h
+++ b/source/cChunk.h
@@ -70,6 +70,23 @@ public:
+/** Interface class used for comparing clients of two chunks.
+Used primarily for entity moving while both chunks are locked.
+*/
+class cClientDiffCallback
+{
+public:
+ /// Called for clients that are in Chunk1 and not in Chunk2,
+ virtual void Removed(cClientHandle * a_Client) = 0;
+
+ /// Called for clients that are in Chunk2 and not in Chunk1.
+ virtual void Added(cClientHandle * a_Client) = 0;
+} ;
+
+
+
+
+
struct sSetBlock
{
int x, y, z;
@@ -121,7 +138,7 @@ public:
/// Returns true if there is a block entity at the coords specified
bool HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ);
-
+
void Tick(float a_Dt, MTRand & a_TickRandom);
int GetPosX() { return m_PosX; }
@@ -149,7 +166,7 @@ public:
bool HasClient (cClientHandle* a_Client );
bool HasAnyClients(void); // Returns true if theres any client in the chunk; false otherwise
- void AddEntity( cEntity * a_Entity );
+ void AddEntity( cEntity * a_Entity);
void RemoveEntity( cEntity * a_Entity);
void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); // [x, y, z] in world block coords
@@ -206,6 +223,8 @@ public:
private:
+ friend class cChunkMap;
+
bool m_IsValid; // True if the chunk is loaded / generated
bool m_IsDirty; // True if the chunk has changed since it was last saved
bool m_IsSaving; // True if the chunk is being saved
@@ -251,6 +270,9 @@ private:
void SpreadLightOfBlockZ(char* a_LightBuffer, int a_X, int a_Y, int a_Z);
void CreateBlockEntities(void);
+
+ // Makes a copy of the list
+ cClientHandleList GetAllClients(void) const {return m_LoadedByClient; }
};
typedef std::tr1::shared_ptr<cChunk> cChunkPtr;
diff --git a/source/cChunkGenerator.h b/source/cChunkGenerator.h
index cb30636c3..de1b081d5 100644
--- a/source/cChunkGenerator.h
+++ b/source/cChunkGenerator.h
@@ -6,7 +6,8 @@
// The object takes requests for generating chunks and processes them in a separate thread one by one.
// The requests are not added to the queue if there is already a request with the same coords
// Before generating, the thread checks if the chunk hasn't been already generated.
-// It is theoretically possible to have multiple generator threads by having multiple instances of this object (if the cChunkPtr is thread-safe)
+// It is theoretically possible to have multiple generator threads by having multiple instances of this object
+// but then it MAY happen that the chunk is generated twice
// If the generator queue is overloaded, the generator skips chunks with no clients in them
diff --git a/source/cChunkMap.cpp b/source/cChunkMap.cpp
index 10577dc6f..6ae3fd987 100644
--- a/source/cChunkMap.cpp
+++ b/source/cChunkMap.cpp
@@ -6,6 +6,7 @@
#include "cWorld.h"
#include "cRoot.h"
#include "cMakeDir.h"
+#include "cPlayer.h"
#ifndef _WIN32
#include <cstdlib> // abs
@@ -15,9 +16,6 @@
#include <json/json.h>
-#define USE_MEMCPY
-
-
@@ -136,6 +134,24 @@ cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ )
+void cChunkMap::BroadcastToChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cPacket & a_Packet, cClientHandle * a_Exclude)
+{
+ // Broadcasts a_Packet to all clients in the chunk where block [x, y, z] is, except to client a_Exclude
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ);
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->Broadcast(a_Packet, a_Exclude);
+}
+
+
+
+
+
void cChunkMap::BroadcastToChunkOfBlock(int a_X, int a_Y, int a_Z, cPacket * a_Packet, cClientHandle * a_Exclude)
{
// Broadcasts a_Packet to all clients in the chunk where block [x, y, z] is, except to client a_Exclude
@@ -143,13 +159,13 @@ void cChunkMap::BroadcastToChunkOfBlock(int a_X, int a_Y, int a_Z, cPacket * a_P
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
BlockToChunk(a_X, a_Y, a_Z, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if (Chunk == NULL)
{
return;
}
- // Packets can be broadcasted even to invalid chunks!
- Chunk->Broadcast(a_Packet);
+ // It's perfectly legal to broadcast packets even to invalid chunks!
+ Chunk->Broadcast(a_Packet, a_Exclude);
}
@@ -402,6 +418,122 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList)
+void cChunkMap::CollectPickupsByPlayer(cPlayer * a_Player)
+{
+ int BlockX = (int)(a_Player->GetPosX()); // Truncating doesn't matter much; we're scanning entire chunks anyway
+ int BlockY = (int)(a_Player->GetPosY());
+ int BlockZ = (int)(a_Player->GetPosZ());
+ int ChunkX, ChunkZ, ChunkY = ZERO_CHUNK_Y;
+ AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
+ int OtherChunkX = ChunkX + ((BlockX > 8) ? 1 : -1);
+ int OtherChunkZ = ChunkZ + ((BlockZ > 8) ? 1 : -1);
+
+ cCSLock Lock(m_CSLayers);
+ GetChunkNoGen(ChunkX, ChunkY, ChunkZ)->CollectPickupsByPlayer(a_Player);
+
+ // Check the neighboring chunks as well:
+ GetChunkNoGen(OtherChunkX, ChunkY, ChunkZ )->CollectPickupsByPlayer(a_Player);
+ GetChunkNoGen(OtherChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player);
+ GetChunkNoGen(ChunkX, ChunkY, ChunkZ )->CollectPickupsByPlayer(a_Player);
+ GetChunkNoGen(ChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player);
+}
+
+
+
+
+
+void cChunkMap::CompareChunkClients(int a_ChunkX1, int a_ChunkY1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkY2, int a_ChunkZ2, cClientDiffCallback & a_Callback)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk1 = GetChunkNoGen(a_ChunkX1, a_ChunkY1, a_ChunkZ1);
+ if (Chunk1 == NULL)
+ {
+ return;
+ }
+ cChunkPtr Chunk2 = GetChunkNoGen(a_ChunkX2, a_ChunkY2, a_ChunkZ2);
+ if (Chunk2 == NULL)
+ {
+ return;
+ }
+
+ cClientHandleList Clients1(Chunk1->GetAllClients());
+ cClientHandleList Clients2(Chunk2->GetAllClients());
+
+ // Find "removed" clients:
+ for (cClientHandleList::iterator itr1 = Clients1.begin(); itr1 != Clients1.end(); ++itr1)
+ {
+ bool Found = false;
+ for (cClientHandleList::iterator itr2 = Clients2.begin(); itr2 != Clients2.end(); ++itr2)
+ {
+ if (*itr1 == *itr2)
+ {
+ Found = true;
+ break;
+ }
+ } // for itr2 - Clients2[]
+ if (!Found)
+ {
+ a_Callback.Removed(*itr1);
+ }
+ } // for itr1 - Clients1[]
+
+ // Find "added" clients:
+ for (cClientHandleList::iterator itr2 = Clients2.begin(); itr2 != Clients2.end(); ++itr2)
+ {
+ bool Found = false;
+ for (cClientHandleList::iterator itr1 = Clients1.begin(); itr1 != Clients1.end(); ++itr1)
+ {
+ if (*itr1 == *itr2)
+ {
+ Found = true;
+ break;
+ }
+ } // for itr1 - Clients1[]
+ if (!Found)
+ {
+ a_Callback.Added(*itr2);
+ }
+ } // for itr2 - Clients2[]
+}
+
+
+
+
+
+void cChunkMap::MoveEntityToChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr OldChunk = GetChunkNoGen(a_Entity->GetChunkX(), a_Entity->GetChunkY(), a_Entity->GetChunkZ());
+ if (OldChunk != NULL)
+ {
+ OldChunk->RemoveEntity(a_Entity);
+ }
+ cChunkPtr NewChunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ);
+ if (NewChunk != NULL)
+ {
+ NewChunk->AddEntity(a_Entity);
+ }
+}
+
+
+
+
+
+void cChunkMap::RemoveEntityFromChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return;
+ }
+ Chunk->RemoveEntity(a_Entity);
+}
+
+
+
+
+
void cChunkMap::Tick( float a_Dt, MTRand & a_TickRandom )
{
cCSLock Lock(m_CSLayers);
diff --git a/source/cChunkMap.h b/source/cChunkMap.h
index a9cb4d661..33e468eb1 100644
--- a/source/cChunkMap.h
+++ b/source/cChunkMap.h
@@ -32,9 +32,14 @@ public:
cChunkPtr GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ ); // Also queues the chunk for loading if not valid; doesn't generate
// Direct action methods:
+ /// Broadcast a_Packet to all clients in the chunk specified
+ void BroadcastToChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cPacket & a_Packet, cClientHandle * a_Exclude = NULL);
+
/// Broadcasts a_Packet to all clients in the chunk where block [x, y, z] is, except to client a_Exclude
void BroadcastToChunkOfBlock(int a_X, int a_Y, int a_Z, cPacket * a_Packet, cClientHandle * a_Exclude = NULL);
- void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); // a_Player rclked block entity at the coords specified, handle it
+
+ /// a_Player rclked block entity at the coords specified, handle it
+ void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z);
void MarkChunkDirty (int a_ChunkX, int a_ChunkY, int a_ChunkZ);
void MarkChunkSaving (int a_ChunkX, int a_ChunkY, int a_ChunkZ);
@@ -48,6 +53,16 @@ public:
void SpreadChunkLighting(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
int GetHeight (int a_BlockX, int a_BlockZ);
void FastSetBlocks (sSetBlockList & a_BlockList);
+ void CollectPickupsByPlayer(cPlayer * a_Player);
+
+ /// Compares clients of two chunks, calls the callback accordingly
+ void CompareChunkClients(int a_ChunkX1, int a_ChunkY1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkY2, int a_ChunkZ2, cClientDiffCallback & a_Callback);
+
+ /// Moves the entity from its current chunk to the new chunk specified
+ void MoveEntityToChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+
+ /// Removes the entity from the chunk specified
+ void RemoveEntityFromChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ);
void Tick( float a_Dt, MTRand & a_TickRand );
diff --git a/source/cClientHandle.cpp b/source/cClientHandle.cpp
index e3662f5ed..51cd31433 100644
--- a/source/cClientHandle.cpp
+++ b/source/cClientHandle.cpp
@@ -66,6 +66,7 @@
#include "packets/cPacket_Ping.h"
#include "packets/cPacket_PlayerListItem.h"
#include "packets/cPacket_NamedEntitySpawn.h"
+#include "packets/cPacket_MapChunk.h"
@@ -1784,7 +1785,9 @@ void cClientHandle::CheckIfWorldDownloaded(void)
void cClientHandle::SendConfirmPosition(void)
{
- LOG("Spawning player \"%s\"", m_Username.c_str());
+ LOG("Spawning player \"%s\" at {%.2f, %.2f, %.2f}",
+ m_Username.c_str(), m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ()
+ );
m_State = csConfirmingPos;
diff --git a/source/cEntity.cpp b/source/cEntity.cpp
index 00faf5baa..49993c093 100644
--- a/source/cEntity.cpp
+++ b/source/cEntity.cpp
@@ -106,27 +106,60 @@ void cEntity::MoveToCorrectChunk(bool a_bIgnoreOldChunk)
return;
}
- if (!a_bIgnoreOldChunk)
+ class cMover :
+ public cClientDiffCallback
{
- cChunkPtr OldChunk = m_World->GetChunk(m_ChunkX, m_ChunkY, m_ChunkZ);
- OldChunk->RemoveEntity(this);
- cPacket_DestroyEntity DestroyEntity( this );
- OldChunk->Broadcast(DestroyEntity);
- }
+ virtual void Removed(cClientHandle * a_Client) override
+ {
+ if (m_IgnoreOldChunk)
+ {
+ return;
+ }
+ if (m_Destroy == NULL)
+ {
+ m_Destroy = new cPacket_DestroyEntity(m_Entity);
+ }
+ a_Client->Send(m_Destroy);
+ }
+
+ virtual void Added(cClientHandle * a_Client) override
+ {
+ if (m_Spawn == NULL)
+ {
+ m_Spawn = m_Entity->GetSpawnPacket(); // Only create the packet when needed
+ }
+ if (m_Spawn != NULL)
+ {
+ a_Client->Send(m_Spawn);
+ }
+ }
+ cPacket * m_Destroy;
+ cPacket * m_Spawn;
+ bool m_IgnoreOldChunk;
+ cEntity * m_Entity;
+
+ public:
+ cMover(cEntity * a_Entity, bool a_IgnoreOldChunk) :
+ m_Destroy(NULL),
+ m_Spawn(NULL),
+ m_IgnoreOldChunk(a_IgnoreOldChunk),
+ m_Entity(a_Entity)
+ {}
+
+ ~cMover()
+ {
+ delete m_Spawn;
+ delete m_Destroy;
+ }
+ } Mover(this, a_bIgnoreOldChunk);
+
+ m_World->CompareChunkClients(m_ChunkX, m_ChunkY, m_ChunkZ, ChunkX, ChunkY, ChunkZ, Mover);
+ m_World->MoveEntityToChunk(this, ChunkX, ChunkY, ChunkZ);
+
m_ChunkX = ChunkX;
m_ChunkY = ChunkY;
m_ChunkZ = ChunkZ;
- cChunkPtr NewChunk = m_World->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
- if ( NewChunk != NULL )
- {
- NewChunk->AddEntity( this );
- std::auto_ptr<cPacket> SpawnPacket(GetSpawnPacket());
- if (SpawnPacket.get() != NULL)
- {
- NewChunk->Broadcast(SpawnPacket.get());
- }
- }
}
@@ -151,19 +184,13 @@ void cEntity::Destroy()
void cEntity::RemoveFromChunk(void)
{
- if ( m_World == NULL )
+ if (m_World == NULL)
{
return;
}
- cChunkPtr Chunk = m_World->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
- if ( Chunk != NULL )
- {
- cPacket_DestroyEntity DestroyEntity( this );
- Chunk->Broadcast( DestroyEntity );
- Chunk->RemoveEntity( this );
- m_bRemovedFromChunk = true;
- }
+ m_World->RemoveEntityFromChunk(this, m_ChunkX, m_ChunkY, m_ChunkZ);
+ m_bRemovedFromChunk = true;
}
@@ -180,11 +207,7 @@ void cEntity::SpawnOn(cClientHandle * a_Client)
if (a_Client == NULL)
{
- cChunkPtr Chunk = m_World->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
- if ( Chunk != NULL )
- {
- Chunk->Broadcast(SpawnPacket.get());
- }
+ m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, *SpawnPacket.get(), NULL);
}
else
{
diff --git a/source/cEntity.h b/source/cEntity.h
index 84d9c766d..0165276b6 100644
--- a/source/cEntity.h
+++ b/source/cEntity.h
@@ -79,6 +79,10 @@ public: //tolua_export
float GetPitch (void) const {return m_Rot.y; } //tolua_export
float GetRoll (void) const {return m_Rot.z; } //tolua_export
Vector3f GetLookVector(); //tolua_export
+
+ int GetChunkX(void) const {return m_ChunkX; }
+ int GetChunkY(void) const {return m_ChunkY; }
+ int GetChunkZ(void) const {return m_ChunkZ; }
void SetPosX( const double & a_PosX ); //tolua_export
void SetPosY( const double & a_PosY ); //tolua_export
diff --git a/source/cMonster.cpp b/source/cMonster.cpp
index 18baa6a87..6fa8561e5 100644
--- a/source/cMonster.cpp
+++ b/source/cMonster.cpp
@@ -206,31 +206,27 @@ void cMonster::Tick(float a_Dt)
void cMonster::ReplicateMovement()
{
- cChunkPtr InChunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
- if ( !InChunk->IsValid() )
- {
- return;
- }
-
if(m_bDirtyOrientation && !m_bDirtyPosition)
{
cPacket_EntityLook EntityLook( this );
- InChunk->Broadcast( EntityLook );
+ m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, EntityLook );
m_bDirtyOrientation = false;
}
+
if( m_bDirtyPosition )
{
-
float DiffX = (float)(GetPosX() - m_LastPosX );
float DiffY = (float)(GetPosY() - m_LastPosY );
float DiffZ = (float)(GetPosZ() - m_LastPosZ );
float SqrDist = DiffX*DiffX + DiffY*DiffY + DiffZ*DiffZ;
- if( SqrDist > 4*4 // 4 blocks is max Relative Move
- || cWorld::GetTime() - m_TimeLastTeleportPacket > 2 ) // Send an absolute position every 2 seconds
+ if (
+ (SqrDist > 4 * 4) // 4 blocks is max Relative Move
+ || (cWorld::GetTime() - m_TimeLastTeleportPacket > 2 ) // Send an absolute position every 2 seconds
+ )
{
//LOG("Teleported %f", sqrtf(SqrDist) );
cPacket_TeleportEntity TeleportEntity( this );
- InChunk->Broadcast( TeleportEntity );
+ m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, TeleportEntity);
m_TimeLastTeleportPacket = cWorld::GetTime();
}
else
@@ -239,21 +235,21 @@ void cMonster::ReplicateMovement()
{
cPacket_RelativeEntityMoveLook RelativeEntityMoveLook;
RelativeEntityMoveLook.m_UniqueID = GetUniqueID();
- RelativeEntityMoveLook.m_MoveX = (char)(DiffX*32);
- RelativeEntityMoveLook.m_MoveY = (char)(DiffY*32);
- RelativeEntityMoveLook.m_MoveZ = (char)(DiffZ*32);
- RelativeEntityMoveLook.m_Yaw = (char)((GetRotation()/360.f)*256);
+ RelativeEntityMoveLook.m_MoveX = (char)(DiffX*32);
+ RelativeEntityMoveLook.m_MoveY = (char)(DiffY*32);
+ RelativeEntityMoveLook.m_MoveZ = (char)(DiffZ*32);
+ RelativeEntityMoveLook.m_Yaw = (char)((GetRotation()/360.f)*256);
RelativeEntityMoveLook.m_Pitch = (char)((GetPitch()/360.f)*256);
- InChunk->Broadcast( RelativeEntityMoveLook );
+ m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, RelativeEntityMoveLook );
}
else
{
cPacket_RelativeEntityMove RelativeEntityMove;
RelativeEntityMove.m_UniqueID = GetUniqueID();
- RelativeEntityMove.m_MoveX = (char)(DiffX*32);
- RelativeEntityMove.m_MoveY = (char)(DiffY*32);
- RelativeEntityMove.m_MoveZ = (char)(DiffZ*32);
- InChunk->Broadcast( RelativeEntityMove );
+ RelativeEntityMove.m_MoveX = (char)(DiffX*32);
+ RelativeEntityMove.m_MoveY = (char)(DiffY*32);
+ RelativeEntityMove.m_MoveZ = (char)(DiffZ*32);
+ m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, RelativeEntityMove );
}
}
m_LastPosX = GetPosX();
diff --git a/source/cPawn.cpp b/source/cPawn.cpp
index ff4c957e8..ed16f57bb 100644
--- a/source/cPawn.cpp
+++ b/source/cPawn.cpp
@@ -80,8 +80,7 @@ void cPawn::TakeDamage( int a_Damage, cEntity* a_Instigator )
cPacket_EntityStatus Status;
Status.m_UniqueID = GetUniqueID();
Status.m_Status = cPacket_EntityStatus::STATUS_TAKEDAMAGE;
- cChunkPtr Chunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
- Chunk->Broadcast( Status );
+ m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, Status);
if (m_Health <= 0)
{
@@ -105,8 +104,7 @@ void cPawn::KilledBy( cEntity* a_Killer )
cPacket_EntityStatus Status;
Status.m_UniqueID = GetUniqueID();
Status.m_Status = cPacket_EntityStatus::STATUS_DIE;
- cChunkPtr Chunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
- Chunk->Broadcast( Status ); // Die
+ m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, Status);
}
@@ -152,12 +150,10 @@ void cPawn::Tick(float a_Dt)
void cPawn::SetMetaData(MetaData a_MetaData)
{
- cChunkPtr InChunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
-
//Broadcast new status to clients in the chunk
m_MetaData = a_MetaData;
cPacket_Metadata md(a_MetaData, GetUniqueID());
- InChunk->Broadcast(md);
+ m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, md);
}
diff --git a/source/cPlayer.cpp b/source/cPlayer.cpp
index f6a0d1fcc..495cc6abf 100644
--- a/source/cPlayer.cpp
+++ b/source/cPlayer.cpp
@@ -101,15 +101,27 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
m_Pos.x = cRoot::Get()->GetDefaultWorld()->GetSpawnX();
m_Pos.y = cRoot::Get()->GetDefaultWorld()->GetSpawnY();
m_Pos.z = cRoot::Get()->GetDefaultWorld()->GetSpawnZ();
+
+ LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}",
+ a_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z
+ );
}
}
+
+
+
+
void cPlayer::Initialize( cWorld* a_World )
{
cPawn::Initialize( a_World );
GetWorld()->AddPlayer( this );
}
+
+
+
+
cPlayer::~cPlayer(void)
{
SaveToDisk();
@@ -135,6 +147,10 @@ cPlayer::~cPlayer(void)
cPacket * cPlayer::GetSpawnPacket(void) const
{
+ LOGD("cPlayer::GetSpawnPacket for \"%s\" at pos {%.2f, %.2f, %.2f}",
+ m_pState->PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z
+ );
+
if (!m_bVisible )
{
return NULL;
@@ -159,14 +175,12 @@ cPacket * cPlayer::GetSpawnPacket(void) const
void cPlayer::Tick(float a_Dt)
{
- cChunkPtr InChunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
-
cPawn::Tick(a_Dt);
if (m_bDirtyOrientation && !m_bDirtyPosition)
{
cPacket_EntityLook EntityLook( this );
- InChunk->Broadcast( EntityLook, m_ClientHandle );
+ m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, EntityLook, m_ClientHandle );
m_bDirtyOrientation = false;
}
else if (m_bDirtyPosition )
@@ -184,7 +198,7 @@ void cPlayer::Tick(float a_Dt)
{
//LOG("Teleported %f", sqrtf(SqrDist) );
cPacket_TeleportEntity TeleportEntity( this );
- InChunk->Broadcast( TeleportEntity, m_ClientHandle );
+ m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, TeleportEntity, m_ClientHandle);
m_TimeLastTeleportPacket = cWorld::GetTime();
}
else
@@ -198,7 +212,7 @@ void cPlayer::Tick(float a_Dt)
RelativeEntityMoveLook.m_MoveZ = (char)(DiffZ*32);
RelativeEntityMoveLook.m_Yaw = (char)((GetRotation()/360.f)*256);
RelativeEntityMoveLook.m_Pitch = (char)((GetPitch()/360.f)*256);
- InChunk->Broadcast( RelativeEntityMoveLook, m_ClientHandle );
+ m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, RelativeEntityMoveLook, m_ClientHandle );
}
else
{
@@ -207,7 +221,7 @@ void cPlayer::Tick(float a_Dt)
RelativeEntityMove.m_MoveX = (char)(DiffX*32);
RelativeEntityMove.m_MoveY = (char)(DiffY*32);
RelativeEntityMove.m_MoveZ = (char)(DiffZ*32);
- InChunk->Broadcast( RelativeEntityMove, m_ClientHandle );
+ m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, RelativeEntityMove, m_ClientHandle );
}
}
m_LastPosX = GetPosX();
@@ -217,10 +231,9 @@ void cPlayer::Tick(float a_Dt)
m_ClientHandle->StreamChunks();
}
- if( m_Health > 0 ) // make sure player is alive
+ if ( m_Health > 0 ) // make sure player is alive
{
- // TODO: Don't only check in current chunks, but also close chunks (chunks within range)
- GetWorld()->GetChunk(m_ChunkX, m_ChunkY, m_ChunkZ)->CollectPickupsByPlayer(this);
+ m_World->CollectPickupsByPlayer(this);
}
cTimer t1;
@@ -521,6 +534,8 @@ void cPlayer::TeleportTo( const double & a_PosX, const double & a_PosY, const do
void cPlayer::MoveTo( const Vector3d & a_NewPos )
{
// TODO: should do some checks to see if player is not moving through terrain
+ // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too
+
SetPosition( a_NewPos );
}
@@ -539,11 +554,7 @@ void cPlayer::SetVisible( bool a_bVisible )
{
m_bVisible = false;
cPacket_DestroyEntity DestroyEntity( this );
- cChunkPtr Chunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
- if ( Chunk != NULL )
- {
- Chunk->Broadcast( DestroyEntity ); // Destroy on all clients
- }
+ m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, DestroyEntity ); // Destroy on all clients
}
}
@@ -560,6 +571,10 @@ void cPlayer::AddToGroup( const char* a_GroupName )
ResolvePermissions();
}
+
+
+
+
bool cPlayer::CanUseCommand( const char* a_Command )
{
for( GroupList::iterator itr = m_pState->Groups.begin(); itr != m_pState->Groups.end(); ++itr )
@@ -753,12 +768,7 @@ bool cPlayer::MoveToWorld( const char* a_WorldName )
/* Remove all links to the old world */
m_World->RemovePlayer( this );
m_ClientHandle->RemoveFromAllChunks();
- cChunkPtr Chunk = m_World->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
- if ( Chunk != NULL )
- {
- Chunk->RemoveEntity( this );
- Chunk->Broadcast( cPacket_DestroyEntity( this ) ); // Remove player entity from all clients in old world
- }
+ m_World->RemoveEntityFromChunk(this, m_ChunkX, m_ChunkY, m_ChunkZ);
/* Add player to all the necessary parts of the new world */
SetWorld( World );
@@ -831,11 +841,8 @@ bool cPlayer::LoadFromDisk()
return false;
}
- // Get file size
- long FileSize = f.GetSize();
-
- std::auto_ptr<char> buffer(new char[FileSize]);
- if (f.Read(buffer.get(), FileSize) != FileSize)
+ AString buffer;
+ if (f.ReadRestOfFile(buffer) != f.GetSize())
{
LOGERROR("ERROR READING FROM FILE \"%s\"", SourceFile.c_str());
return false;
@@ -844,12 +851,10 @@ bool cPlayer::LoadFromDisk()
Json::Value root;
Json::Reader reader;
- if (!reader.parse(buffer.get(), root, false))
+ if (!reader.parse(buffer, root, false))
{
LOGERROR("ERROR WHILE PARSING JSON FROM FILE %s", SourceFile.c_str());
}
-
- buffer.reset();
Json::Value & JSON_PlayerPosition = root["position"];
if( JSON_PlayerPosition.size() == 3 )
@@ -874,6 +879,10 @@ bool cPlayer::LoadFromDisk()
m_pState->LoadedWorldName = root.get("world", "world").asString();
+ LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"",
+ m_pState->PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z, m_pState->LoadedWorldName.c_str()
+ );
+
return true;
}
diff --git a/source/cWorld.cpp b/source/cWorld.cpp
index f5e40ec0b..83f36f1d5 100644
--- a/source/cWorld.cpp
+++ b/source/cWorld.cpp
@@ -431,7 +431,12 @@ void cWorld::InitializeSpawn()
int ChunkX = 0, ChunkY = 0, ChunkZ = 0;
BlockToChunk( (int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ, ChunkX, ChunkY, ChunkZ );
+ // For the debugging builds, don't make the server build too much world upon start:
+ #ifdef _DEBUG
+ int ViewDist = 9;
+ #else
int ViewDist = 20; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is
+ #endif // _DEBUG
LOG("Preparing spawn area in world \"%s\"", m_WorldName.c_str());
for (int x = 0; x < ViewDist; x++)
@@ -807,16 +812,6 @@ void cWorld::GrowTree( int a_X, int a_Y, int a_Z )
-void cWorld::UnloadUnusedChunks()
-{
- m_LastUnload = m_Time;
- m_ChunkMap->UnloadUnusedChunks();
-}
-
-
-
-
-
cChunkPtr cWorld::GetChunkOfBlock( int a_X, int a_Y, int a_Z )
{
int ChunkX, ChunkY, ChunkZ;
@@ -1005,6 +1000,15 @@ void cWorld::Broadcast( const cPacket & a_Packet, cClientHandle* a_Exclude)
+void cWorld::BroadcastToChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cPacket & a_Packet, cClientHandle * a_Exclude)
+{
+ m_ChunkMap->BroadcastToChunk(a_ChunkX, a_ChunkY, a_ChunkZ, a_Packet, a_Exclude);
+}
+
+
+
+
+
void cWorld::BroadcastToChunkOfBlock(int a_X, int a_Y, int a_Z, cPacket * a_Packet, cClientHandle * a_Exclude)
{
m_ChunkMap->BroadcastToChunkOfBlock(a_X, a_Y, a_Z, a_Packet, a_Exclude);
@@ -1095,6 +1099,25 @@ bool cWorld::HasChunkAnyClients(int a_ChunkX, int a_ChunkY, int a_ChunkZ) const
+void cWorld::UnloadUnusedChunks(void )
+{
+ m_LastUnload = m_Time;
+ m_ChunkMap->UnloadUnusedChunks();
+}
+
+
+
+
+
+void cWorld::CollectPickupsByPlayer(cPlayer * a_Player)
+{
+ m_ChunkMap->CollectPickupsByPlayer(a_Player);
+}
+
+
+
+
+
void cWorld::SetMaxPlayers(int iMax)
{
m_MaxPlayers = MAX_PLAYERS;
@@ -1271,10 +1294,27 @@ bool cWorld::DoWithEntity( int a_UniqueID, cEntityCallback & a_Callback )
-void cWorld::RemoveEntityFromChunk(cEntity * a_Entity)
+void cWorld::RemoveEntityFromChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ m_ChunkMap->RemoveEntityFromChunk(a_Entity, a_ChunkX, a_ChunkY, a_ChunkZ);
+}
+
+
+
+
+
+void cWorld::MoveEntityToChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+{
+ m_ChunkMap->MoveEntityToChunk(a_Entity, a_ChunkX, a_ChunkY, a_ChunkZ);
+}
+
+
+
+
+
+void cWorld::CompareChunkClients(int a_ChunkX1, int a_ChunkY1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkY2, int a_ChunkZ2, cClientDiffCallback & a_Callback)
{
- cChunkPtr Chunk = GetChunkOfBlock((int)(a_Entity->GetPosX()), (int)(a_Entity->GetPosY()), (int)(a_Entity->GetPosZ()));
- Chunk->RemoveEntity(a_Entity);
+ m_ChunkMap->CompareChunkClients(a_ChunkX1, a_ChunkY1, a_ChunkZ1, a_ChunkX2, a_ChunkY2, a_ChunkZ2, a_Callback);
}
@@ -1286,7 +1326,6 @@ void cWorld::SaveAllChunks()
LOG("Saving all chunks...");
m_LastSave = m_Time;
m_ChunkMap->SaveAllChunks();
- LOG("Done saving chunks");
}
diff --git a/source/cWorld.h b/source/cWorld.h
index a9c068336..8a1cf729d 100644
--- a/source/cWorld.h
+++ b/source/cWorld.h
@@ -68,6 +68,8 @@ public:
void Broadcast( const cPacket & a_Packet, cClientHandle* a_Exclude = 0 );
+ void BroadcastToChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cPacket & a_Packet, cClientHandle * a_Exclude = NULL);
+
void BroadcastToChunkOfBlock(int a_X, int a_Y, int a_Z, cPacket * a_Packet, cClientHandle * a_Exclude = NULL);
void MarkChunkDirty (int a_ChunkX, int a_ChunkY, int a_ChunkZ);
@@ -80,6 +82,7 @@ public:
bool IsChunkValid (int a_ChunkX, int a_ChunkY, int a_ChunkZ) const;
bool HasChunkAnyClients(int a_ChunkX, int a_ChunkY, int a_ChunkZ) const;
void UnloadUnusedChunks(void);
+ void CollectPickupsByPlayer(cPlayer * a_Player);
// MOTD
const AString & GetDescription(void) const {return m_Description; }
@@ -105,7 +108,18 @@ public:
void SendPlayerList(cPlayer * a_DestPlayer); // Sends playerlist to the player
void AddEntity( cEntity* a_Entity );
- void RemoveEntityFromChunk( cEntity * a_Entity);
+
+ /// Add an entity to the chunk specified; broadcasts the a_SpawnPacket to all clients of that chunk
+ void AddEntityToChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ, cPacket * a_SpawnPacket);
+
+ /// Removes the entity from the chunk specified
+ void RemoveEntityFromChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+
+ /// Moves the entity from its current chunk to the new chunk specified
+ void MoveEntityToChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+
+ /// Compares clients of two chunks, calls the callback accordingly
+ void CompareChunkClients(int a_ChunkX1, int a_ChunkY1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkY2, int a_ChunkZ2, cClientDiffCallback & a_Callback);
// TODO: Export to Lua
bool DoWithEntity( int a_UniqueID, cEntityCallback & a_Callback );