diff --git a/Minecraft.Client/Common/App_Defines.h b/Minecraft.Client/Common/App_Defines.h index eeb9942d..693bad2c 100644 --- a/Minecraft.Client/Common/App_Defines.h +++ b/Minecraft.Client/Common/App_Defines.h @@ -56,7 +56,8 @@ enum EGameHostOptionWorldSize e_worldSize_Classic, e_worldSize_Small, e_worldSize_Medium, - e_worldSize_Large + e_worldSize_Large, + e_worldSize_Expanded }; diff --git a/Minecraft.Client/MultiPlayerChunkCache.cpp b/Minecraft.Client/MultiPlayerChunkCache.cpp index 302ff119..e4cd1e0d 100644 --- a/Minecraft.Client/MultiPlayerChunkCache.cpp +++ b/Minecraft.Client/MultiPlayerChunkCache.cpp @@ -15,8 +15,8 @@ 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); + hasData = new bool[LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH]; + memset(hasData, 0, sizeof(bool) * LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH); emptyChunk = new EmptyLevelChunk(level, byteArray(16 * 16 * Level::maxBuildHeight), 0, 0); @@ -93,8 +93,8 @@ MultiPlayerChunkCache::MultiPlayerChunkCache(Level *level) this->level = level; - this->cache = new LevelChunk *[XZSIZE * XZSIZE]; - memset(this->cache, 0, XZSIZE * XZSIZE * sizeof(LevelChunk *)); + this->cache = new LevelChunk *[LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH]; + memset(this->cache, 0, sizeof(LevelChunk*) * LEVEL_MIN_WIDTH * LEVEL_MIN_WIDTH); InitializeCriticalSectionAndSpinCount(&m_csLoadCreate,4000); } @@ -129,10 +129,11 @@ bool MultiPlayerChunkCache::reallyHasChunk(int x, int z) // 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; + + int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); LevelChunk *chunk = cache[idx]; - if( chunk == nullptr ) + if (chunk == nullptr || chunk->x != x || chunk->z != z) { return false; } @@ -145,10 +146,11 @@ void MultiPlayerChunkCache::drop(const int x, const int z) const int iz = z + XZOFFSET; if ((ix < 0) || (ix >= XZSIZE)) return; if ((iz < 0) || (iz >= XZSIZE)) return; - const int idx = ix * XZSIZE + iz; + + int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); LevelChunk* chunk = cache[idx]; - if (chunk != nullptr && !chunk->isEmpty()) + if (chunk != nullptr && !chunk->isEmpty() && chunk->x == x && chunk->z == z) { // 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. @@ -168,11 +170,12 @@ LevelChunk *MultiPlayerChunkCache::create(int x, int z) // 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; + int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); + LevelChunk *chunk = cache[idx]; LevelChunk *lastChunk = chunk; - if( chunk == nullptr ) + if( chunk == nullptr || chunk->x != x || chunk->z != z ) { EnterCriticalSection(&m_csLoadCreate); @@ -251,10 +254,10 @@ LevelChunk *MultiPlayerChunkCache::getChunk(int x, int z) // 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; + int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); LevelChunk *chunk = cache[idx]; - if( chunk == nullptr ) + if( chunk == nullptr || chunk->x != x || chunk->z != z ) { return emptyChunk; } @@ -313,6 +316,6 @@ void MultiPlayerChunkCache::dataReceived(int x, int z) // 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; + int idx = wrapCoord(x, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + wrapCoord(z, LEVEL_MIN_WIDTH); hasData[idx] = true; } \ No newline at end of file diff --git a/Minecraft.Client/MultiPlayerChunkCache.h b/Minecraft.Client/MultiPlayerChunkCache.h index 203f3124..e018790b 100644 --- a/Minecraft.Client/MultiPlayerChunkCache.h +++ b/Minecraft.Client/MultiPlayerChunkCache.h @@ -45,4 +45,9 @@ public: virtual void dataReceived(int x, int z); // 4J added virtual LevelChunk **getCache() { return cache; } // 4J added + + static inline int wrapCoord(int v, int size) { + int r = v % size; + return (r < 0) ? r + size : r; + } }; \ No newline at end of file diff --git a/Minecraft.Server/ServerProperties.cpp b/Minecraft.Server/ServerProperties.cpp index 1b6c5c0a..525be788 100644 --- a/Minecraft.Server/ServerProperties.cpp +++ b/Minecraft.Server/ServerProperties.cpp @@ -628,6 +628,8 @@ static std::string WorldSizeToPropertyValue(int worldSize) return "medium"; case e_worldSize_Large: return "large"; + case e_worldSize_Expanded: + return "expanded"; case e_worldSize_Classic: default: return "classic"; @@ -644,6 +646,8 @@ static int WorldSizeToXzChunks(int worldSize) return LEVEL_WIDTH_MEDIUM; case e_worldSize_Large: return LEVEL_WIDTH_LARGE; + case e_worldSize_Expanded: + return LEVEL_WIDTH_EXPANDED; case e_worldSize_Classic: default: return LEVEL_WIDTH_CLASSIC; @@ -659,6 +663,7 @@ static int WorldSizeToHellScale(int worldSize) case e_worldSize_Medium: return HELL_LEVEL_SCALE_MEDIUM; case e_worldSize_Large: + case e_worldSize_Expanded: return HELL_LEVEL_SCALE_LARGE; case e_worldSize_Classic: default: @@ -694,6 +699,12 @@ static bool TryParseWorldSize(const std::string &lowered, int *outWorldSize) return true; } + if (lowered == "expanded" || lowered == "344" || lowered == "8") + { + *outWorldSize = e_worldSize_Expanded; + return true; + } + return false; } diff --git a/Minecraft.World/ChunkSource.h b/Minecraft.World/ChunkSource.h index 37b0ecc3..5b510ddb 100644 --- a/Minecraft.World/ChunkSource.h +++ b/Minecraft.World/ChunkSource.h @@ -7,12 +7,14 @@ class TilePos; // The maximum number of chunks that we can store #ifdef _LARGE_WORLDS // 4J Stu - Our default map (at zoom level 3) is 1024x1024 blocks (or 64 chunks) -#define LEVEL_MAX_WIDTH (5*64) //(6*54) - #define LEVEL_WIDTH_CLASSIC 54 #define LEVEL_WIDTH_SMALL 64 #define LEVEL_WIDTH_MEDIUM (3*64) #define LEVEL_WIDTH_LARGE (5*64) +#define LEVEL_WIDTH_EXPANDED (5*64) + 24 + + +#define LEVEL_MAX_WIDTH LEVEL_WIDTH_EXPANDED #else #define LEVEL_MAX_WIDTH 54 diff --git a/Minecraft.World/Level.cpp b/Minecraft.World/Level.cpp index 8478535b..49819aa6 100644 --- a/Minecraft.World/Level.cpp +++ b/Minecraft.World/Level.cpp @@ -46,6 +46,7 @@ DWORD Level::tlsIdxLightCache = TlsAlloc(); // 4J : WESTY : Added for time played stats. #include "net.minecraft.stats.h" +#include "../Minecraft.Client/MultiPlayerChunkCache.h" // 4J - Caching of lighting data added. This is implemented as a 16x16x16 cache of ints (ie 16K storage in total). The index of the element to be used in the array is determined by the lower // four bits of each x/y/z position, and the upper 7/4/7 bits of the x/y/z positions are stored within the element itself along with the cached values etc. The cache can be enabled per thread by @@ -1332,10 +1333,10 @@ int Level::getBrightness(LightLayer::variety layer, int x, int y, int z) if( ( ix < 0 ) || ( ix >= chunkSourceXZSize ) ) return 0; if( ( iz < 0 ) || ( iz >= chunkSourceXZSize ) ) return 0; - int idx = ix * chunkSourceXZSize + iz; + int idx = MultiPlayerChunkCache::wrapCoord(ix, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + MultiPlayerChunkCache::wrapCoord(iz, LEVEL_MIN_WIDTH); LevelChunk *c = chunkSourceCache[idx]; - if( c == nullptr ) return (int)layer; + if( c == nullptr) return (int)layer; if (y < 0) y = 0; if (y >= maxBuildHeight) y = maxBuildHeight - 1; @@ -1381,7 +1382,7 @@ void Level::getNeighbourBrightnesses(int *brightnesses, LightLayer::variety laye return; } - int idx = ix * chunkSourceXZSize + iz; + int idx = MultiPlayerChunkCache::wrapCoord(ix, LEVEL_MIN_WIDTH) * LEVEL_MIN_WIDTH + MultiPlayerChunkCache::wrapCoord(iz, LEVEL_MIN_WIDTH); LevelChunk *c = chunkSourceCache[idx]; // 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we diff --git a/Minecraft.World/LevelData.cpp b/Minecraft.World/LevelData.cpp index 0edd2d7e..e9f22fdb 100644 --- a/Minecraft.World/LevelData.cpp +++ b/Minecraft.World/LevelData.cpp @@ -185,6 +185,7 @@ LevelData::LevelData(CompoundTag *tag) case LEVEL_WIDTH_SMALL: hostOptionworldSize = e_worldSize_Small; break; case LEVEL_WIDTH_MEDIUM: hostOptionworldSize = e_worldSize_Medium; break; case LEVEL_WIDTH_LARGE: hostOptionworldSize = e_worldSize_Large; break; + case LEVEL_WIDTH_EXPANDED: hostOptionworldSize = e_worldSize_Expanded; break; default: assert(0); break; } app.SetGameHostOption(eGameHostOption_WorldSize, hostOptionworldSize );