diff options
| author | daoge_cmd <3523206925@qq.com> | 2026-03-01 12:16:08 +0800 |
|---|---|---|
| committer | daoge_cmd <3523206925@qq.com> | 2026-03-01 12:16:08 +0800 |
| commit | b691c43c44ff180d10e7d4a9afc83b98551ff586 (patch) | |
| tree | 3e9849222cbc6ba49f2f1fc6e5fe7179632c7390 /Minecraft.Client/MultiPlayerChunkCache.cpp | |
| parent | def8cb415354ac390b7e89052a50605285f1aca9 (diff) | |
Initial commit
Diffstat (limited to 'Minecraft.Client/MultiPlayerChunkCache.cpp')
| -rw-r--r-- | Minecraft.Client/MultiPlayerChunkCache.cpp | 306 |
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 |
