From c264262b66e461cfe2dcb527b990d2a0a42e647d Mon Sep 17 00:00:00 2001 From: itsRevela Date: Wed, 25 Mar 2026 19:57:35 -0500 Subject: [PATCH] Fix chunk unloading regression from upstream merge Commit a24318ee changed drop() to immediately remove chunks from cache, bypassing the deferred m_toDrop save/unload pipeline. This caused missing chunks on dedicated servers, iterator invalidation in dropAll() and ServerLevel::save(), and entity duplication (item frames) from chunks being reloaded without their entities first being removed from the level. - ServerChunkCache::drop(): restore m_toDrop queue instead of immediate cache removal, so tick() can save/unload/move to unloadedCache safely - MultiPlayerChunkCache::drop(): restore soft-unload (keep chunk in cache with loaded=true) instead of nulling cache and hasData - PlayerChunkMap::setRadius(): remove dropAll() call when reducing radius, the per-chunk removal loop already handles out-of-range chunks --- Minecraft.Client/MultiPlayerChunkCache.cpp | 13 ++++++------- Minecraft.Client/PlayerChunkMap.cpp | 5 ----- Minecraft.Client/ServerChunkCache.cpp | 6 +----- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/Minecraft.Client/MultiPlayerChunkCache.cpp b/Minecraft.Client/MultiPlayerChunkCache.cpp index 03c47fcc..3db51cf3 100644 --- a/Minecraft.Client/MultiPlayerChunkCache.cpp +++ b/Minecraft.Client/MultiPlayerChunkCache.cpp @@ -150,15 +150,14 @@ void MultiPlayerChunkCache::drop(const int x, const int z) if (chunk != nullptr && !chunk->isEmpty()) { - // Unload chunk but keep tile entities + // Drop entities in the chunks, especially for the case when a player is dead + // as they will not get the RemoveEntity packet if an entity is removed. + // Don't delete tile entities, as they won't get recreated unless they've got + // update packets. Tile entities are created on the client by the chunk rebuild. chunk->unload(false); - const auto it = std::find(loadedChunkList.begin(), loadedChunkList.end(), chunk); - if (it != loadedChunkList.end()) loadedChunkList.erase(it); - - cache[idx] = nullptr; - hasData[idx] = false; - chunk->loaded = false; + // Keep chunk in cache with structural data intact. + chunk->loaded = true; } } diff --git a/Minecraft.Client/PlayerChunkMap.cpp b/Minecraft.Client/PlayerChunkMap.cpp index ddf2bae2..d8d73f09 100644 --- a/Minecraft.Client/PlayerChunkMap.cpp +++ b/Minecraft.Client/PlayerChunkMap.cpp @@ -824,11 +824,6 @@ void PlayerChunkMap::setRadius(int newRadius) } } - if (newRadius < radius) - { - level->cache->dropAll(); - } - assert(radius <= MAX_VIEW_DISTANCE); assert(radius >= MIN_VIEW_DISTANCE); this->radius = newRadius; diff --git a/Minecraft.Client/ServerChunkCache.cpp b/Minecraft.Client/ServerChunkCache.cpp index 54312ffa..1fcaf384 100644 --- a/Minecraft.Client/ServerChunkCache.cpp +++ b/Minecraft.Client/ServerChunkCache.cpp @@ -91,11 +91,7 @@ void ServerChunkCache::drop(const int x, const int z) if (chunk != nullptr) { - const auto it = std::find(m_loadedChunkList.begin(), m_loadedChunkList.end(), chunk); - if (it != m_loadedChunkList.end()) m_loadedChunkList.erase(it); - - cache[idx] = nullptr; - chunk->loaded = false; + m_toDrop.push_back(chunk); } }