summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/ChunkSender.cpp21
-rw-r--r--src/ClientHandle.cpp240
-rw-r--r--src/ClientHandle.h8
-rw-r--r--src/Entities/Player.cpp1
-rw-r--r--src/Protocol/Protocol17x.cpp1
-rw-r--r--src/Protocol/Protocol18x.cpp1
-rw-r--r--src/World.cpp1
7 files changed, 183 insertions, 90 deletions
diff --git a/src/ChunkSender.cpp b/src/ChunkSender.cpp
index a3151eb3f..0bdc0cf75 100644
--- a/src/ChunkSender.cpp
+++ b/src/ChunkSender.cpp
@@ -192,40 +192,37 @@ void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Clien
ASSERT(m_World != NULL);
// Ask the client if it still wants the chunk:
- if (a_Client != NULL)
+ if ((a_Client != NULL) && !a_Client->WantsSendChunk(a_ChunkX, a_ChunkZ))
{
- if (!a_Client->WantsSendChunk(a_ChunkX, a_ChunkZ))
- {
- return;
- }
+ return;
}
-
+
// If the chunk has no clients, no need to packetize it:
if (!m_World->HasChunkAnyClients(a_ChunkX, a_ChunkZ))
{
return;
}
-
+
// If the chunk is not valid, do nothing - whoever needs it has queued it for loading / generating
if (!m_World->IsChunkValid(a_ChunkX, a_ChunkZ))
{
return;
}
-
+
// If the chunk is not lighted, queue it for relighting and get notified when it's ready:
if (!m_World->IsChunkLighted(a_ChunkX, a_ChunkZ))
{
m_World->QueueLightChunk(a_ChunkX, a_ChunkZ, &m_Notify);
return;
}
-
+
// Query and prepare chunk data:
if (!m_World->GetChunkData(a_ChunkX, a_ChunkZ, *this))
{
return;
}
cChunkDataSerializer Data(m_BlockTypes, m_BlockMetas, m_BlockLight, m_BlockSkyLight, m_BiomeMap);
-
+
// Send:
if (a_Client == NULL)
{
@@ -235,7 +232,7 @@ void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Clien
{
a_Client->SendChunkData(a_ChunkX, a_ChunkZ, Data);
}
-
+
// Send block-entity packets:
for (sBlockCoords::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
{
@@ -249,7 +246,7 @@ void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Clien
}
} // for itr - m_Packets[]
m_BlockEntities.clear();
-
+
// TODO: Send entity spawn packets
}
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 3b677460b..535f9d386 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -402,53 +402,173 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID,
-void cClientHandle::StreamChunks(void)
+void cClientHandle::StreamNextChunk(void)
{
if ((m_State < csAuthenticated) || (m_State >= csDestroying))
{
return;
}
-
ASSERT(m_Player != NULL);
- int ChunkPosX = FAST_FLOOR_DIV((int)m_Player->GetPosX(), cChunkDef::Width);
- int ChunkPosZ = FAST_FLOOR_DIV((int)m_Player->GetPosZ(), cChunkDef::Width);
- if ((ChunkPosX == m_LastStreamedChunkX) && (ChunkPosZ == m_LastStreamedChunkZ))
+ int ChunkPosX = m_Player->GetChunkX();
+ int ChunkPosZ = m_Player->GetChunkZ();
+ if ((m_LastStreamedChunkX == ChunkPosX) && (m_LastStreamedChunkZ == ChunkPosZ))
{
- // Already streamed for this position
+ // All chunks are already loaded. Abort loading.
return;
}
+
+ // Get the look vector and normalize it.
+ Vector3d Position = m_Player->GetEyePosition();
+ Vector3d LookVector = m_Player->GetLookVector();
+ LookVector.Normalize();
+
+ // Lock the list
+ cCSLock Lock(m_CSChunkLists);
+
+ // High priority: Load the chunks that are in the view-direction of the player (with a radius of 3)
+ for (size_t Range = 0; Range < (size_t)m_ViewDistance; Range++)
+ {
+ Vector3d Vector = Position + LookVector * cChunkDef::Width * Range;
+
+ // Get the chunk from the x/z coords.
+ int RangeX, RangeZ = 0;
+ cChunkDef::BlockToChunk((int)std::floor(Vector.x), (int)std::floor(Vector.z), RangeX, RangeZ);
+
+ for (size_t X = 0; X < 7; X++)
+ {
+ for (size_t Z = 0; Z < 7; Z++)
+ {
+ int ChunkX = RangeX + ((X >= 4) ? (3 - X) : X);
+ int ChunkZ = RangeZ + ((Z >= 4) ? (3 - Z) : Z);
+ cChunkCoords Coords(ChunkX, ChunkZ);
+
+ // If the chunk already loading/loaded -> skip
+ if (
+ (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), Coords) != m_ChunksToSend.end()) ||
+ (std::find(m_LoadedChunks.begin(), m_LoadedChunks.end(), Coords) != m_LoadedChunks.end())
+ )
+ {
+ continue;
+ }
+
+ // Unloaded chunk found -> Send it to the client.
+ Lock.Unlock();
+ StreamChunk(ChunkX, ChunkZ);
+ return;
+ }
+ }
+ }
+
+ // Medium priority: Load the chunks that are behind the player
+ LookVector = m_Player->GetLookVector() * -1;
+ for (size_t Range = 0; Range < 3; Range++)
+ {
+ Vector3d Vector = Position + LookVector * cChunkDef::Width * Range;
+
+ // Get the chunk from the x/z coords.
+ int RangeX, RangeZ = 0;
+ cChunkDef::BlockToChunk((int)std::floor(Vector.x), (int)std::floor(Vector.z), RangeX, RangeZ);
+
+ for (size_t X = 0; X < 7; X++)
+ {
+ for (size_t Z = 0; Z < 7; Z++)
+ {
+ int ChunkX = RangeX + ((X >= 4) ? (3 - X) : X);
+ int ChunkZ = RangeZ + ((Z >= 4) ? (3 - Z) : Z);
+ cChunkCoords Coords(ChunkX, ChunkZ);
+
+ // If the chunk already loading/loaded -> skip
+ if (
+ (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), Coords) != m_ChunksToSend.end()) ||
+ (std::find(m_LoadedChunks.begin(), m_LoadedChunks.end(), Coords) != m_LoadedChunks.end())
+ )
+ {
+ continue;
+ }
+
+ // Unloaded chunk found -> Send it to the client.
+ Lock.Unlock();
+ StreamChunk(ChunkX, ChunkZ);
+ return;
+ }
+ }
+ }
+
+ // Low priority: Add all chunks that are in range. (From the center out to the edge)
+ for (int d = 0; d <= m_ViewDistance; ++d) // cycle through (square) distance, from nearest to furthest
+ {
+ // For each distance add chunks in a hollow square centered around current position:
+ cChunkCoordsList CurcleChunks;
+ for (int i = -d; i <= d; ++i)
+ {
+ CurcleChunks.push_back(cChunkCoords(ChunkPosX + d, ChunkPosZ + i));
+ CurcleChunks.push_back(cChunkCoords(ChunkPosX - d, ChunkPosZ + i));
+ }
+ for (int i = -d + 1; i < d; ++i)
+ {
+ CurcleChunks.push_back(cChunkCoords(ChunkPosX + i, ChunkPosZ + d));
+ CurcleChunks.push_back(cChunkCoords(ChunkPosX + i, ChunkPosZ - d));
+ }
+
+ // For each the CurcleChunks list and send the first unloaded chunk:
+ for (cChunkCoordsList::iterator itr = CurcleChunks.begin(), end = CurcleChunks.end(); itr != end; ++itr)
+ {
+ cChunkCoords Coords = *itr;
+
+ // If the chunk already loading/loaded -> skip
+ if (
+ (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), Coords) != m_ChunksToSend.end()) ||
+ (std::find(m_LoadedChunks.begin(), m_LoadedChunks.end(), Coords) != m_LoadedChunks.end())
+ )
+ {
+ continue;
+ }
+
+ // Unloaded chunk found -> Send it to the client.
+ Lock.Unlock();
+ StreamChunk(Coords.m_ChunkX, Coords.m_ChunkZ);
+ return;
+ }
+ }
+
+ // All chunks are loaded -> Sets the last loaded chunk coordinates to current coordinates
m_LastStreamedChunkX = ChunkPosX;
m_LastStreamedChunkZ = ChunkPosZ;
-
- LOGD("Streaming chunks centered on [%d, %d], view distance %d", ChunkPosX, ChunkPosZ, m_ViewDistance);
-
- cWorld * World = m_Player->GetWorld();
- ASSERT(World != NULL);
+}
+
+
+
+
+
+void cClientHandle::UnloadOutOfRangeChunks(void)
+{
+ int ChunkPosX = FAST_FLOOR_DIV((int)m_Player->GetPosX(), cChunkDef::Width);
+ int ChunkPosZ = FAST_FLOOR_DIV((int)m_Player->GetPosZ(), cChunkDef::Width);
- // Remove all loaded chunks that are no longer in range; deferred to out-of-CS:
- cChunkCoordsList RemoveChunks;
+ cChunkCoordsList ChunksToRemove;
{
cCSLock Lock(m_CSChunkLists);
for (cChunkCoordsList::iterator itr = m_LoadedChunks.begin(); itr != m_LoadedChunks.end();)
{
- int RelX = (*itr).m_ChunkX - ChunkPosX;
- int RelZ = (*itr).m_ChunkZ - ChunkPosZ;
- if ((RelX > m_ViewDistance) || (RelX < -m_ViewDistance) || (RelZ > m_ViewDistance) || (RelZ < -m_ViewDistance))
+ int DiffX = Diff((*itr).m_ChunkX, ChunkPosX);
+ int DiffZ = Diff((*itr).m_ChunkZ, ChunkPosZ);
+ if ((DiffX > m_ViewDistance) || (DiffZ > m_ViewDistance))
{
- RemoveChunks.push_back(*itr);
+ ChunksToRemove.push_back(*itr);
itr = m_LoadedChunks.erase(itr);
}
else
{
++itr;
}
- } // for itr - m_LoadedChunks[]
+ }
+
for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end();)
{
- int RelX = (*itr).m_ChunkX - ChunkPosX;
- int RelZ = (*itr).m_ChunkZ - ChunkPosZ;
- if ((RelX > m_ViewDistance) || (RelX < -m_ViewDistance) || (RelZ > m_ViewDistance) || (RelZ < -m_ViewDistance))
+ int DiffX = Diff((*itr).m_ChunkX, ChunkPosX);
+ int DiffZ = Diff((*itr).m_ChunkZ, ChunkPosZ);
+ if ((DiffX > m_ViewDistance) || (DiffZ > m_ViewDistance))
{
itr = m_ChunksToSend.erase(itr);
}
@@ -456,51 +576,20 @@ void cClientHandle::StreamChunks(void)
{
++itr;
}
- } // for itr - m_ChunksToSend[]
+ }
}
- for (cChunkCoordsList::iterator itr = RemoveChunks.begin(); itr != RemoveChunks.end(); ++itr)
+
+ for (cChunkCoordsList::iterator itr = ChunksToRemove.begin(); itr != ChunksToRemove.end(); ++itr)
{
- World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this);
+ m_Player->GetWorld()->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this);
m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ);
- } // for itr - RemoveChunks[]
-
- // Add all chunks that are in range and not yet in m_LoadedChunks:
- // Queue these smartly - from the center out to the edge
- for (int d = 0; d <= m_ViewDistance; ++d) // cycle through (square) distance, from nearest to furthest
- {
- // For each distance add chunks in a hollow square centered around current position:
- for (int i = -d; i <= d; ++i)
- {
- StreamChunk(ChunkPosX + d, ChunkPosZ + i);
- StreamChunk(ChunkPosX - d, ChunkPosZ + i);
- } // for i
- for (int i = -d + 1; i < d; ++i)
- {
- StreamChunk(ChunkPosX + i, ChunkPosZ + d);
- StreamChunk(ChunkPosX + i, ChunkPosZ - d);
- } // for i
- } // for d
-
- // Touch chunks GENERATEDISTANCE ahead to let them generate:
- for (int d = m_ViewDistance + 1; d <= m_ViewDistance + GENERATEDISTANCE; ++d) // cycle through (square) distance, from nearest to furthest
- {
- // For each distance touch chunks in a hollow square centered around current position:
- for (int i = -d; i <= d; ++i)
- {
- World->TouchChunk(ChunkPosX + d, ChunkPosZ + i);
- World->TouchChunk(ChunkPosX - d, ChunkPosZ + i);
- } // for i
- for (int i = -d + 1; i < d; ++i)
- {
- World->TouchChunk(ChunkPosX + i, ChunkPosZ + d);
- World->TouchChunk(ChunkPosX + i, ChunkPosZ - d);
- } // for i
- } // for d
+ }
}
+
void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkZ)
{
if (m_State >= csDestroying)
@@ -540,7 +629,7 @@ 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;
@@ -1866,10 +1955,11 @@ void cClientHandle::RemoveFromWorld(void)
{
m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ);
} // for itr - Chunks[]
-
+
// Here, we set last streamed values to bogus ones so everything is resent
m_LastStreamedChunkX = 0x7fffffff;
m_LastStreamedChunkZ = 0x7fffffff;
+
m_HasSentPlayerChunk = false;
}
@@ -1915,7 +2005,7 @@ void cClientHandle::Tick(float a_Dt)
{
return;
}
-
+
// If the chunk the player's in was just sent, spawn the player:
if (m_HasSentPlayerChunk && (m_State == csDownloadingWorld))
{
@@ -1936,6 +2026,18 @@ void cClientHandle::Tick(float a_Dt)
}
}
+ if ((m_State >= csAuthenticated) && (m_State < csDestroying))
+ {
+
+ StreamNextChunk(); // Streams the next chunk
+
+ // Unload all chunks that are out of the view distance (all 5 seconds)
+ if ((m_Player->GetWorld()->GetWorldAge() % 100) == 0)
+ {
+ UnloadOutOfRangeChunks();
+ }
+ }
+
// Handle block break animation:
if (m_BlockDigAnimStage > -1)
{
@@ -1972,7 +2074,7 @@ void cClientHandle::ServerTick(float a_Dt)
if (m_State == csAuthenticated)
{
- StreamChunks();
+ StreamNextChunk();
// Remove the client handle from the server, it will be ticked from its cPlayer object from now on
cRoot::Get()->GetServer()->ClientMovedToWorld(this);
@@ -2731,18 +2833,8 @@ void cClientHandle::SetUsername( const AString & a_Username)
void cClientHandle::SetViewDistance(int a_ViewDistance)
{
- if (a_ViewDistance < MIN_VIEW_DISTANCE)
- {
- a_ViewDistance = MIN_VIEW_DISTANCE;
- }
- if (a_ViewDistance > MAX_VIEW_DISTANCE)
- {
- a_ViewDistance = MAX_VIEW_DISTANCE;
- }
- m_ViewDistance = a_ViewDistance;
-
- // Need to re-stream chunks for the change to become apparent:
- StreamChunks();
+ m_ViewDistance = Clamp(a_ViewDistance, MIN_VIEW_DISTANCE, MAX_VIEW_DISTANCE);
+ LOGD("Setted %s's view distance to %i", GetUsername().c_str(), m_ViewDistance);
}
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index a9cc29d50..eb91b8487 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -113,7 +113,11 @@ public:
/** Authenticates the specified user, called by cAuthenticator */
void Authenticate(const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties);
- void StreamChunks(void);
+ /** This function sends a new unloaded chunk to the player. */
+ void StreamNextChunk(void);
+
+ /** Remove all loaded chunks that are no longer in range */
+ void UnloadOutOfRangeChunks(void);
// Removes the client from all chunks. Used when switching worlds or destroying the player
void RemoveFromAllChunks(void);
@@ -358,7 +362,7 @@ private:
cPlayer * m_Player;
bool m_HasSentDC; ///< True if a D/C packet has been sent in either direction
-
+
// Chunk position when the last StreamChunks() was called; used to avoid re-streaming while in the same chunk
int m_LastStreamedChunkX;
int m_LastStreamedChunkZ;
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index f58a0a016..19273b0a9 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -235,7 +235,6 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
CanMove = false;
TeleportToCoords(m_LastPos.x, m_LastPos.y, m_LastPos.z);
}
- m_ClientHandle->StreamChunks();
}
if (CanMove)
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index a7abd240f..494413f63 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -1901,6 +1901,7 @@ void cProtocol172::HandlePacketClientSettings(cByteBuffer & a_ByteBuffer)
HANDLE_READ(a_ByteBuffer, ReadByte, Byte, ShowCape);
m_Client->SetLocale(Locale);
+ m_Client->SetViewDistance(ViewDistance);
// TODO: Do anything with the other values.
}
diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp
index acdb48cf7..f37409744 100644
--- a/src/Protocol/Protocol18x.cpp
+++ b/src/Protocol/Protocol18x.cpp
@@ -2160,6 +2160,7 @@ void cProtocol180::HandlePacketClientSettings(cByteBuffer & a_ByteBuffer)
HANDLE_READ(a_ByteBuffer, ReadChar, char, SkinFlags);
m_Client->SetLocale(Locale);
+ m_Client->SetViewDistance(ViewDistance);
// TODO: Handle other values
}
diff --git a/src/World.cpp b/src/World.cpp
index a3c804b44..30c1a73b1 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -3491,7 +3491,6 @@ void cWorld::AddQueuedPlayers(void)
cClientHandle * Client = (*itr)->GetClientHandle();
if (Client != NULL)
{
- Client->StreamChunks();
Client->SendPlayerMoveLook();
Client->SendHealth();
Client->SendWholeInventory(*(*itr)->GetWindow());