From 06a2b1211b7241acce8dfbfebc9eed19a987708c Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Mon, 28 Jun 2021 23:40:28 +0100 Subject: [PATCH] Invalidate m_LastStreamedChunk when player position doesn't match So teleporting to and back doesn't cause chunk sending to stop. * Fixes #4531 --- src/ClientHandle.cpp | 102 ++++++++++++++++++++++------------------ src/ClientHandle.h | 6 +-- src/Entities/Player.cpp | 3 -- 3 files changed, 57 insertions(+), 54 deletions(-) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 8dbf4faa5..6c9fa751c 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -40,15 +40,18 @@ -/** Maximum number of explosions to send this tick, server will start dropping if exceeded */ +/** Maximum number of explosions to send this tick, server will start dropping if exceeded. */ #define MAX_EXPLOSIONS_PER_TICK 20 -/** Maximum number of block change interactions a player can perform per tick - exceeding this causes a kick */ +/** Maximum number of block change interactions a player can perform per tick - exceeding this causes a kick. */ #define MAX_BLOCK_CHANGE_INTERACTIONS 20 -/** Maximum number of bytes that a chat message sent by a player may consist of */ +/** Maximum number of bytes that a chat message sent by a player may consist of. */ #define MAX_CHAT_MSG_LENGTH 1024 +/** Maximum number of chunks to stream per tick. */ +#define MAX_CHUNKS_STREAMED_PER_TICK 4 + @@ -389,25 +392,30 @@ void cClientHandle::FinishAuthenticate(const AString & a_Name, const cUUID & a_U -bool cClientHandle::StreamNextChunk(void) +void cClientHandle::StreamNextChunks(void) { ASSERT(m_Player != nullptr); int ChunkPosX = m_Player->GetChunkX(); int ChunkPosZ = m_Player->GetChunkZ(); + if ((m_LastStreamedChunkX == ChunkPosX) && (m_LastStreamedChunkZ == ChunkPosZ)) { // All chunks are already loaded. Abort loading. - return true; + return; + } + else + { + m_LastStreamedChunkX = 0x7fffffff; + m_LastStreamedChunkZ = 0x7fffffff; } - // Get the look vector and normalize it. + int StreamedChunks = 0; Vector3d Position = m_Player->GetEyePosition(); Vector3d LookVector = m_Player->GetLookVector(); - LookVector.Normalize(); - // Lock the list - cCSLock Lock(m_CSChunkLists); + // Get the look vector and normalize it. + LookVector.Normalize(); // High priority: Load the chunks that are in the view-direction of the player (with a radius of 3) for (int Range = 0; Range < m_CurrentViewDistance; Range++) @@ -433,18 +441,24 @@ bool cClientHandle::StreamNextChunk(void) } // If the chunk already loading / loaded -> skip - if ( - (m_ChunksToSend.find(Coords) != m_ChunksToSend.end()) || - (m_LoadedChunks.find(Coords) != m_LoadedChunks.end()) - ) { - continue; + cCSLock Lock(m_CSChunkLists); + if ( + (m_ChunksToSend.find(Coords) != m_ChunksToSend.end()) || + (m_LoadedChunks.find(Coords) != m_LoadedChunks.end()) + ) + { + continue; + } } // Unloaded chunk found -> Send it to the client. - Lock.Unlock(); StreamChunk(ChunkX, ChunkZ, ((Range <= 2) ? cChunkSender::Priority::Critical : cChunkSender::Priority::Medium)); - return false; + + if (++StreamedChunks == MAX_CHUNKS_STREAMED_PER_TICK) + { + return; + } } } } @@ -452,19 +466,21 @@ bool cClientHandle::StreamNextChunk(void) // Low priority: Add all chunks that are in range. (From the center out to the edge) for (int d = 0; d <= m_CurrentViewDistance; ++d) // cycle through (square) distance, from nearest to furthest { - const auto StreamIfUnloaded = [this, &Lock](const cChunkCoords Chunk) + const auto StreamIfUnloaded = [this](const cChunkCoords Chunk) { // If the chunk already loading / loaded -> skip - if ( - (m_ChunksToSend.find(Chunk) != m_ChunksToSend.end()) || - (m_LoadedChunks.find(Chunk) != m_LoadedChunks.end()) - ) { - return false; + cCSLock Lock(m_CSChunkLists); + if ( + (m_ChunksToSend.find(Chunk) != m_ChunksToSend.end()) || + (m_LoadedChunks.find(Chunk) != m_LoadedChunks.end()) + ) + { + return false; + } } // Unloaded chunk found -> Send it to the client. - Lock.Unlock(); StreamChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, cChunkSender::Priority::Low); return true; }; @@ -474,14 +490,20 @@ bool cClientHandle::StreamNextChunk(void) { if (StreamIfUnloaded({ ChunkPosX + d, ChunkPosZ + i }) || StreamIfUnloaded({ ChunkPosX - d, ChunkPosZ + i })) { - return false; + if (++StreamedChunks == MAX_CHUNKS_STREAMED_PER_TICK) + { + return; + } } } for (int i = -d + 1; i < d; ++i) { if (StreamIfUnloaded({ ChunkPosX + i, ChunkPosZ + d }) || StreamIfUnloaded({ ChunkPosX + i, ChunkPosZ - d })) { - return false; + if (++StreamedChunks == MAX_CHUNKS_STREAMED_PER_TICK) + { + return; + } } } } @@ -489,7 +511,6 @@ bool cClientHandle::StreamNextChunk(void) // All chunks are loaded -> Sets the last loaded chunk coordinates to current coordinates m_LastStreamedChunkX = ChunkPosX; m_LastStreamedChunkZ = ChunkPosZ; - return true; } @@ -1923,18 +1944,9 @@ void cClientHandle::RemoveFromWorld(void) // Here, we set last streamed values to bogus ones so everything is resent: m_LastStreamedChunkX = 0x7fffffff; m_LastStreamedChunkZ = 0x7fffffff; -} - - - - -void cClientHandle::InvalidateCachedSentChunk() -{ - ASSERT(m_Player != nullptr); - // Sets this to a junk value different from the player's current chunk, which invalidates it and - // ensures its value will not be used. - m_CachedSentChunk = cChunkCoords(m_Player->GetChunkX() + 500, m_Player->GetChunkZ()); + // Restart player unloaded chunk checking and freezing: + m_CachedSentChunk = cChunkCoords(0x7fffffff, 0x7fffffff); } @@ -2048,16 +2060,8 @@ void cClientHandle::Tick(float a_Dt) } } - // Stream 4 chunks per tick - for (int i = 0; i < 4; i++) - { - // Stream the next chunk - if (StreamNextChunk()) - { - // Streaming finished. All chunks are loaded. - break; - } - } + // Send a couple of chunks to the player: + StreamNextChunks(); // Unload all chunks that are out of the view distance (every 5 seconds): if ((m_Player->GetWorld()->GetWorldAge() - m_LastUnloadCheck) > 5s) @@ -3160,6 +3164,10 @@ void cClientHandle::SetViewDistance(int a_ViewDistance) { // Set the current view distance based on the requested VD and world max VD: m_CurrentViewDistance = Clamp(a_ViewDistance, cClientHandle::MIN_VIEW_DISTANCE, world->GetMaxViewDistance()); + + // Restart chunk streaming to respond to new view distance: + m_LastStreamedChunkX = 0x7fffffff; + m_LastStreamedChunkZ = 0x7fffffff; } } diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 17dbefbeb..c33bd7fe0 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -117,8 +117,8 @@ public: // tolua_export /** Authenticates the specified user, called by cAuthenticator */ void Authenticate(const AString & a_Name, const cUUID & a_UUID, const Json::Value & a_Properties); - /** This function sends a new unloaded chunk to the player. Returns true if all chunks are loaded. */ - bool StreamNextChunk(); + /** Sends a set number of new chunks to the player on every invocation, until all chunks in the view distance have been sent. */ + void StreamNextChunks(); /** Remove all loaded chunks that are no longer in range */ void UnloadOutOfRangeChunks(void); @@ -407,8 +407,6 @@ public: // tolua_export /** Returns the protocol version number of the protocol that the client is talking. Returns zero if the protocol version is not (yet) known. */ UInt32 GetProtocolVersion(void) const { return m_ProtocolVersion; } // tolua_export - void InvalidateCachedSentChunk(); - bool IsPlayerChunkSent(); private: diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index cb548daba..db64d2e1d 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -3120,9 +3120,6 @@ void cPlayer::OnRemoveFromWorld(cWorld & a_World) // Clear sent chunk lists from the clienthandle: m_ClientHandle->RemoveFromWorld(); - - // The clienthandle caches the coords of the chunk we're standing at. Invalidate this. - m_ClientHandle->InvalidateCachedSentChunk(); }