the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 2556 lines 82 kB view raw
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}