the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
1#include "stdafx.h"
2#include <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}