aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.Client/MultiPlayerChunkCache.cpp
diff options
context:
space:
mode:
authordaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
committerdaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
commitb691c43c44ff180d10e7d4a9afc83b98551ff586 (patch)
tree3e9849222cbc6ba49f2f1fc6e5fe7179632c7390 /Minecraft.Client/MultiPlayerChunkCache.cpp
parentdef8cb415354ac390b7e89052a50605285f1aca9 (diff)
Initial commit
Diffstat (limited to 'Minecraft.Client/MultiPlayerChunkCache.cpp')
-rw-r--r--Minecraft.Client/MultiPlayerChunkCache.cpp306
1 files changed, 306 insertions, 0 deletions
diff --git a/Minecraft.Client/MultiPlayerChunkCache.cpp b/Minecraft.Client/MultiPlayerChunkCache.cpp
new file mode 100644
index 00000000..8c6c90e7
--- /dev/null
+++ b/Minecraft.Client/MultiPlayerChunkCache.cpp
@@ -0,0 +1,306 @@
+#include "stdafx.h"
+#include "MultiPlayerChunkCache.h"
+#include "ServerChunkCache.h"
+#include "..\Minecraft.World\net.minecraft.world.level.chunk.h"
+#include "..\Minecraft.World\net.minecraft.world.level.dimension.h"
+#include "..\Minecraft.World\Arrays.h"
+#include "..\Minecraft.World\StringHelpers.h"
+#include "MinecraftServer.h"
+#include "ServerLevel.h"
+#include "..\Minecraft.World\Tile.h"
+#include "..\Minecraft.World\WaterLevelChunk.h"
+
+MultiPlayerChunkCache::MultiPlayerChunkCache(Level *level)
+{
+ XZSIZE = level->dimension->getXZSize(); // 4J Added
+ XZOFFSET = XZSIZE/2; // 4J Added
+ m_XZSize = XZSIZE;
+ hasData = new bool[XZSIZE * XZSIZE];
+ memset(hasData, 0, sizeof(bool) * XZSIZE * XZSIZE);
+
+ emptyChunk = new EmptyLevelChunk(level, byteArray(16 * 16 * Level::maxBuildHeight), 0, 0);
+
+ // For normal world dimension, create a chunk that can be used to create the illusion of infinite water at the edge of the world
+ if( level->dimension->id == 0 )
+ {
+ byteArray bytes = byteArray(16 * 16 * 128);
+
+ // Superflat.... make grass, not water...
+ if(level->getLevelData()->getGenerator() == LevelType::lvl_flat)
+ {
+ for( int x = 0; x < 16; x++ )
+ for( int y = 0; y < 128; y++ )
+ for( int z = 0; z < 16; z++ )
+ {
+ unsigned char tileId = 0;
+ if( y == 3 ) tileId = Tile::grass_Id;
+ else if( y <= 2 ) tileId = Tile::dirt_Id;
+
+ bytes[x << 11 | z << 7 | y] = tileId;
+ }
+ }
+ else
+ {
+ for( int x = 0; x < 16; x++ )
+ for( int y = 0; y < 128; y++ )
+ for( int z = 0; z < 16; z++ )
+ {
+ unsigned char tileId = 0;
+ if( y <= ( level->getSeaLevel() - 10 ) ) tileId = Tile::rock_Id;
+ else if( y < level->getSeaLevel() ) tileId = Tile::calmWater_Id;
+
+ bytes[x << 11 | z << 7 | y] = tileId;
+ }
+ }
+
+ waterChunk = new WaterLevelChunk(level, bytes, 0, 0);
+
+ delete[] bytes.data;
+
+ if(level->getLevelData()->getGenerator() == LevelType::lvl_flat)
+ {
+ for( int x = 0; x < 16; x++ )
+ for( int y = 0; y < 128; y++ )
+ for( int z = 0; z < 16; z++ )
+ {
+ if( y >= 3 )
+ {
+ ((WaterLevelChunk *)waterChunk)->setLevelChunkBrightness(LightLayer::Sky,x,y,z,15);
+ }
+ }
+ }
+ else
+ {
+ for( int x = 0; x < 16; x++ )
+ for( int y = 0; y < 128; y++ )
+ for( int z = 0; z < 16; z++ )
+ {
+ if( y >= ( level->getSeaLevel() - 1 ) )
+ {
+ ((WaterLevelChunk *)waterChunk)->setLevelChunkBrightness(LightLayer::Sky,x,y,z,15);
+ }
+ else
+ {
+ ((WaterLevelChunk *)waterChunk)->setLevelChunkBrightness(LightLayer::Sky,x,y,z,2);
+ }
+ }
+ }
+ }
+ else
+ {
+ waterChunk = NULL;
+ }
+
+ this->level = level;
+
+ this->cache = new LevelChunk *[XZSIZE * XZSIZE];
+ memset(this->cache, 0, XZSIZE * XZSIZE * sizeof(LevelChunk *));
+ InitializeCriticalSectionAndSpinCount(&m_csLoadCreate,4000);
+}
+
+MultiPlayerChunkCache::~MultiPlayerChunkCache()
+{
+ delete emptyChunk;
+ delete waterChunk;
+ delete cache;
+ delete hasData;
+
+ AUTO_VAR(itEnd, loadedChunkList.end());
+ for (AUTO_VAR(it, loadedChunkList.begin()); it != itEnd; it++)
+ delete *it;
+
+ DeleteCriticalSection(&m_csLoadCreate);
+}
+
+
+bool MultiPlayerChunkCache::hasChunk(int x, int z)
+{
+ // This cache always claims to have chunks, although it might actually just return empty data if it doesn't have anything
+ return true;
+}
+
+// 4J added - find out if we actually really do have a chunk in our cache
+bool MultiPlayerChunkCache::reallyHasChunk(int x, int z)
+{
+ int ix = x + XZOFFSET;
+ int iz = z + XZOFFSET;
+ // Check we're in range of the stored level - if we aren't, then consider that we do have that chunk as we'll be able to use the water chunk there
+ if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return true;
+ if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return true;
+ int idx = ix * XZSIZE + iz;
+
+ LevelChunk *chunk = cache[idx];
+ if( chunk == NULL )
+ {
+ return false;
+ }
+ return hasData[idx];
+}
+
+void MultiPlayerChunkCache::drop(int x, int z)
+{
+ // 4J Stu - We do want to drop any 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.
+ LevelChunk *chunk = getChunk(x, z);
+ if (!chunk->isEmpty())
+ {
+ // Added parameter here specifies that we don't want to delete tile entities, as they won't get recreated unless they've got update packets
+ // The tile entities are in general only created on the client by virtue of the chunk rebuild
+ chunk->unload(false);
+
+ // 4J - We just want to clear out the entities in the chunk, but everything else should be valid
+ chunk->loaded = true;
+ }
+}
+
+LevelChunk *MultiPlayerChunkCache::create(int x, int z)
+{
+ int ix = x + XZOFFSET;
+ int iz = z + XZOFFSET;
+ // Check we're in range of the stored level
+ if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk );
+ if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk );
+ int idx = ix * XZSIZE + iz;
+ LevelChunk *chunk = cache[idx];
+ LevelChunk *lastChunk = chunk;
+
+ if( chunk == NULL )
+ {
+ EnterCriticalSection(&m_csLoadCreate);
+
+ //LevelChunk *chunk;
+ if( g_NetworkManager.IsHost() ) // force here to disable sharing of data
+ {
+ // 4J-JEV: We are about to use shared data, abort if the server is stopped and the data is deleted.
+ if (MinecraftServer::getInstance()->serverHalted()) return NULL;
+
+ // If we're the host, then don't create the chunk, share data from the server's copy
+#ifdef _LARGE_WORLDS
+ LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(level->dimension->id)->cache->getChunkLoadedOrUnloaded(x,z);
+#else
+ LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(level->dimension->id)->cache->getChunk(x,z);
+#endif
+ chunk = new LevelChunk(level, x, z, serverChunk);
+ // Let renderer know that this chunk has been created - it might have made render data from the EmptyChunk if it got to a chunk before the server sent it
+ level->setTilesDirty( x * 16 , 0 , z * 16 , x * 16 + 15, 127, z * 16 + 15);
+ hasData[idx] = true;
+ }
+ else
+ {
+ // Passing an empty array into the LevelChunk ctor, which it now detects and sets up the chunk as compressed & empty
+ byteArray bytes;
+
+ chunk = new LevelChunk(level, bytes, x, z);
+
+ // 4J - changed to use new methods for lighting
+ chunk->setSkyLightDataAllBright();
+// Arrays::fill(chunk->skyLight->data, (byte) 255);
+ }
+
+ chunk->loaded = true;
+
+ LeaveCriticalSection(&m_csLoadCreate);
+
+#if ( defined _WIN64 || defined __LP64__ )
+ if( InterlockedCompareExchangeRelease64((LONG64 *)&cache[idx],(LONG64)chunk,(LONG64)lastChunk) == (LONG64)lastChunk )
+#else
+ if( InterlockedCompareExchangeRelease((LONG *)&cache[idx],(LONG)chunk,(LONG)lastChunk) == (LONG)lastChunk )
+#endif // _DURANGO
+ {
+ // If we're sharing with the server, we'll need to calculate our heightmap now, which isn't shared. If we aren't sharing with the server,
+ // then this will be calculated when the chunk data arrives.
+ if( g_NetworkManager.IsHost() )
+ {
+ chunk->recalcHeightmapOnly();
+ }
+
+ // Successfully updated the cache
+ EnterCriticalSection(&m_csLoadCreate);
+ loadedChunkList.push_back(chunk);
+ LeaveCriticalSection(&m_csLoadCreate);
+ }
+ else
+ {
+ // Something else must have updated the cache. Return that chunk and discard this one. This really shouldn't be happening
+ // in multiplayer
+ delete chunk;
+ return cache[idx];
+ }
+
+ }
+ else
+ {
+ chunk->load();
+ }
+
+ return chunk;
+}
+
+LevelChunk *MultiPlayerChunkCache::getChunk(int x, int z)
+{
+ int ix = x + XZOFFSET;
+ int iz = z + XZOFFSET;
+ // Check we're in range of the stored level
+ if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk );
+ if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk );
+ int idx = ix * XZSIZE + iz;
+
+ LevelChunk *chunk = cache[idx];
+ if( chunk == NULL )
+ {
+ return emptyChunk;
+ }
+ else
+ {
+ return chunk;
+ }
+}
+
+bool MultiPlayerChunkCache::save(bool force, ProgressListener *progressListener)
+{
+ return true;
+}
+
+bool MultiPlayerChunkCache::tick()
+{
+ return false;
+}
+
+bool MultiPlayerChunkCache::shouldSave()
+{
+ return false;
+}
+
+void MultiPlayerChunkCache::postProcess(ChunkSource *parent, int x, int z)
+{
+}
+
+vector<Biome::MobSpawnerData *> *MultiPlayerChunkCache::getMobsAt(MobCategory *mobCategory, int x, int y, int z)
+{
+ return NULL;
+}
+
+TilePos *MultiPlayerChunkCache::findNearestMapFeature(Level *level, const wstring &featureName, int x, int y, int z)
+{
+ return NULL;
+}
+
+wstring MultiPlayerChunkCache::gatherStats()
+{
+ EnterCriticalSection(&m_csLoadCreate);
+ int size = (int)loadedChunkList.size();
+ LeaveCriticalSection(&m_csLoadCreate);
+ return L"MultiplayerChunkCache: " + _toString<int>(size);
+
+}
+
+void MultiPlayerChunkCache::dataReceived(int x, int z)
+{
+ int ix = x + XZOFFSET;
+ int iz = z + XZOFFSET;
+ // Check we're in range of the stored level
+ if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return;
+ if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return;
+ int idx = ix * XZSIZE + iz;
+ hasData[idx] = true;
+} \ No newline at end of file