the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2#include "MultiPlayerChunkCache.h"
3#include "ServerChunkCache.h"
4#include "..\Minecraft.World\net.minecraft.world.level.chunk.h"
5#include "..\Minecraft.World\net.minecraft.world.level.dimension.h"
6#include "..\Minecraft.World\Arrays.h"
7#include "..\Minecraft.World\StringHelpers.h"
8#include "MinecraftServer.h"
9#include "ServerLevel.h"
10#include "..\Minecraft.World\Tile.h"
11#include "..\Minecraft.World\WaterLevelChunk.h"
12
13MultiPlayerChunkCache::MultiPlayerChunkCache(Level *level)
14{
15 XZSIZE = level->dimension->getXZSize(); // 4J Added
16 XZOFFSET = XZSIZE/2; // 4J Added
17 m_XZSize = XZSIZE;
18 hasData = new bool[XZSIZE * XZSIZE];
19 memset(hasData, 0, sizeof(bool) * XZSIZE * XZSIZE);
20
21 emptyChunk = new EmptyLevelChunk(level, byteArray(16 * 16 * Level::maxBuildHeight), 0, 0);
22
23 // For normal world dimension, create a chunk that can be used to create the illusion of infinite water at the edge of the world
24 if( level->dimension->id == 0 )
25 {
26 byteArray bytes = byteArray(16 * 16 * 128);
27
28 // Superflat.... make grass, not water...
29 if(level->getLevelData()->getGenerator() == LevelType::lvl_flat)
30 {
31 for( int x = 0; x < 16; x++ )
32 for( int y = 0; y < 128; y++ )
33 for( int z = 0; z < 16; z++ )
34 {
35 unsigned char tileId = 0;
36 if( y == 3 ) tileId = Tile::grass_Id;
37 else if( y <= 2 ) tileId = Tile::dirt_Id;
38
39 bytes[x << 11 | z << 7 | y] = tileId;
40 }
41 }
42 else
43 {
44 for( int x = 0; x < 16; x++ )
45 for( int y = 0; y < 128; y++ )
46 for( int z = 0; z < 16; z++ )
47 {
48 unsigned char tileId = 0;
49 if( y <= ( level->getSeaLevel() - 10 ) ) tileId = Tile::stone_Id;
50 else if( y < level->getSeaLevel() ) tileId = Tile::calmWater_Id;
51
52 bytes[x << 11 | z << 7 | y] = tileId;
53 }
54 }
55
56 waterChunk = new WaterLevelChunk(level, bytes, 0, 0);
57
58 delete[] bytes.data;
59
60 if(level->getLevelData()->getGenerator() == LevelType::lvl_flat)
61 {
62 for( int x = 0; x < 16; x++ )
63 for( int y = 0; y < 128; y++ )
64 for( int z = 0; z < 16; z++ )
65 {
66 if( y >= 3 )
67 {
68 ((WaterLevelChunk *)waterChunk)->setLevelChunkBrightness(LightLayer::Sky,x,y,z,15);
69 }
70 }
71 }
72 else
73 {
74 for( int x = 0; x < 16; x++ )
75 for( int y = 0; y < 128; y++ )
76 for( int z = 0; z < 16; z++ )
77 {
78 if( y >= ( level->getSeaLevel() - 1 ) )
79 {
80 ((WaterLevelChunk *)waterChunk)->setLevelChunkBrightness(LightLayer::Sky,x,y,z,15);
81 }
82 else
83 {
84 ((WaterLevelChunk *)waterChunk)->setLevelChunkBrightness(LightLayer::Sky,x,y,z,2);
85 }
86 }
87 }
88 }
89 else
90 {
91 waterChunk = NULL;
92 }
93
94 this->level = level;
95
96 this->cache = new LevelChunk *[XZSIZE * XZSIZE];
97 memset(this->cache, 0, XZSIZE * XZSIZE * sizeof(LevelChunk *));
98 InitializeCriticalSectionAndSpinCount(&m_csLoadCreate,4000);
99}
100
101MultiPlayerChunkCache::~MultiPlayerChunkCache()
102{
103 delete emptyChunk;
104 delete waterChunk;
105 delete cache;
106 delete hasData;
107
108 AUTO_VAR(itEnd, loadedChunkList.end());
109 for (AUTO_VAR(it, loadedChunkList.begin()); it != itEnd; it++)
110 delete *it;
111
112 DeleteCriticalSection(&m_csLoadCreate);
113}
114
115
116bool MultiPlayerChunkCache::hasChunk(int x, int z)
117{
118 // This cache always claims to have chunks, although it might actually just return empty data if it doesn't have anything
119 return true;
120}
121
122// 4J added - find out if we actually really do have a chunk in our cache
123bool MultiPlayerChunkCache::reallyHasChunk(int x, int z)
124{
125 int ix = x + XZOFFSET;
126 int iz = z + XZOFFSET;
127 // 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
128 if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return true;
129 if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return true;
130 int idx = ix * XZSIZE + iz;
131
132 LevelChunk *chunk = cache[idx];
133 if( chunk == NULL )
134 {
135 return false;
136 }
137 return hasData[idx];
138}
139
140void MultiPlayerChunkCache::drop(int x, int z)
141{
142 // 4J Stu - We do want to drop any entities in the chunks, especially for the case when a player is dead as they will
143 // not get the RemoveEntity packet if an entity is removed.
144 LevelChunk *chunk = getChunk(x, z);
145 if (!chunk->isEmpty())
146 {
147 // 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
148 // The tile entities are in general only created on the client by virtue of the chunk rebuild
149 chunk->unload(false);
150
151 // 4J - We just want to clear out the entities in the chunk, but everything else should be valid
152 chunk->loaded = true;
153 }
154}
155
156LevelChunk *MultiPlayerChunkCache::create(int x, int z)
157{
158 int ix = x + XZOFFSET;
159 int iz = z + XZOFFSET;
160 // Check we're in range of the stored level
161 if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk );
162 if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk );
163 int idx = ix * XZSIZE + iz;
164 LevelChunk *chunk = cache[idx];
165 LevelChunk *lastChunk = chunk;
166
167 if( chunk == NULL )
168 {
169 EnterCriticalSection(&m_csLoadCreate);
170
171 //LevelChunk *chunk;
172 if( g_NetworkManager.IsHost() ) // force here to disable sharing of data
173 {
174 // 4J-JEV: We are about to use shared data, abort if the server is stopped and the data is deleted.
175 if (MinecraftServer::getInstance()->serverHalted()) return NULL;
176
177 // If we're the host, then don't create the chunk, share data from the server's copy
178#ifdef _LARGE_WORLDS
179 LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(level->dimension->id)->cache->getChunkLoadedOrUnloaded(x,z);
180#else
181 LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(level->dimension->id)->cache->getChunk(x,z);
182#endif
183 chunk = new LevelChunk(level, x, z, serverChunk);
184 // 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
185 level->setTilesDirty( x * 16 , 0 , z * 16 , x * 16 + 15, 127, z * 16 + 15);
186 hasData[idx] = true;
187 }
188 else
189 {
190 // Passing an empty array into the LevelChunk ctor, which it now detects and sets up the chunk as compressed & empty
191 byteArray bytes;
192
193 chunk = new LevelChunk(level, bytes, x, z);
194
195 // 4J - changed to use new methods for lighting
196 chunk->setSkyLightDataAllBright();
197 // Arrays::fill(chunk->skyLight->data, (byte) 255);
198 }
199
200 chunk->loaded = true;
201
202 LeaveCriticalSection(&m_csLoadCreate);
203
204#if ( defined _WIN64 || defined __LP64__ )
205 if( InterlockedCompareExchangeRelease64((LONG64 *)&cache[idx],(LONG64)chunk,(LONG64)lastChunk) == (LONG64)lastChunk )
206#else
207 if( InterlockedCompareExchangeRelease((LONG *)&cache[idx],(LONG)chunk,(LONG)lastChunk) == (LONG)lastChunk )
208#endif // _DURANGO
209 {
210 // 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,
211 // then this will be calculated when the chunk data arrives.
212 if( g_NetworkManager.IsHost() )
213 {
214 chunk->recalcHeightmapOnly();
215 }
216
217 // Successfully updated the cache
218 EnterCriticalSection(&m_csLoadCreate);
219 loadedChunkList.push_back(chunk);
220 LeaveCriticalSection(&m_csLoadCreate);
221 }
222 else
223 {
224 // Something else must have updated the cache. Return that chunk and discard this one. This really shouldn't be happening
225 // in multiplayer
226 delete chunk;
227 return cache[idx];
228 }
229
230 }
231 else
232 {
233 chunk->load();
234 }
235
236 return chunk;
237}
238
239LevelChunk *MultiPlayerChunkCache::getChunk(int x, int z)
240{
241 int ix = x + XZOFFSET;
242 int iz = z + XZOFFSET;
243 // Check we're in range of the stored level
244 if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk );
245 if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return ( waterChunk ? waterChunk : emptyChunk );
246 int idx = ix * XZSIZE + iz;
247
248 LevelChunk *chunk = cache[idx];
249 if( chunk == NULL )
250 {
251 return emptyChunk;
252 }
253 else
254 {
255 return chunk;
256 }
257}
258
259bool MultiPlayerChunkCache::save(bool force, ProgressListener *progressListener)
260{
261 return true;
262}
263
264bool MultiPlayerChunkCache::tick()
265{
266 return false;
267}
268
269bool MultiPlayerChunkCache::shouldSave()
270{
271 return false;
272}
273
274void MultiPlayerChunkCache::postProcess(ChunkSource *parent, int x, int z)
275{
276}
277
278vector<Biome::MobSpawnerData *> *MultiPlayerChunkCache::getMobsAt(MobCategory *mobCategory, int x, int y, int z)
279{
280 return NULL;
281}
282
283TilePos *MultiPlayerChunkCache::findNearestMapFeature(Level *level, const wstring &featureName, int x, int y, int z)
284{
285 return NULL;
286}
287
288void MultiPlayerChunkCache::recreateLogicStructuresForChunk(int chunkX, int chunkZ)
289{
290}
291
292wstring MultiPlayerChunkCache::gatherStats()
293{
294 EnterCriticalSection(&m_csLoadCreate);
295 int size = (int)loadedChunkList.size();
296 LeaveCriticalSection(&m_csLoadCreate);
297 return L"MultiplayerChunkCache: " + _toString<int>(size);
298
299}
300
301void MultiPlayerChunkCache::dataReceived(int x, int z)
302{
303 int ix = x + XZOFFSET;
304 int iz = z + XZOFFSET;
305 // Check we're in range of the stored level
306 if( ( ix < 0 ) || ( ix >= XZSIZE ) ) return;
307 if( ( iz < 0 ) || ( iz >= XZSIZE ) ) return;
308 int idx = ix * XZSIZE + iz;
309 hasData[idx] = true;
310}