diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ChunkSender.cpp | 21 | ||||
-rw-r--r-- | src/ClientHandle.cpp | 240 | ||||
-rw-r--r-- | src/ClientHandle.h | 8 | ||||
-rw-r--r-- | src/Entities/Player.cpp | 1 | ||||
-rw-r--r-- | src/Protocol/Protocol17x.cpp | 1 | ||||
-rw-r--r-- | src/Protocol/Protocol18x.cpp | 1 | ||||
-rw-r--r-- | src/World.cpp | 1 |
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()); |