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 "System.h"
3#include "net.minecraft.world.entity.h"
4#include "net.minecraft.world.level.h"
5#include "net.minecraft.world.level.dimension.h"
6#include "net.minecraft.world.level.tile.h"
7#include "net.minecraft.world.phys.h"
8#include "net.minecraft.world.level.biome.h"
9#include "net.minecraft.world.entity.monster.h"
10#include "DataLayer.h"
11#include "SparseLightStorage.h"
12#include "BlockReplacements.h"
13#include "LevelChunk.h"
14#include "BasicTypeContainers.h"
15#include "..\Minecraft.Client\MinecraftServer.h"
16#include "..\Minecraft.Client\ServerLevel.h"
17#include "..\Minecraft.Client\ServerChunkCache.h"
18#include "..\Minecraft.Client\GameRenderer.h"
19#include "ItemEntity.h"
20#include "Minecart.h"
21
22#ifdef __PS3__
23#include "C4JSpursJob.h"
24#endif //__PS3__
25
26
27#ifdef SHARING_ENABLED
28CRITICAL_SECTION LevelChunk::m_csSharing;
29#endif
30#ifdef _ENTITIES_RW_SECTION
31// AP - use a RW critical section so we can have multiple threads reading the same data to avoid a clash
32CRITICAL_RW_SECTION LevelChunk::m_csEntities;
33#else
34CRITICAL_SECTION LevelChunk::m_csEntities;
35#endif
36CRITICAL_SECTION LevelChunk::m_csTileEntities;
37bool LevelChunk::touchedSky = false;
38
39void LevelChunk::staticCtor()
40{
41#ifdef SHARING_ENABLED
42 InitializeCriticalSection(&m_csSharing);
43#endif
44#ifdef _ENTITIES_RW_SECTION
45 InitializeCriticalRWSection(&m_csEntities);
46#else
47 InitializeCriticalSection(&m_csEntities);
48#endif
49 InitializeCriticalSection(&m_csTileEntities);
50}
51
52void LevelChunk::init(Level *level, int x, int z)
53{
54 biomes = byteArray(16 * 16);
55 for(int i = 0; i < 16 * 16; i++ )
56 {
57 biomes[i] = 0xff;
58 }
59#ifdef _ENTITIES_RW_SECTION
60 EnterCriticalRWSection(&m_csEntities, true);
61#else
62 EnterCriticalSection(&m_csEntities);
63#endif
64 entityBlocks = new vector<shared_ptr<Entity> > *[ENTITY_BLOCKS_LENGTH];
65#ifdef _ENTITIES_RW_SECTION
66 LeaveCriticalRWSection(&m_csEntities, true);
67#else
68 LeaveCriticalSection(&m_csEntities);
69#endif
70
71 terrainPopulated = 0;
72 m_unsaved = false;
73 lastSaveHadEntities = false;
74 lastSaveTime = 0;
75 dontSave = false;
76 loaded = false;
77 minHeight = 0;
78 hasGapsToCheck = false;
79 seenByPlayer = true; // 4J Stu - Always true
80
81 // 4J Stu - Not using this
82 checkLightPosition = 0; //LIGHT_CHECK_MAX_POS;
83
84 this->level = level;
85 this->x = x;
86 this->z = z;
87 MemSect(1);
88 heightmap = byteArray(16 * 16);
89#ifdef _ENTITIES_RW_SECTION
90 EnterCriticalRWSection(&m_csEntities, true);
91#else
92 EnterCriticalSection(&m_csEntities);
93#endif
94 for (int i = 0; i < ENTITY_BLOCKS_LENGTH; i++)
95 {
96 entityBlocks[i] = new vector<shared_ptr<Entity> >();
97 }
98#ifdef _ENTITIES_RW_SECTION
99 LeaveCriticalRWSection(&m_csEntities, true);
100#else
101 LeaveCriticalSection(&m_csEntities);
102#endif
103
104 MemSect(0);
105
106 lowestHeightmap = 256;
107 inhabitedTime = 0;
108
109 // Optimisation brought forward from 1.8.2, change from int to unsigned char & this special value changed from -999 to 255
110 for(int i = 0; i < 16 * 16; i++ )
111 {
112 rainHeights[i] = 255;
113 }
114 // 4J - lighting change brought forward from 1.8.2, introduced an array of bools called gapsToRecheck, which are now a single bit in array of nybble flags in this version
115 for(int i = 0; i < 8 * 16; i++ )
116 {
117 columnFlags[i] = 0;
118 }
119
120 // 4J added - to flag if any emissive tile has been added to this chunk (will be cleared when lighting has been successfully completed for this chunk). Defaulting to true
121 // as emissive things can be made and passed in the the initialisation block array.
122 emissiveAdded = true;
123
124#ifdef _LARGE_WORLDS
125 m_bUnloaded = false; // 4J Added
126 m_unloadedEntitiesTag = NULL;
127#endif
128}
129
130// This ctor is used for loading a save into
131LevelChunk::LevelChunk(Level *level, int x, int z) : ENTITY_BLOCKS_LENGTH( Level::maxBuildHeight/16 )
132{
133 init(level, x, z);
134 lowerBlocks = new CompressedTileStorage();
135 lowerData = NULL;
136 lowerSkyLight = NULL;
137 lowerBlockLight = NULL;
138 serverTerrainPopulated = NULL;
139
140 if(Level::maxBuildHeight > Level::COMPRESSED_CHUNK_SECTION_HEIGHT)
141 {
142 // Create all these as empty, as we may not be loading any data into them
143 upperBlocks = new CompressedTileStorage(true);
144 upperData = new SparseDataStorage(true);
145 upperSkyLight = new SparseLightStorage(true, true);
146 upperBlockLight = new SparseLightStorage(false, true);
147 }
148 else
149 {
150 upperBlocks = NULL;
151 upperData = NULL;
152 upperSkyLight = NULL;
153 upperBlockLight = NULL;
154 }
155
156#ifdef SHARING_ENABLED
157 sharingTilesAndData = false;
158#endif
159}
160
161// 4J - note that since we now compress the block storage, the parameter blocks is used as a source of data, but doesn't get used As the source data so needs
162// to be deleted after calling this ctor.
163LevelChunk::LevelChunk(Level *level, byteArray blocks, int x, int z) : ENTITY_BLOCKS_LENGTH( Level::maxBuildHeight/16 )
164{
165 init(level, x, z);
166
167 // We'll be creating this as "empty" when this ctor is called on the client, as a result of a chunk becoming visible (but we don't have the data yet for it).
168 // In this case, we want to keep memory usage down and so create all data as empty/compressed as possible. On the client we get the full data for the chunk as a single
169 // update in a block region update packet, and so there is a single point where it is good to compress the data.
170 bool createEmpty = ( blocks.data == NULL );
171
172 if( createEmpty )
173 {
174 lowerBlocks = new CompressedTileStorage(true);
175 lowerData = new SparseDataStorage(true);
176
177 lowerSkyLight = new SparseLightStorage(true, true);
178 lowerBlockLight = new SparseLightStorage(false, true);
179 }
180 else
181 {
182 lowerBlocks = new CompressedTileStorage(blocks,0);
183 lowerData = new SparseDataStorage();
184
185 // 4J - changed to new SpareLightStorage class for these
186 lowerSkyLight = new SparseLightStorage(true);
187 lowerBlockLight = new SparseLightStorage(false);
188 }
189 // skyLight = new DataLayer(blocks.length, level->depthBits);
190 // blockLight = new DataLayer(blocks.length, level->depthBits);
191
192 if(Level::maxBuildHeight > Level::COMPRESSED_CHUNK_SECTION_HEIGHT)
193 {
194 if(blocks.length > Level::COMPRESSED_CHUNK_SECTION_TILES) upperBlocks = new CompressedTileStorage(blocks,Level::COMPRESSED_CHUNK_SECTION_TILES);
195 else upperBlocks = new CompressedTileStorage(true);
196 upperData = new SparseDataStorage(true);
197 upperSkyLight = new SparseLightStorage(true, true);
198 upperBlockLight = new SparseLightStorage(false, true);
199 }
200 else
201 {
202 upperBlocks = NULL;
203 upperData = NULL;
204 upperSkyLight = NULL;
205 upperBlockLight = NULL;
206 }
207
208 serverTerrainPopulated = NULL;
209#ifdef SHARING_ENABLED
210 sharingTilesAndData = false;
211#endif
212}
213
214// 4J - this ctor added to be able to make a levelchunk that shares its underlying block data between the server chunk cache & the multiplayer chunk cache.
215// The original version this is shared from owns all the data that is shared into this copy, so it isn't deleted in the dtor.
216LevelChunk::LevelChunk(Level *level, int x, int z, LevelChunk *lc) : ENTITY_BLOCKS_LENGTH( Level::maxBuildHeight/16 )
217{
218 init(level, x, z);
219
220 // 4J Stu - Copy over the biome data
221 memcpy(biomes.data,lc->biomes.data,biomes.length);
222
223#ifdef SHARING_ENABLED
224 lowerBlocks = lc->lowerBlocks;
225 lowerData = lc->lowerData;
226 lowerSkyLight = new SparseLightStorage( lc->lowerSkyLight );
227 lowerBlockLight = new SparseLightStorage( lc->lowerBlockLight );
228 upperBlocks = lc->upperBlocks;
229 upperData = lc->upperData;
230 upperSkyLight = new SparseLightStorage( lc->upperSkyLight );
231 upperBlockLight = new SparseLightStorage( lc->upperBlockLight );
232
233 sharingTilesAndData = true;
234 serverTerrainPopulated = &lc->terrainPopulated;
235#else
236 this->blocks = new CompressedTileStorage(lc->blocks);
237 this->data = new SparseDataStorage(lc->data);
238 this->skyLight = new SparseLightStorage(lc->skyLight);
239 this->blockLight = new SparseLightStorage(lc->blockLight);
240 serverTerrainPopulated = NULL;
241#endif
242}
243
244// 4J Added so we can track unsaved chunks better
245void LevelChunk::setUnsaved(bool unsaved)
246{
247#ifdef _LARGE_WORLDS
248 if(m_unsaved != unsaved)
249 {
250 if(unsaved) level->incrementUnsavedChunkCount();
251 else level->decrementUnsavedChunkCount();
252 }
253#endif
254 m_unsaved = unsaved;
255}
256
257void LevelChunk::stopSharingTilesAndData()
258{
259#ifdef SHARING_ENABLED
260 EnterCriticalSection(&m_csSharing);
261 lastUnsharedTime = System::currentTimeMillis();
262 if( !sharingTilesAndData )
263 {
264 LeaveCriticalSection(&m_csSharing);
265 return;
266 }
267
268 // If we've got a reference to a server chunk's terrainPopulated flag that this LevelChunk is sharing with, then don't consider unsharing
269 // if it hasn't been set. This is because post-processing things that update the server chunks won't actually cause the server to send any updates
270 // to the tiles that they alter, so they completely depend on the data not being shared for it to get from the server to here
271 if( ( serverTerrainPopulated ) && ( ( (*serverTerrainPopulated) & sTerrainPopulatedAllAffecting ) != sTerrainPopulatedAllAffecting ) )
272 {
273 LeaveCriticalSection(&m_csSharing);
274 return;
275 }
276
277 // If this is the empty chunk, then it will have a x & z of 0,0 - if we don't drop out here we'll end up unsharing the chunk at this location for no reason
278 if( isEmpty() )
279 {
280 LeaveCriticalSection(&m_csSharing);
281 return;
282 }
283
284 MemSect(47);
285
286 // Changed to used compressed storage - these CTORs make deep copies of the storage passed as a parameter
287 lowerBlocks = new CompressedTileStorage( lowerBlocks );
288
289 // Changed to use new sparse data storage - this CTOR makes a deep copy of the storage passed as a parameter
290 lowerData = new SparseDataStorage(lowerData);
291
292 if(Level::maxBuildHeight > Level::COMPRESSED_CHUNK_SECTION_HEIGHT)
293 {
294 upperBlocks = new CompressedTileStorage( upperBlocks );
295 upperData = new SparseDataStorage(upperData);
296 }
297 else
298 {
299 upperBlocks = NULL;
300 upperData = NULL;
301 }
302
303 /*
304 newDataLayer = new DataLayer(skyLight->data.length*2, level->depthBits);
305 XMemCpy(newDataLayer->data.data, skyLight->data.data, skyLight->data.length);
306 skyLight = newDataLayer;
307
308 newDataLayer = new DataLayer(blockLight->data.length*2, level->depthBits);
309 XMemCpy(newDataLayer->data.data, blockLight->data.data, blockLight->data.length);
310 blockLight = newDataLayer;
311 */
312
313 sharingTilesAndData = false;
314 MemSect(0);
315 LeaveCriticalSection(&m_csSharing);
316#endif
317}
318
319// This is a slight variation on the normal start/stop sharing methods here as in general we aren't sharing lighting anymore. This method
320// discards the client lighting information, and sets up new (non-shared) lighting to match the server. So generally like stop sharing,
321// for the case where we're already not sharing
322void LevelChunk::reSyncLighting()
323{
324#ifdef SHARING_ENABLED
325 EnterCriticalSection(&m_csSharing);
326
327 if( isEmpty() )
328 {
329 LeaveCriticalSection(&m_csSharing);
330 return;
331 }
332
333#ifdef _LARGE_WORLDS
334 LevelChunk *lc = MinecraftServer::getInstance()->getLevel( level->dimension->id )->cache->getChunkLoadedOrUnloaded(x,z);
335#else
336 LevelChunk *lc = MinecraftServer::getInstance()->getLevel( level->dimension->id )->cache->getChunk(x,z);
337#endif
338
339 GameRenderer::AddForDelete(lowerSkyLight);
340 lowerSkyLight = new SparseLightStorage( lc->lowerSkyLight );
341 GameRenderer::FinishedReassigning();
342 GameRenderer::AddForDelete(lowerBlockLight);
343 lowerBlockLight = new SparseLightStorage( lc->lowerBlockLight );
344 GameRenderer::FinishedReassigning();
345
346 if(Level::maxBuildHeight > Level::COMPRESSED_CHUNK_SECTION_HEIGHT)
347 {
348 GameRenderer::AddForDelete(upperSkyLight);
349 upperSkyLight = new SparseLightStorage( lc->upperSkyLight );
350 GameRenderer::FinishedReassigning();
351 GameRenderer::AddForDelete(upperBlockLight);
352 upperBlockLight = new SparseLightStorage( lc->upperBlockLight );
353 GameRenderer::FinishedReassigning();
354 }
355 LeaveCriticalSection(&m_csSharing);
356#endif
357}
358
359void LevelChunk::startSharingTilesAndData(int forceMs)
360{
361#ifdef SHARING_ENABLED
362 EnterCriticalSection(&m_csSharing);
363 if( sharingTilesAndData )
364 {
365 LeaveCriticalSection(&m_csSharing);
366 return;
367 }
368
369 // If this is the empty chunk, then it will have a x & z of 0,0 - we'll end up potentially loading the 0,0 block if we proceed. And it obviously
370 // doesn't make sense to go resharing the 0,0 block on behalf of an empty chunk either
371 if( isEmpty() )
372 {
373 LeaveCriticalSection(&m_csSharing);
374 return;
375 }
376
377#ifdef _LARGE_WORLDS
378 LevelChunk *lc = MinecraftServer::getInstance()->getLevel( level->dimension->id )->cache->getChunkLoadedOrUnloaded(x,z);
379#else
380 LevelChunk *lc = MinecraftServer::getInstance()->getLevel( level->dimension->id )->cache->getChunk(x,z);
381#endif
382
383 // In normal usage, chunks should only reshare if their local data matched that on the server. The forceMs parameter though can
384 // be used to force a share if resharing hasn't happened after a period of time
385 if( forceMs == 0 )
386 {
387 // Normal behaviour - just check that the data matches, and don't start sharing data if it doesn't (yet)
388 if( !lowerBlocks->isSameAs(lc->lowerBlocks) || (upperBlocks && lc->upperBlocks && !upperBlocks->isSameAs(lc->upperBlocks) ) )
389 {
390 LeaveCriticalSection(&m_csSharing);
391 return;
392 }
393 }
394 else
395 {
396 // Only force if it has been more than forceMs milliseconds since we last wanted to unshare this chunk
397 __int64 timenow = System::currentTimeMillis();
398 if( ( timenow - lastUnsharedTime ) < forceMs )
399 {
400 LeaveCriticalSection(&m_csSharing);
401 return;
402 }
403 }
404
405 // Note - data that was shared isn't directly deleted here, as it might still be in use in the game render update thread. Let that thread
406 // delete it when it is safe to do so instead.
407 GameRenderer::AddForDelete(lowerBlocks);
408 lowerBlocks = lc->lowerBlocks;
409 GameRenderer::FinishedReassigning();
410
411 GameRenderer::AddForDelete(lowerData);
412 lowerData = lc->lowerData;
413 GameRenderer::FinishedReassigning();
414
415 if(Level::maxBuildHeight > Level::COMPRESSED_CHUNK_SECTION_HEIGHT)
416 {
417 GameRenderer::AddForDelete(upperBlocks);
418 upperBlocks = lc->upperBlocks;
419 GameRenderer::FinishedReassigning();
420
421 GameRenderer::AddForDelete(upperData);
422 upperData = lc->upperData;
423 GameRenderer::FinishedReassigning();
424 }
425
426 sharingTilesAndData = true;
427 LeaveCriticalSection(&m_csSharing);
428#endif
429}
430
431LevelChunk::~LevelChunk()
432{
433#ifdef SHARING_ENABLED
434 if( !sharingTilesAndData )
435#endif
436 {
437 delete lowerData;
438 delete lowerBlocks;
439 if(upperData) delete upperData;
440 if(upperBlocks) delete upperBlocks;
441 }
442
443 delete lowerSkyLight;
444 delete lowerBlockLight;
445 if(upperSkyLight) delete upperSkyLight;
446 if(upperBlockLight) delete upperBlockLight;
447
448 delete[] heightmap.data;
449
450 for(int i = 0; i < ENTITY_BLOCKS_LENGTH; ++i)
451 delete entityBlocks[i];
452 delete[] entityBlocks;
453
454 delete[] biomes.data;
455
456#ifdef _LARGE_WORLDS
457 delete m_unloadedEntitiesTag;
458#endif
459}
460
461bool LevelChunk::isAt(int x, int z)
462{
463 return x == this->x && z == this->z;
464}
465
466int LevelChunk::getHeightmap(int x, int z)
467{
468 return heightmap[z << 4 | x] & 0xff;
469}
470
471int LevelChunk::getHighestSectionPosition()
472{
473 return Level::maxBuildHeight - 16;
474 // 4J Stu - Unused
475 //for (int i = sections.length - 1; i >= 0; i--) {
476 // if (sections[i] != null) { // && !sections[i].isEmpty()) {
477 // return sections[i].getYPosition();
478 // }
479 //}
480 //return 0;
481}
482
483void LevelChunk::recalcBlockLights()
484{
485}
486
487
488
489void LevelChunk::recalcHeightmapOnly()
490{
491#ifdef __PSVITA__
492 // AP - lets fetch ALL the chunk data at the same time for a good speed up
493 byteArray blockData = byteArray(Level::CHUNK_TILE_COUNT);
494 getBlockData(blockData);
495#endif
496
497 int min = Level::maxBuildHeight - 1;
498 for (int x = 0; x < 16; x++)
499 for (int z = 0; z < 16; z++)
500 {
501 rainHeights[x + (z << 4)] = 255; // 4J - changed from int to unsigned char & this special value changed from -999 to 255
502
503 int y = Level::maxBuildHeight - 1;
504 // int p = x << level->depthBitsPlusFour | z << level->depthBits; // 4J - removed
505#ifdef __PSVITA__
506 int Index = ( x << 11 ) + ( z << 7 );
507 int offset = Level::COMPRESSED_CHUNK_SECTION_TILES;
508 y = 127;
509 while (y > 0 && Tile::lightBlock[blockData[Index + offset + (y - 1)]] == 0) // 4J - was blocks->get() was blocks[p + y - 1]
510 {
511 y--;
512 }
513 if( y == 0 )
514 {
515 offset = 0;
516 y = 127;
517 while (y > 0 && Tile::lightBlock[blockData[Index + offset + (y - 1)]] == 0) // 4J - was blocks->get() was blocks[p + y - 1]
518 {
519 y--;
520 }
521 }
522 else
523 {
524 y += 128;
525 }
526#else
527 CompressedTileStorage *blocks = (y-1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT?upperBlocks : lowerBlocks;
528 while (y > 0 && Tile::lightBlock[blocks->get(x,(y - 1) % Level::COMPRESSED_CHUNK_SECTION_HEIGHT,z) & 0xff] == 0) // 4J - was blocks->get() was blocks[p + y - 1]
529 {
530 y--;
531 blocks = (y-1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT?upperBlocks : lowerBlocks;
532 }
533#endif
534 heightmap[z << 4 | x] = (byte) y;
535 if (y < min) min = y;
536 }
537
538 this->minHeight = min;
539 this->setUnsaved(true);
540
541#ifdef __PSVITA__
542 delete blockData.data;
543#endif
544}
545
546void LevelChunk::recalcHeightmap()
547{
548#ifdef __PSVITA__
549 // AP - lets fetch ALL the chunk data at the same time for a good speed up
550 byteArray blockData = byteArray(Level::CHUNK_TILE_COUNT);
551 getBlockData(blockData);
552#endif
553 lowestHeightmap = Integer::MAX_VALUE;
554
555 int min = Level::maxBuildHeight - 1;
556 for (int x = 0; x < 16; x++)
557 for (int z = 0; z < 16; z++)
558 {
559 int y = Level::maxBuildHeight - 1;
560 // int p = x << level->depthBitsPlusFour | z << level->depthBits; // 4J - removed
561
562#ifdef __PSVITA__
563 int Index = ( x << 11 ) + ( z << 7 );
564 int offset = Level::COMPRESSED_CHUNK_SECTION_TILES;
565 y = 127;
566 while (y > 0 && Tile::lightBlock[blockData[Index + offset + (y - 1)]] == 0) // 4J - was blocks->get() was blocks[p + y - 1]
567 {
568 y--;
569 }
570 if( y == 0 )
571 {
572 offset = 0;
573 y = 127;
574 while (y > 0 && Tile::lightBlock[blockData[Index + offset + (y - 1)]] == 0) // 4J - was blocks->get() was blocks[p + y - 1]
575 {
576 y--;
577 }
578 }
579 else
580 {
581 y += 128;
582 }
583#else
584 CompressedTileStorage *blocks = (y-1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT?upperBlocks : lowerBlocks;
585 while (y > 0 && Tile::lightBlock[blocks->get(x,(y-1) % Level::COMPRESSED_CHUNK_SECTION_HEIGHT,z) & 0xff] == 0) // 4J - was blocks->get() was blocks[p + y - 1]
586 {
587 y--;
588 blocks = (y-1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT?upperBlocks : lowerBlocks;
589 }
590#endif
591 heightmap[z << 4 | x] = (byte) y;
592 if (y < min) min = y;
593 if (y < lowestHeightmap) lowestHeightmap = y;
594
595 if (!level->dimension->hasCeiling)
596 {
597 int br = Level::MAX_BRIGHTNESS;
598 int yy = Level::maxBuildHeight - 1;
599#ifdef __PSVITA__
600 int offset = Level::COMPRESSED_CHUNK_SECTION_TILES;
601 SparseLightStorage *skyLight = upperSkyLight;
602 yy = 127;
603 do
604 {
605 br -= Tile::lightBlock[blockData[Index + offset + yy]]; // 4J - blocks->get() was blocks[p + yy]
606 if (br > 0)
607 {
608 skyLight->set(x, yy, z, br);
609 }
610 yy--;
611 } while (yy > 0 && br > 0);
612
613 if( yy == 0 && br > 0 )
614 {
615 offset = 0;
616 skyLight = lowerSkyLight;
617 yy = 127;
618 do
619 {
620 br -= Tile::lightBlock[blockData[Index + offset + yy]]; // 4J - blocks->get() was blocks[p + yy]
621 if (br > 0)
622 {
623 skyLight->set(x, yy, z, br);
624 }
625 yy--;
626 } while (yy > 0 && br > 0);
627 }
628#else
629 CompressedTileStorage *blocks = yy >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT?upperBlocks : lowerBlocks;
630 SparseLightStorage *skyLight = yy >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT? upperSkyLight : lowerSkyLight;
631 do
632 {
633 br -= Tile::lightBlock[blocks->get(x,(yy % Level::COMPRESSED_CHUNK_SECTION_HEIGHT),z) & 0xff]; // 4J - blocks->get() was blocks[p + yy]
634 if (br > 0)
635 {
636 skyLight->set(x, (yy % Level::COMPRESSED_CHUNK_SECTION_HEIGHT), z, br);
637 }
638 yy--;
639 blocks = yy >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT?upperBlocks : lowerBlocks;
640 skyLight = yy >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT? upperSkyLight : lowerSkyLight;
641 } while (yy > 0 && br > 0);
642#endif
643 }
644 }
645
646 this->minHeight = min;
647
648 for (int x = 0; x < 16; x++)
649 for (int z = 0; z < 16; z++)
650 {
651 lightGaps(x, z);
652 }
653
654 this->setUnsaved(true);
655
656#ifdef __PSVITA__
657 delete blockData.data;
658#endif
659}
660
661// 4J - this code is fully commented out in the java version, but we have reimplemented something here to try and light
662// lava as chunks are created, as otherwise they get shared before being lit, and then their lighting gets updated on the client
663// and causes framerate stutters.
664void LevelChunk::lightLava()
665{
666 if( !emissiveAdded ) return;
667
668 for (int x = 0; x < 16; x++)
669 for (int z = 0; z < 16; z++)
670 {
671 // int p = x << 11 | z << 7; // 4J - removed
672 int ymax = getHeightmap(x,z);
673 for (int y = 0; y < Level::COMPRESSED_CHUNK_SECTION_HEIGHT; y++)
674 {
675 CompressedTileStorage *blocks = lowerBlocks;
676 int emit = Tile::lightEmission[blocks->get(x,y,z)]; // 4J - blocks->get() was blocks[p + y]
677 if( emit > 0 )
678 {
679 // printf("(%d,%d,%d)",this->x * 16 + x, y, this->z * 16 + z);
680 // We'll be calling this function for a lot of chunks as they are post-processed. For every chunk that is
681 // post-processed we're calling this for each of its neighbours in case some post-processing also created something
682 // that needed lighting outside the starting chunk. Because of this, do a quick test on any emissive blocks that have
683 // been added to see if checkLight has already been run on this particular block - this is straightforward to check
684 // as being emissive blocks they'll have their block brightness set to their lightEmission level in this case.
685 if( getBrightness(LightLayer::Block, x, y, z) < emit )
686 {
687 level->checkLight( LightLayer::Block, this->x * 16 + x, y, this->z * 16 + z, true);
688 }
689 }
690 }
691 }
692 emissiveAdded = false;
693}
694
695void LevelChunk::lightGaps(int x, int z)
696{
697 // 4J - lighting change brought forward from 1.8.2, introduced an array of bools called gapsToRecheck, which are now a single bit in array of nybbles in this version
698 int slot = ( x >> 1 ) | (z * 8);
699 int shift = ( x & 1 ) * 4;
700 columnFlags[slot] |= ( eColumnFlag_recheck << shift );
701 hasGapsToCheck = true;
702}
703void LevelChunk::recheckGaps(bool bForce)
704{
705 // 4J added - otherwise we can end up doing a very broken kind of lighting since for an empty chunk, the heightmap is all zero, but it
706 // still has an x and z of 0 which means that the level->getHeightmap references in here find a real chunk near the origin, and then attempt
707 // to light massive gaps between the height of 0 and whatever heights are in those.
708 if( isEmpty() ) return;
709
710 // 4J added
711 int minXZ = - (level->dimension->getXZSize() * 16 ) / 2;
712 int maxXZ = (level->dimension->getXZSize() * 16 ) / 2 - 1;
713
714 // 4J - note - this test will currently return true for chunks at the edge of our world. Making further checks inside the loop now to address this issue.
715 if (level->hasChunksAt(x * 16 + 8, Level::maxBuildHeight / 2, z * 16 + 8, 16))
716 {
717 for (int x = 0; x < 16; x++)
718 for (int z = 0; z < 16; z++)
719 {
720 int slot = ( x >> 1 ) | (z * 8);
721 int shift = ( x & 1 ) * 4;
722 if (bForce || ( columnFlags[slot] & ( eColumnFlag_recheck << shift ) ) )
723 {
724 columnFlags[slot] &= ~( eColumnFlag_recheck << shift );
725 int height = getHeightmap(x, z);
726 int xOffs = (this->x * 16) + x;
727 int zOffs = (this->z * 16) + z;
728
729 // 4J - rewritten this to make sure that the minimum neighbour height which is calculated doesn't involve getting any heights from beyond the edge of the world,
730 // which can lead to large, very expensive, non-existent cliff edges to be lit
731 int nmin = level->getHeightmap(xOffs, zOffs);
732 if( xOffs - 1 >= minXZ )
733 {
734 int n = level->getHeightmap(xOffs - 1, zOffs);
735 if ( n < nmin ) nmin = n;
736 }
737 if( xOffs + 1 <= maxXZ )
738 {
739 int n = level->getHeightmap(xOffs + 1, zOffs);
740 if ( n < nmin ) nmin = n;
741 }
742 if( zOffs - 1 >= minXZ )
743 {
744 int n = level->getHeightmap(xOffs, zOffs - 1);
745 if ( n < nmin ) nmin = n;
746 }
747 if( zOffs + 1 <= maxXZ )
748 {
749 int n = level->getHeightmap(xOffs, zOffs + 1);
750 if ( n < nmin ) nmin = n;
751 }
752 lightGap(xOffs, zOffs, nmin);
753
754 if( !bForce ) // 4J - if doing a full forced thing over every single column, we don't need to do these offset checks too
755 {
756 if( xOffs - 1 >= minXZ ) lightGap(xOffs - 1, zOffs, height);
757 if( xOffs + 1 <= maxXZ ) lightGap(xOffs + 1, zOffs, height);
758 if( zOffs - 1 >= minXZ ) lightGap(xOffs, zOffs - 1, height);
759 if( zOffs + 1 <= maxXZ ) lightGap(xOffs, zOffs + 1, height);
760 }
761 hasGapsToCheck = false;
762 }
763 }
764 }
765}
766
767
768void LevelChunk::lightGap(int x, int z, int source)
769{
770 int height = level->getHeightmap(x, z);
771
772 if (height > source)
773 {
774 lightGap(x, z, source, height + 1);
775 }
776 else if (height < source)
777 {
778 lightGap(x, z, height, source + 1);
779 }
780}
781
782void LevelChunk::lightGap(int x, int z, int y1, int y2)
783{
784 if (y2 > y1)
785 {
786 if (level->hasChunksAt(x, Level::maxBuildHeight / 2, z, 16))
787 {
788 for (int y = y1; y < y2; y++)
789 {
790 level->checkLight(LightLayer::Sky, x, y, z);
791 }
792 this->setUnsaved(true);
793 }
794 }
795}
796
797void LevelChunk::recalcHeight(int x, int yStart, int z)
798{
799 int yOld = heightmap[z << 4 | x] & 0xff;
800 int y = yOld;
801 if (yStart > yOld) y = yStart;
802
803 // int p = x << level->depthBitsPlusFour | z << level->depthBits; // 4J - removed
804
805 CompressedTileStorage *blocks = (y-1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT?upperBlocks : lowerBlocks;
806 while (y > 0 && Tile::lightBlock[blocks->get(x,(y-1) % Level::COMPRESSED_CHUNK_SECTION_HEIGHT,z) & 0xff] == 0) // 4J - blocks->get() was blocks[p + y - 1]
807 {
808 y--;
809 blocks = (y-1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT?upperBlocks : lowerBlocks;
810 }
811 if (y == yOld) return;
812
813 // level->lightColumnChanged(x, z, y, yOld); // 4J - this call moved below & corrected - see comment further down
814 heightmap[z << 4 | x] = (byte) y;
815
816 if (y < minHeight)
817 {
818 minHeight = y;
819 }
820 else
821 {
822 int min = Level::maxBuildHeight - 1;
823 for (int _x = 0; _x < 16; _x++)
824 for (int _z = 0; _z < 16; _z++)
825 {
826 if ((heightmap[_z << 4 | _x] & 0xff) < min) min = (heightmap[_z << 4 | _x] & 0xff);
827 }
828 this->minHeight = min;
829 }
830
831 int xOffs = (this->x * 16) + x;
832 int zOffs = (this->z * 16) + z;
833 if (!level->dimension->hasCeiling)
834 {
835 if (y < yOld)
836 {
837 SparseLightStorage *skyLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT? upperSkyLight : lowerSkyLight;
838 for (int yy = y; yy < yOld; yy++)
839 {
840 skyLight = yy >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT? upperSkyLight : lowerSkyLight;
841 skyLight->set(x, (yy % Level::COMPRESSED_CHUNK_SECTION_HEIGHT), z, 15);
842 }
843 } else
844 {
845 // 4J - lighting change brought forward from 1.8.2
846 // level->updateLight(LightLayer::Sky, xOffs, yOld, zOffs, xOffs, y, zOffs);
847 SparseLightStorage *skyLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT? upperSkyLight : lowerSkyLight;
848 for (int yy = yOld; yy < y; yy++)
849 {
850 skyLight = yy >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT? upperSkyLight : lowerSkyLight;
851 skyLight->set(x, (yy % Level::COMPRESSED_CHUNK_SECTION_HEIGHT), z, 0);
852 }
853 }
854
855 int br = 15;
856
857 SparseLightStorage *skyLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT? upperSkyLight : lowerSkyLight;
858 while (y > 0 && br > 0)
859 {
860 y--;
861 skyLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT? upperSkyLight : lowerSkyLight;
862 int block = Tile::lightBlock[getTile(x, y, z)];
863 if (block == 0) block = 1;
864 br -= block;
865 if (br < 0) br = 0;
866 skyLight->set(x, (y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT), z, br);
867 // level.updateLightIfOtherThan(LightLayer.Sky, xOffs, y, zOffs,
868 // -1);
869 }
870 }
871 // 4J - changed to use xOffs and zOffs rather than the (incorrect) x and z it used to, and also moved so that it happens after all the lighting should be
872 // done by this stage, as this will trigger our asynchronous render updates immediately (potentially) so don't want to say that the lighting is done & then do it
873 level->lightColumnChanged(xOffs, zOffs, y, yOld);
874
875 // 4J - lighting changes brought forward from 1.8.2
876 int height = heightmap[z << 4 | x];
877 int y1 = yOld;
878 int y2 = height;
879 if (y2 < y1)
880 {
881 int tmp = y1;
882 y1 = y2;
883 y2 = tmp;
884 }
885 if (height < lowestHeightmap) lowestHeightmap = height;
886 if (!level->dimension->hasCeiling)
887 {
888 PIXBeginNamedEvent(0,"Light gaps");
889 lightGap(xOffs - 1, zOffs, y1, y2);
890 lightGap(xOffs + 1, zOffs, y1, y2);
891 lightGap(xOffs, zOffs - 1, y1, y2);
892 lightGap(xOffs, zOffs + 1, y1, y2);
893 lightGap(xOffs, zOffs, y1, y2);
894 PIXEndNamedEvent();
895 }
896
897 this->setUnsaved(true);
898}
899
900/**
901* The purpose of this method is to allow the EmptyLevelChunk to be all air
902* but still block light. See EmptyLevelChunk.java
903*
904* @param x
905* @param y
906* @param z
907* @return
908*/
909int LevelChunk::getTileLightBlock(int x, int y, int z)
910{
911 return Tile::lightBlock[getTile(x, y, z)];
912}
913
914int LevelChunk::getTile(int x, int y, int z)
915{
916 CompressedTileStorage *blocks = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperBlocks : lowerBlocks;
917 return blocks->get(x,y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT,z);
918}
919
920bool LevelChunk::setTileAndData(int x, int y, int z, int _tile, int _data)
921{
922 byte tile = (byte) _tile;
923
924 // Optimisation brought forward from 1.8.2, change from int to unsigned char & this special value changed from -999 to 255
925 int slot = z << 4 | x;
926
927 if (y >= ((int)rainHeights[slot]) - 1)
928 {
929 rainHeights[slot] = 255;
930 }
931
932 int oldHeight = heightmap[slot] & 0xff;
933
934 CompressedTileStorage *blocks = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperBlocks : lowerBlocks;
935 SparseDataStorage *data = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperData : lowerData;
936 int old = blocks->get(x,y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT,z);
937 int oldData = data->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z);
938 if (old == _tile && oldData == _data)
939 {
940 // 4J Stu - Need to do this here otherwise double chests don't always work correctly
941 shared_ptr<TileEntity> te = getTileEntity(x, y, z);
942 if (te != NULL)
943 {
944 te->clearCache();
945 }
946
947 return false;
948 }
949 int xOffs = this->x * 16 + x;
950 int zOffs = this->z * 16 + z;
951 if (old != 0 && !level->isClientSide)
952 {
953 Tile::tiles[old]->onRemoving(level, xOffs, y, zOffs, oldData);
954 }
955 PIXBeginNamedEvent(0,"Chunk setting tile");
956 blocks->set(x,y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT,z,tile);
957 PIXEndNamedEvent();
958 if (old != 0)
959 {
960 if (!level->isClientSide)
961 {
962 Tile::tiles[old]->onRemove(level, xOffs, y, zOffs, old, oldData);
963 }
964 else if (Tile::tiles[old]->isEntityTile() && old != _tile )
965 {
966 level->removeTileEntity(xOffs, y, zOffs);
967 }
968 }
969 PIXBeginNamedEvent(0,"Chunk setting data");
970 data->set(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z, _data);
971 PIXEndNamedEvent();
972
973 // 4J added - flag if something emissive is being added. This is used during level creation to determine what chunks need extra lighting processing
974 if( Tile::lightEmission[tile & 0xff] > 0 )
975 {
976 emissiveAdded = true;
977 }
978
979 PIXBeginNamedEvent(0,"Updating lighting");
980 // 4J - There isn't any point in recalculating heights or updating sky lighting if this tile has
981 // the same light-blocking capabilities as the one it is replacing
982 if( Tile::lightBlock[tile & 0xff] != Tile::lightBlock[old & 0xff] )
983 {
984 if (!level->dimension->hasCeiling)
985 {
986 if (Tile::lightBlock[tile & 0xff] != 0)
987 {
988 if (y >= oldHeight)
989 {
990 PIXBeginNamedEvent(0,"Recalc height 1");
991 recalcHeight(x, y + 1, z);
992 PIXEndNamedEvent();
993 }
994 } else
995 {
996 if (y == oldHeight - 1)
997 {
998 PIXBeginNamedEvent(0,"Recalc height 2");
999 recalcHeight(x, y, z);
1000 PIXEndNamedEvent();
1001 }
1002 }
1003 }
1004
1005 // level.updateLight(LightLayer.Carried, xOffs, y, zOffs, xOffs, y,
1006 // zOffs);
1007 PIXBeginNamedEvent(0,"Lighting gaps");
1008 lightGaps(x, z);
1009 PIXEndNamedEvent();
1010 }
1011 PIXEndNamedEvent();
1012 data->set(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z, _data);
1013 if (_tile != 0)
1014 {
1015 if (!level->isClientSide)
1016 {
1017 Tile::tiles[_tile]->onPlace(level, xOffs, y, zOffs);
1018 }
1019 else
1020 {
1021 // 4J - in general we don't want to run the onPlace method on the client, but do a specific bit of the fireTile onPlace code here,
1022 // otherwise we'll place fire on the client and if it isn't a suitable location then we have to wait a few frames before the server
1023 // updates us to say it wasn't right. In the meantime, the client will have done some local lighting etc. and we can end up
1024 // with errors when the update from the server comes in.
1025 if( _tile == Tile::fire_Id )
1026 {
1027 if(!Tile::tiles[_tile]->mayPlace(level, xOffs, y, zOffs ))
1028 {
1029 blocks->set(x,y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT,z,0);
1030 // blocks[x << level->depthBitsPlusFour | z << level->depthBits | y] = 0;
1031 }
1032 }
1033 }
1034 // AP - changed the method of EntityTile detection cos it's well slow on Vita mate
1035 // if (_tile > 0 && dynamic_cast<EntityTile *>(Tile::tiles[_tile]) != NULL)
1036 if (_tile > 0 && Tile::tiles[_tile] != NULL && Tile::tiles[_tile]->isEntityTile())
1037 {
1038 shared_ptr<TileEntity> te = getTileEntity(x, y, z);
1039 if (te == NULL)
1040 {
1041 te = dynamic_cast<EntityTile *>(Tile::tiles[_tile])->newTileEntity(level);
1042 //app.DebugPrintf("%s: Setting tile id %d, created tileEntity type %d\n", level->isClientSide?"Client":"Server", _tile, te->GetType());
1043 level->setTileEntity(xOffs, y, zOffs, te);
1044 }
1045 if (te != NULL)
1046 {
1047 //app.DebugPrintf("%s: Setting tile id %d, found tileEntity type %d\n", level->isClientSide?"Client":"Server", _tile, te->GetType());
1048 te->clearCache();
1049 }
1050 }
1051 }
1052 // AP - changed the method of EntityTile detection cos it's well slow on Vita mate
1053 // else if (old > 0 && dynamic_cast<EntityTile *>(Tile::tiles[old]) != NULL)
1054 else if (old > 0 && Tile::tiles[_tile] != NULL && Tile::tiles[_tile]->isEntityTile())
1055 {
1056 shared_ptr<TileEntity> te = getTileEntity(x, y, z);
1057 if (te != NULL)
1058 {
1059 te->clearCache();
1060 }
1061 }
1062
1063 this->setUnsaved(true);
1064 return true;
1065}
1066
1067bool LevelChunk::setTile(int x, int y, int z, int _tile)
1068{
1069 // 4J Stu - Now using setTileAndData (like in 1.5 Java) so there is only one place we have to fix things
1070 return setTileAndData(x,y,z,_tile,0);
1071}
1072
1073int LevelChunk::getData(int x, int y, int z)
1074{
1075 SparseDataStorage *data = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperData : lowerData;
1076 return data->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z);
1077}
1078
1079bool LevelChunk::setData(int x, int y, int z, int val, int mask, bool *maskedBitsChanged)
1080{
1081 SparseDataStorage *data = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperData : lowerData;
1082 this->setUnsaved(true);
1083 int old = data->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z);
1084
1085 *maskedBitsChanged = ( ( old & mask ) != ( val & mask ) );
1086
1087 if (old == val)
1088 {
1089 return false;
1090 }
1091
1092 data->set(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z, val);
1093 int _tile = getTile(x, y, z);
1094 if (_tile > 0 && dynamic_cast<EntityTile *>( Tile::tiles[_tile] ) != NULL)
1095 {
1096 shared_ptr<TileEntity> te = getTileEntity(x, y, z);
1097 if (te != NULL)
1098 {
1099 te->clearCache();
1100 te->data = val;
1101 }
1102 }
1103 return true;
1104}
1105
1106int LevelChunk::getBrightness(LightLayer::variety layer, int x, int y, int z)
1107{
1108 if (layer == LightLayer::Sky)
1109 {
1110 if (level->dimension->hasCeiling)
1111 {
1112 return 0;
1113 }
1114 SparseLightStorage *skyLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperSkyLight : lowerSkyLight;
1115 if(!skyLight) return 0;
1116 return skyLight->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z);
1117 }
1118 else if (layer == LightLayer::Block)
1119 {
1120 SparseLightStorage *blockLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperBlockLight : lowerBlockLight;
1121 if(!blockLight) return 0;
1122 return blockLight->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z);
1123 }
1124 else return 0;
1125}
1126
1127// 4J added
1128void LevelChunk::getNeighbourBrightnesses(int *brightnesses, LightLayer::variety layer, int x, int y, int z)
1129{
1130 SparseLightStorage *light;
1131 if( layer == LightLayer::Sky ) light = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperSkyLight : lowerSkyLight;
1132 else light = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperBlockLight : lowerBlockLight;
1133
1134 if(light)
1135 {
1136 brightnesses[0] = light->get(x - 1, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z);
1137 brightnesses[1] = light->get(x + 1, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z);
1138 brightnesses[4] = light->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z - 1);
1139 brightnesses[5] = light->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z + 1);
1140 }
1141
1142
1143 if( layer == LightLayer::Sky ) light = (y-1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperSkyLight : lowerSkyLight;
1144 else light = (y-1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperBlockLight : lowerBlockLight;
1145 if(light) brightnesses[2] = light->get(x, (y - 1) % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z);
1146
1147
1148 if( layer == LightLayer::Sky ) light = (y+1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperSkyLight : lowerSkyLight;
1149 else light = (y+1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperBlockLight : lowerBlockLight;
1150 if(light) brightnesses[3] = light->get(x, (y + 1) % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z);
1151}
1152
1153void LevelChunk::setBrightness(LightLayer::variety layer, int x, int y, int z, int brightness)
1154{
1155 this->setUnsaved(true);
1156 if (layer == LightLayer::Sky)
1157 {
1158 if(!level->dimension->hasCeiling)
1159 {
1160 SparseLightStorage *skyLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperSkyLight : lowerSkyLight;
1161 skyLight->set(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z, brightness);
1162 }
1163 }
1164 else if (layer == LightLayer::Block)
1165 {
1166 SparseLightStorage *blockLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperBlockLight : lowerBlockLight;
1167 blockLight->set(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z, brightness);
1168 }
1169}
1170
1171int LevelChunk::getRawBrightness(int x, int y, int z, int skyDampen)
1172{
1173 SparseLightStorage *skyLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperSkyLight : lowerSkyLight;
1174 int light = level->dimension->hasCeiling ? 0 : skyLight->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z);
1175 if (light > 0) touchedSky = true;
1176 light -= skyDampen;
1177 SparseLightStorage *blockLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperBlockLight : lowerBlockLight;
1178 int block = blockLight->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z);
1179 if (block > light) light = block;
1180
1181 /*
1182 * int xd = (absFloor(level.player.x-(this->x*16+x))); int yd =
1183 * (absFloor(level.player.y-(y))); int zd =
1184 * (absFloor(level.player.z-(this->z*16+z))); int dd = xd+yd+zd; if
1185 * (dd<15){ int carried = 15-dd; if (carried<0) carried = 0; if
1186 * (carried>15) carried = 15; if (carried > light) light = carried; }
1187 */
1188
1189 return light;
1190}
1191
1192void LevelChunk::addEntity(shared_ptr<Entity> e)
1193{
1194 lastSaveHadEntities = true;
1195
1196 int xc = Mth::floor(e->x / 16);
1197 int zc = Mth::floor(e->z / 16);
1198 if (xc != this->x || zc != this->z)
1199 {
1200 app.DebugPrintf("Wrong location!");
1201 // System.out.println("Wrong location! " + e);
1202 // Thread.dumpStack();
1203 }
1204 int yc = Mth::floor(e->y / 16);
1205 if (yc < 0) yc = 0;
1206 if (yc >= ENTITY_BLOCKS_LENGTH) yc = ENTITY_BLOCKS_LENGTH - 1;
1207 e->inChunk = true;
1208 e->xChunk = x;
1209 e->yChunk = yc;
1210 e->zChunk = z;
1211
1212#ifdef _ENTITIES_RW_SECTION
1213 EnterCriticalRWSection(&m_csEntities, true);
1214#else
1215 EnterCriticalSection(&m_csEntities);
1216#endif
1217 entityBlocks[yc]->push_back(e);
1218#ifdef _ENTITIES_RW_SECTION
1219 LeaveCriticalRWSection(&m_csEntities, true);
1220#else
1221 LeaveCriticalSection(&m_csEntities);
1222#endif
1223}
1224
1225
1226void LevelChunk::removeEntity(shared_ptr<Entity> e)
1227{
1228 removeEntity(e, e->yChunk);
1229}
1230
1231void LevelChunk::removeEntity(shared_ptr<Entity> e, int yc)
1232{
1233 if (yc < 0) yc = 0;
1234 if (yc >= ENTITY_BLOCKS_LENGTH) yc = ENTITY_BLOCKS_LENGTH - 1;
1235
1236#ifdef _ENTITIES_RW_SECTION
1237 EnterCriticalRWSection(&m_csEntities, true);
1238#else
1239 EnterCriticalSection(&m_csEntities);
1240#endif
1241
1242 // 4J - was entityBlocks[yc]->remove(e);
1243 AUTO_VAR(it, find(entityBlocks[yc]->begin(),entityBlocks[yc]->end(),e));
1244 if( it != entityBlocks[yc]->end() )
1245 {
1246 entityBlocks[yc]->erase(it);
1247 // 4J - we don't want storage creeping up here as thinkgs move round the world accumulating up spare space
1248 MemSect(31);
1249#ifdef __PS3__
1250 // MGH - have to sort this C++11 code
1251 static bool bShowMsg = true;
1252 if(bShowMsg)
1253 {
1254 app.DebugPrintf("Need to add C++11 shrink_to_fit for PS3\n");
1255 bShowMsg = false;
1256 }
1257#else
1258 entityBlocks[yc]->shrink_to_fit();
1259#endif
1260 MemSect(0);
1261 }
1262
1263#ifdef _ENTITIES_RW_SECTION
1264 LeaveCriticalRWSection(&m_csEntities, true);
1265#else
1266 LeaveCriticalSection(&m_csEntities);
1267#endif
1268}
1269
1270bool LevelChunk::isSkyLit(int x, int y, int z)
1271{
1272 return y >= (heightmap[z << 4 | x] & 0xff);
1273}
1274
1275void LevelChunk::skyBrightnessChanged()
1276{
1277 int x0 = this->x * 16;
1278 int y0 = this->minHeight - 16;
1279 int z0 = this->z * 16;
1280 int x1 = this->x * 16 + 16;
1281 int y1 = Level::maxBuildHeight - 1;
1282 int z1 = this->z * 16 + 16;
1283
1284 level->setTilesDirty(x0, y0, z0, x1, y1, z1);
1285}
1286
1287shared_ptr<TileEntity> LevelChunk::getTileEntity(int x, int y, int z)
1288{
1289 TilePos pos(x, y, z);
1290
1291 // 4J Stu - Changed as we should not be using the [] accessor (causes an insert when we don't want one)
1292 //shared_ptr<TileEntity> tileEntity = tileEntities[pos];
1293 EnterCriticalSection(&m_csTileEntities);
1294 shared_ptr<TileEntity> tileEntity = nullptr;
1295 AUTO_VAR(it, tileEntities.find(pos));
1296
1297 if (it == tileEntities.end())
1298 {
1299 LeaveCriticalSection(&m_csTileEntities); // Note: don't assume iterator is valid for tileEntities after this point
1300
1301 // Fix for #48450 - All: Code Defect: Hang: Game hangs in tutorial, when player arrive at the particular coordinate
1302 // 4J Stu - Chests try to get their neighbours when being destroyed,
1303 // which then causes new tile entities to be created if the neighbour has already been destroyed
1304 if(level->m_bDisableAddNewTileEntities) return nullptr;
1305
1306 int t = getTile(x, y, z);
1307 if (t <= 0 || !Tile::tiles[t]->isEntityTile()) return nullptr;
1308
1309 // 4J-PB changed from this in 1.7.3
1310 //EntityTile *et = (EntityTile *) Tile::tiles[t];
1311 //et->onPlace(level, this->x * 16 + x, y, this->z * 16 + z);
1312
1313 //if (tileEntity == NULL)
1314 //{
1315 tileEntity = dynamic_cast<EntityTile *>(Tile::tiles[t])->newTileEntity(level);
1316 level->setTileEntity(this->x * 16 + x, y, this->z * 16 + z, tileEntity);
1317 //}
1318
1319 //tileEntity = tileEntities[pos]; // 4J - TODO - this doesn't seem right - assignment wrong way? Check
1320
1321 // 4J Stu - It should have been inserted by now, but check to be sure
1322 EnterCriticalSection(&m_csTileEntities);
1323 AUTO_VAR(newIt, tileEntities.find(pos));
1324 if (newIt != tileEntities.end())
1325 {
1326 tileEntity = newIt->second;
1327 }
1328 LeaveCriticalSection(&m_csTileEntities);
1329 }
1330 else
1331 {
1332 tileEntity = it->second;
1333 LeaveCriticalSection(&m_csTileEntities);
1334 }
1335 if (tileEntity != NULL && tileEntity->isRemoved())
1336 {
1337 EnterCriticalSection(&m_csTileEntities);
1338 tileEntities.erase(pos);
1339 LeaveCriticalSection(&m_csTileEntities);
1340 return nullptr;
1341 }
1342
1343 return tileEntity;
1344}
1345
1346void LevelChunk::addTileEntity(shared_ptr<TileEntity> te)
1347{
1348 int xx = (int)(te->x - this->x * 16);
1349 int yy = (int)te->y;
1350 int zz = (int)(te->z - this->z * 16);
1351 setTileEntity(xx, yy, zz, te);
1352 if( loaded )
1353 {
1354 EnterCriticalSection(&level->m_tileEntityListCS);
1355 level->tileEntityList.push_back(te);
1356 LeaveCriticalSection(&level->m_tileEntityListCS);
1357 }
1358}
1359
1360void LevelChunk::setTileEntity(int x, int y, int z, shared_ptr<TileEntity> tileEntity)
1361{
1362 TilePos pos(x, y, z);
1363
1364 tileEntity->setLevel(level);
1365 tileEntity->x = this->x * 16 + x;
1366 tileEntity->y = y;
1367 tileEntity->z = this->z * 16 + z;
1368
1369 if (getTile(x, y, z) == 0 || !Tile::tiles[getTile(x, y, z)]->isEntityTile()) // 4J - was !(Tile.tiles[getTile(x, y, z)] instanceof EntityTile))
1370 {
1371 app.DebugPrintf("Attempted to place a tile entity where there was no entity tile!\n");
1372 return;
1373 }
1374 AUTO_VAR(it, tileEntities.find(pos) );
1375 if(it != tileEntities.end()) it->second->setRemoved();
1376
1377 tileEntity->clearRemoved();
1378
1379 EnterCriticalSection(&m_csTileEntities);
1380 tileEntities[pos] = tileEntity;
1381 LeaveCriticalSection(&m_csTileEntities);
1382}
1383
1384void LevelChunk::removeTileEntity(int x, int y, int z)
1385{
1386 TilePos pos(x, y, z);
1387
1388 if (loaded)
1389 {
1390 // 4J - was:
1391 // TileEntity removeThis = tileEntities.remove(pos);
1392 // if (removeThis != null) {
1393 // removeThis.setRemoved();
1394 // }
1395 EnterCriticalSection(&m_csTileEntities);
1396 AUTO_VAR(it, tileEntities.find(pos));
1397 if( it != tileEntities.end() )
1398 {
1399 shared_ptr<TileEntity> te = tileEntities[pos];
1400 tileEntities.erase(pos);
1401 if( te != NULL )
1402 {
1403 if(level->isClientSide)
1404 {
1405 app.DebugPrintf("Removing tile entity of type %d\n", te->GetType());
1406 }
1407 te->setRemoved();
1408 }
1409 }
1410 LeaveCriticalSection(&m_csTileEntities);
1411 }
1412}
1413
1414void LevelChunk::load()
1415{
1416 loaded = true;
1417
1418 if(!level->isClientSide)
1419 {
1420#ifdef _LARGE_WORLDS
1421 if(m_bUnloaded && m_unloadedEntitiesTag)
1422 {
1423 ListTag<CompoundTag> *entityTags = (ListTag<CompoundTag> *) m_unloadedEntitiesTag->getList(L"Entities");
1424 if (entityTags != NULL)
1425 {
1426 for (int i = 0; i < entityTags->size(); i++)
1427 {
1428 CompoundTag *teTag = entityTags->get(i);
1429 shared_ptr<Entity> ent = EntityIO::loadStatic(teTag, level);
1430 if (ent != NULL)
1431 {
1432 ent->onLoadedFromSave();
1433 addEntity(ent);
1434 }
1435 }
1436 }
1437
1438 ListTag<CompoundTag> *tileEntityTags = (ListTag<CompoundTag> *) m_unloadedEntitiesTag->getList(L"TileEntities");
1439 if (tileEntityTags != NULL)
1440 {
1441 for (int i = 0; i < tileEntityTags->size(); i++)
1442 {
1443 CompoundTag *teTag = tileEntityTags->get(i);
1444 shared_ptr<TileEntity> te = TileEntity::loadStatic(teTag);
1445 if (te != NULL)
1446 {
1447 addTileEntity(te);
1448 }
1449 }
1450 }
1451 delete m_unloadedEntitiesTag;
1452 m_unloadedEntitiesTag = NULL;
1453 m_bUnloaded = false;
1454 }
1455#endif
1456
1457 vector< shared_ptr<TileEntity> > values;
1458 EnterCriticalSection(&m_csTileEntities);
1459 for( AUTO_VAR(it, tileEntities.begin()); it != tileEntities.end(); it++ )
1460 {
1461 values.push_back(it->second);
1462 }
1463 LeaveCriticalSection(&m_csTileEntities);
1464 level->addAllPendingTileEntities(values);
1465
1466#ifdef _ENTITIES_RW_SECTION
1467 EnterCriticalRWSection(&m_csEntities, true);
1468#else
1469 EnterCriticalSection(&m_csEntities);
1470#endif
1471 for (int i = 0; i < ENTITY_BLOCKS_LENGTH; i++)
1472 {
1473 level->addEntities(entityBlocks[i]);
1474 }
1475#ifdef _ENTITIES_RW_SECTION
1476 LeaveCriticalRWSection(&m_csEntities, true);
1477#else
1478 LeaveCriticalSection(&m_csEntities);
1479#endif
1480 }
1481 else
1482 {
1483#ifdef _LARGE_WORLDS
1484 m_bUnloaded = false;
1485#endif
1486 }
1487}
1488
1489void LevelChunk::unload(bool unloadTileEntities) // 4J - added parameter
1490{
1491 loaded = false;
1492 if( unloadTileEntities )
1493 {
1494 EnterCriticalSection(&m_csTileEntities);
1495 for( AUTO_VAR(it, tileEntities.begin()); it != tileEntities.end(); it++ )
1496 {
1497 // 4J-PB -m 1.7.3 was it->second->setRemoved();
1498 level->markForRemoval(it->second);
1499 }
1500 LeaveCriticalSection(&m_csTileEntities);
1501 }
1502
1503#ifdef _ENTITIES_RW_SECTION
1504 EnterCriticalRWSection(&m_csEntities, true);
1505#else
1506 EnterCriticalSection(&m_csEntities);
1507#endif
1508 for (int i = 0; i < ENTITY_BLOCKS_LENGTH; i++)
1509 {
1510 level->removeEntities(entityBlocks[i]);
1511 }
1512#ifdef _ENTITIES_RW_SECTION
1513 LeaveCriticalRWSection(&m_csEntities, true);
1514#else
1515 LeaveCriticalSection(&m_csEntities);
1516#endif
1517 //app.DebugPrintf("Unloaded chunk %d, %d\n", x, z);
1518
1519#ifdef _LARGE_WORLDS
1520 if ( !m_bUnloaded ) // 4J-JEV: If we unload a chunk twice, we delete all the entities/tile-entities its saved in the entitiesTag.
1521 {
1522 m_bUnloaded = true;
1523 if(!level->isClientSide)
1524 {
1525 delete m_unloadedEntitiesTag;
1526 // 4J Stu - Save out entities to a cached format that won't interfere with other systems
1527 m_unloadedEntitiesTag = new CompoundTag();
1528 PIXBeginNamedEvent(0,"Saving entities");
1529 ListTag<CompoundTag> *entityTags = new ListTag<CompoundTag>();
1530
1531 EnterCriticalSection(&m_csEntities);
1532 for (int i = 0; i < ENTITY_BLOCKS_LENGTH; i++)
1533 {
1534 AUTO_VAR(itEnd, entityBlocks[i]->end());
1535 for( vector<shared_ptr<Entity> >::iterator it = entityBlocks[i]->begin(); it != itEnd; it++ )
1536 {
1537 shared_ptr<Entity> e = *it;
1538 CompoundTag *teTag = new CompoundTag();
1539 if (e->save(teTag))
1540 {
1541 entityTags->add(teTag);
1542 }
1543
1544 }
1545
1546 // Clear out this list
1547 entityBlocks[i]->clear();
1548 }
1549 LeaveCriticalSection(&m_csEntities);
1550
1551 m_unloadedEntitiesTag->put(L"Entities", entityTags);
1552 PIXEndNamedEvent();
1553
1554 PIXBeginNamedEvent(0,"Saving tile entities");
1555 ListTag<CompoundTag> *tileEntityTags = new ListTag<CompoundTag>();
1556
1557 AUTO_VAR(itEnd,tileEntities.end());
1558 for( unordered_map<TilePos, shared_ptr<TileEntity>, TilePosKeyHash, TilePosKeyEq>::iterator it = tileEntities.begin();
1559 it != itEnd; it++)
1560 {
1561 shared_ptr<TileEntity> te = it->second;
1562 CompoundTag *teTag = new CompoundTag();
1563 te->save(teTag);
1564 tileEntityTags->add(teTag);
1565 }
1566 // Clear out the tileEntities list
1567 tileEntities.clear();
1568
1569 m_unloadedEntitiesTag->put(L"TileEntities", tileEntityTags);
1570 PIXEndNamedEvent();
1571 }
1572 }
1573#endif
1574}
1575
1576bool LevelChunk::containsPlayer()
1577{
1578#ifdef _ENTITIES_RW_SECTION
1579 EnterCriticalRWSection(&m_csEntities, true);
1580#else
1581 EnterCriticalSection(&m_csEntities);
1582#endif
1583 for (int i = 0; i < ENTITY_BLOCKS_LENGTH; i++)
1584 {
1585 vector<shared_ptr<Entity> > *vecEntity = entityBlocks[i];
1586 for( int j = 0; j < vecEntity->size(); j++ )
1587 {
1588 if(vecEntity->at(j)->GetType() == eTYPE_SERVERPLAYER )
1589 {
1590#ifdef _ENTITIES_RW_SECTION
1591 LeaveCriticalRWSection(&m_csEntities, true);
1592#else
1593 LeaveCriticalSection(&m_csEntities);
1594#endif
1595 return true;
1596 }
1597 }
1598 }
1599#ifdef _ENTITIES_RW_SECTION
1600 LeaveCriticalRWSection(&m_csEntities, true);
1601#else
1602 LeaveCriticalSection(&m_csEntities);
1603#endif
1604 return false;
1605}
1606
1607#ifdef _LARGE_WORLDS
1608bool LevelChunk::isUnloaded()
1609{
1610 return m_bUnloaded;
1611}
1612#endif
1613
1614void LevelChunk::markUnsaved()
1615{
1616 this->setUnsaved(true);
1617}
1618
1619
1620void LevelChunk::getEntities(shared_ptr<Entity> except, AABB *bb, vector<shared_ptr<Entity> > &es, const EntitySelector *selector)
1621{
1622 int yc0 = Mth::floor((bb->y0 - 2) / 16);
1623 int yc1 = Mth::floor((bb->y1 + 2) / 16);
1624 if (yc0 < 0) yc0 = 0;
1625 if (yc1 >= ENTITY_BLOCKS_LENGTH) yc1 = ENTITY_BLOCKS_LENGTH - 1;
1626
1627#ifndef __PSVITA__
1628 // AP - RW critical sections are expensive so enter once in Level::getEntities
1629 EnterCriticalSection(&m_csEntities);
1630#endif
1631 for (int yc = yc0; yc <= yc1; yc++)
1632 {
1633 vector<shared_ptr<Entity> > *entities = entityBlocks[yc];
1634
1635 AUTO_VAR(itEnd, entities->end());
1636 for (AUTO_VAR(it, entities->begin()); it != itEnd; it++)
1637 {
1638 shared_ptr<Entity> e = *it; //entities->at(i);
1639 if (e != except && e->bb->intersects(bb) && (selector == NULL || selector->matches(e)))
1640 {
1641 es.push_back(e);
1642 vector<shared_ptr<Entity> > *subs = e->getSubEntities();
1643 if (subs != NULL)
1644 {
1645 for (int j = 0; j < subs->size(); j++)
1646 {
1647 e = subs->at(j);
1648 if (e != except && e->bb->intersects(bb) && (selector == NULL || selector->matches(e)))
1649 {
1650 es.push_back(e);
1651 }
1652 }
1653 }
1654 }
1655 }
1656 }
1657#ifndef __PSVITA__
1658 LeaveCriticalSection(&m_csEntities);
1659#endif
1660}
1661
1662void LevelChunk::getEntitiesOfClass(const type_info& ec, AABB *bb, vector<shared_ptr<Entity> > &es, const EntitySelector *selector)
1663{
1664 int yc0 = Mth::floor((bb->y0 - 2) / 16);
1665 int yc1 = Mth::floor((bb->y1 + 2) / 16);
1666
1667 if (yc0 < 0)
1668 {
1669 yc0 = 0;
1670 }
1671 else if (yc0 >= ENTITY_BLOCKS_LENGTH)
1672 {
1673 yc0 = ENTITY_BLOCKS_LENGTH - 1;
1674 }
1675 if (yc1 >= ENTITY_BLOCKS_LENGTH)
1676 {
1677 yc1 = ENTITY_BLOCKS_LENGTH - 1;
1678 }
1679 else if (yc1 < 0)
1680 {
1681 yc1 = 0;
1682 }
1683
1684#ifndef __PSVITA__
1685 // AP - RW critical sections are expensive so enter once in Level::getEntitiesOfClass
1686 EnterCriticalSection(&m_csEntities);
1687#endif
1688 for (int yc = yc0; yc <= yc1; yc++)
1689 {
1690 vector<shared_ptr<Entity> > *entities = entityBlocks[yc];
1691
1692 AUTO_VAR(itEnd, entities->end());
1693 for (AUTO_VAR(it, entities->begin()); it != itEnd; it++)
1694 {
1695 shared_ptr<Entity> e = *it; //entities->at(i);
1696
1697 bool isAssignableFrom = false;
1698 // Some special cases where the base class is a general type that our class may be derived from, otherwise do a direct comparison of type_info
1699 if ( ec==typeid(Player) ) isAssignableFrom = e->instanceof(eTYPE_PLAYER);
1700 else if ( ec==typeid(Entity) ) isAssignableFrom = e->instanceof(eTYPE_ENTITY);
1701 else if ( ec==typeid(Mob) ) isAssignableFrom = e->instanceof(eTYPE_MOB);
1702 else if ( ec==typeid(LivingEntity) ) isAssignableFrom = e->instanceof(eTYPE_LIVINGENTITY);
1703 else if ( ec==typeid(ItemEntity) ) isAssignableFrom = e->instanceof(eTYPE_ITEMENTITY);
1704 else if ( ec==typeid(Minecart) ) isAssignableFrom = e->instanceof(eTYPE_MINECART);
1705 else if ( ec==typeid(Monster) ) isAssignableFrom = e->instanceof(eTYPE_MONSTER);
1706 else if ( ec==typeid(Zombie) ) isAssignableFrom = e->instanceof(eTYPE_ZOMBIE);
1707 else if(e != NULL && ec == typeid(*(e.get())) ) isAssignableFrom = true;
1708 if (isAssignableFrom && e->bb->intersects(bb))
1709 {
1710 if (selector == NULL || selector->matches(e))
1711 {
1712 es.push_back(e);
1713 }
1714 }
1715 // 4J - note needs to be equivalent to baseClass.isAssignableFrom(e.getClass())
1716 }
1717 }
1718#ifndef __PSVITA__
1719 LeaveCriticalSection(&m_csEntities);
1720#endif
1721}
1722
1723int LevelChunk::countEntities()
1724{
1725 int entityCount = 0;
1726#ifdef _ENTITIES_RW_SECTION
1727 EnterCriticalRWSection(&m_csEntities, false);
1728#else
1729 EnterCriticalSection(&m_csEntities);
1730#endif
1731 for (int yc = 0; yc < ENTITY_BLOCKS_LENGTH; yc++)
1732 {
1733 entityCount += (int)entityBlocks[yc]->size();
1734 }
1735#ifdef _ENTITIES_RW_SECTION
1736 LeaveCriticalRWSection(&m_csEntities, false);
1737#else
1738 LeaveCriticalSection(&m_csEntities);
1739#endif
1740 return entityCount;
1741}
1742
1743bool LevelChunk::shouldSave(bool force)
1744{
1745 if (dontSave) return false;
1746 if (force)
1747 {
1748 if ((lastSaveHadEntities && level->getGameTime() != lastSaveTime) || m_unsaved)
1749 {
1750 return true;
1751 }
1752 }
1753 else
1754 {
1755 if (lastSaveHadEntities && level->getGameTime() >= lastSaveTime + 20 * 30) return true;
1756 }
1757
1758 return m_unsaved;
1759}
1760
1761int LevelChunk::getBlocksAndData(byteArray *data, int x0, int y0, int z0, int x1, int y1, int z1, int p, bool includeLighting/* = true*/)
1762{
1763 int xs = x1 - x0;
1764 int ys = y1 - y0;
1765 int zs = z1 - z0;
1766
1767 // 4J Stu - Added this because some "min" functions don't let us use our constants :(
1768 int compressedHeight = Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
1769
1770 // 4J - replaced block storage as now using CompressedTileStorage
1771 if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerBlocks->getDataRegion( *data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p );
1772 if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperBlocks->getDataRegion( *data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p );
1773
1774 // 4J - replaced data storage as now using SparseDataStorage
1775 if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerData->getDataRegion( *data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p );
1776 if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperData->getDataRegion( *data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p );
1777
1778 if( includeLighting )
1779 {
1780 // 4J - replaced block and skylight storage as these now use our SparseLightStorage
1781 if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerBlockLight->getDataRegion( *data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p );
1782 if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperBlockLight->getDataRegion( *data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p );
1783
1784 if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerSkyLight->getDataRegion( *data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p );
1785 if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperSkyLight->getDataRegion( *data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p );
1786 }
1787
1788 /*
1789 for (int x = x0; x < x1; x++)
1790 for (int z = z0; z < z1; z++)
1791 {
1792 int slot = (x << level->depthBitsPlusFour | z << level->depthBits | y0) >> 1;
1793 int len = (y1 - y0) / 2;
1794 System::arraycopy(blockLight->data, slot, data, p, len);
1795 p += len;
1796 }
1797
1798 for (int x = x0; x < x1; x++)
1799 for (int z = z0; z < z1; z++)
1800 {
1801 int slot = (x << level->depthBitsPlusFour | z << level->depthBits | y0) >> 1;
1802 int len = (y1 - y0) / 2;
1803 System::arraycopy(skyLight->data, slot, data, p, len);
1804 p += len;
1805 }
1806 */
1807
1808 return p;
1809}
1810
1811// 4J added - return true if setBlocksAndData would change any blocks
1812bool LevelChunk::testSetBlocksAndData(byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int p)
1813{
1814 bool changed = false;
1815
1816 // 4J Stu - Added this because some "min" functions don't let us use our constants :(
1817 int compressedHeight = Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
1818
1819 if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) changed = lowerBlocks->testSetDataRegion(data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p);
1820 if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) changed = changed || upperBlocks->testSetDataRegion(data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p);
1821
1822 return changed;
1823}
1824
1825void LevelChunk::tileUpdatedCallback(int x, int y, int z, void *param, int yparam)
1826{
1827 LevelChunk *lc = (LevelChunk *)param;
1828 int xx = lc->x * 16 + x;
1829 int yy = y + yparam;
1830 int zz = lc->z * 16 + z;
1831 lc->level->checkLight(xx, yy, zz);
1832}
1833
1834int LevelChunk::setBlocksAndData(byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int p, bool includeLighting/* = true*/)
1835{
1836 // If includeLighting is set, then this is a full chunk's worth of data that we are receiving on the client. We'll have made this chunk initially as compressed,
1837 // so throw that data away and make some fully uncompressed storage now to improve the speed up writing to it. Only doing this for lower chunks as quite likely
1838 // that the upper chunk doesn't have anything in anyway.
1839 if( includeLighting )
1840 {
1841 GameRenderer::AddForDelete(lowerBlocks);
1842 byteArray emptyByteArray;
1843 lowerBlocks = new CompressedTileStorage(emptyByteArray,0);
1844 GameRenderer::FinishedReassigning();
1845
1846 GameRenderer::AddForDelete(lowerSkyLight);
1847 lowerSkyLight = new SparseLightStorage(true,false);
1848 GameRenderer::FinishedReassigning();
1849
1850 GameRenderer::AddForDelete(lowerBlockLight);
1851 lowerBlockLight = new SparseLightStorage(false,false);
1852 GameRenderer::FinishedReassigning();
1853
1854 GameRenderer::AddForDelete(lowerData);
1855 lowerData = new SparseDataStorage(false);
1856 GameRenderer::FinishedReassigning();
1857 }
1858
1859 // 4J Stu - Added this because some "min" functions don't let us use our constants :(
1860 int compressedHeight = Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
1861
1862 // 4J - replaced block storage as now uses CompressedTileStorage
1863 if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerBlocks->setDataRegion( data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p, includeLighting ? NULL : tileUpdatedCallback, this, 0 );
1864 if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperBlocks->setDataRegion( data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p, includeLighting ? NULL : tileUpdatedCallback, this, Level::COMPRESSED_CHUNK_SECTION_HEIGHT );
1865 /*
1866 for (int x = x0; x < x1; x++)
1867 for (int z = z0; z < z1; z++)
1868 {
1869 int slot = x << level->depthBitsPlusFour | z << level->depthBits | y0;
1870 int len = y1 - y0;
1871 System::arraycopy(data, p, &blocks, slot, len);
1872 p += len;
1873 }*/
1874
1875 recalcHeightmapOnly();
1876
1877 // 4J - replaced data storage as now uses SparseDataStorage
1878 if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerData->setDataRegion( data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p, includeLighting ? NULL : tileUpdatedCallback, this, 0 );
1879 if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperData->setDataRegion( data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p, includeLighting ? NULL : tileUpdatedCallback, this, Level::COMPRESSED_CHUNK_SECTION_HEIGHT );
1880
1881 if( includeLighting )
1882 {
1883 // 4J - replaced block and skylight storage as these now use our SparseLightStorage
1884 if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerBlockLight->setDataRegion( data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p );
1885 if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperBlockLight->setDataRegion( data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p );
1886
1887 if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerSkyLight->setDataRegion( data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p );
1888 if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperSkyLight->setDataRegion( data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p );
1889
1890 memcpy(biomes.data, &data.data[p],biomes.length);
1891 p += biomes.length;
1892 }
1893 else
1894 {
1895 // Because the host's local client shares data with it, the lighting updates that are done via callbacks in the setDataRegion calls above when things don't work, as they
1896 // don't detect changes because they've already happened just because the data was being shared when the server updated them. This will leave the lighting information out of sync on
1897 // the client, so resync for this & surrounding chunks that might have been affected
1898 if( level->isClientSide && g_NetworkManager.IsHost() )
1899 {
1900 reSyncLighting();
1901 level->getChunk(x-1,z-1)->reSyncLighting();
1902 level->getChunk(x-0,z-1)->reSyncLighting();
1903 level->getChunk(x+1,z-1)->reSyncLighting();
1904 level->getChunk(x-1,z+0)->reSyncLighting();
1905 level->getChunk(x+1,z+0)->reSyncLighting();
1906 level->getChunk(x-1,z+1)->reSyncLighting();
1907 level->getChunk(x+0,z+1)->reSyncLighting();
1908 level->getChunk(x+1,z+1)->reSyncLighting();
1909 }
1910 }
1911
1912 /*
1913 for (int x = x0; x < x1; x++)
1914 for (int z = z0; z < z1; z++)
1915 {
1916 int slot = (x << level->depthBitsPlusFour | z << level->depthBits | y0) >> 1;
1917 int len = (y1 - y0) / 2;
1918 System::arraycopy(data, p, &blockLight->data, slot, len);
1919 p += len;
1920 }
1921
1922 for (int x = x0; x < x1; x++)
1923 for (int z = z0; z < z1; z++)
1924 {
1925 int slot = (x << level->depthBitsPlusFour | z << level->depthBits | y0) >> 1;
1926 int len = (y1 - y0) / 2;
1927 System::arraycopy(data, p, &skyLight->data, slot, len);
1928 p += len;
1929 }
1930 */
1931
1932 for(AUTO_VAR(it, tileEntities.begin()); it != tileEntities.end(); ++it)
1933 {
1934 it->second->clearCache();
1935 }
1936 // recalcHeightmap();
1937
1938 // If the includeLighting flag is set, then this is a full chunk's worth of data. This is a good time to compress everything that we've just set up.
1939 if( includeLighting )
1940 {
1941 compressLighting();
1942 compressBlocks();
1943 compressData();
1944 }
1945
1946 return p;
1947}
1948
1949void LevelChunk::setCheckAllLight()
1950{
1951 checkLightPosition = 0;
1952}
1953
1954Random *LevelChunk::getRandom(__int64 l)
1955{
1956 return new Random((level->getSeed() + x * x * 4987142 + x * 5947611 + z * z * 4392871l + z * 389711) ^ l);
1957}
1958
1959bool LevelChunk::isEmpty()
1960{
1961 return false;
1962}
1963void LevelChunk::attemptCompression()
1964{
1965 // 4J - removed
1966#if 0
1967 try {
1968 ByteArrayOutputStream *baos = new ByteArrayOutputStream();
1969 GZIPOutputStream *gzos = new GZIPOutputStream(baos);
1970 DataOutputStream *dos = new DataOutputStream(gzos);
1971 dos.close();
1972 System.out.println("Compressed size: " + baos.toByteArray().length);
1973 } catch (Exception e) {
1974
1975 }
1976#endif
1977}
1978
1979void LevelChunk::checkPostProcess(ChunkSource *source, ChunkSource *parent, int x, int z)
1980{
1981 if ( ( ( terrainPopulated & sTerrainPopulatedFromHere ) == 0 ) && source->hasChunk(x + 1, z + 1) && source->hasChunk(x, z + 1) && source->hasChunk(x + 1, z))
1982 {
1983 source->postProcess(parent, x, z);
1984 }
1985 if (source->hasChunk(x - 1, z) && ( (source->getChunk(x - 1, z)->terrainPopulated & sTerrainPopulatedFromHere ) == 0 ) && source->hasChunk(x - 1, z + 1) && source->hasChunk(x, z + 1) && source->hasChunk(x - 1, z + 1))
1986 {
1987 source->postProcess(parent, x - 1, z);
1988 }
1989 if (source->hasChunk(x, z - 1) && ( (source->getChunk(x, z - 1)->terrainPopulated & sTerrainPopulatedFromHere ) == 0 ) && source->hasChunk(x + 1, z - 1) && source->hasChunk(x + 1, z))
1990 {
1991 source->postProcess(parent, x, z - 1);
1992 }
1993 if (source->hasChunk(x - 1, z - 1) && ( (source->getChunk(x - 1, z - 1)->terrainPopulated & sTerrainPopulatedFromHere ) == 0 ) && source->hasChunk(x, z - 1) && source->hasChunk(x - 1, z))
1994 {
1995 source->postProcess(parent, x - 1, z - 1);
1996 }
1997}
1998
1999// 4J added - check for any pre-1.8.2 chests in the chunk at (x,z), and calculate their facing direction & relight to bring up to date with the post 1.8.2 build
2000void LevelChunk::checkChests(ChunkSource *source, int x, int z )
2001{
2002 LevelChunk *lc = source->getChunk( x, z );
2003
2004 for( int xx = 0; xx < 16; xx++ )
2005 for( int zz = 0; zz < 16; zz++ )
2006 for( int yy = 0; yy < 128; yy++ )
2007 {
2008 if( lc->getTile( xx, yy, zz ) == Tile::chest_Id )
2009 {
2010 if( lc->getData( xx, yy, zz ) == 0 )
2011 {
2012 int xOffs = x * 16 + xx;
2013 int zOffs = z * 16 + zz;
2014 ChestTile *tile = (ChestTile *)Tile::tiles[Tile::chest_Id];
2015 tile->recalcLockDir( level, xOffs, yy, zOffs );
2016 level->checkLight(xOffs, yy, zOffs, true);
2017 }
2018 }
2019 }
2020}
2021
2022// 4J - lighting change brought forward from 1.8.2
2023void LevelChunk::tick()
2024{
2025 if (hasGapsToCheck && !level->dimension->hasCeiling) recheckGaps();
2026}
2027
2028ChunkPos *LevelChunk::getPos()
2029{
2030 return new ChunkPos(x, z);
2031}
2032
2033bool LevelChunk::isYSpaceEmpty(int y1, int y2)
2034{
2035 return false;
2036 // 4J Unused
2037 /*if (y1 < 0) {
2038 y1 = 0;
2039 }
2040 if (y2 >= Level.maxBuildHeight) {
2041 y2 = Level.maxBuildHeight - 1;
2042 }
2043 for (int y = y1; y <= y2; y += 16) {
2044 LevelChunkSection section = sections[y >> 4];
2045 if (section != null && !section.isEmpty()) {
2046 return false;
2047 }
2048 }
2049 return true;*/
2050}
2051
2052// 4J Added
2053void LevelChunk::reloadBiomes()
2054{
2055 BiomeSource *biomeSource = level->dimension->biomeSource;
2056 for(unsigned int x = 0; x < 16; ++x)
2057 {
2058 for(unsigned int z = 0; z < 16; ++z)
2059 {
2060 Biome *biome = biomeSource->getBiome((this->x << 4) + x, (this->z << 4) + z);
2061 biomes[(z << 4) | x] = (byte) ( (biome->id) & 0xff);
2062 }
2063 }
2064}
2065
2066Biome *LevelChunk::getBiome(int x, int z, BiomeSource *biomeSource)
2067{
2068 int value = biomes[(z << 4) | x] & 0xff;
2069 if (value == 0xff)
2070 {
2071 Biome *biome = biomeSource->getBiome((this->x << 4) + x, (this->z << 4) + z);
2072 value = biome->id;
2073 biomes[(z << 4) | x] = (byte) (value & 0xff);
2074 }
2075 if (Biome::biomes[value] == NULL)
2076 {
2077 return Biome::plains;
2078 }
2079 return Biome::biomes[value];
2080}
2081
2082byteArray LevelChunk::getBiomes()
2083{
2084 return biomes;
2085}
2086
2087void LevelChunk::setBiomes(byteArray biomes)
2088{
2089 if(this->biomes.data != NULL) delete[] this->biomes.data;
2090 this->biomes = biomes;
2091}
2092
2093// 4J - optimisation brought forward from 1.8.2
2094int LevelChunk::getTopRainBlock(int x, int z)
2095{
2096 int slot = x | (z << 4);
2097 int h = rainHeights[slot];
2098
2099 if (h == 255)
2100 {
2101 int y = Level::maxBuildHeight - 1;
2102 h = -1;
2103 while (y > 0 && h == -1)
2104 {
2105 int t = getTile(x, y, z);
2106 Material *m = t == 0 ? Material::air : Tile::tiles[t]->material;
2107 if (!m->blocksMotion() && !m->isLiquid())
2108 {
2109 y--;
2110 }
2111 else
2112 {
2113 h = y + 1;
2114 }
2115 }
2116 // 255 indicates that the rain height needs recalculated. If the rain height ever actually Does get to 255, then it will just keep not being cached, so
2117 // probably better just to let the rain height be 254 in this instance and suffer a slightly incorrect results
2118 if( h == 255 ) h = 254;
2119 rainHeights[slot] = h;
2120 }
2121
2122 return h;
2123}
2124
2125// 4J added as optimisation, these biome checks are expensive so caching through flags in levelchunk
2126bool LevelChunk::biomeHasRain(int x, int z)
2127{
2128 updateBiomeFlags(x, z);
2129 int slot = ( x >> 1 ) | (z * 8);
2130 int shift = ( x & 1 ) * 4;
2131 return ( ( columnFlags[slot] & ( eColumnFlag_biomeHasRain << shift ) ) != 0 );
2132}
2133
2134// 4J added as optimisation, these biome checks are expensive so caching through flags in levelchunk
2135bool LevelChunk::biomeHasSnow(int x, int z)
2136{
2137 updateBiomeFlags(x, z);
2138 int slot = ( x >> 1 ) | (z * 8);
2139 int shift = ( x & 1 ) * 4;
2140 return ( ( columnFlags[slot] & ( eColumnFlag_biomeHasSnow << shift ) ) != 0 );
2141}
2142
2143void LevelChunk::updateBiomeFlags(int x, int z)
2144{
2145 int slot = ( x >> 1 ) | (z * 8);
2146 int shift = ( x & 1 ) * 4;
2147 if( ( columnFlags[slot] & ( eColumnFlag_biomeOk << shift ) ) == 0 )
2148 {
2149 int xOffs = (this->x * 16) + x;
2150 int zOffs = (this->z * 16) + z;
2151 BiomeArray biomes;
2152 level->getBiomeSource()->getBiomeBlock(biomes, xOffs, zOffs, 1, 1, true);
2153 if( biomes[0]->hasRain()) columnFlags[slot] |= ( eColumnFlag_biomeHasRain << shift );
2154 if( biomes[0]->hasSnow()) columnFlags[slot] |= ( eColumnFlag_biomeHasSnow << shift );
2155 columnFlags[slot] |= ( eColumnFlag_biomeOk << shift );
2156 delete biomes.data;
2157 }
2158}
2159
2160// Get a byte array of length 16384 ( 128 x 16 x 16 x 0.5 ), containing data. Ordering same as java version if originalOrder set;
2161void LevelChunk::getDataData(byteArray data)
2162{
2163 lowerData->getData(data,0);
2164 if(data.length > Level::COMPRESSED_CHUNK_SECTION_TILES/2) upperData->getData(data,Level::COMPRESSED_CHUNK_SECTION_TILES/2);
2165}
2166
2167// Set data to data passed in input byte array of length 16384. This data must be in original (java version) order if originalOrder set.
2168void LevelChunk::setDataData(byteArray data)
2169{
2170 if( lowerData == NULL ) lowerData = new SparseDataStorage();
2171 if( upperData == NULL ) upperData = new SparseDataStorage(true);
2172 lowerData->setData(data,0);
2173 if(data.length > Level::COMPRESSED_CHUNK_SECTION_TILES/2) upperData->setData(data,Level::COMPRESSED_CHUNK_SECTION_TILES/2);
2174}
2175
2176// Get a byte array of length 16384 ( 128 x 16 x 16 x 0.5 ), containing sky light data. Ordering same as java version if originalOrder set;
2177void LevelChunk::getSkyLightData(byteArray data)
2178{
2179 lowerSkyLight->getData(data,0);
2180 if(data.length > Level::COMPRESSED_CHUNK_SECTION_TILES/2) upperSkyLight->getData(data,Level::COMPRESSED_CHUNK_SECTION_TILES/2);
2181}
2182
2183// Get a byte array of length 16384 ( 128 x 16 x 16 x 0.5 ), containing block light data. Ordering same as java version if originalOrder set;
2184void LevelChunk::getBlockLightData(byteArray data)
2185{
2186 lowerBlockLight->getData(data,0);
2187 if(data.length > Level::COMPRESSED_CHUNK_SECTION_TILES/2) upperBlockLight->getData(data,Level::COMPRESSED_CHUNK_SECTION_TILES/2);
2188}
2189
2190// Set sky light data to data passed in input byte array of length 16384. This data must be in original (java version) order if originalOrder set.
2191void LevelChunk::setSkyLightData(byteArray data)
2192{
2193 if( lowerSkyLight == NULL ) lowerSkyLight = new SparseLightStorage(true);
2194 if( upperSkyLight == NULL ) upperSkyLight = new SparseLightStorage(true,true);
2195 lowerSkyLight->setData(data,0);
2196 if(data.length > Level::COMPRESSED_CHUNK_SECTION_TILES/2) upperSkyLight->setData(data,Level::COMPRESSED_CHUNK_SECTION_TILES/2);
2197}
2198
2199// Set block light data to data passed in input byte array of length 16384. This data must be in original (java version) order if originalOrder set.
2200void LevelChunk::setBlockLightData(byteArray data)
2201{
2202 if( lowerBlockLight == NULL ) lowerBlockLight = new SparseLightStorage(false);
2203 if( upperBlockLight == NULL ) upperBlockLight = new SparseLightStorage(false, true);
2204 lowerBlockLight->setData(data,0);
2205 if(data.length > Level::COMPRESSED_CHUNK_SECTION_TILES/2) upperBlockLight->setData(data,Level::COMPRESSED_CHUNK_SECTION_TILES/2);
2206}
2207
2208// Set sky light data to be all fully lit
2209void LevelChunk::setSkyLightDataAllBright()
2210{
2211 lowerSkyLight->setAllBright();
2212 upperSkyLight->setAllBright();
2213}
2214
2215// Attempt to compress lighting data. Doesn't make any guarantee that it will succeed - can only compress if the lighting data is being shared, and nothing else is trying to update it from another thread.
2216void LevelChunk::compressLighting()
2217{
2218 // The lighting data is now generally not shared between host & local client, but is for a while at the start of level creation (until the point where the chunk data would be transferred by network
2219 // data for remote clients). We'll therefore either be compressing a shared copy here or one of the server or client copies depending on
2220 lowerSkyLight->compress();
2221 upperSkyLight->compress();
2222 lowerBlockLight->compress();
2223 upperBlockLight->compress();
2224}
2225
2226void LevelChunk::compressBlocks()
2227{
2228#ifdef SHARING_ENABLED
2229 CompressedTileStorage *blocksToCompressLower = NULL;
2230 CompressedTileStorage *blocksToCompressUpper = NULL;
2231
2232 // If we're the host machine, and this is the client level, then we only want to do this if we are sharing data. This means that we will be compressing the data that is shared from the server.
2233 // No point trying to compress the local client copy of the data if the data is unshared, since we'll be throwing this data away again anyway once we share with the server again.
2234 if( level->isClientSide && g_NetworkManager.IsHost() )
2235 {
2236 // Note - only the extraction of the pointers needs to be done in the critical section, since even if the data is unshared whilst we are processing this data is still valid (for the server)
2237 EnterCriticalSection(&m_csSharing);
2238 if( sharingTilesAndData )
2239 {
2240 blocksToCompressLower = lowerBlocks;
2241 blocksToCompressUpper = upperBlocks;
2242 }
2243 LeaveCriticalSection(&m_csSharing);
2244 }
2245 else
2246 {
2247 // Not the host, simple case
2248 blocksToCompressLower = lowerBlocks;
2249 blocksToCompressUpper = upperBlocks;
2250 }
2251
2252 // Attempt to do the actual compression
2253 if( blocksToCompressLower ) blocksToCompressLower->compress();
2254 if( blocksToCompressUpper ) blocksToCompressUpper->compress();
2255#else
2256 blocks->compress();
2257#endif
2258}
2259
2260bool LevelChunk::isLowerBlockStorageCompressed()
2261{
2262 return lowerBlocks->isCompressed();
2263}
2264
2265int LevelChunk::isLowerBlockLightStorageCompressed()
2266{
2267 return lowerBlockLight->isCompressed();
2268}
2269
2270int LevelChunk::isLowerDataStorageCompressed()
2271{
2272 return lowerData->isCompressed();
2273}
2274
2275void LevelChunk::writeCompressedBlockData(DataOutputStream *dos)
2276{
2277 lowerBlocks->write(dos);
2278 upperBlocks->write(dos);
2279}
2280
2281void LevelChunk::writeCompressedDataData(DataOutputStream *dos)
2282{
2283 lowerData->write(dos);
2284 upperData->write(dos);
2285}
2286
2287void LevelChunk::writeCompressedSkyLightData(DataOutputStream *dos)
2288{
2289 lowerSkyLight->write(dos);
2290 upperSkyLight->write(dos);
2291}
2292
2293void LevelChunk::writeCompressedBlockLightData(DataOutputStream *dos)
2294{
2295 lowerBlockLight->write(dos);
2296 upperBlockLight->write(dos);
2297}
2298
2299void LevelChunk::readCompressedBlockData(DataInputStream *dis)
2300{
2301 lowerBlocks->read(dis);
2302 upperBlocks->read(dis);
2303}
2304
2305void LevelChunk::readCompressedDataData(DataInputStream *dis)
2306{
2307 if( lowerData == NULL ) lowerData = new SparseDataStorage();
2308 if( upperData == NULL ) upperData = new SparseDataStorage(true);
2309 lowerData->read(dis);
2310 upperData->read(dis);
2311}
2312
2313void LevelChunk::readCompressedSkyLightData(DataInputStream *dis)
2314{
2315 if( lowerSkyLight == NULL ) lowerSkyLight = new SparseLightStorage(true);
2316 if( upperSkyLight == NULL ) upperSkyLight = new SparseLightStorage(true,true);
2317 lowerSkyLight->read(dis);
2318 upperSkyLight->read(dis);
2319}
2320
2321void LevelChunk::readCompressedBlockLightData(DataInputStream *dis)
2322{
2323 if( lowerBlockLight == NULL ) lowerBlockLight = new SparseLightStorage(false);
2324 if( upperBlockLight == NULL ) upperBlockLight = new SparseLightStorage(false, true);
2325 lowerBlockLight->read(dis);
2326 upperBlockLight->read(dis);
2327}
2328
2329// Attempt to compress data. Doesn't make any guarantee that it will succeed - can only compress if the data is being shared, and nothing else is trying to update it from another thread.
2330void LevelChunk::compressData()
2331{
2332#ifdef SHARING_ENABLED
2333 SparseDataStorage *dataToCompressLower = NULL;
2334 SparseDataStorage *dataToCompressUpper = NULL;
2335
2336 // If we're the host machine, and this is the client level, then we only want to do this if we are sharing data. This means that we will be compressing the data that is shared from the server.
2337 // No point trying to compress the local client copy of the data if the data is unshared, since we'll be throwing this data away again anyway once we share with the server again.
2338 if( level->isClientSide && g_NetworkManager.IsHost() )
2339 {
2340 // Note - only the extraction of the pointers needs to be done in the critical section, since even if the data is unshared whilst we are processing this data is still valid (for the server)
2341 EnterCriticalSection(&m_csSharing);
2342 if( sharingTilesAndData )
2343 {
2344 dataToCompressLower = lowerData;
2345 dataToCompressUpper = upperData;
2346 }
2347 LeaveCriticalSection(&m_csSharing);
2348 }
2349 else
2350 {
2351 // Not the host, simple case
2352 dataToCompressLower = lowerData;
2353 dataToCompressUpper = upperData;
2354 }
2355
2356 // Attempt to do the actual compression
2357 if( dataToCompressLower ) dataToCompressLower->compress();
2358 if( dataToCompressUpper ) dataToCompressUpper->compress();
2359#else
2360 data->compress();
2361#endif
2362}
2363
2364bool LevelChunk::isRenderChunkEmpty(int y)
2365{
2366 if( isEmpty() )
2367 {
2368 return true;
2369 }
2370 if( y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT )
2371 {
2372 return upperBlocks->isRenderChunkEmpty( y - Level::COMPRESSED_CHUNK_SECTION_HEIGHT );
2373 }
2374 else
2375 {
2376 return lowerBlocks->isRenderChunkEmpty( y );
2377 }
2378}
2379
2380// Set block data to that passed in in the input array of size 32768
2381void LevelChunk::setBlockData(byteArray data)
2382{
2383 lowerBlocks->setData(data,0);
2384 if(data.length > Level::COMPRESSED_CHUNK_SECTION_TILES) upperBlocks->setData(data,Level::COMPRESSED_CHUNK_SECTION_TILES);
2385}
2386
2387// Sets data in passed in array of size 32768, from the block data in this chunk
2388void LevelChunk::getBlockData(byteArray data)
2389{
2390 lowerBlocks->getData(data,0);
2391 if(data.length > Level::COMPRESSED_CHUNK_SECTION_TILES) upperBlocks->getData(data,Level::COMPRESSED_CHUNK_SECTION_TILES);
2392}
2393
2394int LevelChunk::getBlocksAllocatedSize(int *count0, int *count1, int *count2, int *count4, int *count8)
2395{
2396 return lowerBlocks->getAllocatedSize(count0, count1, count2, count4, count8);
2397}
2398
2399int LevelChunk::getHighestNonEmptyY()
2400{
2401 int highestNonEmptyY = -1;
2402 if(upperBlocks)
2403 {
2404 int upperNonEmpty = upperBlocks->getHighestNonEmptyY();
2405 if( upperNonEmpty >= 0 )
2406 {
2407 highestNonEmptyY = upperNonEmpty + Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
2408 }
2409 }
2410 if(highestNonEmptyY < 0) highestNonEmptyY = lowerBlocks->getHighestNonEmptyY();
2411 if(highestNonEmptyY < 0) highestNonEmptyY = 0;
2412
2413 return highestNonEmptyY;
2414}
2415
2416byteArray LevelChunk::getReorderedBlocksAndData(int x0, int y0, int z0, int xs, int &ys, int zs)
2417{
2418 int highestNonEmpty = getHighestNonEmptyY();
2419
2420 ys = min(highestNonEmpty - y0, ys);
2421 if(ys < 0 ) ys = 0;
2422
2423 int x1 = x0 + xs;
2424 int y1 = y0 + ys;
2425 int z1 = z0 + zs;
2426
2427 unsigned int tileCount = xs * ys * zs;
2428 unsigned int halfTileCount = tileCount/2;
2429
2430 byteArray data = byteArray( tileCount + (3* halfTileCount) + biomes.length );
2431 for( int x = 0; x < xs; x++ )
2432 {
2433 for( int z = 0; z < zs; z++ )
2434 {
2435 for( int y = 0; y < ys; y++ )
2436 {
2437 int slot = (y*xs*zs) + (z*xs) + x;
2438
2439 data[slot] = getTile(x,y,z);
2440 }
2441 }
2442 }
2443
2444 int p = tileCount;
2445
2446 // 4J Stu - Added this because some "min" functions don't let us use our constants :(
2447 int compressedHeight = Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
2448
2449 // 4J - replaced data storage as now using SparseDataStorage
2450 if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerData->getDataRegion( data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p );
2451 if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperData->getDataRegion( data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p );
2452
2453 // 4J - replaced block and skylight storage as these now use our SparseLightStorage
2454 if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerBlockLight->getDataRegion( data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p );
2455 if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperBlockLight->getDataRegion( data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p );
2456
2457 if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerSkyLight->getDataRegion( data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p );
2458 if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperSkyLight->getDataRegion( data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p );
2459
2460 memcpy(&data.data[p],biomes.data,biomes.length);
2461
2462 return data;
2463
2464 //byteArray rawBuffer = byteArray( Level::CHUNK_TILE_COUNT + (3* Level::HALF_CHUNK_TILE_COUNT) );
2465 //for( int x = 0; x < 16; x++ )
2466 //{
2467 // for( int z = 0; z < 16; z++ )
2468 // {
2469 // for( int y = 0; y < Level::maxBuildHeight; y++ )
2470 // {
2471 // int slot = y << 8 | z << 4 | x;
2472
2473 // rawBuffer[slot] = lc->getTile(x,y,z);
2474 // }
2475 // }
2476 //}
2477 //
2478 //unsigned int offset = Level::CHUNK_TILE_COUNT;
2479 //// Don't bother reordering block data, block light or sky light as they don't seem to make much difference
2480 //byteArray dataData = byteArray(rawBuffer.data+offset, Level::HALF_CHUNK_TILE_COUNT);
2481 //lc->getDataData(dataData);
2482 //offset += Level::HALF_CHUNK_TILE_COUNT;
2483 //byteArray blockLightData = byteArray(rawBuffer.data + offset, Level::HALF_CHUNK_TILE_COUNT);
2484 //offset += Level::HALF_CHUNK_TILE_COUNT;
2485 //byteArray skyLightData = byteArray(rawBuffer.data + offset, Level::HALF_CHUNK_TILE_COUNT);
2486 //lc->getBlockLightData(blockLightData);
2487 //lc->getSkyLightData(skyLightData);
2488 //return rawBuffer;
2489}
2490
2491void LevelChunk::reorderBlocksAndDataToXZY(int y0, int xs, int ys, int zs, byteArray *data)
2492{
2493 int y1 = y0 + ys;
2494 unsigned int tileCount = xs * ys * zs;
2495 unsigned int halfTileCount = tileCount/2;
2496
2497 int sectionHeight = Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
2498 int lowerYSpan = min(y1, sectionHeight) - y0;
2499 int upperYSpan = ys - lowerYSpan;
2500 int upperSlotOffset = xs * zs * lowerYSpan;
2501
2502 int biomesLength = 16 * 16;
2503 byteArray newBuffer = byteArray(tileCount + (3* halfTileCount) + biomesLength);
2504 for( int x = 0; x < xs; x++ )
2505 {
2506 for( int z = 0; z < zs; z++ )
2507 {
2508 for( int y = 0; y < ys; y++ )
2509 {
2510 int slotY = y;
2511 unsigned int targetSlotOffset = 0;
2512 int ySpan = lowerYSpan;
2513 if(y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT)
2514 {
2515 slotY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
2516 targetSlotOffset = upperSlotOffset;
2517 ySpan = upperYSpan;
2518 }
2519 int slot = (x*zs*ySpan) + (z*ySpan) + slotY;
2520 int slot2 = (y*xs*zs) + (z*xs) + x;
2521
2522 newBuffer[slot + targetSlotOffset] = data->data[slot2];
2523 }
2524 }
2525 }
2526 // Copy over block data, block light, skylight and biomes as-is
2527 memcpy(newBuffer.data + tileCount, data->data + tileCount, 3*halfTileCount + biomesLength);
2528 delete [] data->data;
2529 data->data = newBuffer.data;
2530
2531 //int p = 0;
2532 //setBlocksAndData(*data, x0, y0, z0, x1, y1, z1, p);
2533
2534 //// If it is a full chunk, we'll need to rearrange into the order the rest of the game expects
2535 //if( xs == 16 && ys == 128 && zs == 16 && ( ( x & 15 ) == 0 ) && ( y == 0 ) && ( ( z & 15 ) == 0 ) )
2536 //{
2537 // byteArray newBuffer = byteArray(81920);
2538 // for( int x = 0; x < 16; x++ )
2539 // {
2540 // for( int z = 0; z < 16; z++ )
2541 // {
2542 // for( int y = 0; y < 128; y++ )
2543 // {
2544 // int slot = x << 11 | z << 7 | y;
2545 // int slot2 = y << 8 | z << 4 | x;
2546
2547 // newBuffer[slot] = buffer[slot2];
2548 // }
2549 // }
2550 // }
2551 // // Copy over block data, block light & skylight as-is
2552 // memcpy(newBuffer.data + 32768, buffer.data + 32768, 49152);
2553 // delete buffer.data;
2554 // buffer.data = newBuffer.data;
2555 //}
2556}