the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 1024 lines 33 kB view raw
1#include "stdafx.h" 2#include <vector> 3#include "..\..\..\Minecraft.World\com.mojang.nbt.h" 4#include "..\..\..\Minecraft.World\System.h" 5#include "ConsoleSchematicFile.h" 6#include "..\..\..\Minecraft.World\InputOutputStream.h" 7#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" 8#include "..\..\..\Minecraft.World\net.minecraft.world.level.chunk.h" 9#include "..\..\..\Minecraft.World\net.minecraft.world.level.tile.entity.h" 10#include "..\..\..\Minecraft.World\net.minecraft.world.entity.h" 11#include "..\..\..\Minecraft.World\net.minecraft.world.entity.item.h" 12#include "..\..\..\Minecraft.World\net.minecraft.world.phys.h" 13#include "..\..\..\Minecraft.World\compression.h" 14 15ConsoleSchematicFile::ConsoleSchematicFile() 16{ 17 m_xSize = m_ySize = m_zSize = 0; 18 m_refCount = 1; 19 m_data.data = NULL; 20} 21 22ConsoleSchematicFile::~ConsoleSchematicFile() 23{ 24 app.DebugPrintf("Deleting schematic file\n"); 25 if(m_data.data != NULL) delete [] m_data.data; 26} 27 28void ConsoleSchematicFile::save(DataOutputStream *dos) 29{ 30 if(dos != NULL) 31 { 32 dos->writeInt(XBOX_SCHEMATIC_CURRENT_VERSION); 33 34 dos->writeByte(APPROPRIATE_COMPRESSION_TYPE); 35 36 dos->writeInt(m_xSize); 37 dos->writeInt(m_ySize); 38 dos->writeInt(m_zSize); 39 40 byteArray ba(new BYTE[ m_data.length ], m_data.length); 41 Compression::getCompression()->CompressLZXRLE( ba.data, &ba.length, 42 m_data.data, m_data.length); 43 44 dos->writeInt(ba.length); 45 dos->write(ba); 46 47 save_tags(dos); 48 49 delete [] ba.data; 50 } 51} 52 53void ConsoleSchematicFile::load(DataInputStream *dis) 54{ 55 if(dis != NULL) 56 { 57 // VERSION CHECK // 58 int version = dis->readInt(); 59 60 Compression::ECompressionTypes compressionType = Compression::eCompressionType_LZXRLE; 61 62 if (version > XBOX_SCHEMATIC_ORIGINAL_VERSION) // Or later versions 63 { 64 compressionType = (Compression::ECompressionTypes)dis->readByte(); 65 } 66 67 if (version > XBOX_SCHEMATIC_CURRENT_VERSION) 68 assert(false && "Unrecognised schematic version!!"); 69 70 m_xSize = dis->readInt(); 71 m_ySize = dis->readInt(); 72 m_zSize = dis->readInt(); 73 74 int compressedSize = dis->readInt(); 75 byteArray compressedBuffer(compressedSize); 76 dis->readFully(compressedBuffer); 77 78 if(m_data.data != NULL) 79 { 80 delete [] m_data.data; 81 m_data.data = NULL; 82 } 83 84 if(compressionType == Compression::eCompressionType_None) 85 { 86 m_data = compressedBuffer; 87 } 88 else 89 { 90 unsigned int outputSize = m_xSize * m_ySize * m_zSize * 3/2; 91 m_data = byteArray(outputSize); 92 93 switch(compressionType) 94 { 95 case Compression::eCompressionType_RLE: 96 Compression::getCompression()->DecompressRLE( m_data.data, &m_data.length, compressedBuffer.data, compressedSize); 97 break; 98 case APPROPRIATE_COMPRESSION_TYPE: 99 Compression::getCompression()->DecompressLZXRLE( m_data.data, &m_data.length, compressedBuffer.data, compressedSize); 100 break; 101 default: 102 app.DebugPrintf("Unrecognized compression type for Schematic file (%d)\n", (int)compressionType); 103 Compression::getCompression()->SetDecompressionType( (Compression::ECompressionTypes)compressionType ); 104 Compression::getCompression()->DecompressLZXRLE( m_data.data, &m_data.length, compressedBuffer.data, compressedSize); 105 Compression::getCompression()->SetDecompressionType( APPROPRIATE_COMPRESSION_TYPE ); 106 }; 107 108 delete [] compressedBuffer.data; 109 } 110 111 // READ TAGS // 112 CompoundTag *tag = NbtIo::read(dis); 113 ListTag<CompoundTag> *tileEntityTags = (ListTag<CompoundTag> *) tag->getList(L"TileEntities"); 114 if (tileEntityTags != NULL) 115 { 116 for (int i = 0; i < tileEntityTags->size(); i++) 117 { 118 CompoundTag *teTag = tileEntityTags->get(i); 119 shared_ptr<TileEntity> te = TileEntity::loadStatic(teTag); 120 121 if(te == NULL) 122 { 123#ifndef _CONTENT_PACKAGE 124 app.DebugPrintf("ConsoleSchematicFile has read a NULL tile entity\n"); 125 __debugbreak(); 126#endif 127 } 128 else 129 { 130 m_tileEntities.push_back(te); 131 } 132 } 133 } 134 ListTag<CompoundTag> *entityTags = (ListTag<CompoundTag> *) tag->getList(L"Entities"); 135 if (entityTags != NULL) 136 { 137 for (int i = 0; i < entityTags->size(); i++) 138 { 139 CompoundTag *eTag = entityTags->get(i); 140 eINSTANCEOF type = EntityIO::getType(eTag->getString(L"id")); 141 ListTag<DoubleTag> *pos = (ListTag<DoubleTag> *) eTag->getList(L"Pos"); 142 143 double x = pos->get(0)->data; 144 double y = pos->get(1)->data; 145 double z = pos->get(2)->data; 146 147 if( type == eTYPE_PAINTING || type == eTYPE_ITEM_FRAME ) 148 { 149 x = ((IntTag *) eTag->get(L"TileX") )->data; 150 y = ((IntTag *) eTag->get(L"TileY") )->data; 151 z = ((IntTag *) eTag->get(L"TileZ") )->data; 152 } 153#ifdef _DEBUG 154 //app.DebugPrintf(1,"Loaded entity type %d at (%f,%f,%f)\n",(int)type,x,y,z); 155#endif 156 m_entities.push_back( pair<Vec3 *, CompoundTag *>(Vec3::newPermanent(x,y,z),(CompoundTag *)eTag->copy())); 157 } 158 } 159 delete tag; 160 } 161} 162 163void ConsoleSchematicFile::save_tags(DataOutputStream *dos) 164{ 165 CompoundTag *tag = new CompoundTag(); 166 167 ListTag<CompoundTag> *tileEntityTags = new ListTag<CompoundTag>(); 168 tag->put(L"TileEntities", tileEntityTags); 169 170 for (AUTO_VAR(it, m_tileEntities.begin()); it != m_tileEntities.end(); it++) 171 { 172 CompoundTag *cTag = new CompoundTag(); 173 (*it)->save(cTag); 174 tileEntityTags->add(cTag); 175 } 176 177 ListTag<CompoundTag> *entityTags = new ListTag<CompoundTag>(); 178 tag->put(L"Entities", entityTags); 179 180 for (AUTO_VAR(it, m_entities.begin()); it != m_entities.end(); it++) 181 entityTags->add( (CompoundTag *)(*it).second->copy() ); 182 183 NbtIo::write(tag,dos); 184 delete tag; 185} 186 187__int64 ConsoleSchematicFile::applyBlocksAndData(LevelChunk *chunk, AABB *chunkBox, AABB *destinationBox, ESchematicRotation rot) 188{ 189 int xStart = max(destinationBox->x0, (double)chunk->x*16); 190 int xEnd = min(destinationBox->x1, (double)((xStart>>4)<<4) + 16); 191 192 int yStart = destinationBox->y0; 193 int yEnd = destinationBox->y1; 194 if(yEnd > Level::maxBuildHeight) yEnd = Level::maxBuildHeight; 195 196 int zStart = max(destinationBox->z0, (double)chunk->z*16); 197 int zEnd = min(destinationBox->z1, (double)((zStart>>4)<<4) + 16); 198 199#ifdef _DEBUG 200 app.DebugPrintf("Range is (%d,%d,%d) to (%d,%d,%d)\n",xStart,yStart,zStart,xEnd-1,yEnd-1,zEnd-1); 201#endif 202 203 int rowBlocksIncluded = (yEnd-yStart)*(zEnd-zStart); 204 int blocksIncluded = (xEnd-xStart)*rowBlocksIncluded; 205 206 int rowBlockCount = getYSize() * getZSize(); 207 int totalBlockCount = getXSize() * rowBlockCount; 208 209 byteArray blockData = byteArray(Level::CHUNK_TILE_COUNT); 210 PIXBeginNamedEvent(0,"Getting block data"); 211 chunk->getBlockData(blockData); 212 PIXEndNamedEvent(); 213 byteArray dataData = byteArray(Level::HALF_CHUNK_TILE_COUNT); 214 PIXBeginNamedEvent(0,"Getting Data data"); 215 chunk->getDataData(dataData); 216 PIXEndNamedEvent(); 217 218 // Ignore light data 219 int blockLightP = -1; 220 int skyLightP = -1; 221 if( rot == eSchematicRot_90 || rot == eSchematicRot_180 || rot == eSchematicRot_270 ) 222 { 223 int schematicXRow = 0; 224 int schematicZRow = 0; 225 int blocksP = 0; 226 int dataP = 0; 227 228 for(int x = xStart; x < xEnd; ++x) 229 { 230 int x0 = x - chunk->x*16; 231 int x1 = x0 + 1; 232 233 for(int z = zStart; z < zEnd; ++z) 234 { 235 int z0 = z - chunk->z*16; 236 int z1 = z0 + 1; 237 238 chunkCoordToSchematicCoord(destinationBox, x, z, rot, schematicXRow, schematicZRow); 239 blocksP = (schematicXRow*rowBlockCount) + (schematicZRow*getYSize()); 240 dataP = totalBlockCount + (blocksP)/2; 241 242 ConsoleSchematicFile::setBlocksAndData(chunk,blockData,dataData,m_data, x0, yStart, z0, x1, yEnd, z1, blocksP, dataP, blockLightP, skyLightP); 243 } 244 } 245 } 246 else if( rot == eSchematicRot_0 ) 247 { 248 // The initial pointer offsets for the different data types 249 int schematicXRow = xStart - destinationBox->x0; 250 int schematicZRow = zStart - destinationBox->z0; 251 int blocksP = (schematicXRow*rowBlockCount) + (schematicZRow*getYSize()); 252 int dataP = totalBlockCount + (schematicXRow*rowBlockCount + (schematicZRow*getYSize()))/2; 253 254 for(int x = xStart; x < xEnd; ++x) 255 { 256 int x0 = x - chunk->x*16; 257 int x1 = x0 + 1; 258 259 int z0 = zStart - chunk->z*16; 260 int z1 = zEnd - chunk->z*16; 261 262 ConsoleSchematicFile::setBlocksAndData(chunk,blockData,dataData,m_data, x0, yStart, z0, x1, yEnd, z1, blocksP, dataP, blockLightP, skyLightP); 263 // update all pointer positions 264 // For z start to z end 265 // Set blocks and data 266 // increment z by the right amount 267 blocksP += (rowBlockCount-rowBlocksIncluded); 268 dataP += (rowBlockCount-rowBlocksIncluded)/2; 269 } 270 } 271 else 272 { 273 app.DebugPrintf("ERROR: Rotation of block and data not implemented!!\n"); 274 } 275 276 // 4J Stu - Hack for ME pack to replace sand with end stone in schematics 277 //for(int i = 0; i < blockData.length; ++i) 278 //{ 279 // if(blockData[i] == Tile::sand_Id || blockData[i] == Tile::sandStone_Id) 280 // { 281 // blockData[i] = Tile::endStone_Id; 282 // } 283 //} 284 285 PIXBeginNamedEvent(0,"Setting Block data"); 286 chunk->setBlockData(blockData); 287 PIXEndNamedEvent(); 288 delete blockData.data; 289 chunk->recalcHeightmapOnly(); 290 PIXBeginNamedEvent(0,"Setting Data data"); 291 chunk->setDataData(dataData); 292 PIXEndNamedEvent(); 293 delete dataData.data; 294 295 // A basic pass through to roughly do the lighting. At this point of post-processing, we don't have all the neighbouring chunks loaded in, 296 // so any lighting here should be things that won't propagate out of this chunk. 297 for( int xx = xStart ; xx < xEnd; xx++ ) 298 for( int y = yStart ; y < yEnd; y++ ) 299 for( int zz = zStart ; zz < zEnd; zz++ ) 300 { 301 int x = xx - chunk->x * 16; 302 int z = zz - chunk->z * 16; 303 chunk->setBrightness(LightLayer::Block,x,y,z,0); 304 if( chunk->getTile(x,y,z) ) 305 { 306 chunk->setBrightness(LightLayer::Sky,x,y,z,0); 307 } 308 else 309 { 310 if( chunk->isSkyLit(x,y,z) ) 311 { 312 chunk->setBrightness(LightLayer::Sky,x,y,z,15); 313 } 314 else 315 { 316 chunk->setBrightness(LightLayer::Sky,x,y,z,0); 317 } 318 } 319 } 320 321 return blocksIncluded; 322} 323 324// At the point that this is called, we have all the neighbouring chunks loaded in (and generally post-processed, apart from this lighting pass), so 325// we can do the sort of lighting that might propagate out of the chunk. 326__int64 ConsoleSchematicFile::applyLighting(LevelChunk *chunk, AABB *chunkBox, AABB *destinationBox, ESchematicRotation rot) 327{ 328 int xStart = max(destinationBox->x0, (double)chunk->x*16); 329 int xEnd = min(destinationBox->x1, (double)((xStart>>4)<<4) + 16); 330 331 int yStart = destinationBox->y0; 332 int yEnd = destinationBox->y1; 333 if(yEnd > Level::maxBuildHeight) yEnd = Level::maxBuildHeight; 334 335 int zStart = max(destinationBox->z0, (double)chunk->z*16); 336 int zEnd = min(destinationBox->z1, (double)((zStart>>4)<<4) + 16); 337 338 int rowBlocksIncluded = (yEnd-yStart)*(zEnd-zStart); 339 int blocksIncluded = (xEnd-xStart)*rowBlocksIncluded; 340 341 // Now actually do a checkLight on blocks that might need it, which should more accurately put everything in place 342 for( int xx = xStart ; xx < xEnd; xx++ ) 343 for( int y = yStart ; y < yEnd; y++ ) 344 for( int zz = zStart ; zz < zEnd; zz++ ) 345 { 346 int x = xx - chunk->x * 16; 347 int z = zz - chunk->z * 16; 348 349 if( y <= chunk->getHeightmap( x, z ) ) 350 { 351 chunk->level->checkLight(LightLayer::Sky, xx, y, zz, true); 352 } 353 if( Tile::lightEmission[chunk->getTile(x,y,z)] ) 354 { 355 // Note that this lighting passes a rootOnlyEmissive flag of true, which means that only the location xx/y/zz is considered 356 // as possibly being a source of emissive light, not other tiles that we might encounter whilst propagating the light from 357 // the start location. If we don't do this, and Do encounter another emissive source in the radius of influence that the first 358 // light source had, then we'll start also lighting from that tile but won't actually be able to progatate that second light 359 // fully since checkLight only has a finite radius of 17 from the start position that it can light. Then when we do a checkLight 360 // on the second light later, it won't bother doing anything because the light level at the location of the tile itself will be correct. 361 chunk->level->checkLight(LightLayer::Block, xx, y, zz, true, true); 362 } 363 } 364 365 return blocksIncluded; 366} 367 368void ConsoleSchematicFile::chunkCoordToSchematicCoord(AABB *destinationBox, int chunkX, int chunkZ, ESchematicRotation rot, int &schematicX, int &schematicZ) 369{ 370 switch(rot) 371 { 372 case eSchematicRot_90: 373 // schematicX decreases as chunkZ increases 374 // schematicZ increases as chunkX increases 375 schematicX = chunkZ - destinationBox->z0; 376 schematicZ = (destinationBox->x1 - 1 - destinationBox->x0) - (chunkX - destinationBox->x0); 377 break; 378 case eSchematicRot_180: 379 // schematicX decreases as chunkX increases 380 // schematicZ decreases as chunkZ increases 381 schematicX = (destinationBox->x1 - 1 - destinationBox->x0) - (chunkX - destinationBox->x0); 382 schematicZ = (destinationBox->z1 - 1 - destinationBox->z0) - (chunkZ - destinationBox->z0); 383 break; 384 case eSchematicRot_270: 385 // schematicX increases as chunkZ increases 386 // shcematicZ decreases as chunkX increases 387 schematicX = (destinationBox->z1 - 1 - destinationBox->z0) - (chunkZ - destinationBox->z0); 388 schematicZ = chunkX - destinationBox->x0; 389 break; 390 case eSchematicRot_0: 391 default: 392 // schematicX increases as chunkX increases 393 // schematicZ increases as chunkZ increases 394 schematicX = chunkX - destinationBox->x0; 395 schematicZ = chunkZ - destinationBox->z0; 396 break; 397 }; 398} 399 400void ConsoleSchematicFile::schematicCoordToChunkCoord(AABB *destinationBox, double schematicX, double schematicZ, ESchematicRotation rot, double &chunkX, double &chunkZ) 401{ 402 switch(rot) 403 { 404 case eSchematicRot_90: 405 // schematicX decreases as chunkZ increases 406 // schematicZ increases as chunkX increases 407 chunkX = (destinationBox->x1 - 1 - schematicZ); 408 chunkZ = schematicX + destinationBox->z0; 409 break; 410 case eSchematicRot_180: 411 // schematicX decreases as chunkX increases 412 // schematicZ decreases as chunkZ increases 413 chunkX = (destinationBox->x1 - 1 - schematicX); 414 chunkZ = (destinationBox->z1 - 1 - schematicZ); 415 break; 416 case eSchematicRot_270: 417 // schematicX increases as chunkZ increases 418 // shcematicZ decreases as chunkX increases 419 chunkX = schematicZ + destinationBox->x0; 420 chunkZ = (destinationBox->z1 - 1 - schematicX); 421 break; 422 case eSchematicRot_0: 423 default: 424 // schematicX increases as chunkX increases 425 // schematicZ increases as chunkZ increases 426 chunkX = schematicX + destinationBox->x0; 427 chunkZ = schematicZ + destinationBox->z0; 428 break; 429 }; 430} 431 432void ConsoleSchematicFile::applyTileEntities(LevelChunk *chunk, AABB *chunkBox, AABB *destinationBox, ESchematicRotation rot) 433{ 434 for(AUTO_VAR(it, m_tileEntities.begin()); it != m_tileEntities.end();++it) 435 { 436 shared_ptr<TileEntity> te = *it; 437 438 double targetX = te->x; 439 double targetY = te->y + destinationBox->y0; 440 double targetZ = te->z; 441 442 schematicCoordToChunkCoord(destinationBox, te->x, te->z, rot, targetX, targetZ); 443 444 Vec3 *pos = Vec3::newTemp(targetX,targetY,targetZ); 445 if( chunkBox->containsIncludingLowerBound(pos) ) 446 { 447 shared_ptr<TileEntity> teCopy = chunk->getTileEntity( (int)targetX & 15, (int)targetY & 15, (int)targetZ & 15 ); 448 449 if ( teCopy != NULL ) 450 { 451 CompoundTag *teData = new CompoundTag(); 452 te->save(teData); 453 454 teCopy->load(teData); 455 456 delete teData; 457 458 // Adjust the tileEntity position to world coords from schematic co-ords 459 teCopy->x = targetX; 460 teCopy->y = targetY; 461 teCopy->z = targetZ; 462 463 // Remove the current tile entity 464 //chunk->removeTileEntity( (int)targetX & 15, (int)targetY & 15, (int)targetZ & 15 ); 465 } 466 else 467 { 468 teCopy = te->clone(); 469 470 // Adjust the tileEntity position to world coords from schematic co-ords 471 teCopy->x = targetX; 472 teCopy->y = targetY; 473 teCopy->z = targetZ; 474 chunk->addTileEntity(teCopy); 475 } 476 477 teCopy->setChanged(); 478 } 479 } 480 for(AUTO_VAR(it, m_entities.begin()); it != m_entities.end();) 481 { 482 Vec3 *source = it->first; 483 484 double targetX = source->x; 485 double targetY = source->y + destinationBox->y0; 486 double targetZ = source->z; 487 schematicCoordToChunkCoord(destinationBox, source->x, source->z, rot, targetX, targetZ); 488 489 // Add 0.01 as the AABB::contains function returns false if a value is <= the lower bound 490 Vec3 *pos = Vec3::newTemp(targetX+0.01,targetY+0.01,targetZ+0.01); 491 if( !chunkBox->containsIncludingLowerBound(pos) ) 492 { 493 ++it; 494 continue; 495 } 496 497 CompoundTag *eTag = it->second; 498 shared_ptr<Entity> e = EntityIO::loadStatic(eTag, NULL); 499 500 if( e->GetType() == eTYPE_PAINTING ) 501 { 502 shared_ptr<Painting> painting = dynamic_pointer_cast<Painting>(e); 503 504 double tileX = painting->xTile; 505 double tileZ = painting->zTile; 506 schematicCoordToChunkCoord(destinationBox, painting->xTile, painting->zTile, rot, tileX, tileZ); 507 508 painting->yTile += destinationBox->y0; 509 painting->xTile = tileX; 510 painting->zTile = tileZ; 511 painting->setDir(painting->dir); 512 } 513 else if( e->GetType() == eTYPE_ITEM_FRAME ) 514 { 515 shared_ptr<ItemFrame> frame = dynamic_pointer_cast<ItemFrame>(e); 516 517 double tileX = frame->xTile; 518 double tileZ = frame->zTile; 519 schematicCoordToChunkCoord(destinationBox, frame->xTile, frame->zTile, rot, tileX, tileZ); 520 521 frame->yTile += destinationBox->y0; 522 frame->xTile = tileX; 523 frame->zTile = tileZ; 524 frame->setDir(frame->dir); 525 } 526 else 527 { 528 e->absMoveTo(targetX, targetY, targetZ,e->yRot,e->xRot); 529 } 530#ifdef _DEBUG 531 app.DebugPrintf("Adding entity type %d at (%f,%f,%f)\n",e->GetType(),e->x,e->y,e->z); 532#endif 533 e->setLevel(chunk->level); 534 e->resetSmallId(); 535 e->setDespawnProtected(); // default to being protected against despawning 536 chunk->level->addEntity(e); 537 538 // 4J Stu - Until we can copy every type of entity, remove them from this vector 539 // This means that the entities will only exist in the first use of the schematic that is processed 540 //it = m_entities.erase(it); 541 ++it; 542 } 543} 544 545void ConsoleSchematicFile::generateSchematicFile(DataOutputStream *dos, Level *level, int xStart, int yStart, int zStart, int xEnd, int yEnd, int zEnd, bool bSaveMobs, Compression::ECompressionTypes compressionType) 546{ 547 assert(xEnd > xStart); 548 assert(yEnd > yStart); 549 assert(zEnd > zStart); 550 // 4J Stu - Enforce even numbered positions to start with to avoid problems with half-bytes in data 551 552 // We want the start to be even 553 if(xStart > 0 && xStart%2 != 0) 554 xStart-=1; 555 else if(xStart < 0 && xStart%2 !=0) 556 xStart-=1; 557 if(yStart < 0) yStart = 0; 558 else if(yStart > 0 && yStart%2 != 0) 559 yStart-=1; 560 if(zStart > 0 && zStart%2 != 0) 561 zStart-=1; 562 else if(zStart < 0 && zStart%2 !=0) 563 zStart-=1; 564 565 // We want the end to be odd to have a total size that is even 566 if(xEnd > 0 && xEnd%2 == 0) 567 xEnd+=1; 568 else if(xEnd < 0 && xEnd%2 ==0) 569 xEnd+=1; 570 if(yEnd > Level::maxBuildHeight) 571 yEnd = Level::maxBuildHeight; 572 else if(yEnd > 0 && yEnd%2 == 0) 573 yEnd+=1; 574 else if(yEnd < 0 && yEnd%2 ==0) 575 yEnd+=1; 576 if(zEnd > 0 && zEnd%2 == 0) 577 zEnd+=1; 578 else if(zEnd < 0 && zEnd%2 ==0) 579 zEnd+=1; 580 581 int xSize = xEnd - xStart + 1; 582 int ySize = yEnd - yStart + 1; 583 int zSize = zEnd - zStart + 1; 584 585 app.DebugPrintf("Generating schematic file for area (%d,%d,%d) to (%d,%d,%d), %dx%dx%d\n",xStart,yStart,zStart,xEnd,yEnd,zEnd,xSize,ySize,zSize); 586 587 if(dos != NULL) dos->writeInt(XBOX_SCHEMATIC_CURRENT_VERSION); 588 589 if(dos != NULL) dos->writeByte(compressionType); 590 591 //Write xSize 592 if(dos != NULL) dos->writeInt(xSize); 593 594 //Write ySize 595 if(dos != NULL) dos->writeInt(ySize); 596 597 //Write zSize 598 if(dos != NULL) dos->writeInt(zSize); 599 600 //byteArray rawBuffer = level->getBlocksAndData(xStart, yStart, zStart, xSize, ySize, zSize, false); 601 int xRowSize = ySize * zSize; 602 int blockCount = xSize * xRowSize; 603 byteArray result( blockCount * 3 / 2 ); 604 605 // Position pointers into the data when not ordered by chunk 606 int p = 0; 607 int dataP = blockCount; 608 int blockLightP = -1; 609 int skyLightP = -1; 610 611 int y0 = yStart; 612 int y1 = yStart + ySize; 613 if (y0 < 0) y0 = 0; 614 if (y1 > Level::maxBuildHeight) y1 = Level::maxBuildHeight; 615 616 // Every x is a whole row 617 for(int xPos = xStart; xPos < xStart + xSize; ++xPos) 618 { 619 int xc = xPos >> 4; 620 621 int x0 = xPos - xc * 16; 622 if (x0 < 0) x0 = 0; 623 int x1 = x0 + 1; 624 if (x1 > 16) x1 = 16; 625 626 for(int zPos = zStart; zPos < zStart + zSize;) 627 { 628 int zc = zPos >> 4; 629 630 int z0 = zStart - zc * 16; 631 int z1 = zStart + zSize - zc * 16; 632 if (z0 < 0) z0 = 0; 633 if (z1 > 16) z1 = 16; 634 getBlocksAndData(level->getChunk(xc, zc), &result, x0, y0, z0, x1, y1, z1, p, dataP, blockLightP, skyLightP); 635 zPos += (z1-z0); 636 } 637 } 638 639#ifndef _CONTENT_PACKAGE 640 if(p!=blockCount) __debugbreak(); 641#endif 642 643 // We don't know how this will compress - just make a fixed length buffer to initially decompress into 644 // Some small sets of blocks can end up compressing into something bigger than their source 645 unsigned int inputSize = blockCount * 3 / 2; 646 unsigned char *ucTemp = new unsigned char[inputSize]; 647 648 switch(compressionType) 649 { 650 case Compression::eCompressionType_LZXRLE: 651 Compression::getCompression()->CompressLZXRLE( ucTemp, &inputSize, result.data, (unsigned int) result.length ); 652 break; 653 case Compression::eCompressionType_RLE: 654 Compression::getCompression()->CompressRLE( ucTemp, &inputSize, result.data, (unsigned int) result.length ); 655 break; 656 case Compression::eCompressionType_None: 657 default: 658 memcpy( ucTemp, result.data, inputSize ); 659 break; 660 }; 661 662 delete [] result.data; 663 byteArray buffer = byteArray(ucTemp,inputSize); 664 665 if(dos != NULL) dos->writeInt(inputSize); 666 if(dos != NULL) dos->write(buffer); 667 delete [] buffer.data; 668 669 CompoundTag tag; 670 ListTag<CompoundTag> *tileEntitiesTag = new ListTag<CompoundTag>(L"tileEntities"); 671 672 int xc0 = xStart >> 4; 673 int zc0 = zStart >> 4; 674 int xc1 = (xStart + xSize - 1) >> 4; 675 int zc1 = (zStart + zSize - 1) >> 4; 676 677 for (int xc = xc0; xc <= xc1; xc++) 678 { 679 for (int zc = zc0; zc <= zc1; zc++) 680 { 681 vector<shared_ptr<TileEntity> > *tileEntities = getTileEntitiesInRegion(level->getChunk(xc, zc), xStart, yStart, zStart, xStart + xSize, yStart + ySize, zStart + zSize); 682 for(AUTO_VAR(it, tileEntities->begin()); it != tileEntities->end(); ++it) 683 { 684 shared_ptr<TileEntity> te = *it; 685 CompoundTag *teTag = new CompoundTag(); 686 shared_ptr<TileEntity> teCopy = te->clone(); 687 688 // Adjust the tileEntity position to schematic coords from world co-ords 689 teCopy->x -= xStart; 690 teCopy->y -= yStart; 691 teCopy->z -= zStart; 692 teCopy->save(teTag); 693 tileEntitiesTag->add(teTag); 694 } 695 delete tileEntities; 696 } 697 } 698 tag.put(L"TileEntities", tileEntitiesTag); 699 700 AABB *bb = AABB::newTemp(xStart,yStart,zStart,xEnd,yEnd,zEnd); 701 vector<shared_ptr<Entity> > *entities = level->getEntities(nullptr, bb); 702 ListTag<CompoundTag> *entitiesTag = new ListTag<CompoundTag>(L"entities"); 703 704 for(AUTO_VAR(it, entities->begin()); it != entities->end(); ++it) 705 { 706 shared_ptr<Entity> e = *it; 707 708 bool mobCanBeSaved = false; 709 if (bSaveMobs) 710 { 711 if ( e->instanceof(eTYPE_MONSTER) || e->instanceof(eTYPE_WATERANIMAL) || e->instanceof(eTYPE_ANIMAL) || (e->GetType() == eTYPE_VILLAGER) ) 712 713 // 4J-JEV: All these are derived from eTYPE_ANIMAL and true implicitly. 714 //|| ( e->GetType() == eTYPE_CHICKEN ) || ( e->GetType() == eTYPE_WOLF ) || ( e->GetType() == eTYPE_MUSHROOMCOW ) ) 715 { 716 mobCanBeSaved = true; 717 } 718 } 719 720 // 4J-JEV: Changed to check for instances of minecarts and hangingEntities instead of just eTYPE_PAINTING, eTYPE_ITEM_FRAME and eTYPE_MINECART 721 if (mobCanBeSaved || e->instanceof(eTYPE_MINECART) || e->GetType() == eTYPE_BOAT || e->instanceof(eTYPE_HANGING_ENTITY)) 722 { 723 CompoundTag *eTag = new CompoundTag(); 724 if( e->save(eTag) ) 725 { 726 ListTag<DoubleTag> *pos = (ListTag<DoubleTag> *) eTag->getList(L"Pos"); 727 728 pos->get(0)->data -= xStart; 729 pos->get(1)->data -= yStart; 730 pos->get(2)->data -= zStart; 731 732 if( e->instanceof(eTYPE_HANGING_ENTITY) ) 733 { 734 ((IntTag *) eTag->get(L"TileX") )->data -= xStart; 735 ((IntTag *) eTag->get(L"TileY") )->data -= yStart; 736 ((IntTag *) eTag->get(L"TileZ") )->data -= zStart; 737 } 738 739 entitiesTag->add(eTag); 740 } 741 } 742 } 743 744 tag.put(L"Entities", entitiesTag); 745 746 if(dos != NULL) NbtIo::write(&tag,dos); 747} 748 749void ConsoleSchematicFile::getBlocksAndData(LevelChunk *chunk, byteArray *data, int x0, int y0, int z0, int x1, int y1, int z1, int &blocksP, int &dataP, int &blockLightP, int &skyLightP) 750{ 751 // 4J Stu - Needs updated to work with higher worlds, should still work with non-optimised version below 752 //int xs = x1 - x0; 753 //int ys = y1 - y0; 754 //int zs = z1 - z0; 755 //if (xs * ys * zs == LevelChunk::BLOCKS_LENGTH) 756 //{ 757 // byteArray blockData = byteArray(data->data + blocksP, Level::CHUNK_TILE_COUNT); 758 // chunk->getBlockData(blockData); 759 // blocksP += blockData.length; 760 761 // byteArray dataData = byteArray(data->data + dataP, 16384); 762 // chunk->getBlockLightData(dataData); 763 // dataP += dataData.length; 764 765 // byteArray blockLightData = byteArray(data->data + blockLightP, 16384); 766 // chunk->getBlockLightData(blockLightData); 767 // blockLightP += blockLightData.length; 768 769 // byteArray skyLightData = byteArray(data->data + skyLightP, 16384); 770 // chunk->getSkyLightData(skyLightData); 771 // skyLightP += skyLightData.length; 772 // return; 773 //} 774 775 bool bHasLower, bHasUpper; 776 bHasLower = bHasUpper = false; 777 int lowerY0, lowerY1, upperY0, upperY1; 778 lowerY0 = upperY0 = y0; 779 lowerY1 = upperY1 = y1; 780 781 int compressedHeight = Level::COMPRESSED_CHUNK_SECTION_HEIGHT; 782 if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) 783 { 784 lowerY0 = y0; 785 lowerY1 = min(y1, compressedHeight); 786 bHasLower = true; 787 } 788 if(y1 >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) 789 { 790 upperY0 = max(y0, compressedHeight) - Level::COMPRESSED_CHUNK_SECTION_HEIGHT; 791 upperY1 = y1 - Level::COMPRESSED_CHUNK_SECTION_HEIGHT; 792 bHasUpper = true; 793 } 794 795 byteArray blockData = byteArray(Level::CHUNK_TILE_COUNT); 796 chunk->getBlockData(blockData); 797 for (int x = x0; x < x1; x++) 798 for (int z = z0; z < z1; z++) 799 { 800 if(bHasLower) 801 { 802 int slot = x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0; 803 int len = lowerY1 - lowerY0; 804 System::arraycopy(blockData, slot, data, blocksP, len); 805 blocksP += len; 806 } 807 if(bHasUpper) 808 { 809 int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) + Level::COMPRESSED_CHUNK_SECTION_TILES; 810 int len = upperY1 - upperY0; 811 System::arraycopy(blockData, slot, data, blocksP, len); 812 blocksP += len; 813 } 814 } 815 delete blockData.data; 816 817 byteArray dataData = byteArray(Level::CHUNK_TILE_COUNT); 818 chunk->getDataData(dataData); 819 for (int x = x0; x < x1; x++) 820 for (int z = z0; z < z1; z++) 821 { 822 if(bHasLower) 823 { 824 int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; 825 int len = (lowerY1 - lowerY0) / 2; 826 System::arraycopy(dataData, slot, data, dataP, len); 827 dataP += len; 828 } 829 if(bHasUpper) 830 { 831 int slot = ((x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) + Level::COMPRESSED_CHUNK_SECTION_TILES) >> 1; 832 int len = (upperY1 - upperY0) / 2; 833 System::arraycopy(dataData, slot, data, dataP, len); 834 dataP += len; 835 } 836 } 837 delete dataData.data; 838 839 // 4J Stu - Allow ignoring light data 840 if(blockLightP > -1) 841 { 842 byteArray blockLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); 843 chunk->getBlockLightData(blockLightData); 844 for (int x = x0; x < x1; x++) 845 for (int z = z0; z < z1; z++) 846 { 847 if(bHasLower) 848 { 849 int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; 850 int len = (lowerY1 - lowerY0) / 2; 851 System::arraycopy(blockLightData, slot, data, blockLightP, len); 852 blockLightP += len; 853 } 854 if(bHasUpper) 855 { 856 int slot = ((x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) >> 1) + (Level::COMPRESSED_CHUNK_SECTION_TILES/2); 857 int len = (upperY1 - upperY0) / 2; 858 System::arraycopy(blockLightData, slot, data, blockLightP, len); 859 blockLightP += len; 860 } 861 } 862 delete blockLightData.data; 863 } 864 865 866 // 4J Stu - Allow ignoring light data 867 if(skyLightP > -1) 868 { 869 byteArray skyLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); 870 chunk->getSkyLightData(skyLightData); 871 for (int x = x0; x < x1; x++) 872 for (int z = z0; z < z1; z++) 873 { 874 if(bHasLower) 875 { 876 int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; 877 int len = (lowerY1 - lowerY0) / 2; 878 System::arraycopy(skyLightData, slot, data, skyLightP, len); 879 skyLightP += len; 880 } 881 if(bHasUpper) 882 { 883 int slot = ((x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) >> 1) + (Level::COMPRESSED_CHUNK_SECTION_TILES/2); 884 int len = (upperY1 - upperY0) / 2; 885 System::arraycopy(skyLightData, slot, data, skyLightP, len); 886 skyLightP += len; 887 } 888 } 889 delete skyLightData.data; 890 } 891 892 return; 893} 894 895void ConsoleSchematicFile::setBlocksAndData(LevelChunk *chunk, byteArray blockData, byteArray dataData, byteArray inputData, int x0, int y0, int z0, int x1, int y1, int z1, int &blocksP, int &dataP, int &blockLightP, int &skyLightP) 896{ 897 bool bHasLower, bHasUpper; 898 bHasLower = bHasUpper = false; 899 int lowerY0, lowerY1, upperY0, upperY1; 900 lowerY0 = upperY0 = y0; 901 lowerY1 = upperY1 = y1; 902 903 int compressedHeight = Level::COMPRESSED_CHUNK_SECTION_HEIGHT; 904 if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) 905 { 906 lowerY0 = y0; 907 lowerY1 = min(y1, compressedHeight); 908 bHasLower = true; 909 } 910 if(y1 >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) 911 { 912 upperY0 = max(y0, compressedHeight) - Level::COMPRESSED_CHUNK_SECTION_HEIGHT; 913 upperY1 = y1 - Level::COMPRESSED_CHUNK_SECTION_HEIGHT; 914 bHasUpper = true; 915 } 916 PIXBeginNamedEvent(0,"Applying block data"); 917 for (int x = x0; x < x1; x++) 918 for (int z = z0; z < z1; z++) 919 { 920 if(bHasLower) 921 { 922 int slot = x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0; 923 int len = lowerY1 - lowerY0; 924 System::arraycopy(inputData, blocksP, &blockData, slot, len); 925 blocksP += len; 926 } 927 if(bHasUpper) 928 { 929 int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) + Level::COMPRESSED_CHUNK_SECTION_TILES; 930 int len = upperY1 - upperY0; 931 System::arraycopy(inputData, blocksP, &blockData, slot, len); 932 blocksP += len; 933 } 934 } 935 PIXEndNamedEvent(); 936 937 PIXBeginNamedEvent(0,"Applying Data data"); 938 for (int x = x0; x < x1; x++) 939 for (int z = z0; z < z1; z++) 940 { 941 if(bHasLower) 942 { 943 int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; 944 int len = (lowerY1 - lowerY0) / 2; 945 System::arraycopy(inputData, dataP, &dataData, slot, len); 946 dataP += len; 947 } 948 if(bHasUpper) 949 { 950 int slot = ((x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) + Level::COMPRESSED_CHUNK_SECTION_TILES) >> 1; 951 int len = (upperY1 - upperY0) / 2; 952 System::arraycopy(inputData, dataP, &dataData, slot, len); 953 dataP += len; 954 } 955 } 956 PIXEndNamedEvent(); 957 // 4J Stu - Allow ignoring light data 958 if(blockLightP > -1) 959 { 960 byteArray blockLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); 961 chunk->getBlockLightData(blockLightData); 962 for (int x = x0; x < x1; x++) 963 for (int z = z0; z < z1; z++) 964 { 965 if(bHasLower) 966 { 967 int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; 968 int len = (lowerY1 - lowerY0) / 2; 969 System::arraycopy(inputData, blockLightP, &blockLightData, slot, len); 970 blockLightP += len; 971 } 972 if(bHasUpper) 973 { 974 int slot = ( (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) >> 1) + (Level::COMPRESSED_CHUNK_SECTION_TILES/2); 975 int len = (upperY1 - upperY0) / 2; 976 System::arraycopy(inputData, blockLightP, &blockLightData, slot, len); 977 blockLightP += len; 978 } 979 } 980 chunk->setBlockLightData(blockLightData); 981 delete blockLightData.data; 982 } 983 984 // 4J Stu - Allow ignoring light data 985 if(skyLightP > -1) 986 { 987 byteArray skyLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); 988 chunk->getSkyLightData(skyLightData); 989 for (int x = x0; x < x1; x++) 990 for (int z = z0; z < z1; z++) 991 { 992 if(bHasLower) 993 { 994 int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | lowerY0) >> 1; 995 int len = (lowerY1 - lowerY0) / 2; 996 System::arraycopy(inputData, skyLightP, &skyLightData, slot, len); 997 skyLightP += len; 998 } 999 if(bHasUpper) 1000 { 1001 int slot = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | upperY0) + (Level::COMPRESSED_CHUNK_SECTION_TILES/2); 1002 int len = (upperY1 - upperY0) / 2; 1003 System::arraycopy(inputData, skyLightP, &skyLightData, slot, len); 1004 skyLightP += len; 1005 } 1006 } 1007 chunk->setSkyLightData(skyLightData); 1008 delete skyLightData.data; 1009 } 1010} 1011 1012vector<shared_ptr<TileEntity> > *ConsoleSchematicFile::getTileEntitiesInRegion(LevelChunk *chunk, int x0, int y0, int z0, int x1, int y1, int z1) 1013{ 1014 vector<shared_ptr<TileEntity> > *result = new vector<shared_ptr<TileEntity> >; 1015 for (AUTO_VAR(it, chunk->tileEntities.begin()); it != chunk->tileEntities.end(); ++it) 1016 { 1017 shared_ptr<TileEntity> te = it->second; 1018 if (te->x >= x0 && te->y >= y0 && te->z >= z0 && te->x < x1 && te->y < y1 && te->z < z1) 1019 { 1020 result->push_back(te); 1021 } 1022 } 1023 return result; 1024}