2012-06-14 13:06:06 +00:00
// WSSAnvil.cpp
// Implements the cWSSAnvil class representing the Anvil world storage scheme
#include "Globals.h"
#include "WSSAnvil.h"
2013-03-09 14:35:43 +00:00
#include "NBTChunkSerializer.h"
2014-01-19 07:38:59 -08:00
#include "EnchantmentSerializer.h"
2013-11-27 07:40:59 +00:00
#include "zlib/zlib.h"
2017-06-21 07:47:26 +02:00
#include "json/json.h"
2013-12-25 16:07:52 +00:00
#include "../World.h"
#include "../Item.h"
#include "../ItemGrid.h"
#include "../StringCompression.h"
2014-07-24 18:32:05 +02:00
#include "../SetChunkData.h"
2014-08-03 22:03:48 +02:00
#include "../Root.h"
2020-04-03 08:57:01 +02:00
#include "../BlockType.h"
2020-05-09 15:51:15 +01:00
#include "../JsonUtils.h"
2013-12-25 16:07:52 +00:00
2014-07-30 22:50:34 +02:00
#include "../BlockEntities/BeaconEntity.h"
2017-07-07 09:31:45 +02:00
#include "../BlockEntities/BedEntity.h"
2015-09-24 10:48:33 +02:00
#include "../BlockEntities/BrewingstandEntity.h"
2013-05-28 19:12:47 +00:00
#include "../BlockEntities/ChestEntity.h"
2014-01-18 15:40:47 +02:00
#include "../BlockEntities/CommandBlockEntity.h"
2013-05-28 19:12:47 +00:00
#include "../BlockEntities/DispenserEntity.h"
#include "../BlockEntities/DropperEntity.h"
2020-09-17 16:16:20 +02:00
#include "../BlockEntities/EnchantingTableEntity.h"
2020-08-28 21:40:40 +01:00
#include "../BlockEntities/EnderChestEntity.h"
2020-09-17 16:16:20 +02:00
#include "../BlockEntities/EndPortalEntity.h"
2013-05-28 19:12:47 +00:00
#include "../BlockEntities/FurnaceEntity.h"
2013-06-13 07:36:43 +00:00
#include "../BlockEntities/HopperEntity.h"
2013-05-28 19:12:47 +00:00
#include "../BlockEntities/JukeboxEntity.h"
#include "../BlockEntities/NoteEntity.h"
#include "../BlockEntities/SignEntity.h"
2014-02-19 14:45:09 +01:00
#include "../BlockEntities/MobHeadEntity.h"
2014-11-18 15:33:41 +01:00
#include "../BlockEntities/MobSpawnerEntity.h"
2014-03-07 01:30:34 +01:00
#include "../BlockEntities/FlowerPotEntity.h"
2013-12-25 16:07:52 +00:00
#include "../Mobs/IncludeAllMonsters.h"
2013-09-08 00:14:57 +01:00
#include "../Entities/Boat.h"
2014-03-25 20:59:33 +02:00
#include "../Entities/EnderCrystal.h"
2013-08-19 11:39:13 +02:00
#include "../Entities/FallingBlock.h"
#include "../Entities/Minecart.h"
#include "../Entities/Pickup.h"
2014-04-27 17:03:06 -07:00
#include "../Entities/ArrowEntity.h"
2014-07-11 18:58:11 -07:00
#include "../Entities/SplashPotionEntity.h"
2014-04-27 17:03:06 -07:00
#include "../Entities/ThrownEggEntity.h"
#include "../Entities/ThrownEnderPearlEntity.h"
#include "../Entities/ThrownSnowballEntity.h"
#include "../Entities/FireChargeEntity.h"
#include "../Entities/GhastFireballEntity.h"
2014-03-08 10:25:46 +01:00
#include "../Entities/TNTEntity.h"
2014-03-15 00:32:49 +01:00
#include "../Entities/ExpOrb.h"
2014-03-15 02:45:25 +01:00
#include "../Entities/HangingEntity.h"
#include "../Entities/ItemFrame.h"
2017-08-21 10:46:41 +02:00
#include "../Entities/LeashKnot.h"
2015-03-13 23:05:06 +00:00
#include "../Entities/Painting.h"
2012-06-14 13:06:06 +00:00
2014-08-03 22:03:48 +02:00
#include "../Protocol/MojangAPI.h"
2018-08-29 01:51:25 +01:00
#include "../Server.h"
#include "../BoundingBox.h"
2014-08-03 22:03:48 +02:00
2012-06-14 13:06:06 +00:00
2013-05-05 14:48:18 +00:00
/** If defined, the BlockSkyLight values will be copied over to BlockLight upon chunk saving,
thus making skylight visible in Minutor's Lighting mode
*/
// #define DEBUG_SKYLIGHT
2014-07-17 22:50:58 +02:00
/** Maximum number of MCA files that are cached in memory.
2012-06-14 13:06:06 +00:00
Since only the header is actually in the memory, this number can be high, but still, each file means an OS FS handle.
*/
#define MAX_MCA_FILES 32
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2012-06-14 13:06:06 +00:00
// cWSSAnvil:
2014-01-17 11:01:14 -08:00
cWSSAnvil :: cWSSAnvil ( cWorld * a_World , int a_CompressionFactor ) :
2020-04-13 18:38:06 +02:00
Super ( a_World ),
2014-01-17 11:01:14 -08:00
m_CompressionFactor ( a_CompressionFactor )
2012-06-14 13:06:06 +00:00
{
// Create a level.dat file for mapping tools, if it doesn't already exist:
AString fnam ;
2018-01-03 17:41:16 +00:00
Printf ( fnam , "%s%clevel.dat" , a_World -> GetDataPath (). c_str (), cFile :: PathSeparator ());
2012-06-14 13:06:06 +00:00
if ( ! cFile :: Exists ( fnam ))
{
cFastNBTWriter Writer ;
2014-09-23 19:16:17 +02:00
Writer . BeginCompound ( "Data" );
Writer . AddByte ( "allowCommands" , 1 );
Writer . AddByte ( "Difficulty" , 2 );
2014-09-24 15:17:20 +02:00
Writer . AddByte ( "hardcore" , cRoot :: Get () -> GetServer () -> IsHardcore () ? 1 : 0 );
2014-09-23 19:16:17 +02:00
Writer . AddByte ( "initialized" , 1 );
Writer . AddByte ( "MapFeatures" , 1 );
Writer . AddByte ( "raining" , a_World -> IsWeatherRain () ? 1 : 0 );
Writer . AddByte ( "thundering" , a_World -> IsWeatherStorm () ? 1 : 0 );
2015-07-29 09:04:03 -06:00
Writer . AddInt ( "GameType" , static_cast < int > ( a_World -> GetGameMode ()));
2014-09-23 19:16:17 +02:00
Writer . AddInt ( "generatorVersion" , 1 );
2015-06-24 16:38:40 +02:00
Writer . AddInt ( "SpawnX" , FloorC ( a_World -> GetSpawnX ()));
Writer . AddInt ( "SpawnY" , FloorC ( a_World -> GetSpawnY ()));
Writer . AddInt ( "SpawnZ" , FloorC ( a_World -> GetSpawnZ ()));
2014-09-23 19:16:17 +02:00
Writer . AddInt ( "version" , 19133 );
2015-06-24 16:38:40 +02:00
Writer . AddLong ( "DayTime" , a_World -> GetTimeOfDay ());
2014-09-23 19:16:17 +02:00
Writer . AddLong ( "Time" , a_World -> GetWorldAge ());
Writer . AddLong ( "SizeOnDisk" , 0 );
Writer . AddString ( "generatorName" , "default" );
Writer . AddString ( "generatorOptions" , "" );
Writer . AddString ( "LevelName" , a_World -> GetName ());
2012-06-14 13:06:06 +00:00
Writer . EndCompound ();
Writer . Finish ();
2016-02-05 23:45:45 +02:00
2020-05-07 22:14:00 +03:00
gzFile gz = gzopen (( fnam ). c_str (), "wb" );
2014-10-20 21:55:07 +01:00
if ( gz != nullptr )
2012-06-14 13:06:06 +00:00
{
2015-07-09 11:15:37 -06:00
gzwrite ( gz , Writer . GetResult (). data (), static_cast < unsigned > ( Writer . GetResult (). size ()));
2012-06-14 13:06:06 +00:00
}
gzclose ( gz );
}
}
cWSSAnvil ::~ cWSSAnvil ()
{
cCSLock Lock ( m_CS );
for ( cMCAFiles :: iterator itr = m_Files . begin (); itr != m_Files . end (); ++ itr )
{
delete * itr ;
} // for itr - m_Files[]
}
bool cWSSAnvil :: LoadChunk ( const cChunkCoords & a_Chunk )
{
AString ChunkData ;
if ( ! GetChunkData ( a_Chunk , ChunkData ))
{
// The reason for failure is already printed in GetChunkData()
return false ;
}
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
return LoadChunkFromData ( a_Chunk , ChunkData );
}
bool cWSSAnvil :: SaveChunk ( const cChunkCoords & a_Chunk )
{
AString ChunkData ;
if ( ! SaveChunkToData ( a_Chunk , ChunkData ))
{
2013-04-13 21:02:10 +00:00
LOGWARNING ( "Cannot serialize chunk [%d, %d] into data" , a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ );
2012-06-14 13:06:06 +00:00
return false ;
}
if ( ! SetChunkData ( a_Chunk , ChunkData ))
{
2013-04-13 21:02:10 +00:00
LOGWARNING ( "Cannot store chunk [%d, %d] data" , a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ );
2012-06-14 13:06:06 +00:00
return false ;
}
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
// Everything successful
return true ;
}
2015-07-28 15:53:28 +02:00
void cWSSAnvil :: ChunkLoadFailed ( int a_ChunkX , int a_ChunkZ , const AString & a_Reason , const AString & a_ChunkDataToSave )
{
// Construct the filename for offloading:
AString OffloadFileName ;
2018-01-03 17:41:16 +00:00
Printf ( OffloadFileName , "%s%cregion%cbadchunks" , m_World -> GetDataPath (). c_str (), cFile :: PathSeparator (), cFile :: PathSeparator ());
2020-05-07 22:14:00 +03:00
cFile :: CreateFolder ( OffloadFileName );
2015-07-28 15:53:28 +02:00
auto t = time ( nullptr );
struct tm stm ;
#ifdef _MSC_VER
localtime_s ( & stm , & t );
#else
localtime_r ( & t , & stm );
#endif
AppendPrintf ( OffloadFileName , "%cch.%d.%d.%d-%02d-%02d-%02d-%02d-%02d.dat" ,
2018-01-03 17:41:16 +00:00
cFile :: PathSeparator (), a_ChunkX , a_ChunkZ ,
2015-07-28 15:53:28 +02:00
stm . tm_year + 1900 , stm . tm_mon + 1 , stm . tm_mday , stm . tm_hour , stm . tm_min , stm . tm_sec
);
// Log the warning to console:
const int RegionX = FAST_FLOOR_DIV ( a_ChunkX , 32 );
const int RegionZ = FAST_FLOOR_DIV ( a_ChunkZ , 32 );
AString Info = Printf ( "Loading chunk [%d, %d] for world %s from file r.%d.%d.mca failed: %s. Offloading old chunk data to file %s and regenerating chunk." ,
a_ChunkX , a_ChunkZ , m_World -> GetName (). c_str (), RegionX , RegionZ , a_Reason . c_str (), OffloadFileName . c_str ()
);
LOGWARNING ( "%s" , Info . c_str ());
// Write the data:
cFile f ;
if ( ! f . Open ( OffloadFileName , cFile :: fmWrite ))
{
LOGWARNING ( "Cannot open file %s for writing! Old chunk data is lost." , OffloadFileName . c_str ());
return ;
}
f . Write ( a_ChunkDataToSave . data (), a_ChunkDataToSave . size ());
f . Close ();
// Write a description file:
if ( ! f . Open ( OffloadFileName + ".info" , cFile :: fmWrite ))
{
LOGWARNING ( "Cannot open file %s.info for writing! The information about the failed chunk will not be written." , OffloadFileName . c_str ());
return ;
}
f . Write ( Info . c_str (), Info . size ());
f . Close ();
}
2012-06-14 13:06:06 +00:00
bool cWSSAnvil :: GetChunkData ( const cChunkCoords & a_Chunk , AString & a_Data )
{
cCSLock Lock ( m_CS );
cMCAFile * File = LoadMCAFile ( a_Chunk );
2014-10-20 21:55:07 +01:00
if ( File == nullptr )
2012-06-14 13:06:06 +00:00
{
return false ;
}
return File -> GetChunkData ( a_Chunk , a_Data );
}
bool cWSSAnvil :: SetChunkData ( const cChunkCoords & a_Chunk , const AString & a_Data )
{
cCSLock Lock ( m_CS );
cMCAFile * File = LoadMCAFile ( a_Chunk );
2014-10-20 21:55:07 +01:00
if ( File == nullptr )
2012-06-14 13:06:06 +00:00
{
return false ;
}
return File -> SetChunkData ( a_Chunk , a_Data );
}
cWSSAnvil :: cMCAFile * cWSSAnvil :: LoadMCAFile ( const cChunkCoords & a_Chunk )
{
// ASSUME m_CS is locked
2013-02-27 10:01:20 +00:00
ASSERT ( m_CS . IsLocked ());
2012-06-14 13:06:06 +00:00
2013-02-27 10:01:20 +00:00
const int RegionX = FAST_FLOOR_DIV ( a_Chunk . m_ChunkX , 32 );
const int RegionZ = FAST_FLOOR_DIV ( a_Chunk . m_ChunkZ , 32 );
2013-03-09 14:35:43 +00:00
ASSERT ( a_Chunk . m_ChunkX - RegionX * 32 >= 0 );
ASSERT ( a_Chunk . m_ChunkZ - RegionZ * 32 >= 0 );
ASSERT ( a_Chunk . m_ChunkX - RegionX * 32 < 32 );
ASSERT ( a_Chunk . m_ChunkZ - RegionZ * 32 < 32 );
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
// Is it already cached?
for ( cMCAFiles :: iterator itr = m_Files . begin (); itr != m_Files . end (); ++ itr )
{
2014-10-20 21:55:07 +01:00
if ((( * itr ) != nullptr ) && (( * itr ) -> GetRegionX () == RegionX ) && (( * itr ) -> GetRegionZ () == RegionZ ))
2012-06-14 13:06:06 +00:00
{
// Move the file to front and return it:
cMCAFile * f = * itr ;
if ( itr != m_Files . begin ())
{
m_Files . erase ( itr );
m_Files . push_front ( f );
}
return f ;
}
}
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
// Load it anew:
AString FileName ;
2018-01-03 17:41:16 +00:00
Printf ( FileName , "%s%cregion" , m_World -> GetDataPath (). c_str (), cFile :: PathSeparator ());
2020-05-07 22:14:00 +03:00
cFile :: CreateFolder ( FileName );
2012-06-14 13:06:06 +00:00
AppendPrintf ( FileName , "/r.%d.%d.mca" , RegionX , RegionZ );
2015-07-28 15:53:28 +02:00
cMCAFile * f = new cMCAFile ( * this , FileName , RegionX , RegionZ );
2014-10-20 21:55:07 +01:00
if ( f == nullptr )
2012-06-14 13:06:06 +00:00
{
2014-10-20 21:55:07 +01:00
return nullptr ;
2012-06-14 13:06:06 +00:00
}
m_Files . push_front ( f );
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
// If there are too many MCA files cached, delete the last one used:
if ( m_Files . size () > MAX_MCA_FILES )
{
delete m_Files . back ();
m_Files . pop_back ();
}
return f ;
}
bool cWSSAnvil :: LoadChunkFromData ( const cChunkCoords & a_Chunk , const AString & a_Data )
{
2014-09-03 19:36:53 +02:00
// Uncompress the data:
AString Uncompressed ;
int res = InflateString ( a_Data . data (), a_Data . size (), Uncompressed );
if ( res != Z_OK )
2012-06-14 13:06:06 +00:00
{
2014-09-03 19:36:53 +02:00
LOGWARNING ( "Uncompressing chunk [%d, %d] failed: %d" , a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , res );
2015-07-28 15:53:28 +02:00
ChunkLoadFailed ( a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , "InflateString() failed" , a_Data );
2012-06-14 13:06:06 +00:00
return false ;
}
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
// Parse the NBT data:
2014-09-03 19:36:53 +02:00
cParsedNBT NBT ( Uncompressed . data (), Uncompressed . size ());
2012-06-14 13:06:06 +00:00
if ( ! NBT . IsValid ())
{
// NBT Parsing failed
2020-04-30 08:44:49 +02:00
auto msg = fmt :: format ( "NBT parsing failed: {}, pos {}" , NBT . GetErrorCode (). message (), NBT . GetErrorPos ());
ChunkLoadFailed ( a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , msg , a_Data );
2012-06-14 13:06:06 +00:00
return false ;
}
// Load the data from NBT:
2015-07-28 15:53:28 +02:00
return LoadChunkFromNBT ( a_Chunk , NBT , a_Data );
2012-06-14 13:06:06 +00:00
}
bool cWSSAnvil :: SaveChunkToData ( const cChunkCoords & a_Chunk , AString & a_Data )
{
cFastNBTWriter Writer ;
if ( ! SaveChunkToNBT ( a_Chunk , Writer ))
{
2013-04-13 21:02:10 +00:00
LOGWARNING ( "Cannot save chunk [%d, %d] to NBT" , a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ );
2012-06-14 13:06:06 +00:00
return false ;
}
Writer . Finish ();
2016-02-05 23:45:45 +02:00
2014-01-17 11:01:14 -08:00
CompressString ( Writer . GetResult (). data (), Writer . GetResult (). size (), a_Data , m_CompressionFactor );
2012-06-14 13:06:06 +00:00
return true ;
}
2015-07-28 15:53:28 +02:00
bool cWSSAnvil :: LoadChunkFromNBT ( const cChunkCoords & a_Chunk , const cParsedNBT & a_NBT , const AString & a_RawChunkData )
2012-06-14 13:06:06 +00:00
{
2015-05-09 09:25:09 +02:00
// The data arrays, in MCA-native y / z / x ordering (will be reordered for the final chunk data)
2012-06-14 13:06:06 +00:00
cChunkDef :: BlockTypes BlockTypes ;
cChunkDef :: BlockNibbles MetaData ;
cChunkDef :: BlockNibbles BlockLight ;
cChunkDef :: BlockNibbles SkyLight ;
2016-02-05 23:45:45 +02:00
2020-04-03 08:57:01 +02:00
memset ( BlockTypes , 0 , sizeof ( BlockTypes ));
memset ( MetaData , 0 , sizeof ( MetaData ));
memset ( SkyLight , 0xff , sizeof ( SkyLight )); // By default, data not present in the NBT means air, which means full skylight
memset ( BlockLight , 0x00 , sizeof ( BlockLight ));
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
// Load the blockdata, blocklight and skylight:
int Level = a_NBT . FindChildByName ( 0 , "Level" );
if ( Level < 0 )
{
2015-07-28 15:53:28 +02:00
ChunkLoadFailed ( a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , "Missing NBT tag: Level" , a_RawChunkData );
2012-06-14 13:06:06 +00:00
return false ;
}
int Sections = a_NBT . FindChildByName ( Level , "Sections" );
2014-09-05 22:55:39 +02:00
if (( Sections < 0 ) || ( a_NBT . GetType ( Sections ) != TAG_List ))
{
2015-07-28 15:53:28 +02:00
ChunkLoadFailed ( a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , "Missing NBT tag: Sections" , a_RawChunkData );
2014-09-05 22:55:39 +02:00
return false ;
}
eTagType SectionsType = a_NBT . GetChildrenType ( Sections );
if (( SectionsType != TAG_Compound ) && ( SectionsType != TAG_End ))
2012-06-14 13:06:06 +00:00
{
2015-07-28 15:53:28 +02:00
ChunkLoadFailed ( a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , "NBT tag has wrong type: Sections" , a_RawChunkData );
2012-06-14 13:06:06 +00:00
return false ;
}
for ( int Child = a_NBT . GetFirstChild ( Sections ); Child >= 0 ; Child = a_NBT . GetNextSibling ( Child ))
{
int y = 0 ;
int SectionY = a_NBT . FindChildByName ( Child , "Y" );
if (( SectionY < 0 ) || ( a_NBT . GetType ( SectionY ) != TAG_Byte ))
{
continue ;
}
y = a_NBT . GetByte ( SectionY );
if (( y < 0 ) || ( y > 15 ))
{
continue ;
}
2015-07-29 09:04:03 -06:00
CopyNBTData ( a_NBT , Child , "Blocks" , reinterpret_cast < char *> ( & ( BlockTypes [ y * 4096 ])), 4096 );
CopyNBTData ( a_NBT , Child , "Data" , reinterpret_cast < char *> ( & ( MetaData [ y * 2048 ])), 2048 );
CopyNBTData ( a_NBT , Child , "SkyLight" , reinterpret_cast < char *> ( & ( SkyLight [ y * 2048 ])), 2048 );
CopyNBTData ( a_NBT , Child , "BlockLight" , reinterpret_cast < char *> ( & ( BlockLight [ y * 2048 ])), 2048 );
2012-06-14 13:06:06 +00:00
} // for itr - LevelSections[]
2016-02-05 23:45:45 +02:00
2012-10-28 17:30:10 +00:00
// Load the biomes from NBT, if present and valid. First try MCS-style, then Vanilla-style:
2012-06-14 13:06:06 +00:00
cChunkDef :: BiomeMap BiomeMap ;
2012-10-28 17:30:10 +00:00
cChunkDef :: BiomeMap * Biomes = LoadBiomeMapFromNBT ( & BiomeMap , a_NBT , a_NBT . FindChildByName ( Level , "MCSBiomes" ));
2014-10-20 21:55:07 +01:00
if ( Biomes == nullptr )
2012-10-28 17:30:10 +00:00
{
// MCS-style biomes not available, load vanilla-style:
Biomes = LoadVanillaBiomeMapFromNBT ( & BiomeMap , a_NBT , a_NBT . FindChildByName ( Level , "Biomes" ));
}
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
// Load the entities from NBT:
cEntityList Entities ;
2017-05-22 21:27:55 +01:00
cBlockEntities BlockEntities ;
2012-06-14 13:06:06 +00:00
LoadEntitiesFromNBT ( Entities , a_NBT , a_NBT . FindChildByName ( Level , "Entities" ));
2013-06-20 11:41:44 +00:00
LoadBlockEntitiesFromNBT ( BlockEntities , a_NBT , a_NBT . FindChildByName ( Level , "TileEntities" ), BlockTypes , MetaData );
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
bool IsLightValid = ( a_NBT . FindChildByName ( Level , "MCSIsLightValid" ) > 0 );
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
/*
// Uncomment this block for really cool stuff :)
// DEBUG magic: Invert the underground, so that we can see the MC generator in action :)
bool ShouldInvert[cChunkDef::Width * cChunkDef::Width];
memset(ShouldInvert, 0, sizeof(ShouldInvert));
for (int y = cChunkDef::Height - 1; y >= 0; y--)
{
for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++)
{
int Index = cChunkDef::MakeIndexNoCheck(x, y, z);
if (ShouldInvert[x + cChunkDef::Width * z])
{
2012-07-21 17:07:46 +00:00
BlockTypes[Index] = (BlockTypes[Index] == E_BLOCK_AIR) ? E_BLOCK_STONE : E_BLOCK_AIR;
2012-06-14 13:06:06 +00:00
}
else
{
2012-07-21 17:07:46 +00:00
switch (BlockTypes[Index])
{
case E_BLOCK_AIR:
case E_BLOCK_LEAVES:
2014-03-16 14:01:22 +01:00
case E_BLOCK_NEW_LEAVES:
2012-07-21 17:07:46 +00:00
{
// nothing needed
break;
}
default:
{
ShouldInvert[x + cChunkDef::Width * z] = true;
}
}
BlockTypes[Index] = E_BLOCK_AIR;
2012-06-14 13:06:06 +00:00
}
}
} // for y
//*/
2016-02-05 23:45:45 +02:00
2020-08-01 20:18:03 +02:00
auto SetChunkData = std :: make_unique < cSetChunkData > (
2013-04-13 21:02:10 +00:00
a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ ,
2014-07-17 22:50:58 +02:00
BlockTypes , MetaData ,
2014-10-20 21:55:07 +01:00
IsLightValid ? BlockLight : nullptr ,
IsLightValid ? SkyLight : nullptr ,
nullptr , Biomes ,
2015-04-27 21:18:21 +02:00
std :: move ( Entities ), std :: move ( BlockEntities ),
2012-06-14 13:06:06 +00:00
false
2016-12-19 20:12:23 +00:00
);
2017-07-20 12:19:18 +01:00
m_World -> QueueSetChunkData ( std :: move ( SetChunkData ));
2012-06-14 13:06:06 +00:00
return true ;
}
2018-07-26 22:24:36 +01:00
2014-05-09 19:57:59 +02:00
void cWSSAnvil :: CopyNBTData ( const cParsedNBT & a_NBT , int a_Tag , const AString & a_ChildName , char * a_Destination , size_t a_Length )
2012-06-14 13:06:06 +00:00
{
int Child = a_NBT . FindChildByName ( a_Tag , a_ChildName );
if (( Child >= 0 ) && ( a_NBT . GetType ( Child ) == TAG_ByteArray ) && ( a_NBT . GetDataLength ( Child ) == a_Length ))
{
memcpy ( a_Destination , a_NBT . GetData ( Child ), a_Length );
}
}
bool cWSSAnvil :: SaveChunkToNBT ( const cChunkCoords & a_Chunk , cFastNBTWriter & a_Writer )
{
2019-09-24 14:20:50 +02:00
if ( ! NBTChunkSerializer :: serialize ( * m_World , a_Chunk , a_Writer ))
2012-06-14 13:06:06 +00:00
{
2019-09-24 14:20:50 +02:00
LOGWARNING ( "Failed to save chunk %s." , a_Chunk . ToString ());
2012-06-14 13:06:06 +00:00
return false ;
}
return true ;
}
2012-10-28 17:30:10 +00:00
cChunkDef :: BiomeMap * cWSSAnvil :: LoadVanillaBiomeMapFromNBT ( cChunkDef :: BiomeMap * a_BiomeMap , const cParsedNBT & a_NBT , int a_TagIdx )
2012-06-14 13:06:06 +00:00
{
if (( a_TagIdx < 0 ) || ( a_NBT . GetType ( a_TagIdx ) != TAG_ByteArray ))
{
2014-10-20 21:55:07 +01:00
return nullptr ;
2012-06-14 13:06:06 +00:00
}
2013-04-07 21:04:54 +00:00
if ( a_NBT . GetDataLength ( a_TagIdx ) != 16 * 16 )
2012-06-14 13:06:06 +00:00
{
// The biomes stored don't match in size
2014-10-20 21:55:07 +01:00
return nullptr ;
2012-06-14 13:06:06 +00:00
}
2015-07-29 09:04:03 -06:00
const unsigned char * VanillaBiomeData = reinterpret_cast < const unsigned char *> ( a_NBT . GetData ( a_TagIdx ));
2013-12-20 16:01:34 +01:00
for ( size_t i = 0 ; i < ARRAYCOUNT ( * a_BiomeMap ); i ++ )
2012-10-28 17:30:10 +00:00
{
if (( VanillaBiomeData )[ i ] == 0xff )
{
// Unassigned biomes
2014-10-20 21:55:07 +01:00
return nullptr ;
2012-10-28 17:30:10 +00:00
}
2015-07-29 09:04:03 -06:00
( * a_BiomeMap )[ i ] = static_cast < EMCSBiome > ( VanillaBiomeData [ i ]);
2012-10-28 17:30:10 +00:00
}
return a_BiomeMap ;
}
cChunkDef :: BiomeMap * cWSSAnvil :: LoadBiomeMapFromNBT ( cChunkDef :: BiomeMap * a_BiomeMap , const cParsedNBT & a_NBT , int a_TagIdx )
{
if (( a_TagIdx < 0 ) || ( a_NBT . GetType ( a_TagIdx ) != TAG_IntArray ))
{
2014-10-20 21:55:07 +01:00
return nullptr ;
2012-10-28 17:30:10 +00:00
}
if ( a_NBT . GetDataLength ( a_TagIdx ) != sizeof ( * a_BiomeMap ))
{
// The biomes stored don't match in size
2014-10-20 21:55:07 +01:00
return nullptr ;
2012-10-28 17:30:10 +00:00
}
2014-03-10 11:34:20 -07:00
const char * BiomeData = ( a_NBT . GetData ( a_TagIdx ));
2013-12-20 16:01:34 +01:00
for ( size_t i = 0 ; i < ARRAYCOUNT ( * a_BiomeMap ); i ++ )
2012-06-14 13:06:06 +00:00
{
2015-07-29 09:04:03 -06:00
( * a_BiomeMap )[ i ] = static_cast < EMCSBiome > ( GetBEInt ( & BiomeData [ i * 4 ]));
2012-06-14 13:06:06 +00:00
if (( * a_BiomeMap )[ i ] == 0xff )
{
// Unassigned biomes
2014-10-20 21:55:07 +01:00
return nullptr ;
2012-06-14 13:06:06 +00:00
}
}
return a_BiomeMap ;
}
2013-03-09 14:35:43 +00:00
void cWSSAnvil :: LoadEntitiesFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
2012-06-14 13:06:06 +00:00
{
2013-03-09 14:35:43 +00:00
if (( a_TagIdx < 0 ) || ( a_NBT . GetType ( a_TagIdx ) != TAG_List ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2013-03-09 14:35:43 +00:00
for ( int Child = a_NBT . GetFirstChild ( a_TagIdx ); Child != - 1 ; Child = a_NBT . GetNextSibling ( Child ))
{
if ( a_NBT . GetType ( Child ) != TAG_Compound )
{
continue ;
}
int sID = a_NBT . FindChildByName ( Child , "id" );
if ( sID < 0 )
{
continue ;
}
LoadEntityFromNBT ( a_Entities , a_NBT , Child , a_NBT . GetData ( sID ), a_NBT . GetDataLength ( sID ));
} // for Child - a_NBT[]
2012-06-14 13:06:06 +00:00
}
2017-05-22 21:27:55 +01:00
void cWSSAnvil :: LoadBlockEntitiesFromNBT ( cBlockEntities & a_BlockEntities , const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE * a_BlockTypes , NIBBLETYPE * a_BlockMetas )
2012-06-14 13:06:06 +00:00
{
if (( a_TagIdx < 0 ) || ( a_NBT . GetType ( a_TagIdx ) != TAG_List ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
for ( int Child = a_NBT . GetFirstChild ( a_TagIdx ); Child != - 1 ; Child = a_NBT . GetNextSibling ( Child ))
{
if ( a_NBT . GetType ( Child ) != TAG_Compound )
{
continue ;
}
2014-08-29 19:19:27 +03:00
// Get the BlockEntity's position
2019-09-29 14:59:24 +02:00
Vector3i absPos ;
if ( ! GetBlockEntityNBTPos ( a_NBT , Child , absPos ) || ( absPos . y < 0 ) || ( absPos . y >= cChunkDef :: Height ))
2012-06-14 13:06:06 +00:00
{
2014-08-29 19:19:27 +03:00
LOGWARNING ( "Bad block entity, missing the coords. Will be ignored." );
2012-06-14 13:06:06 +00:00
continue ;
}
2019-09-29 14:59:24 +02:00
auto relPos = cChunkDef :: AbsoluteToRelative ( absPos );
2014-08-29 19:19:27 +03:00
// Load the proper BlockEntity type based on the block type:
2019-09-29 14:59:24 +02:00
BLOCKTYPE BlockType = cChunkDef :: GetBlock ( a_BlockTypes , relPos );
NIBBLETYPE BlockMeta = cChunkDef :: GetNibble ( a_BlockMetas , relPos );
2020-04-03 22:23:38 +01:00
auto be = LoadBlockEntityFromNBT ( a_NBT , Child , absPos , BlockType , BlockMeta );
if ( be == nullptr )
2014-07-06 23:50:22 +01:00
{
2014-08-29 19:19:27 +03:00
continue ;
2014-07-06 23:50:22 +01:00
}
2014-08-29 19:19:27 +03:00
// Add the BlockEntity to the loaded data:
2017-05-22 21:27:55 +01:00
auto Idx = cChunkDef :: MakeIndex ( be -> GetRelX (), be -> GetPosY (), be -> GetRelZ ());
2020-04-03 22:23:38 +01:00
a_BlockEntities . emplace ( Idx , std :: move ( be ));
2012-06-14 13:06:06 +00:00
} // for Child - tag children
}
2020-04-03 22:23:38 +01:00
OwnedBlockEntity cWSSAnvil :: LoadBlockEntityFromNBT ( const cParsedNBT & a_NBT , int a_Tag , Vector3i a_Pos , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta )
2014-08-29 19:19:27 +03:00
{
2019-09-29 14:59:24 +02:00
ASSERT (( a_Pos . y >= 0 ) && ( a_Pos . y < cChunkDef :: Height ));
2014-09-06 18:59:17 +02:00
2014-08-29 19:19:27 +03:00
// Load the specific BlockEntity type:
switch ( a_BlockType )
{
// Specific entity loaders:
2020-09-17 16:16:20 +02:00
case E_BLOCK_BEACON : return LoadBeaconFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_BED : return LoadBedFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_BREWING_STAND : return LoadBrewingstandFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_CHEST : return LoadChestFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_COMMAND_BLOCK : return LoadCommandBlockFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_DISPENSER : return LoadDispenserFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_DROPPER : return LoadDropperFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_ENCHANTMENT_TABLE : return LoadEnchantingTableFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_ENDER_CHEST : return LoadEnderChestFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_END_PORTAL : return LoadEndPortalFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_FLOWER_POT : return LoadFlowerPotFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_FURNACE : return LoadFurnaceFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_HEAD : return LoadMobHeadFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_HOPPER : return LoadHopperFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_JUKEBOX : return LoadJukeboxFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_LIT_FURNACE : return LoadFurnaceFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_MOB_SPAWNER : return LoadMobSpawnerFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_NOTE_BLOCK : return LoadNoteBlockFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_SIGN_POST : return LoadSignFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_TRAPPED_CHEST : return LoadChestFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
case E_BLOCK_WALLSIGN : return LoadSignFromNBT ( a_NBT , a_Tag , a_BlockType , a_BlockMeta , a_Pos );
2014-08-29 19:19:27 +03:00
2017-09-14 09:48:57 +01:00
default :
{
// All the other blocktypes should have no entities assigned to them. Report an error:
// Get the "id" tag:
int TagID = a_NBT . FindChildByName ( a_Tag , "id" );
AString TypeName ( "<unknown>" );
if ( TagID >= 0 )
{
TypeName . assign ( a_NBT . GetData ( TagID ), static_cast < size_t > ( a_NBT . GetDataLength ( TagID )));
}
2020-05-02 23:10:54 +01:00
FLOGINFO ( "WorldLoader({0}): Block entity mismatch: block type {1} ({2}), type \" {3} \" , at {4}; the entity will be lost." ,
2018-09-24 21:33:39 +01:00
m_World -> GetName (),
ItemTypeToString ( a_BlockType ), a_BlockType , TypeName ,
2019-09-29 14:59:24 +02:00
a_Pos
2017-09-14 09:48:57 +01:00
);
return nullptr ;
}
2014-08-29 19:19:27 +03:00
}
}
2013-03-09 14:35:43 +00:00
bool cWSSAnvil :: LoadItemFromNBT ( cItem & a_Item , const cParsedNBT & a_NBT , int a_TagIdx )
{
2014-01-26 14:43:54 +01:00
int Type = a_NBT . FindChildByName ( a_TagIdx , "id" );
2014-10-29 15:47:43 +01:00
if ( Type <= 0 )
2013-03-09 14:35:43 +00:00
{
return false ;
}
2014-10-29 15:47:43 +01:00
if ( a_NBT . GetType ( Type ) == TAG_String )
{
if ( ! StringToItem ( a_NBT . GetString ( Type ), a_Item ))
{
// Can't resolve item type
return false ;
}
}
else if ( a_NBT . GetType ( Type ) == TAG_Short )
{
a_Item . m_ItemType = a_NBT . GetShort ( Type );
}
else
{
return false ;
}
2014-01-26 14:43:54 +01:00
if ( a_Item . m_ItemType < 0 )
{
a_Item . Empty ();
return true ;
}
2016-02-05 23:45:45 +02:00
2013-03-09 14:35:43 +00:00
int Damage = a_NBT . FindChildByName ( a_TagIdx , "Damage" );
2014-05-07 12:30:30 +02:00
if (( Damage > 0 ) && ( a_NBT . GetType ( Damage ) == TAG_Short ))
2013-03-09 14:35:43 +00:00
{
2014-05-07 12:30:30 +02:00
a_Item . m_ItemDamage = a_NBT . GetShort ( Damage );
2013-03-09 14:35:43 +00:00
}
2016-02-05 23:45:45 +02:00
2013-03-09 14:35:43 +00:00
int Count = a_NBT . FindChildByName ( a_TagIdx , "Count" );
2014-05-07 12:30:30 +02:00
if (( Count > 0 ) && ( a_NBT . GetType ( Count ) == TAG_Byte ))
2013-03-09 14:35:43 +00:00
{
2015-07-29 09:04:03 -06:00
a_Item . m_ItemCount = static_cast < char > ( a_NBT . GetByte ( Count ));
2013-03-09 14:35:43 +00:00
}
2016-02-05 23:45:45 +02:00
2013-06-02 21:21:32 +00:00
// Find the "tag" tag, used for enchantments and other extra data
int TagTag = a_NBT . FindChildByName ( a_TagIdx , "tag" );
if ( TagTag <= 0 )
{
// No extra data
return true ;
}
2014-05-07 12:30:30 +02:00
// Load repair cost:
int RepairCost = a_NBT . FindChildByName ( TagTag , "RepairCost" );
if (( RepairCost > 0 ) && ( a_NBT . GetType ( RepairCost ) == TAG_Int ))
{
2014-05-07 20:43:37 +02:00
a_Item . m_RepairCost = a_NBT . GetInt ( RepairCost );
2014-05-07 12:30:30 +02:00
}
// Load display name:
int DisplayTag = a_NBT . FindChildByName ( TagTag , "display" );
if ( DisplayTag > 0 )
{
int DisplayName = a_NBT . FindChildByName ( DisplayTag , "Name" );
if (( DisplayName > 0 ) && ( a_NBT . GetType ( DisplayName ) == TAG_String ))
{
a_Item . m_CustomName = a_NBT . GetString ( DisplayName );
}
int Lore = a_NBT . FindChildByName ( DisplayTag , "Lore" );
if (( Lore > 0 ) && ( a_NBT . GetType ( Lore ) == TAG_String ))
{
2017-08-18 11:29:54 +01:00
// Legacy string lore
a_Item . m_LoreTable = StringSplit ( a_NBT . GetString ( Lore ), "`" );
}
else if (( Lore > 0 ) && ( a_NBT . GetType ( Lore ) == TAG_List ))
{
// Lore table
a_Item . m_LoreTable . clear ();
for ( int loretag = a_NBT . GetFirstChild ( Lore ); loretag >= 0 ; loretag = a_NBT . GetNextSibling ( loretag )) // Loop through array of strings
{
a_Item . m_LoreTable . push_back ( a_NBT . GetString ( loretag ));
}
2014-05-07 12:30:30 +02:00
}
}
2013-06-02 21:21:32 +00:00
// Load enchantments:
const char * EnchName = ( a_Item . m_ItemType == E_ITEM_BOOK ) ? "StoredEnchantments" : "ench" ;
int EnchTag = a_NBT . FindChildByName ( TagTag , EnchName );
if ( EnchTag > 0 )
{
2014-01-19 08:52:45 -08:00
EnchantmentSerializer :: ParseFromNBT ( a_Item . m_Enchantments , a_NBT , EnchTag );
2013-06-02 21:21:32 +00:00
}
2014-02-26 23:29:14 +00:00
2014-05-07 12:30:30 +02:00
// Load firework data:
2017-03-22 14:18:48 +01:00
int FireworksTag = a_NBT . FindChildByName ( TagTag , (( a_Item . m_ItemType == E_ITEM_FIREWORK_STAR ) ? "Explosion" : "Fireworks" ));
if ( FireworksTag > 0 )
2014-02-26 23:29:14 +00:00
{
2020-04-03 08:57:01 +02:00
cFireworkItem :: ParseFromNBT ( a_Item . m_FireworkItem , a_NBT , FireworksTag , static_cast < ENUM_ITEM_TYPE > ( a_Item . m_ItemType ));
2014-02-26 23:29:14 +00:00
}
2016-02-05 23:45:45 +02:00
2013-03-09 14:35:43 +00:00
return true ;
}
2013-05-26 15:29:43 +00:00
void cWSSAnvil :: LoadItemGridFromNBT ( cItemGrid & a_ItemGrid , const cParsedNBT & a_NBT , int a_ItemsTagIdx , int a_SlotOffset )
{
int NumSlots = a_ItemGrid . GetNumSlots ();
for ( int Child = a_NBT . GetFirstChild ( a_ItemsTagIdx ); Child != - 1 ; Child = a_NBT . GetNextSibling ( Child ))
{
int SlotTag = a_NBT . FindChildByName ( Child , "Slot" );
if (( SlotTag < 0 ) || ( a_NBT . GetType ( SlotTag ) != TAG_Byte ))
{
continue ;
}
2015-07-29 09:04:03 -06:00
int SlotNum = static_cast < int > ( a_NBT . GetByte ( SlotTag )) - a_SlotOffset ;
2013-05-26 15:29:43 +00:00
if (( SlotNum < 0 ) || ( SlotNum >= NumSlots ))
{
// SlotNum outside of the range
continue ;
}
cItem Item ;
if ( LoadItemFromNBT ( Item , a_NBT , Child ))
{
a_ItemGrid . SetSlot ( SlotNum , Item );
}
} // for itr - ItemDefs[]
}
2017-06-21 07:47:26 +02:00
AString cWSSAnvil :: DecodeSignLine ( const AString & a_Line )
{
if ( a_Line . empty ())
{
return AString ();
}
if ( a_Line [ 0 ] != '{' )
{
return a_Line ;
}
// Try to parse the JSON:
Json :: Value root ;
2020-05-09 15:51:15 +01:00
if ( ! JsonUtils :: ParseString ( a_Line , root ) || ! root . isObject ())
2017-06-21 07:47:26 +02:00
{
return a_Line ;
}
const auto & txt = root [ "text" ];
if ( txt . isString ())
{
return txt . asString ();
}
return a_Line ;
}
2019-09-29 14:59:24 +02:00
bool cWSSAnvil :: CheckBlockEntityType ( const cParsedNBT & a_NBT , int a_TagIdx , const AStringVector & a_ExpectedTypes , Vector3i a_Pos )
2014-07-30 22:50:34 +02:00
{
2014-08-29 19:19:27 +03:00
// Check if the given tag is a compound:
if ( a_NBT . GetType ( a_TagIdx ) != TAG_Compound )
2014-07-30 22:50:34 +02:00
{
2014-08-29 19:19:27 +03:00
return false ;
}
// Get the "id" tag:
int TagID = a_NBT . FindChildByName ( a_TagIdx , "id" );
if ( TagID < 0 )
{
return false ;
}
// Compare the value:
2017-06-21 07:47:26 +02:00
for ( const auto & et : a_ExpectedTypes )
2014-08-29 19:19:27 +03:00
{
2017-06-21 07:47:26 +02:00
if ( strncmp ( a_NBT . GetData ( TagID ), et . c_str (), static_cast < size_t > ( a_NBT . GetDataLength ( TagID ))) == 0 )
{
return true ;
}
}
// Expectation not met, output an error into the log:
AString expectedTypes ;
for ( const auto & et : a_ExpectedTypes )
{
expectedTypes . append ( ", \" " );
expectedTypes . append ( et );
expectedTypes . push_back ( '\"' );
2014-08-29 19:19:27 +03:00
}
2020-05-02 23:10:54 +01:00
FLOGWARNING ( "Block entity type mismatch: exp {0}, got \" {1} \" . The block entity at {2} will lose all its properties." ,
2017-06-21 07:47:26 +02:00
expectedTypes . c_str () + 2 , // Skip the first ", " that is extra in the string
2018-09-24 21:33:39 +01:00
AString ( a_NBT . GetData ( TagID ), static_cast < size_t > ( a_NBT . GetDataLength ( TagID ))),
2019-09-29 14:59:24 +02:00
a_Pos
2014-08-29 19:19:27 +03:00
);
return false ;
}
2020-04-03 22:23:38 +01:00
OwnedBlockEntity cWSSAnvil :: LoadBeaconFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
2014-08-29 19:19:27 +03:00
{
// Check if the data has a proper type:
2017-06-21 07:47:26 +02:00
static const AStringVector expectedTypes ({ "Beacon" , "minecraft:beacon" });
2019-09-29 14:59:24 +02:00
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
2014-08-29 19:19:27 +03:00
{
2014-10-20 21:55:07 +01:00
return nullptr ;
2014-07-30 22:50:34 +02:00
}
2020-08-01 20:18:03 +02:00
auto Beacon = std :: make_unique < cBeaconEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World );
2014-07-30 22:50:34 +02:00
int CurrentLine = a_NBT . FindChildByName ( a_TagIdx , "Levels" );
if ( CurrentLine >= 0 )
{
2015-07-29 09:04:03 -06:00
Beacon -> SetBeaconLevel ( static_cast < char > ( a_NBT . GetInt ( CurrentLine )));
2014-07-30 22:50:34 +02:00
}
CurrentLine = a_NBT . FindChildByName ( a_TagIdx , "Primary" );
if ( CurrentLine >= 0 )
{
2015-07-29 09:04:03 -06:00
Beacon -> SetPrimaryEffect ( static_cast < cEntityEffect :: eType > ( a_NBT . GetInt ( CurrentLine )));
2014-07-30 22:50:34 +02:00
}
CurrentLine = a_NBT . FindChildByName ( a_TagIdx , "Secondary" );
if ( CurrentLine >= 0 )
{
2015-07-29 09:04:03 -06:00
Beacon -> SetSecondaryEffect ( static_cast < cEntityEffect :: eType > ( a_NBT . GetInt ( CurrentLine )));
2014-07-30 22:50:34 +02:00
}
2015-05-09 09:25:09 +02:00
// We are better than mojang, we load / save the beacon inventory!
2014-07-30 22:50:34 +02:00
int Items = a_NBT . FindChildByName ( a_TagIdx , "Items" );
if (( Items >= 0 ) && ( a_NBT . GetType ( Items ) == TAG_List ))
{
LoadItemGridFromNBT ( Beacon -> GetContents (), a_NBT , Items );
}
2020-08-04 19:02:15 +01:00
return Beacon ;
2014-07-30 22:50:34 +02:00
}
2020-04-03 22:23:38 +01:00
OwnedBlockEntity cWSSAnvil :: LoadBedFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
2017-07-07 09:31:45 +02:00
{
// Check if the data has a proper type:
static const AStringVector expectedTypes ({ "Bed" , "minecraft:bed" });
2019-09-29 14:59:24 +02:00
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
2017-07-07 09:31:45 +02:00
{
return nullptr ;
}
// Use color red as default
short Color = E_META_WOOL_RED ;
int ColorIDx = a_NBT . FindChildByName ( a_TagIdx , "color" );
if ( ColorIDx >= 0 )
{
Color = static_cast < short > ( a_NBT . GetInt ( ColorIDx ));
}
2020-08-01 20:18:03 +02:00
return std :: make_unique < cBedEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World , Color );
2017-07-07 09:31:45 +02:00
}
2020-04-03 22:23:38 +01:00
OwnedBlockEntity cWSSAnvil :: LoadBrewingstandFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
2015-09-24 10:48:33 +02:00
{
// Check if the data has a proper type:
2017-06-21 07:47:26 +02:00
static const AStringVector expectedTypes ({ "Brewingstand" , "minecraft:brewing_stand" });
2019-09-29 14:59:24 +02:00
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
2015-09-24 10:48:33 +02:00
{
return nullptr ;
}
int Items = a_NBT . FindChildByName ( a_TagIdx , "Items" );
if (( Items < 0 ) || ( a_NBT . GetType ( Items ) != TAG_List ))
{
return nullptr ; // Make it an empty brewingstand - the chunk loader will provide an empty cBrewingstandEntity for this
}
2020-08-01 20:18:03 +02:00
auto Brewingstand = std :: make_unique < cBrewingstandEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World );
2015-09-24 10:48:33 +02:00
2017-05-05 11:58:21 +02:00
// Fuel has to be loaded at first, because of slot events:
int Fuel = a_NBT . FindChildByName ( a_TagIdx , "Fuel" );
if ( Fuel >= 0 )
{
Int16 tb = a_NBT . GetShort ( Fuel );
Brewingstand -> SetRemainingFuel ( tb );
}
2015-09-24 10:48:33 +02:00
// Load slots:
for ( int Child = a_NBT . GetFirstChild ( Items ); Child != - 1 ; Child = a_NBT . GetNextSibling ( Child ))
{
int Slot = a_NBT . FindChildByName ( Child , "Slot" );
if (( Slot < 0 ) || ( a_NBT . GetType ( Slot ) != TAG_Byte ))
{
continue ;
}
cItem Item ;
if ( LoadItemFromNBT ( Item , a_NBT , Child ))
{
Brewingstand -> SetSlot ( a_NBT . GetByte ( Slot ), Item );
}
} // for itr - ItemDefs[]
// Load brewing time:
int BrewTime = a_NBT . FindChildByName ( a_TagIdx , "BrewTime" );
if ( BrewTime >= 0 )
{
Int16 tb = a_NBT . GetShort ( BrewTime );
2017-05-05 11:58:21 +02:00
Brewingstand -> SetTimeBrewed ( tb );
2015-09-24 10:48:33 +02:00
}
// Restart brewing:
2017-05-05 11:58:21 +02:00
Brewingstand -> LoadRecipes ();
2015-09-24 10:48:33 +02:00
Brewingstand -> ContinueBrewing ();
2020-08-04 19:02:15 +01:00
return Brewingstand ;
2015-09-24 10:48:33 +02:00
}
2020-04-03 22:23:38 +01:00
OwnedBlockEntity cWSSAnvil :: LoadChestFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
2012-06-14 13:06:06 +00:00
{
2014-08-29 19:19:27 +03:00
// Check if the data has a proper type:
2016-11-07 21:16:55 +01:00
// Note that older Cuberite code used "TrappedChest" for trapped chests; new code mimics vanilla and uses "Chest" throughout, but we allow migration here:
2017-06-21 07:47:26 +02:00
static const AStringVector expectedTypes ({ "Chest" , "TrappedChest" , "minecraft:chest" });
2019-09-29 14:59:24 +02:00
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
2016-11-07 21:16:55 +01:00
{
return nullptr ;
}
2014-08-29 19:19:27 +03:00
2012-06-14 13:06:06 +00:00
int Items = a_NBT . FindChildByName ( a_TagIdx , "Items" );
if (( Items < 0 ) || ( a_NBT . GetType ( Items ) != TAG_List ))
{
2014-10-20 21:55:07 +01:00
return nullptr ; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this
2012-06-14 13:06:06 +00:00
}
2020-08-01 20:18:03 +02:00
auto Chest = std :: make_unique < cChestEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World );
2013-05-26 15:29:43 +00:00
LoadItemGridFromNBT ( Chest -> GetContents (), a_NBT , Items );
2020-08-04 19:02:15 +01:00
return Chest ;
2012-06-14 13:06:06 +00:00
}
2020-04-03 22:23:38 +01:00
OwnedBlockEntity cWSSAnvil :: LoadCommandBlockFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
2012-12-19 21:19:36 +00:00
{
2014-08-29 19:19:27 +03:00
// Check if the data has a proper type:
2017-06-21 07:47:26 +02:00
static const AStringVector expectedTypes ({ "Control" , "minecraft:command_block" });
2019-09-29 14:59:24 +02:00
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
2012-12-19 21:19:36 +00:00
{
2014-10-20 21:55:07 +01:00
return nullptr ;
2014-08-29 19:19:27 +03:00
}
2020-08-01 20:18:03 +02:00
auto CmdBlock = std :: make_unique < cCommandBlockEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World );
2014-08-29 19:19:27 +03:00
int currentLine = a_NBT . FindChildByName ( a_TagIdx , "Command" );
if ( currentLine >= 0 )
{
CmdBlock -> SetCommand ( a_NBT . GetString ( currentLine ));
}
currentLine = a_NBT . FindChildByName ( a_TagIdx , "SuccessCount" );
if ( currentLine >= 0 )
{
2015-07-29 09:04:03 -06:00
CmdBlock -> SetResult ( static_cast < NIBBLETYPE > ( a_NBT . GetInt ( currentLine )));
2014-08-29 19:19:27 +03:00
}
currentLine = a_NBT . FindChildByName ( a_TagIdx , "LastOutput" );
if ( currentLine >= 0 )
{
CmdBlock -> SetLastOutput ( a_NBT . GetString ( currentLine ));
}
// TODO 2014-01-18 xdot: Figure out what TrackOutput is and parse it.
2020-08-04 19:02:15 +01:00
return CmdBlock ;
2014-08-29 19:19:27 +03:00
}
2020-04-03 22:23:38 +01:00
OwnedBlockEntity cWSSAnvil :: LoadDispenserFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
2014-08-29 19:19:27 +03:00
{
// Check if the data has a proper type:
2017-06-21 07:47:26 +02:00
static const AStringVector expectedTypes ({ "Trap" , "minecraft:dispenser" });
2019-09-29 14:59:24 +02:00
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
2014-08-29 19:19:27 +03:00
{
2014-10-20 21:55:07 +01:00
return nullptr ;
2012-12-19 21:19:36 +00:00
}
2014-08-29 19:19:27 +03:00
2012-12-19 21:19:36 +00:00
int Items = a_NBT . FindChildByName ( a_TagIdx , "Items" );
if (( Items < 0 ) || ( a_NBT . GetType ( Items ) != TAG_List ))
{
2014-10-20 21:55:07 +01:00
return nullptr ; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this
2012-12-19 21:19:36 +00:00
}
2020-08-01 20:18:03 +02:00
auto Dispenser = std :: make_unique < cDispenserEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World );
2013-05-26 15:29:43 +00:00
LoadItemGridFromNBT ( Dispenser -> GetContents (), a_NBT , Items );
2020-08-04 19:02:15 +01:00
return Dispenser ;
2012-12-19 21:19:36 +00:00
}
2020-04-03 22:23:38 +01:00
OwnedBlockEntity cWSSAnvil :: LoadDropperFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
2013-05-26 15:29:43 +00:00
{
2014-08-29 19:19:27 +03:00
// Check if the data has a proper type:
2017-06-21 07:47:26 +02:00
static const AStringVector expectedTypes ({ "Dropper" , "minecraft:dropper" });
2019-09-29 14:59:24 +02:00
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
2013-05-26 15:29:43 +00:00
{
2014-10-20 21:55:07 +01:00
return nullptr ;
2013-05-26 15:29:43 +00:00
}
2014-08-29 19:19:27 +03:00
2013-05-26 15:29:43 +00:00
int Items = a_NBT . FindChildByName ( a_TagIdx , "Items" );
if (( Items < 0 ) || ( a_NBT . GetType ( Items ) != TAG_List ))
{
2014-10-20 21:55:07 +01:00
return nullptr ; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this
2013-05-26 15:29:43 +00:00
}
2020-08-01 20:18:03 +02:00
auto Dropper = std :: make_unique < cDropperEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World );
2013-05-26 15:29:43 +00:00
LoadItemGridFromNBT ( Dropper -> GetContents (), a_NBT , Items );
2020-08-04 19:02:15 +01:00
return Dropper ;
2013-05-26 15:29:43 +00:00
}
2020-09-17 16:16:20 +02:00
OwnedBlockEntity cWSSAnvil :: LoadEnchantingTableFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
{
// Check if the data has a proper type:
static const AStringVector expectedTypes ({ "EnchantingTable" , "minecraft:enchanting_table" });
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
{
return nullptr ;
}
AString CustomName ;
int currentLine = a_NBT . FindChildByName ( a_TagIdx , "CustomName" );
if ( currentLine >= 0 )
{
if ( a_NBT . GetType ( currentLine ) == TAG_String )
{
CustomName = a_NBT . GetString ( currentLine );
}
}
return std :: make_unique < cEnchantingTableEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World , CustomName );
}
2020-08-28 21:40:40 +01:00
OwnedBlockEntity cWSSAnvil :: LoadEnderChestFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
{
// Check if the data has a proper type:
static const AStringVector expectedTypes ({ "EnderChest" , "minecraft:ender_chest" });
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
{
return nullptr ;
}
return std :: make_unique < cEnderChestEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World );
}
2020-09-17 16:16:20 +02:00
OwnedBlockEntity cWSSAnvil :: LoadEndPortalFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
{
// Check if the data has a proper type:
static const AStringVector expectedTypes ({ "EndPortal" , "minecraft:end_portal" });
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
{
return nullptr ;
}
return std :: make_unique < cEndPortalEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World );
}
2020-04-03 22:23:38 +01:00
OwnedBlockEntity cWSSAnvil :: LoadFlowerPotFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
2014-03-07 01:30:34 +01:00
{
2014-08-29 19:19:27 +03:00
// Check if the data has a proper type:
2017-06-21 07:47:26 +02:00
static const AStringVector expectedTypes ({ "FlowerPot" , "minecraft:flower_pot" });
2019-09-29 14:59:24 +02:00
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
2014-03-07 01:30:34 +01:00
{
2014-10-20 21:55:07 +01:00
return nullptr ;
2014-03-07 01:30:34 +01:00
}
2014-08-29 19:19:27 +03:00
2020-08-01 20:18:03 +02:00
auto FlowerPot = std :: make_unique < cFlowerPotEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World );
2015-02-18 23:33:27 +01:00
cItem Item ;
2014-03-07 01:30:34 +01:00
int currentLine = a_NBT . FindChildByName ( a_TagIdx , "Item" );
if ( currentLine >= 0 )
{
2015-02-18 23:33:27 +01:00
if ( a_NBT . GetType ( currentLine ) == TAG_String )
{
StringToItem ( a_NBT . GetString ( currentLine ), Item );
}
else if ( a_NBT . GetType ( currentLine ) == TAG_Int )
{
2015-07-29 09:04:03 -06:00
Item . m_ItemType = static_cast < short > ( a_NBT . GetInt ( currentLine ));
2015-02-18 23:33:27 +01:00
}
2014-03-07 01:30:34 +01:00
}
currentLine = a_NBT . FindChildByName ( a_TagIdx , "Data" );
2015-02-18 23:33:27 +01:00
if (( currentLine >= 0 ) && ( a_NBT . GetType ( currentLine ) == TAG_Int ))
2014-03-07 01:30:34 +01:00
{
2015-07-29 09:04:03 -06:00
Item . m_ItemDamage = static_cast < short > ( a_NBT . GetInt ( currentLine ));
2014-03-07 01:30:34 +01:00
}
2015-02-18 23:33:27 +01:00
FlowerPot -> SetItem ( Item );
2020-08-04 19:02:15 +01:00
return FlowerPot ;
2014-03-07 01:30:34 +01:00
}
2020-04-03 22:23:38 +01:00
OwnedBlockEntity cWSSAnvil :: LoadFurnaceFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
2012-06-14 13:06:06 +00:00
{
2014-08-29 19:19:27 +03:00
// Check if the data has a proper type:
2017-06-21 07:47:26 +02:00
static const AStringVector expectedTypes ({ "Furnace" , "minecraft:furnace" });
2019-09-29 14:59:24 +02:00
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
2012-06-14 13:06:06 +00:00
{
2014-10-20 21:55:07 +01:00
return nullptr ;
2012-06-14 13:06:06 +00:00
}
2014-08-29 19:19:27 +03:00
2012-06-14 13:06:06 +00:00
int Items = a_NBT . FindChildByName ( a_TagIdx , "Items" );
if (( Items < 0 ) || ( a_NBT . GetType ( Items ) != TAG_List ))
{
2014-10-20 21:55:07 +01:00
return nullptr ; // Make it an empty furnace - the chunk loader will provide an empty cFurnaceEntity for this
2012-06-14 13:06:06 +00:00
}
2016-02-05 23:45:45 +02:00
2020-08-01 20:18:03 +02:00
auto Furnace = std :: make_unique < cFurnaceEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World );
2015-06-02 17:26:21 -07:00
Furnace -> SetLoading ( true );
2013-06-20 11:41:44 +00:00
// Load slots:
2012-06-14 13:06:06 +00:00
for ( int Child = a_NBT . GetFirstChild ( Items ); Child != - 1 ; Child = a_NBT . GetNextSibling ( Child ))
{
int Slot = a_NBT . FindChildByName ( Child , "Slot" );
if (( Slot < 0 ) || ( a_NBT . GetType ( Slot ) != TAG_Byte ))
{
continue ;
}
cItem Item ;
2013-03-09 14:35:43 +00:00
if ( LoadItemFromNBT ( Item , a_NBT , Child ))
2012-06-14 13:06:06 +00:00
{
2013-03-09 14:35:43 +00:00
Furnace -> SetSlot ( a_NBT . GetByte ( Slot ), Item );
2012-06-14 13:06:06 +00:00
}
} // for itr - ItemDefs[]
2016-02-05 23:45:45 +02:00
2013-06-20 11:41:44 +00:00
// Load burn time:
2012-06-14 13:06:06 +00:00
int BurnTime = a_NBT . FindChildByName ( a_TagIdx , "BurnTime" );
if ( BurnTime >= 0 )
{
Int16 bt = a_NBT . GetShort ( BurnTime );
// Anvil doesn't store the time that the fuel can burn. We simply "reset" the current value to be the 100%
2013-06-16 20:24:07 +00:00
Furnace -> SetBurnTimes ( bt , 0 );
2012-06-14 13:06:06 +00:00
}
2016-02-05 23:45:45 +02:00
2013-06-20 11:41:44 +00:00
// Load cook time:
2012-06-14 13:06:06 +00:00
int CookTime = a_NBT . FindChildByName ( a_TagIdx , "CookTime" );
if ( CookTime >= 0 )
{
Int16 ct = a_NBT . GetShort ( CookTime );
2013-06-16 20:24:07 +00:00
// Anvil doesn't store the time that an item takes to cook. We simply use the default - 10 seconds (200 ticks)
Furnace -> SetCookTimes ( 200 , ct );
2012-06-14 13:06:06 +00:00
}
2013-06-20 11:41:44 +00:00
// Restart cooking:
2012-06-14 13:06:06 +00:00
Furnace -> ContinueCooking ();
2015-06-02 17:26:21 -07:00
Furnace -> SetLoading ( false );
2020-08-04 19:02:15 +01:00
return Furnace ;
2012-06-14 13:06:06 +00:00
}
2020-04-03 22:23:38 +01:00
OwnedBlockEntity cWSSAnvil :: LoadHopperFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
2017-06-21 07:47:26 +02:00
{
// Check if the data has a proper type:
static const AStringVector expectedTypes ({ "Hopper" , "minecraft:hopper" });
2019-09-29 14:59:24 +02:00
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
2017-06-21 07:47:26 +02:00
{
return nullptr ;
}
int Items = a_NBT . FindChildByName ( a_TagIdx , "Items" );
if (( Items < 0 ) || ( a_NBT . GetType ( Items ) != TAG_List ))
{
return nullptr ; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this
}
2020-08-01 20:18:03 +02:00
auto Hopper = std :: make_unique < cHopperEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World );
2017-06-21 07:47:26 +02:00
LoadItemGridFromNBT ( Hopper -> GetContents (), a_NBT , Items );
2020-08-04 19:02:15 +01:00
return Hopper ;
2017-06-21 07:47:26 +02:00
}
2020-04-03 22:23:38 +01:00
OwnedBlockEntity cWSSAnvil :: LoadJukeboxFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
2017-06-21 07:47:26 +02:00
{
// Check if the data has a proper type:
static const AStringVector expectedTypes ({ "RecordPlayer" , "minecraft:jukebox" });
2019-09-29 14:59:24 +02:00
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
2017-06-21 07:47:26 +02:00
{
return nullptr ;
}
2020-08-01 20:18:03 +02:00
auto Jukebox = std :: make_unique < cJukeboxEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World );
2017-06-21 07:47:26 +02:00
int Record = a_NBT . FindChildByName ( a_TagIdx , "Record" );
if ( Record >= 0 )
{
Jukebox -> SetRecord ( a_NBT . GetInt ( Record ));
}
2020-08-04 19:02:15 +01:00
return Jukebox ;
2017-06-21 07:47:26 +02:00
}
2020-04-03 22:23:38 +01:00
OwnedBlockEntity cWSSAnvil :: LoadMobSpawnerFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
2014-11-18 15:33:41 +01:00
{
// Check if the data has a proper type:
2017-06-21 07:47:26 +02:00
static const AStringVector expectedTypes ({ "MobSpawner" , "minecraft:mob_spawner" });
2019-09-29 14:59:24 +02:00
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
2014-11-18 15:33:41 +01:00
{
return nullptr ;
}
2020-08-01 20:18:03 +02:00
auto MobSpawner = std :: make_unique < cMobSpawnerEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World );
2014-11-18 15:33:41 +01:00
2020-09-27 19:02:16 +02:00
// Load entity type
int Type = a_NBT . FindChildByName ( a_TagIdx , "EntityId" );
if (( Type >= 0 ) && ( a_NBT . GetType ( Type ) == TAG_String ))
2014-11-18 15:33:41 +01:00
{
2020-09-27 19:02:16 +02:00
eMonsterType MonsterType = cMonster :: StringToMobType ( a_NBT . GetString ( Type ));
if ( MonsterType != eMonsterType :: mtInvalidType )
2014-11-18 15:33:41 +01:00
{
2020-09-27 19:02:16 +02:00
MobSpawner -> SetEntity ( MonsterType );
2014-11-18 15:33:41 +01:00
}
}
// Load delay:
int Delay = a_NBT . FindChildByName ( a_TagIdx , "Delay" );
if (( Delay >= 0 ) && ( a_NBT . GetType ( Delay ) == TAG_Short ))
{
MobSpawner -> SetSpawnDelay ( a_NBT . GetShort ( Delay ));
}
2020-08-04 19:02:15 +01:00
return MobSpawner ;
2014-11-18 15:33:41 +01:00
}
2020-04-03 22:23:38 +01:00
OwnedBlockEntity cWSSAnvil :: LoadMobHeadFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
2012-10-21 07:46:28 +00:00
{
2014-08-29 19:19:27 +03:00
// Check if the data has a proper type:
2017-06-21 07:47:26 +02:00
static const AStringVector expectedTypes ({ "Skull" , "minecraft:skull" });
2019-09-29 14:59:24 +02:00
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
2012-10-21 07:46:28 +00:00
{
2014-10-20 21:55:07 +01:00
return nullptr ;
2012-10-21 07:46:28 +00:00
}
2013-06-13 07:36:43 +00:00
2020-08-01 20:18:03 +02:00
auto MobHead = std :: make_unique < cMobHeadEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World );
2013-06-13 07:36:43 +00:00
2014-08-29 19:19:27 +03:00
int currentLine = a_NBT . FindChildByName ( a_TagIdx , "SkullType" );
2013-06-13 07:36:43 +00:00
if ( currentLine >= 0 )
{
2014-08-29 19:19:27 +03:00
MobHead -> SetType ( static_cast < eMobHeadType > ( a_NBT . GetByte ( currentLine )));
2013-06-13 07:36:43 +00:00
}
2014-08-29 19:19:27 +03:00
currentLine = a_NBT . FindChildByName ( a_TagIdx , "Rot" );
2013-06-13 07:36:43 +00:00
if ( currentLine >= 0 )
{
2014-08-29 19:19:27 +03:00
MobHead -> SetRotation ( static_cast < eMobHeadRotation > ( a_NBT . GetByte ( currentLine )));
2013-06-13 07:36:43 +00:00
}
2016-01-11 17:55:32 +01:00
int ownerLine = a_NBT . FindChildByName ( a_TagIdx , "Owner" );
if ( ownerLine >= 0 )
2013-06-13 07:36:43 +00:00
{
2017-08-25 13:43:18 +01:00
AString OwnerName , OwnerTexture , OwnerTextureSignature ;
cUUID OwnerUUID ;
2016-01-11 17:55:32 +01:00
currentLine = a_NBT . FindChildByName ( ownerLine , "Id" );
if ( currentLine >= 0 )
{
2017-08-25 13:43:18 +01:00
OwnerUUID . FromString ( a_NBT . GetString ( currentLine ));
2016-01-11 17:55:32 +01:00
}
currentLine = a_NBT . FindChildByName ( ownerLine , "Name" );
if ( currentLine >= 0 )
{
OwnerName = a_NBT . GetString ( currentLine );
}
int textureLine = a_NBT . GetFirstChild ( // The first texture of
a_NBT . FindChildByName ( // The texture list of
a_NBT . FindChildByName ( // The Properties compound of
ownerLine , // The Owner compound
"Properties"
),
"textures"
)
);
if ( textureLine >= 0 )
{
currentLine = a_NBT . FindChildByName ( textureLine , "Signature" );
if ( currentLine >= 0 )
{
OwnerTextureSignature = a_NBT . GetString ( currentLine );
}
currentLine = a_NBT . FindChildByName ( textureLine , "Value" );
if ( currentLine >= 0 )
{
OwnerTexture = a_NBT . GetString ( currentLine );
}
}
MobHead -> SetOwner ( OwnerUUID , OwnerName , OwnerTexture , OwnerTextureSignature );
2013-06-13 07:36:43 +00:00
}
2020-08-04 19:02:15 +01:00
return MobHead ;
2012-10-21 07:46:28 +00:00
}
2020-04-03 22:23:38 +01:00
OwnedBlockEntity cWSSAnvil :: LoadNoteBlockFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
2014-02-17 20:14:08 +01:00
{
2014-08-29 19:19:27 +03:00
// Check if the data has a proper type:
2017-06-21 07:47:26 +02:00
static const AStringVector expectedTypes ({ "Music" , "minecraft:noteblock" });
2019-09-29 14:59:24 +02:00
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
2014-02-17 20:14:08 +01:00
{
2014-10-20 21:55:07 +01:00
return nullptr ;
2014-02-17 20:14:08 +01:00
}
2020-08-01 20:18:03 +02:00
auto NoteBlock = std :: make_unique < cNoteEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World );
2014-08-29 19:19:27 +03:00
int note = a_NBT . FindChildByName ( a_TagIdx , "note" );
if ( note >= 0 )
2014-02-17 20:14:08 +01:00
{
2020-09-05 17:07:01 +02:00
NoteBlock -> SetNote ( static_cast < char > ( a_NBT . GetByte ( note )));
2014-02-17 20:14:08 +01:00
}
2020-08-04 19:02:15 +01:00
return NoteBlock ;
2014-02-17 20:14:08 +01:00
}
2020-04-03 22:23:38 +01:00
OwnedBlockEntity cWSSAnvil :: LoadSignFromNBT ( const cParsedNBT & a_NBT , int a_TagIdx , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta , Vector3i a_Pos )
2014-01-18 15:40:47 +02:00
{
2014-08-29 19:19:27 +03:00
// Check if the data has a proper type:
2017-06-21 07:47:26 +02:00
static const AStringVector expectedTypes ({ "Sign" , "minecraft:sign" });
2019-09-29 14:59:24 +02:00
if ( ! CheckBlockEntityType ( a_NBT , a_TagIdx , expectedTypes , a_Pos ))
2014-01-18 15:40:47 +02:00
{
2014-10-20 21:55:07 +01:00
return nullptr ;
2014-01-18 15:40:47 +02:00
}
2020-08-01 20:18:03 +02:00
auto Sign = std :: make_unique < cSignEntity > ( a_BlockType , a_BlockMeta , a_Pos , m_World );
2014-08-29 19:19:27 +03:00
int currentLine = a_NBT . FindChildByName ( a_TagIdx , "Text1" );
2014-01-18 15:40:47 +02:00
if ( currentLine >= 0 )
{
2017-06-21 07:47:26 +02:00
Sign -> SetLine ( 0 , DecodeSignLine ( a_NBT . GetString ( currentLine )));
2014-01-18 15:40:47 +02:00
}
2014-08-29 19:19:27 +03:00
currentLine = a_NBT . FindChildByName ( a_TagIdx , "Text2" );
2014-01-18 15:40:47 +02:00
if ( currentLine >= 0 )
{
2017-06-21 07:47:26 +02:00
Sign -> SetLine ( 1 , DecodeSignLine ( a_NBT . GetString ( currentLine )));
2014-01-18 15:40:47 +02:00
}
2014-08-29 19:19:27 +03:00
currentLine = a_NBT . FindChildByName ( a_TagIdx , "Text3" );
2014-01-18 15:40:47 +02:00
if ( currentLine >= 0 )
{
2017-06-21 07:47:26 +02:00
Sign -> SetLine ( 2 , DecodeSignLine ( a_NBT . GetString ( currentLine )));
2014-01-18 15:40:47 +02:00
}
2014-08-29 19:19:27 +03:00
currentLine = a_NBT . FindChildByName ( a_TagIdx , "Text4" );
if ( currentLine >= 0 )
{
2017-06-21 07:47:26 +02:00
Sign -> SetLine ( 3 , DecodeSignLine ( a_NBT . GetString ( currentLine )));
2014-08-29 19:19:27 +03:00
}
2014-01-18 15:40:47 +02:00
2020-08-04 19:02:15 +01:00
return Sign ;
2014-01-18 15:40:47 +02:00
}
2014-05-09 19:33:22 +02:00
void cWSSAnvil :: LoadEntityFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_EntityTagIdx , const char * a_IDTag , size_t a_IDTagLength )
2013-03-09 14:35:43 +00:00
{
2017-06-22 17:30:11 +02:00
typedef void ( cWSSAnvil ::* EntityLoaderFunc )( cEntityList & , const cParsedNBT & , int a_EntityTagIdx );
typedef std :: map < AString , EntityLoaderFunc > EntityLoaderMap ;
static const EntityLoaderMap EntityTypeToFunction
{
{ "Boat" , & cWSSAnvil :: LoadBoatFromNBT },
{ "minecraft:boat" , & cWSSAnvil :: LoadBoatFromNBT },
{ "EnderCrystal" , & cWSSAnvil :: LoadEnderCrystalFromNBT },
{ "minecraft:ender_crystal" , & cWSSAnvil :: LoadEnderCrystalFromNBT },
{ "FallingBlock" , & cWSSAnvil :: LoadFallingBlockFromNBT },
{ "minecraft:falling_block" , & cWSSAnvil :: LoadFallingBlockFromNBT },
{ "Minecart" , & cWSSAnvil :: LoadOldMinecartFromNBT },
{ "MinecartChest" , & cWSSAnvil :: LoadMinecartCFromNBT },
{ "minecraft:chest_minecart" , & cWSSAnvil :: LoadMinecartCFromNBT },
{ "MinecartFurnace" , & cWSSAnvil :: LoadMinecartFFromNBT },
{ "minecraft:furnace_minecart" , & cWSSAnvil :: LoadMinecartFFromNBT },
{ "MinecartTNT" , & cWSSAnvil :: LoadMinecartTFromNBT },
{ "minecraft:tnt_minecart" , & cWSSAnvil :: LoadMinecartTFromNBT },
{ "MinecartHopper" , & cWSSAnvil :: LoadMinecartHFromNBT },
{ "minecraft:hopper_minecart" , & cWSSAnvil :: LoadMinecartHFromNBT },
{ "MinecartRideable" , & cWSSAnvil :: LoadMinecartRFromNBT },
{ "minecraft:minecart" , & cWSSAnvil :: LoadMinecartRFromNBT },
{ "Item" , & cWSSAnvil :: LoadPickupFromNBT },
{ "minecraft:item" , & cWSSAnvil :: LoadPickupFromNBT },
{ "Painting" , & cWSSAnvil :: LoadPaintingFromNBT },
{ "minecraft:painting" , & cWSSAnvil :: LoadPaintingFromNBT },
{ "PrimedTnt" , & cWSSAnvil :: LoadTNTFromNBT },
{ "minecraft:tnt" , & cWSSAnvil :: LoadTNTFromNBT },
{ "XPOrb" , & cWSSAnvil :: LoadExpOrbFromNBT },
{ "minecraft:xp_orb" , & cWSSAnvil :: LoadExpOrbFromNBT },
{ "ItemFrame" , & cWSSAnvil :: LoadItemFrameFromNBT },
{ "minecraft:item_frame" , & cWSSAnvil :: LoadItemFrameFromNBT },
2017-08-21 10:46:41 +02:00
{ "LeashKnot" , & cWSSAnvil :: LoadLeashKnotFromNBT },
{ "minecraft:leash_knot" , & cWSSAnvil :: LoadLeashKnotFromNBT },
2017-06-22 17:30:11 +02:00
{ "Arrow" , & cWSSAnvil :: LoadArrowFromNBT },
{ "minecraft:arrow" , & cWSSAnvil :: LoadArrowFromNBT },
{ "SplashPotion" , & cWSSAnvil :: LoadSplashPotionFromNBT },
{ "minecraft:potion" , & cWSSAnvil :: LoadSplashPotionFromNBT },
{ "Snowball" , & cWSSAnvil :: LoadSnowballFromNBT },
{ "minecraft:snowball" , & cWSSAnvil :: LoadSnowballFromNBT },
{ "Egg" , & cWSSAnvil :: LoadEggFromNBT },
{ "minecraft:egg" , & cWSSAnvil :: LoadEggFromNBT },
{ "Fireball" , & cWSSAnvil :: LoadFireballFromNBT },
{ "minecraft:fireball" , & cWSSAnvil :: LoadFireballFromNBT },
{ "SmallFireball" , & cWSSAnvil :: LoadFireChargeFromNBT },
{ "minecraft:small_fireball" , & cWSSAnvil :: LoadFireChargeFromNBT },
{ "ThrownEnderpearl" , & cWSSAnvil :: LoadThrownEnderpearlFromNBT },
{ "minecraft:ender_pearl" , & cWSSAnvil :: LoadThrownEnderpearlFromNBT },
{ "Bat" , & cWSSAnvil :: LoadBatFromNBT },
{ "minecraft:bat" , & cWSSAnvil :: LoadBatFromNBT },
{ "Blaze" , & cWSSAnvil :: LoadBlazeFromNBT },
{ "minecraft:blaze" , & cWSSAnvil :: LoadBlazeFromNBT },
{ "CaveSpider" , & cWSSAnvil :: LoadCaveSpiderFromNBT },
{ "minecraft:cave_spider" , & cWSSAnvil :: LoadCaveSpiderFromNBT },
{ "Chicken" , & cWSSAnvil :: LoadChickenFromNBT },
{ "minecraft:chicken" , & cWSSAnvil :: LoadChickenFromNBT },
{ "Cow" , & cWSSAnvil :: LoadCowFromNBT },
{ "minecraft:cow" , & cWSSAnvil :: LoadCowFromNBT },
{ "Creeper" , & cWSSAnvil :: LoadCreeperFromNBT },
{ "minecraft:creeper" , & cWSSAnvil :: LoadCreeperFromNBT },
{ "EnderDragon" , & cWSSAnvil :: LoadEnderDragonFromNBT },
{ "minecraft:ender_dragon" , & cWSSAnvil :: LoadEnderDragonFromNBT },
{ "Enderman" , & cWSSAnvil :: LoadEndermanFromNBT },
{ "minecraft:enderman" , & cWSSAnvil :: LoadEndermanFromNBT },
{ "Ghast" , & cWSSAnvil :: LoadGhastFromNBT },
{ "minecraft:ghast" , & cWSSAnvil :: LoadGhastFromNBT },
{ "Giant" , & cWSSAnvil :: LoadGiantFromNBT },
{ "minecraft:giant" , & cWSSAnvil :: LoadGiantFromNBT },
{ "Guardian" , & cWSSAnvil :: LoadGuardianFromNBT },
{ "minecraft:guardian" , & cWSSAnvil :: LoadGuardianFromNBT },
{ "Horse" , & cWSSAnvil :: LoadHorseFromNBT },
{ "minecraft:horse" , & cWSSAnvil :: LoadHorseFromNBT },
{ "Villager" , & cWSSAnvil :: LoadVillagerFromNBT },
{ "minecraft:villager" , & cWSSAnvil :: LoadVillagerFromNBT },
{ "VillagerGolem" , & cWSSAnvil :: LoadIronGolemFromNBT },
{ "minecraft:villager_golem" , & cWSSAnvil :: LoadIronGolemFromNBT },
{ "LavaSlime" , & cWSSAnvil :: LoadMagmaCubeFromNBT },
{ "minecraft:magma_cube" , & cWSSAnvil :: LoadMagmaCubeFromNBT },
{ "MushroomCow" , & cWSSAnvil :: LoadMooshroomFromNBT },
{ "minecraft:mooshroom" , & cWSSAnvil :: LoadMooshroomFromNBT },
{ "Ozelot" , & cWSSAnvil :: LoadOcelotFromNBT },
{ "minecraft:ocelot" , & cWSSAnvil :: LoadOcelotFromNBT },
{ "Pig" , & cWSSAnvil :: LoadPigFromNBT },
{ "minecraft:pig" , & cWSSAnvil :: LoadPigFromNBT },
{ "Rabbit" , & cWSSAnvil :: LoadRabbitFromNBT },
{ "minecraft:rabbit" , & cWSSAnvil :: LoadRabbitFromNBT },
{ "Sheep" , & cWSSAnvil :: LoadSheepFromNBT },
{ "minecraft:sheep" , & cWSSAnvil :: LoadSheepFromNBT },
{ "Silverfish" , & cWSSAnvil :: LoadSilverfishFromNBT },
{ "minecraft:silverfish" , & cWSSAnvil :: LoadSilverfishFromNBT },
{ "Skeleton" , & cWSSAnvil :: LoadSkeletonFromNBT },
{ "minecraft:skeleton" , & cWSSAnvil :: LoadSkeletonFromNBT },
{ "Slime" , & cWSSAnvil :: LoadSlimeFromNBT },
{ "minecraft:slime" , & cWSSAnvil :: LoadSlimeFromNBT },
{ "SnowMan" , & cWSSAnvil :: LoadSnowGolemFromNBT },
{ "minecraft:snowman" , & cWSSAnvil :: LoadSnowGolemFromNBT },
{ "Spider" , & cWSSAnvil :: LoadSpiderFromNBT },
{ "minecraft:spider" , & cWSSAnvil :: LoadSpiderFromNBT },
{ "Squid" , & cWSSAnvil :: LoadSquidFromNBT },
{ "minecraft:squid" , & cWSSAnvil :: LoadSquidFromNBT },
{ "Witch" , & cWSSAnvil :: LoadWitchFromNBT },
{ "minecraft:witch" , & cWSSAnvil :: LoadWitchFromNBT },
{ "WitherBoss" , & cWSSAnvil :: LoadWitherFromNBT },
{ "minecraft:wither" , & cWSSAnvil :: LoadWitherFromNBT },
2020-04-04 14:44:17 +03:00
{ "WitherSkeleton" , & cWSSAnvil :: LoadWitherSkeletonFromNBT },
{ "minecraft:wither_skeleton" , & cWSSAnvil :: LoadWitherSkeletonFromNBT },
2017-06-22 17:30:11 +02:00
{ "Wolf" , & cWSSAnvil :: LoadWolfFromNBT },
{ "minecraft:wolf" , & cWSSAnvil :: LoadWolfFromNBT },
{ "Zombie" , & cWSSAnvil :: LoadZombieFromNBT },
{ "minecraft:zombie" , & cWSSAnvil :: LoadZombieFromNBT },
{ "PigZombie" , & cWSSAnvil :: LoadPigZombieFromNBT },
{ "minecraft:zombie_pigman" , & cWSSAnvil :: LoadPigZombieFromNBT },
2018-02-03 12:24:19 +01:00
{ "ZombieVillager" , & cWSSAnvil :: LoadZombieVillagerFromNBT },
{ "minecraft:zombie_villager" , & cWSSAnvil :: LoadZombieVillagerFromNBT },
2017-06-22 17:30:11 +02:00
};
auto it = EntityTypeToFunction . find ( AString ( a_IDTag , a_IDTagLength ));
if ( it != EntityTypeToFunction . end ())
{
( this ->* it -> second )( a_Entities , a_NBT , a_EntityTagIdx );
2013-12-25 16:07:52 +00:00
}
2017-06-22 17:30:11 +02:00
// TODO: other entities
}
void cWSSAnvil :: LoadOldMinecartFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
// It is a minecart, old style, find out the type:
int TypeTag = a_NBT . FindChildByName ( a_TagIdx , "Type" );
if (( TypeTag < 0 ) || ( a_NBT . GetType ( TypeTag ) != TAG_Int ))
2013-12-25 16:07:52 +00:00
{
2017-06-22 17:30:11 +02:00
return ;
2013-12-25 16:07:52 +00:00
}
2017-09-14 09:48:57 +01:00
int MinecartType = a_NBT . GetInt ( TypeTag );
switch ( MinecartType )
2013-12-25 16:07:52 +00:00
{
2017-06-22 17:30:11 +02:00
case 0 : LoadMinecartRFromNBT ( a_Entities , a_NBT , a_TagIdx ); break ; // Rideable minecart
case 1 : LoadMinecartCFromNBT ( a_Entities , a_NBT , a_TagIdx ); break ; // Minecart with chest
case 2 : LoadMinecartFFromNBT ( a_Entities , a_NBT , a_TagIdx ); break ; // Minecart with furnace
case 3 : LoadMinecartTFromNBT ( a_Entities , a_NBT , a_TagIdx ); break ; // Minecart with TNT
case 4 : LoadMinecartHFromNBT ( a_Entities , a_NBT , a_TagIdx ); break ; // Minecart with Hopper
2017-09-14 09:48:57 +01:00
default : LOGWARNING ( "cWSSAnvil::LoadOldMinecartFromNBT: Unhandled minecart type (%d)" , MinecartType ); break ;
2013-12-25 16:07:52 +00:00
}
2013-03-09 14:35:43 +00:00
}
2013-09-08 00:14:57 +01:00
void cWSSAnvil :: LoadBoatFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto Boat = std :: make_unique < cBoat > ( Vector3d (), cBoat :: bmOak );
2013-09-08 00:14:57 +01:00
if ( ! LoadEntityBaseFromNBT ( * Boat . get (), a_NBT , a_TagIdx ))
{
return ;
}
2017-05-09 14:21:25 +02:00
int TypeIdx = a_NBT . FindChildByName ( a_TagIdx , "Type" );
if ( TypeIdx > 0 )
{
Boat -> SetMaterial ( cBoat :: StringToMaterial ( a_NBT . GetString ( TypeIdx )));
}
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Boat ));
2013-09-08 00:14:57 +01:00
}
2014-03-25 20:59:33 +02:00
void cWSSAnvil :: LoadEnderCrystalFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto EnderCrystal = std :: make_unique < cEnderCrystal > ( Vector3d ());
2014-03-25 20:59:33 +02:00
if ( ! LoadEntityBaseFromNBT ( * EnderCrystal . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( EnderCrystal ));
2014-03-25 20:59:33 +02:00
}
2013-03-09 14:35:43 +00:00
void cWSSAnvil :: LoadFallingBlockFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2013-12-25 16:07:52 +00:00
int TypeIdx = a_NBT . FindChildByName ( a_TagIdx , "TileID" );
int MetaIdx = a_NBT . FindChildByName ( a_TagIdx , "Data" );
2014-12-05 16:59:11 +01:00
if (( TypeIdx < 0 ) || ( MetaIdx < 0 ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2015-07-31 16:49:10 +02:00
BLOCKTYPE Type = static_cast < BLOCKTYPE > ( a_NBT . GetInt ( TypeIdx ));
2015-07-29 09:04:03 -06:00
NIBBLETYPE Meta = static_cast < NIBBLETYPE > ( a_NBT . GetByte ( MetaIdx ));
2013-12-25 16:07:52 +00:00
2020-08-01 20:18:03 +02:00
auto FallingBlock = std :: make_unique < cFallingBlock > ( Vector3i ( 0 , 0 , 0 ), Type , Meta );
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * FallingBlock . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( FallingBlock ));
2013-03-09 14:35:43 +00:00
}
void cWSSAnvil :: LoadMinecartRFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto Minecart = std :: make_unique < cRideableMinecart > ( Vector3d (), cItem (), 1 ); // TODO: Load the block and the height
2013-03-09 20:08:39 +00:00
if ( ! LoadEntityBaseFromNBT ( * Minecart . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Minecart ));
2013-03-09 14:35:43 +00:00
}
void cWSSAnvil :: LoadMinecartCFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2013-03-09 20:08:39 +00:00
int Items = a_NBT . FindChildByName ( a_TagIdx , "Items" );
if (( Items < 0 ) || ( a_NBT . GetType ( Items ) != TAG_List ))
{
return ; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this
}
2020-08-01 20:18:03 +02:00
auto Minecart = std :: make_unique < cMinecartWithChest > ( Vector3d ());
2013-03-09 20:08:39 +00:00
if ( ! LoadEntityBaseFromNBT ( * Minecart . get (), a_NBT , a_TagIdx ))
{
return ;
}
for ( int Child = a_NBT . GetFirstChild ( Items ); Child != - 1 ; Child = a_NBT . GetNextSibling ( Child ))
{
int Slot = a_NBT . FindChildByName ( Child , "Slot" );
if (( Slot < 0 ) || ( a_NBT . GetType ( Slot ) != TAG_Byte ))
{
continue ;
}
cItem Item ;
if ( LoadItemFromNBT ( Item , a_NBT , Child ))
{
Minecart -> SetSlot ( a_NBT . GetByte ( Slot ), Item );
}
} // for itr - ItemDefs[]
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Minecart ));
2013-03-09 14:35:43 +00:00
}
void cWSSAnvil :: LoadMinecartFFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto Minecart = std :: make_unique < cMinecartWithFurnace > ( Vector3d ());
2013-03-09 20:08:39 +00:00
if ( ! LoadEntityBaseFromNBT ( * Minecart . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2013-03-09 20:08:39 +00:00
// TODO: Load the Push and Fuel tags
2016-02-05 23:45:45 +02:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Minecart ));
2013-03-09 14:35:43 +00:00
}
2013-08-16 13:04:06 +01:00
void cWSSAnvil :: LoadMinecartTFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto Minecart = std :: make_unique < cMinecartWithTNT > ( Vector3d ());
2013-08-16 13:04:06 +01:00
if ( ! LoadEntityBaseFromNBT ( * Minecart . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2013-08-25 21:32:17 +02:00
// TODO: Everything to do with TNT carts
2013-08-16 13:04:06 +01:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Minecart ));
2013-08-16 13:04:06 +01:00
}
void cWSSAnvil :: LoadMinecartHFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto Minecart = std :: make_unique < cMinecartWithHopper > ( Vector3d ());
2013-08-16 13:04:06 +01:00
if ( ! LoadEntityBaseFromNBT ( * Minecart . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2013-08-25 21:32:17 +02:00
// TODO: Everything to do with hopper carts
2013-08-16 13:04:06 +01:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Minecart ));
2013-08-16 13:04:06 +01:00
}
2013-03-09 14:35:43 +00:00
void cWSSAnvil :: LoadPickupFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2014-03-15 00:43:38 +01:00
// Load item:
2013-03-09 14:35:43 +00:00
int ItemTag = a_NBT . FindChildByName ( a_TagIdx , "Item" );
if (( ItemTag < 0 ) || ( a_NBT . GetType ( ItemTag ) != TAG_Compound ))
{
return ;
}
cItem Item ;
if ( ! LoadItemFromNBT ( Item , a_NBT , ItemTag ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2020-08-01 20:18:03 +02:00
auto Pickup = std :: make_unique < cPickup > ( Vector3d (), Item , false ); // Pickup delay doesn't matter, just say false
2013-03-09 14:35:43 +00:00
if ( ! LoadEntityBaseFromNBT ( * Pickup . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-03-15 00:43:38 +01:00
// Load age:
int Age = a_NBT . FindChildByName ( a_TagIdx , "Age" );
if ( Age > 0 )
{
Pickup -> SetAge ( a_NBT . GetShort ( Age ));
}
2014-03-15 00:32:49 +01:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Pickup ));
2013-03-09 14:35:43 +00:00
}
2014-03-15 00:32:49 +01:00
void cWSSAnvil :: LoadTNTFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto TNT = std :: make_unique < cTNTEntity > ( Vector3d (), 0 );
2014-03-15 00:32:49 +01:00
if ( ! LoadEntityBaseFromNBT ( * TNT . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-03-15 00:32:49 +01:00
// Load Fuse Ticks:
int FuseTicks = a_NBT . FindChildByName ( a_TagIdx , "Fuse" );
if ( FuseTicks > 0 )
{
2015-07-29 09:04:03 -06:00
TNT -> SetFuseTicks ( static_cast < int > ( a_NBT . GetByte ( FuseTicks )));
2014-03-15 00:32:49 +01:00
}
2016-02-05 23:45:45 +02:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( TNT ));
2014-03-15 00:32:49 +01:00
}
void cWSSAnvil :: LoadExpOrbFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto ExpOrb = std :: make_unique < cExpOrb > ( Vector3d (), 0 );
2014-03-15 00:32:49 +01:00
if ( ! LoadEntityBaseFromNBT ( * ExpOrb . get (), a_NBT , a_TagIdx ))
{
return ;
}
// Load Age:
int Age = a_NBT . FindChildByName ( a_TagIdx , "Age" );
if ( Age > 0 )
{
ExpOrb -> SetAge ( a_NBT . GetShort ( Age ));
}
// Load Reward (Value):
int Reward = a_NBT . FindChildByName ( a_TagIdx , "Value" );
if ( Reward > 0 )
{
ExpOrb -> SetReward ( a_NBT . GetShort ( Reward ));
}
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( ExpOrb ));
2014-03-15 00:32:49 +01:00
}
2014-03-15 02:45:25 +01:00
void cWSSAnvil :: LoadHangingFromNBT ( cHangingEntity & a_Hanging , const cParsedNBT & a_NBT , int a_TagIdx )
{
2014-10-21 22:02:30 +02:00
// "Facing" tag is the prime source of the Facing; if not available, translate from older "Direction" or "Dir"
int Facing = a_NBT . FindChildByName ( a_TagIdx , "Facing" );
2015-03-13 22:29:27 +00:00
if ( Facing < 0 )
2014-03-15 02:45:25 +01:00
{
2015-03-13 22:29:27 +00:00
return ;
2014-03-15 02:45:25 +01:00
}
2015-03-13 22:29:27 +00:00
a_Hanging . SetProtocolFacing ( a_NBT . GetByte ( Facing ));
2014-03-15 02:45:25 +01:00
int TileX = a_NBT . FindChildByName ( a_TagIdx , "TileX" );
int TileY = a_NBT . FindChildByName ( a_TagIdx , "TileY" );
int TileZ = a_NBT . FindChildByName ( a_TagIdx , "TileZ" );
if (( TileX > 0 ) && ( TileY > 0 ) && ( TileZ > 0 ))
{
a_Hanging . SetPosition (
2015-03-13 22:29:27 +00:00
static_cast < double > ( a_NBT . GetInt ( TileX )),
static_cast < double > ( a_NBT . GetInt ( TileY )),
static_cast < double > ( a_NBT . GetInt ( TileZ ))
2014-03-15 02:45:25 +01:00
);
}
}
void cWSSAnvil :: LoadItemFrameFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
// Load item:
int ItemTag = a_NBT . FindChildByName ( a_TagIdx , "Item" );
if (( ItemTag < 0 ) || ( a_NBT . GetType ( ItemTag ) != TAG_Compound ))
{
return ;
}
cItem Item ;
if ( ! LoadItemFromNBT ( Item , a_NBT , ItemTag ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2020-08-01 20:18:03 +02:00
auto ItemFrame = std :: make_unique < cItemFrame > ( BLOCK_FACE_NONE , Vector3d ());
2014-03-15 02:45:25 +01:00
if ( ! LoadEntityBaseFromNBT ( * ItemFrame . get (), a_NBT , a_TagIdx ))
{
return ;
}
ItemFrame -> SetItem ( Item );
LoadHangingFromNBT ( * ItemFrame . get (), a_NBT , a_TagIdx );
2016-02-05 23:45:45 +02:00
2014-03-15 02:45:25 +01:00
// Load Rotation:
int Rotation = a_NBT . FindChildByName ( a_TagIdx , "ItemRotation" );
if ( Rotation > 0 )
{
2015-07-29 09:04:03 -06:00
ItemFrame -> SetItemRotation ( static_cast < Byte > ( a_NBT . GetByte ( Rotation )));
2014-03-15 02:45:25 +01:00
}
2016-02-05 23:45:45 +02:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( ItemFrame ));
2014-03-15 02:45:25 +01:00
}
2017-08-21 10:46:41 +02:00
void cWSSAnvil :: LoadLeashKnotFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto LeashKnot = std :: make_unique < cLeashKnot > ( BLOCK_FACE_NONE , Vector3d ());
2017-08-21 10:46:41 +02:00
if ( ! LoadEntityBaseFromNBT ( * LeashKnot . get (), a_NBT , a_TagIdx ))
{
return ;
}
LoadHangingFromNBT ( * LeashKnot . get (), a_NBT , a_TagIdx );
a_Entities . emplace_back ( std :: move ( LeashKnot ));
}
2015-03-13 23:05:06 +00:00
void cWSSAnvil :: LoadPaintingFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
// Load painting name:
int MotiveTag = a_NBT . FindChildByName ( a_TagIdx , "Motive" );
if (( MotiveTag < 0 ) || ( a_NBT . GetType ( MotiveTag ) != TAG_String ))
{
return ;
}
2020-08-01 20:18:03 +02:00
auto Painting = std :: make_unique < cPainting > ( a_NBT . GetString ( MotiveTag ), BLOCK_FACE_NONE , Vector3d ());
2015-03-13 23:05:06 +00:00
if ( ! LoadEntityBaseFromNBT ( * Painting . get (), a_NBT , a_TagIdx ))
{
return ;
}
LoadHangingFromNBT ( * Painting . get (), a_NBT , a_TagIdx );
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Painting ));
2015-03-13 23:05:06 +00:00
}
2013-08-25 21:32:17 +02:00
void cWSSAnvil :: LoadArrowFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto Arrow = std :: make_unique < cArrowEntity > ( nullptr , Vector3d (), Vector3d ());
2013-09-07 21:26:17 +02:00
if ( ! LoadProjectileBaseFromNBT ( * Arrow . get (), a_NBT , a_TagIdx ))
2013-08-25 21:32:17 +02:00
{
return ;
}
2016-02-05 23:45:45 +02:00
2013-08-25 21:32:17 +02:00
// Load pickup state:
int PickupIdx = a_NBT . FindChildByName ( a_TagIdx , "pickup" );
2014-09-25 19:01:44 +02:00
if (( PickupIdx > 0 ) && ( a_NBT . GetType ( PickupIdx ) == TAG_Byte ))
2013-08-25 21:32:17 +02:00
{
2015-07-29 09:04:03 -06:00
Arrow -> SetPickupState ( static_cast < cArrowEntity :: ePickupState > ( a_NBT . GetByte ( PickupIdx )));
2013-08-25 21:32:17 +02:00
}
else
{
// Try the older "player" tag:
int PlayerIdx = a_NBT . FindChildByName ( a_TagIdx , "player" );
2014-09-25 19:01:44 +02:00
if (( PlayerIdx > 0 ) && ( a_NBT . GetType ( PlayerIdx ) == TAG_Byte ))
2013-08-25 21:32:17 +02:00
{
Arrow -> SetPickupState (( a_NBT . GetByte ( PlayerIdx ) == 0 ) ? cArrowEntity :: psNoPickup : cArrowEntity :: psInSurvivalOrCreative );
}
}
2016-02-05 23:45:45 +02:00
2013-08-25 21:32:17 +02:00
// Load damage:
int DamageIdx = a_NBT . FindChildByName ( a_TagIdx , "damage" );
2014-09-25 19:01:44 +02:00
if (( DamageIdx > 0 ) && ( a_NBT . GetType ( DamageIdx ) == TAG_Double ))
2013-08-25 21:32:17 +02:00
{
Arrow -> SetDamageCoeff ( a_NBT . GetDouble ( DamageIdx ));
}
2016-02-05 23:45:45 +02:00
2014-07-02 21:07:34 +01:00
// Load block hit:
int InBlockXIdx = a_NBT . FindChildByName ( a_TagIdx , "xTile" );
int InBlockYIdx = a_NBT . FindChildByName ( a_TagIdx , "yTile" );
int InBlockZIdx = a_NBT . FindChildByName ( a_TagIdx , "zTile" );
if (( InBlockXIdx > 0 ) && ( InBlockYIdx > 0 ) && ( InBlockZIdx > 0 ))
{
2014-11-26 10:14:11 +01:00
eTagType typeX = a_NBT . GetType ( InBlockXIdx );
if (( typeX == a_NBT . GetType ( InBlockYIdx )) && ( typeX == a_NBT . GetType ( InBlockZIdx )))
2014-09-25 19:01:44 +02:00
{
2014-11-26 10:14:11 +01:00
switch ( typeX )
2014-09-25 19:01:44 +02:00
{
case TAG_Int :
{
// Old MCS code used this, keep reading it for compatibility reasons:
Arrow -> SetBlockHit ( Vector3i ( a_NBT . GetInt ( InBlockXIdx ), a_NBT . GetInt ( InBlockYIdx ), a_NBT . GetInt ( InBlockZIdx )));
break ;
}
case TAG_Short :
{
// Vanilla uses this
2015-06-24 16:38:40 +02:00
Arrow -> SetBlockHit ( Vector3i ( a_NBT . GetShort ( InBlockXIdx ), a_NBT . GetShort ( InBlockYIdx ), a_NBT . GetShort ( InBlockZIdx )));
2014-09-25 19:01:44 +02:00
break ;
}
2014-11-26 10:14:11 +01:00
default :
{
// No hit block, the arrow is still flying?
break ;
}
2014-09-25 19:01:44 +02:00
}
}
2014-07-02 21:07:34 +01:00
}
2016-02-05 23:45:45 +02:00
2013-08-25 21:32:17 +02:00
// Store the new arrow in the entities list:
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Arrow ));
2013-08-25 21:32:17 +02:00
}
2018-07-26 22:24:36 +01:00
2014-07-11 18:58:11 -07:00
void cWSSAnvil :: LoadSplashPotionFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto SplashPotion = std :: make_unique < cSplashPotionEntity > ( nullptr , Vector3d (), Vector3d (), cItem ());
2014-07-11 18:58:11 -07:00
if ( ! LoadProjectileBaseFromNBT ( * SplashPotion . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-07-11 18:58:11 -07:00
int EffectDuration = a_NBT . FindChildByName ( a_TagIdx , "EffectDuration" );
int EffectIntensity = a_NBT . FindChildByName ( a_TagIdx , "EffectIntensity" );
int EffectDistanceModifier = a_NBT . FindChildByName ( a_TagIdx , "EffectDistanceModifier" );
2016-02-05 23:45:45 +02:00
2015-07-29 09:04:03 -06:00
SplashPotion -> SetEntityEffectType ( static_cast < cEntityEffect :: eType > ( a_NBT . FindChildByName ( a_TagIdx , "EffectType" )));
SplashPotion -> SetEntityEffect ( cEntityEffect ( EffectDuration , static_cast < Int16 > ( EffectIntensity ), EffectDistanceModifier ));
2014-07-19 02:51:39 -07:00
SplashPotion -> SetPotionColor ( a_NBT . FindChildByName ( a_TagIdx , "PotionName" ));
2016-02-05 23:45:45 +02:00
2014-07-11 18:58:11 -07:00
// Store the new splash potion in the entities list:
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( SplashPotion ));
2014-07-11 18:58:11 -07:00
}
2013-08-25 21:32:17 +02:00
2013-09-07 21:26:17 +02:00
void cWSSAnvil :: LoadSnowballFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto Snowball = std :: make_unique < cThrownSnowballEntity > ( nullptr , Vector3d (), Vector3d ());
2013-09-07 21:26:17 +02:00
if ( ! LoadProjectileBaseFromNBT ( * Snowball . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2013-09-07 21:26:17 +02:00
// Store the new snowball in the entities list:
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Snowball ));
2013-09-07 21:26:17 +02:00
}
void cWSSAnvil :: LoadEggFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto Egg = std :: make_unique < cThrownEggEntity > ( nullptr , Vector3d (), Vector3d ());
2013-09-07 21:26:17 +02:00
if ( ! LoadProjectileBaseFromNBT ( * Egg . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2013-09-07 21:26:17 +02:00
// Store the new egg in the entities list:
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Egg ));
2013-09-07 21:26:17 +02:00
}
void cWSSAnvil :: LoadFireballFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto Fireball = std :: make_unique < cGhastFireballEntity > ( nullptr , Vector3d (), Vector3d ());
2013-09-07 21:26:17 +02:00
if ( ! LoadProjectileBaseFromNBT ( * Fireball . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2013-09-07 21:26:17 +02:00
// Store the new fireball in the entities list:
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Fireball ));
2013-09-07 21:26:17 +02:00
}
void cWSSAnvil :: LoadFireChargeFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto FireCharge = std :: make_unique < cFireChargeEntity > ( nullptr , Vector3d (), Vector3d ());
2013-09-07 21:26:17 +02:00
if ( ! LoadProjectileBaseFromNBT ( * FireCharge . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2013-09-07 21:26:17 +02:00
// Store the new FireCharge in the entities list:
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( FireCharge ));
2013-09-07 21:26:17 +02:00
}
void cWSSAnvil :: LoadThrownEnderpearlFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto Enderpearl = std :: make_unique < cThrownEnderPearlEntity > ( nullptr , Vector3d (), Vector3d ());
2013-09-07 21:26:17 +02:00
if ( ! LoadProjectileBaseFromNBT ( * Enderpearl . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2013-09-07 21:26:17 +02:00
// Store the new enderpearl in the entities list:
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Enderpearl ));
2013-09-07 21:26:17 +02:00
}
2013-12-25 16:07:52 +00:00
void cWSSAnvil :: LoadBatFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto Monster = std :: make_unique < cBat > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadBlazeFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cBlaze > Monster = std :: make_unique < cBlaze > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadCaveSpiderFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cCaveSpider > Monster = std :: make_unique < cCaveSpider > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadChickenFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cChicken > Monster = std :: make_unique < cChicken > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadCowFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cCow > Monster = std :: make_unique < cCow > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadCreeperFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cCreeper > Monster = std :: make_unique < cCreeper > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadEnderDragonFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cEnderDragon > Monster = std :: make_unique < cEnderDragon > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadEndermanFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cEnderman > Monster = std :: make_unique < cEnderman > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadGhastFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cGhast > Monster = std :: make_unique < cGhast > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadGiantFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cGiant > Monster = std :: make_unique < cGiant > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
2014-12-18 19:30:32 +01:00
void cWSSAnvil :: LoadGuardianFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cGuardian > Monster = std :: make_unique < cGuardian > ();
2014-12-18 19:30:32 +01:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-12-18 19:30:32 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2014-12-18 19:30:32 +01:00
}
2013-12-25 16:07:52 +00:00
void cWSSAnvil :: LoadHorseFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2014-12-05 16:59:11 +01:00
int TypeIdx = a_NBT . FindChildByName ( a_TagIdx , "Type" );
2013-12-25 16:07:52 +00:00
int ColorIdx = a_NBT . FindChildByName ( a_TagIdx , "Color" );
int StyleIdx = a_NBT . FindChildByName ( a_TagIdx , "Style" );
2014-12-05 16:59:11 +01:00
if (( TypeIdx < 0 ) || ( ColorIdx < 0 ) || ( StyleIdx < 0 ))
{
return ;
}
2013-12-25 16:07:52 +00:00
int Type = a_NBT . GetInt ( TypeIdx );
int Color = a_NBT . GetInt ( ColorIdx );
int Style = a_NBT . GetInt ( StyleIdx );
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cHorse > Monster = std :: make_unique < cHorse > ( Type , Color , Style , 1 );
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2015-07-12 18:00:56 +02:00
int AgeableIdx = a_NBT . FindChildByName ( a_TagIdx , "Age" );
if ( AgeableIdx > 0 )
{
2015-08-24 23:05:15 +02:00
int Age ;
switch ( a_NBT . GetType ( AgeableIdx ))
{
case TAG_Byte : Age = static_cast < int > ( a_NBT . GetByte ( AgeableIdx )); break ;
case TAG_Int : Age = a_NBT . GetInt ( AgeableIdx ); break ;
default : Age = 0 ; break ;
}
Monster -> SetAge ( Age );
2015-07-12 18:00:56 +02:00
}
2016-02-05 23:45:45 +02:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadIronGolemFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cIronGolem > Monster = std :: make_unique < cIronGolem > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadMagmaCubeFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
int SizeIdx = a_NBT . FindChildByName ( a_TagIdx , "Size" );
2014-05-07 12:43:21 +02:00
if ( SizeIdx < 0 )
{
return ;
}
2013-12-25 16:07:52 +00:00
int Size = a_NBT . GetInt ( SizeIdx );
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cMagmaCube > Monster = std :: make_unique < cMagmaCube > ( Size );
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadMooshroomFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cMooshroom > Monster = std :: make_unique < cMooshroom > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadOcelotFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cOcelot > Monster = std :: make_unique < cOcelot > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2017-07-12 12:42:02 +02:00
auto OwnerInfo = LoadEntityOwner ( a_NBT , a_TagIdx );
2017-08-25 13:43:18 +01:00
if ( ! OwnerInfo . first . empty () && ! OwnerInfo . second . IsNil ())
2017-07-12 12:42:02 +02:00
{
Monster -> SetOwner ( OwnerInfo . first , OwnerInfo . second );
Monster -> SetIsTame ( true );
}
int TypeIdx = a_NBT . FindChildByName ( a_TagIdx , "CatType" );
if ( TypeIdx > 0 )
{
int Type = a_NBT . GetInt ( TypeIdx );
Monster -> SetCatType ( static_cast < cOcelot :: eCatType > ( Type ));
}
int SittingIdx = a_NBT . FindChildByName ( a_TagIdx , "Sitting" );
if (( SittingIdx > 0 ) && ( a_NBT . GetType ( SittingIdx ) == TAG_Byte ))
{
2020-05-14 23:15:35 +01:00
bool Sitting = ( a_NBT . GetByte ( SittingIdx ) == 1 );
2017-07-12 12:42:02 +02:00
Monster -> SetIsSitting ( Sitting );
}
2015-07-12 18:00:56 +02:00
int AgeableIdx = a_NBT . FindChildByName ( a_TagIdx , "Age" );
if ( AgeableIdx > 0 )
{
2015-08-24 23:05:15 +02:00
int Age ;
switch ( a_NBT . GetType ( AgeableIdx ))
{
case TAG_Byte : Age = static_cast < int > ( a_NBT . GetByte ( AgeableIdx )); break ;
case TAG_Int : Age = a_NBT . GetInt ( AgeableIdx ); break ;
default : Age = 0 ; break ;
}
Monster -> SetAge ( Age );
2015-07-12 18:00:56 +02:00
}
2016-02-05 23:45:45 +02:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadPigFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cPig > Monster = std :: make_unique < cPig > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2015-07-12 18:00:56 +02:00
int AgeableIdx = a_NBT . FindChildByName ( a_TagIdx , "Age" );
if ( AgeableIdx > 0 )
{
2015-08-24 23:05:15 +02:00
int Age ;
switch ( a_NBT . GetType ( AgeableIdx ))
{
case TAG_Byte : Age = static_cast < int > ( a_NBT . GetByte ( AgeableIdx )); break ;
case TAG_Int : Age = a_NBT . GetInt ( AgeableIdx ); break ;
default : Age = 0 ; break ;
}
Monster -> SetAge ( Age );
2015-07-12 18:00:56 +02:00
}
2016-02-05 23:45:45 +02:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
2014-12-20 10:31:34 +01:00
void cWSSAnvil :: LoadRabbitFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2015-07-17 01:09:06 +02:00
int TypeIdx = a_NBT . FindChildByName ( a_TagIdx , "RabbitType" );
int MoreCarrotTicksIdx = a_NBT . FindChildByName ( a_TagIdx , "MoreCarrotTicks" );
if (( TypeIdx < 0 ) || ( MoreCarrotTicksIdx < 0 ))
{
return ;
}
int Type = a_NBT . GetInt ( TypeIdx );
int MoreCarrotTicks = a_NBT . GetInt ( MoreCarrotTicksIdx );
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cRabbit > Monster = std :: make_unique < cRabbit > ( static_cast < eRabbitType > ( Type ), MoreCarrotTicks );
2014-12-20 10:31:34 +01:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-12-20 10:31:34 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2015-07-12 18:00:56 +02:00
int AgeableIdx = a_NBT . FindChildByName ( a_TagIdx , "Age" );
if ( AgeableIdx > 0 )
{
2015-08-24 23:05:15 +02:00
int Age ;
switch ( a_NBT . GetType ( AgeableIdx ))
{
case TAG_Byte : Age = static_cast < int > ( a_NBT . GetByte ( AgeableIdx )); break ;
case TAG_Int : Age = a_NBT . GetInt ( AgeableIdx ); break ;
default : Age = 0 ; break ;
}
Monster -> SetAge ( Age );
2015-07-12 18:00:56 +02:00
}
2016-02-05 23:45:45 +02:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2014-12-20 10:31:34 +01:00
}
2013-12-25 16:07:52 +00:00
void cWSSAnvil :: LoadSheepFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
int ColorIdx = a_NBT . FindChildByName ( a_TagIdx , "Color" );
2014-06-28 13:19:32 +02:00
int Color = - 1 ;
if ( ColorIdx > 0 )
{
2015-07-29 09:04:03 -06:00
Color = static_cast < int > ( a_NBT . GetByte ( ColorIdx ));
2014-06-28 13:19:32 +02:00
}
2013-12-25 16:07:52 +00:00
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cSheep > Monster = std :: make_unique < cSheep > ( Color );
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2014-06-28 13:19:32 +02:00
int ShearedIdx = a_NBT . FindChildByName ( a_TagIdx , "Sheared" );
if ( ShearedIdx > 0 )
{
2014-06-29 01:01:10 +02:00
Monster -> SetSheared ( a_NBT . GetByte ( ShearedIdx ) != 0 );
2014-06-28 13:19:32 +02:00
}
2016-02-05 23:45:45 +02:00
2015-07-12 18:00:56 +02:00
int AgeableIdx = a_NBT . FindChildByName ( a_TagIdx , "Age" );
if ( AgeableIdx > 0 )
{
2015-08-24 23:05:15 +02:00
int Age ;
switch ( a_NBT . GetType ( AgeableIdx ))
{
case TAG_Byte : Age = static_cast < int > ( a_NBT . GetByte ( AgeableIdx )); break ;
case TAG_Int : Age = a_NBT . GetInt ( AgeableIdx ); break ;
default : Age = 0 ; break ;
}
Monster -> SetAge ( Age );
2015-07-12 18:00:56 +02:00
}
2016-02-05 23:45:45 +02:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadSilverfishFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cSilverfish > Monster = std :: make_unique < cSilverfish > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadSkeletonFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-04-04 14:44:17 +03:00
// Wither skeleton is a separate mob in Minecraft 1.11+, but we need this to
// load them from older worlds where wither skeletons were only a skeleton with a flag
2013-12-25 16:07:52 +00:00
int TypeIdx = a_NBT . FindChildByName ( a_TagIdx , "SkeletonType" );
2020-04-04 14:44:17 +03:00
std :: unique_ptr < cMonster > Monster ;
if (( TypeIdx > 0 ) && ( a_NBT . GetByte ( TypeIdx ) == 1 ))
2014-12-05 16:59:11 +01:00
{
2020-08-01 20:18:03 +02:00
Monster = std :: make_unique < cWitherSkeleton > ();
2020-04-04 14:44:17 +03:00
}
else
{
2020-08-01 20:18:03 +02:00
Monster = std :: make_unique < cSkeleton > ();
2014-12-05 16:59:11 +01:00
}
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadSlimeFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
int SizeIdx = a_NBT . FindChildByName ( a_TagIdx , "Size" );
2014-05-07 12:43:21 +02:00
if ( SizeIdx < 0 )
{
return ;
}
2013-12-25 16:07:52 +00:00
int Size = a_NBT . GetInt ( SizeIdx );
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cSlime > Monster = std :: make_unique < cSlime > ( Size );
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadSnowGolemFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cSnowGolem > Monster = std :: make_unique < cSnowGolem > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadSpiderFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cSpider > Monster = std :: make_unique < cSpider > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadSquidFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cSquid > Monster = std :: make_unique < cSquid > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadVillagerFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
int TypeIdx = a_NBT . FindChildByName ( a_TagIdx , "Profession" );
2014-12-05 16:59:11 +01:00
if ( TypeIdx < 0 )
{
return ;
}
2013-12-25 16:07:52 +00:00
int Type = a_NBT . GetInt ( TypeIdx );
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cVillager > Monster = std :: make_unique < cVillager > ( cVillager :: eVillagerType ( Type ));
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2015-07-12 18:00:56 +02:00
int AgeableIdx = a_NBT . FindChildByName ( a_TagIdx , "Age" );
if ( AgeableIdx > 0 )
{
2015-08-24 23:05:15 +02:00
int Age ;
switch ( a_NBT . GetType ( AgeableIdx ))
{
case TAG_Byte : Age = static_cast < int > ( a_NBT . GetByte ( AgeableIdx )); break ;
case TAG_Int : Age = a_NBT . GetInt ( AgeableIdx ); break ;
default : Age = 0 ; break ;
}
Monster -> SetAge ( Age );
2015-07-12 18:00:56 +02:00
}
2016-02-05 23:45:45 +02:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadWitchFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cWitch > Monster = std :: make_unique < cWitch > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadWitherFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cWither > Monster = std :: make_unique < cWither > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2013-12-25 16:07:52 +00:00
2014-03-24 12:29:19 +02:00
int CurrLine = a_NBT . FindChildByName ( a_TagIdx , "Invul" );
if ( CurrLine > 0 )
{
2015-07-29 09:04:03 -06:00
Monster -> SetWitherInvulnerableTicks ( static_cast < unsigned int > ( a_NBT . GetInt ( CurrLine )));
2014-03-24 12:29:19 +02:00
}
2020-04-04 14:44:17 +03:00
a_Entities . emplace_back ( std :: move ( Monster ));
}
void cWSSAnvil :: LoadWitherSkeletonFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
auto Monster = std :: make_unique < cWitherSkeleton > ();
2020-04-04 14:44:17 +03:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadWolfFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cWolf > Monster = std :: make_unique < cWolf > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2017-07-12 12:42:02 +02:00
auto OwnerInfo = LoadEntityOwner ( a_NBT , a_TagIdx );
2017-08-25 13:43:18 +01:00
if ( ! OwnerInfo . first . empty () && ! OwnerInfo . second . IsNil ())
2017-07-12 12:42:02 +02:00
{
Monster -> SetOwner ( OwnerInfo . first , OwnerInfo . second );
Monster -> SetIsTame ( true );
}
2014-08-03 22:03:48 +02:00
2014-02-02 13:56:55 +04:00
int SittingIdx = a_NBT . FindChildByName ( a_TagIdx , "Sitting" );
2014-09-23 22:11:25 +02:00
if (( SittingIdx > 0 ) && ( a_NBT . GetType ( SittingIdx ) == TAG_Byte ))
2014-01-31 21:34:00 +04:00
{
2020-05-14 23:15:35 +01:00
bool Sitting = ( a_NBT . GetByte ( SittingIdx ) == 1 );
2014-02-02 13:56:55 +04:00
Monster -> SetIsSitting ( Sitting );
2014-01-31 21:34:00 +04:00
}
2014-02-02 14:28:42 +04:00
int AngryIdx = a_NBT . FindChildByName ( a_TagIdx , "Angry" );
2014-09-23 22:11:25 +02:00
if (( AngryIdx > 0 ) && ( a_NBT . GetType ( AngryIdx ) == TAG_Byte ))
2014-02-02 14:28:42 +04:00
{
2020-05-14 23:15:35 +01:00
bool Angry = ( a_NBT . GetByte ( AngryIdx ) == 1 );
2014-02-02 14:28:42 +04:00
Monster -> SetIsAngry ( Angry );
}
2014-02-01 20:16:42 +04:00
int CollarColorIdx = a_NBT . FindChildByName ( a_TagIdx , "CollarColor" );
2014-09-23 22:22:38 +02:00
if ( CollarColorIdx > 0 )
2014-02-01 20:16:42 +04:00
{
2014-09-23 22:22:38 +02:00
switch ( a_NBT . GetType ( CollarColorIdx ))
{
case TAG_Byte :
{
// Vanilla uses this
2016-08-24 21:45:03 +02:00
Monster -> SetCollarColor ( a_NBT . GetByte ( CollarColorIdx ));
2014-09-23 22:22:38 +02:00
break ;
}
case TAG_Int :
{
// Old MCS code used this, keep reading it for compatibility reasons:
Monster -> SetCollarColor ( a_NBT . GetInt ( CollarColorIdx ));
break ;
}
2016-08-24 21:45:03 +02:00
default :
{
// No other values are interpreted
break ;
}
2014-09-23 22:22:38 +02:00
}
2014-02-01 20:16:42 +04:00
}
2016-02-05 23:45:45 +02:00
2015-07-12 18:00:56 +02:00
int AgeableIdx = a_NBT . FindChildByName ( a_TagIdx , "Age" );
if ( AgeableIdx > 0 )
{
2015-08-24 23:05:15 +02:00
int Age ;
switch ( a_NBT . GetType ( AgeableIdx ))
{
case TAG_Byte : Age = static_cast < int > ( a_NBT . GetByte ( AgeableIdx )); break ;
case TAG_Int : Age = a_NBT . GetInt ( AgeableIdx ); break ;
default : Age = 0 ; break ;
}
Monster -> SetAge ( Age );
2015-07-12 18:00:56 +02:00
}
2016-02-05 23:45:45 +02:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
void cWSSAnvil :: LoadZombieFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cZombie > Monster = std :: make_unique < cZombie > ();
2018-02-03 12:24:19 +01:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
2014-12-05 16:59:11 +01:00
{
return ;
}
2013-12-25 16:07:52 +00:00
2018-02-03 12:24:19 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
int AgeableIdx = a_NBT . FindChildByName ( a_TagIdx , "Age" );
if ( AgeableIdx > 0 )
{
int Age ;
switch ( a_NBT . GetType ( AgeableIdx ))
{
case TAG_Byte : Age = static_cast < int > ( a_NBT . GetByte ( AgeableIdx )); break ;
case TAG_Int : Age = a_NBT . GetInt ( AgeableIdx ); break ;
default : Age = 0 ; break ;
}
Monster -> SetAge ( Age );
}
a_Entities . emplace_back ( std :: move ( Monster ));
}
2013-12-25 16:07:52 +00:00
2018-02-03 12:24:19 +01:00
void cWSSAnvil :: LoadPigZombieFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
{
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cZombiePigman > Monster = std :: make_unique < cZombiePigman > ();
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2015-07-12 18:00:56 +02:00
int AgeableIdx = a_NBT . FindChildByName ( a_TagIdx , "Age" );
if ( AgeableIdx > 0 )
{
2015-08-24 23:05:15 +02:00
int Age ;
switch ( a_NBT . GetType ( AgeableIdx ))
{
case TAG_Byte : Age = static_cast < int > ( a_NBT . GetByte ( AgeableIdx )); break ;
case TAG_Int : Age = a_NBT . GetInt ( AgeableIdx ); break ;
default : Age = 0 ; break ;
}
Monster -> SetAge ( Age );
2015-07-12 18:00:56 +02:00
}
2016-02-05 23:45:45 +02:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
2018-02-03 12:24:19 +01:00
void cWSSAnvil :: LoadZombieVillagerFromNBT ( cEntityList & a_Entities , const cParsedNBT & a_NBT , int a_TagIdx )
2013-12-25 16:07:52 +00:00
{
2018-02-03 12:24:19 +01:00
int ProfessionIdx = a_NBT . FindChildByName ( a_TagIdx , "Profession" );
if ( ProfessionIdx < 0 )
{
return ;
}
cVillager :: eVillagerType Profession = static_cast < cVillager :: eVillagerType > ( a_NBT . GetInt ( ProfessionIdx ));
2020-08-01 20:18:03 +02:00
std :: unique_ptr < cZombieVillager > Monster = std :: make_unique < cZombieVillager > ( Profession );
2013-12-25 16:07:52 +00:00
if ( ! LoadEntityBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2014-02-23 19:44:58 +01:00
if ( ! LoadMonsterBaseFromNBT ( * Monster . get (), a_NBT , a_TagIdx ))
{
return ;
}
2016-02-05 23:45:45 +02:00
2018-02-03 12:24:19 +01:00
// TODO: Conversion time
2015-07-12 18:00:56 +02:00
int AgeableIdx = a_NBT . FindChildByName ( a_TagIdx , "Age" );
if ( AgeableIdx > 0 )
{
2015-08-24 23:05:15 +02:00
int Age ;
switch ( a_NBT . GetType ( AgeableIdx ))
{
case TAG_Byte : Age = static_cast < int > ( a_NBT . GetByte ( AgeableIdx )); break ;
case TAG_Int : Age = a_NBT . GetInt ( AgeableIdx ); break ;
2018-02-03 12:24:19 +01:00
default : Age = 0 ; break ;
2015-08-24 23:05:15 +02:00
}
Monster -> SetAge ( Age );
2015-07-12 18:00:56 +02:00
}
2016-02-05 23:45:45 +02:00
2016-12-19 20:12:23 +00:00
a_Entities . emplace_back ( std :: move ( Monster ));
2013-12-25 16:07:52 +00:00
}
2017-08-25 13:43:18 +01:00
std :: pair < AString , cUUID > cWSSAnvil :: LoadEntityOwner ( const cParsedNBT & a_NBT , int a_TagIdx )
2014-08-03 22:03:48 +02:00
{
// Load the owner information. OwnerUUID or Owner may be specified, possibly both:
2017-08-25 13:43:18 +01:00
AString OwnerName ;
cUUID OwnerUUID ;
2014-08-03 22:03:48 +02:00
int OwnerUUIDIdx = a_NBT . FindChildByName ( a_TagIdx , "OwnerUUID" );
if ( OwnerUUIDIdx > 0 )
{
2017-08-25 13:43:18 +01:00
OwnerUUID . FromString ( a_NBT . GetString ( OwnerUUIDIdx ));
2014-08-03 22:03:48 +02:00
}
int OwnerIdx = a_NBT . FindChildByName ( a_TagIdx , "Owner" );
if ( OwnerIdx > 0 )
{
OwnerName = a_NBT . GetString ( OwnerIdx );
}
2017-08-25 13:43:18 +01:00
if ( OwnerName . empty () && OwnerUUID . IsNil ())
2014-08-03 22:03:48 +02:00
{
// There is no owner, bail out:
2017-08-25 13:43:18 +01:00
return {};
2014-08-03 22:03:48 +02:00
}
2016-02-05 23:45:45 +02:00
2014-08-03 22:03:48 +02:00
// Convert name to UUID, if needed:
2017-08-25 13:43:18 +01:00
if ( OwnerUUID . IsNil ())
2014-08-03 22:03:48 +02:00
{
2017-07-12 12:42:02 +02:00
// This entity has only playername stored (pre-1.7.6), look up the UUID
2014-08-03 22:03:48 +02:00
// The lookup is blocking, but we're running in a separate thread, so it's ok
OwnerUUID = cRoot :: Get () -> GetMojangAPI (). GetUUIDFromPlayerName ( OwnerName );
2017-08-25 13:43:18 +01:00
if ( OwnerUUID . IsNil ())
2014-08-03 22:03:48 +02:00
{
2017-07-12 12:42:02 +02:00
// Not a known player, un-tame the entity by bailing out
2017-08-25 13:43:18 +01:00
return {};
2014-08-03 22:03:48 +02:00
}
}
2016-02-05 23:45:45 +02:00
2014-08-03 22:03:48 +02:00
// Convert UUID to name, if needed:
if ( OwnerName . empty ())
{
// The lookup is blocking, but we're running in a separate thread, so it's ok
OwnerName = cRoot :: Get () -> GetMojangAPI (). GetPlayerNameFromUUID ( OwnerUUID );
if ( OwnerName . empty ())
{
2017-07-12 12:42:02 +02:00
// Not a known player, un-tame the entity by bailing out
2017-08-25 13:43:18 +01:00
return {};
2014-08-03 22:03:48 +02:00
}
}
2016-02-05 23:45:45 +02:00
2017-08-25 13:43:18 +01:00
return { OwnerName , OwnerUUID };
2014-08-03 22:03:48 +02:00
}
2013-03-09 14:35:43 +00:00
bool cWSSAnvil :: LoadEntityBaseFromNBT ( cEntity & a_Entity , const cParsedNBT & a_NBT , int a_TagIdx )
{
double Pos [ 3 ];
if ( ! LoadDoublesListFromNBT ( Pos , 3 , a_NBT , a_NBT . FindChildByName ( a_TagIdx , "Pos" )))
{
return false ;
}
a_Entity . SetPosition ( Pos [ 0 ], Pos [ 1 ], Pos [ 2 ]);
2016-02-05 23:45:45 +02:00
2013-03-09 14:35:43 +00:00
double Speed [ 3 ];
if ( ! LoadDoublesListFromNBT ( Speed , 3 , a_NBT , a_NBT . FindChildByName ( a_TagIdx , "Motion" )))
{
2014-01-24 09:57:12 +01:00
// Provide default speed:
Speed [ 0 ] = 0 ;
Speed [ 1 ] = 0 ;
Speed [ 2 ] = 0 ;
2013-03-09 14:35:43 +00:00
}
a_Entity . SetSpeed ( Speed [ 0 ], Speed [ 1 ], Speed [ 2 ]);
2016-02-05 23:45:45 +02:00
2013-03-09 14:35:43 +00:00
double Rotation [ 3 ];
if ( ! LoadDoublesListFromNBT ( Rotation , 2 , a_NBT , a_NBT . FindChildByName ( a_TagIdx , "Rotation" )))
{
2014-01-24 09:57:12 +01:00
// Provide default rotation:
Rotation [ 0 ] = 0 ;
Rotation [ 1 ] = 0 ;
2013-03-09 14:35:43 +00:00
}
2014-01-16 19:00:49 +00:00
a_Entity . SetYaw ( Rotation [ 0 ]);
2014-01-20 16:10:39 +02:00
a_Entity . SetRoll ( Rotation [ 1 ]);
2014-06-24 16:06:26 +02:00
2017-11-22 14:47:52 +01:00
// Depending on the Minecraft version, the entity's health is
// stored either as a float Health tag (HealF prior to 1.9) or
// as a short Health tag. The float tags should be preferred.
2014-06-24 16:06:26 +02:00
int Health = a_NBT . FindChildByName ( a_TagIdx , "Health" );
2017-11-22 14:47:52 +01:00
int HealF = a_NBT . FindChildByName ( a_TagIdx , "HealF" );
if ( Health > 0 && a_NBT . GetType ( Health ) == TAG_Float )
{
a_Entity . SetHealth ( a_NBT . GetFloat ( Health ));
}
else if ( HealF > 0 )
{
a_Entity . SetHealth ( a_NBT . GetFloat ( HealF ));
}
else if ( Health > 0 )
{
a_Entity . SetHealth ( static_cast < float > ( a_NBT . GetShort ( Health )));
}
else
{
a_Entity . SetHealth ( a_Entity . GetMaxHealth ());
}
2016-02-05 23:45:45 +02:00
2013-03-09 14:35:43 +00:00
return true ;
}
2014-02-23 19:44:58 +01:00
bool cWSSAnvil :: LoadMonsterBaseFromNBT ( cMonster & a_Monster , const cParsedNBT & a_NBT , int a_TagIdx )
{
float DropChance [ 5 ];
2017-06-22 17:30:11 +02:00
if ( LoadFloatsListFromNBT ( DropChance , 5 , a_NBT , a_NBT . FindChildByName ( a_TagIdx , "DropChances" )))
2014-02-23 19:44:58 +01:00
{
2017-06-22 17:30:11 +02:00
a_Monster . SetDropChanceWeapon ( DropChance [ 0 ]);
a_Monster . SetDropChanceHelmet ( DropChance [ 1 ]);
a_Monster . SetDropChanceChestplate ( DropChance [ 2 ]);
a_Monster . SetDropChanceLeggings ( DropChance [ 3 ]);
a_Monster . SetDropChanceBoots ( DropChance [ 4 ]);
}
if ( LoadFloatsListFromNBT ( DropChance , 2 , a_NBT , a_NBT . FindChildByName ( a_TagIdx , "HandDropChances" )))
{
a_Monster . SetDropChanceWeapon ( DropChance [ 0 ]);
}
if ( LoadFloatsListFromNBT ( DropChance , 4 , a_NBT , a_NBT . FindChildByName ( a_TagIdx , "ArmorDropChances" )))
{
a_Monster . SetDropChanceHelmet ( DropChance [ 0 ]);
a_Monster . SetDropChanceChestplate ( DropChance [ 1 ]);
a_Monster . SetDropChanceLeggings ( DropChance [ 2 ]);
a_Monster . SetDropChanceBoots ( DropChance [ 3 ]);
2014-02-23 19:44:58 +01:00
}
2014-06-04 20:52:54 +01:00
int LootTag = a_NBT . FindChildByName ( a_TagIdx , "CanPickUpLoot" );
if ( LootTag > 0 )
{
bool CanPickUpLoot = ( a_NBT . GetByte ( LootTag ) == 1 );
a_Monster . SetCanPickUpLoot ( CanPickUpLoot );
}
2014-09-02 19:34:58 +02:00
int CustomNameTag = a_NBT . FindChildByName ( a_TagIdx , "CustomName" );
if (( CustomNameTag > 0 ) && ( a_NBT . GetType ( CustomNameTag ) == TAG_String ))
{
a_Monster . SetCustomName ( a_NBT . GetString ( CustomNameTag ));
}
int CustomNameVisibleTag = a_NBT . FindChildByName ( a_TagIdx , "CustomNameVisible" );
if (( CustomNameVisibleTag > 0 ) && ( a_NBT . GetType ( CustomNameVisibleTag ) == TAG_Byte ))
{
bool CustomNameVisible = ( a_NBT . GetByte ( CustomNameVisibleTag ) == 1 );
a_Monster . SetCustomNameAlwaysVisible ( CustomNameVisible );
}
2017-08-21 10:46:41 +02:00
// Leashed to a knot
int LeashedIdx = a_NBT . FindChildByName ( a_TagIdx , "Leashed" );
if (( LeashedIdx >= 0 ) && a_NBT . GetByte ( LeashedIdx ))
{
LoadLeashToPosition ( a_Monster , a_NBT , a_TagIdx );
}
2014-02-23 19:44:58 +01:00
return true ;
}
2017-08-21 10:46:41 +02:00
void cWSSAnvil :: LoadLeashToPosition ( cMonster & a_Monster , const cParsedNBT & a_NBT , int a_TagIdx )
{
int LeashIdx = a_NBT . FindChildByName ( a_TagIdx , "Leash" );
if ( LeashIdx < 0 )
{
return ;
}
double PosX = 0.0 , PosY = 0.0 , PosZ = 0.0 ;
bool KnotPosPresent = true ;
int LeashDataLine = a_NBT . FindChildByName ( LeashIdx , "X" );
if ( LeashDataLine >= 0 )
{
PosX = a_NBT . GetDouble ( LeashDataLine );
}
else
{
KnotPosPresent = false ;
}
LeashDataLine = a_NBT . FindChildByName ( LeashIdx , "Y" );
if ( LeashDataLine >= 0 )
{
PosY = a_NBT . GetDouble ( LeashDataLine );
}
else
{
KnotPosPresent = false ;
}
LeashDataLine = a_NBT . FindChildByName ( LeashIdx , "Z" );
if ( LeashDataLine >= 0 )
{
PosZ = a_NBT . GetDouble ( LeashDataLine );
}
else
{
KnotPosPresent = false ;
}
if ( KnotPosPresent )
{
// Set leash pos for the mob
a_Monster . SetLeashToPos ( new Vector3d ( PosX , PosY , PosZ ));
}
}
2013-09-07 21:26:17 +02:00
bool cWSSAnvil :: LoadProjectileBaseFromNBT ( cProjectileEntity & a_Entity , const cParsedNBT & a_NBT , int a_TagIdx )
{
if ( ! LoadEntityBaseFromNBT ( a_Entity , a_NBT , a_TagIdx ))
{
return false ;
}
2016-02-05 23:45:45 +02:00
2013-09-07 21:26:17 +02:00
bool IsInGround = false ;
int InGroundIdx = a_NBT . FindChildByName ( a_TagIdx , "inGround" );
if ( InGroundIdx > 0 )
{
IsInGround = ( a_NBT . GetByte ( InGroundIdx ) != 0 );
}
a_Entity . SetIsInGround ( IsInGround );
2016-02-05 23:45:45 +02:00
2013-09-07 21:26:17 +02:00
return true ;
}
2013-03-09 14:35:43 +00:00
bool cWSSAnvil :: LoadDoublesListFromNBT ( double * a_Doubles , int a_NumDoubles , const cParsedNBT & a_NBT , int a_TagIdx )
{
if (( a_TagIdx < 0 ) || ( a_NBT . GetType ( a_TagIdx ) != TAG_List ) || ( a_NBT . GetChildrenType ( a_TagIdx ) != TAG_Double ))
{
return false ;
}
int idx = 0 ;
for ( int Tag = a_NBT . GetFirstChild ( a_TagIdx ); ( Tag > 0 ) && ( idx < a_NumDoubles ); Tag = a_NBT . GetNextSibling ( Tag ), ++ idx )
{
a_Doubles [ idx ] = a_NBT . GetDouble ( Tag );
} // for Tag - PosTag[]
return ( idx == a_NumDoubles ); // Did we read enough doubles?
}
2014-02-23 19:44:58 +01:00
bool cWSSAnvil :: LoadFloatsListFromNBT ( float * a_Floats , int a_NumFloats , const cParsedNBT & a_NBT , int a_TagIdx )
{
if (( a_TagIdx < 0 ) || ( a_NBT . GetType ( a_TagIdx ) != TAG_List ) || ( a_NBT . GetChildrenType ( a_TagIdx ) != TAG_Float ))
{
return false ;
}
int idx = 0 ;
for ( int Tag = a_NBT . GetFirstChild ( a_TagIdx ); ( Tag > 0 ) && ( idx < a_NumFloats ); Tag = a_NBT . GetNextSibling ( Tag ), ++ idx )
{
a_Floats [ idx ] = a_NBT . GetFloat ( Tag );
} // for Tag - PosTag[]
return ( idx == a_NumFloats ); // Did we read enough doubles?
}
2019-09-29 14:59:24 +02:00
bool cWSSAnvil :: GetBlockEntityNBTPos ( const cParsedNBT & a_NBT , int a_TagIdx , Vector3i & a_AbsPos )
2012-06-14 13:06:06 +00:00
{
int x = a_NBT . FindChildByName ( a_TagIdx , "x" );
if (( x < 0 ) || ( a_NBT . GetType ( x ) != TAG_Int ))
{
return false ;
}
int y = a_NBT . FindChildByName ( a_TagIdx , "y" );
if (( y < 0 ) || ( a_NBT . GetType ( y ) != TAG_Int ))
{
return false ;
}
int z = a_NBT . FindChildByName ( a_TagIdx , "z" );
if (( z < 0 ) || ( a_NBT . GetType ( z ) != TAG_Int ))
{
return false ;
}
2019-09-29 14:59:24 +02:00
a_AbsPos . Set (
Clamp ( a_NBT . GetInt ( x ), - 40000000 , 40000000 ), // World is limited to 30M blocks in XZ, we clamp to 40M
Clamp ( a_NBT . GetInt ( y ), - 10000 , 10000 ), // Y is limited to 0 .. 255, we clamp to 10K
Clamp ( a_NBT . GetInt ( z ), - 40000000 , 40000000 )
);
2012-06-14 13:06:06 +00:00
return true ;
}
2014-07-17 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2012-06-14 13:06:06 +00:00
// cWSSAnvil::cMCAFile:
2015-07-28 15:53:28 +02:00
cWSSAnvil :: cMCAFile :: cMCAFile ( cWSSAnvil & a_ParentSchema , const AString & a_FileName , int a_RegionX , int a_RegionZ ) :
m_ParentSchema ( a_ParentSchema ),
2012-06-14 13:06:06 +00:00
m_RegionX ( a_RegionX ),
m_RegionZ ( a_RegionZ ),
m_FileName ( a_FileName )
{
}
bool cWSSAnvil :: cMCAFile :: OpenFile ( bool a_IsForReading )
{
2014-12-08 11:23:19 -08:00
bool writeOutNeeded = false ;
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
if ( m_File . IsOpen ())
{
// Already open
return true ;
}
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
if ( a_IsForReading )
{
if ( ! cFile :: Exists ( m_FileName ))
{
// We want to read and the file doesn't exist. Fail.
return false ;
}
}
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
if ( ! m_File . Open ( m_FileName , cFile :: fmReadWrite ))
{
// The file failed to open
return false ;
}
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
// Load the header:
if ( m_File . Read ( m_Header , sizeof ( m_Header )) != sizeof ( m_Header ))
{
// Cannot read the header - perhaps the file has just been created?
2014-12-08 11:23:19 -08:00
// Try writing a nullptr header for chunk offsets:
2012-06-14 13:06:06 +00:00
memset ( m_Header , 0 , sizeof ( m_Header ));
2014-12-08 11:23:19 -08:00
writeOutNeeded = true ;
2012-06-14 13:06:06 +00:00
}
2014-12-07 14:44:08 -08:00
// Load the TimeStamps:
if ( m_File . Read ( m_TimeStamps , sizeof ( m_TimeStamps )) != sizeof ( m_TimeStamps ))
{
// Cannot read the time stamps - perhaps the file has just been created?
2014-12-08 11:23:19 -08:00
// Try writing a nullptr header for timestamps:
2014-12-07 14:44:08 -08:00
memset ( m_TimeStamps , 0 , sizeof ( m_TimeStamps ));
2014-12-08 11:23:19 -08:00
writeOutNeeded = true ;
}
2016-02-05 23:45:45 +02:00
2014-12-08 11:23:19 -08:00
if ( writeOutNeeded )
{
2014-12-11 14:34:09 +01:00
m_File . Seek ( 0 );
2012-06-14 13:06:06 +00:00
if (
2014-12-11 14:34:09 +01:00
( m_File . Write ( m_Header , sizeof ( m_Header )) != sizeof ( m_Header )) || // Write chunk offsets
( m_File . Write ( m_TimeStamps , sizeof ( m_TimeStamps )) != sizeof ( m_TimeStamps )) // Write chunk timestamps
)
2012-06-14 13:06:06 +00:00
{
LOGWARNING ( "Cannot process MCA header in file \" %s \" , chunks in that file will be lost" , m_FileName . c_str ());
m_File . Close ();
return false ;
}
}
return true ;
}
bool cWSSAnvil :: cMCAFile :: GetChunkData ( const cChunkCoords & a_Chunk , AString & a_Data )
{
if ( ! OpenFile ( true ))
{
return false ;
}
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
int LocalX = a_Chunk . m_ChunkX % 32 ;
if ( LocalX < 0 )
{
LocalX = 32 + LocalX ;
}
int LocalZ = a_Chunk . m_ChunkZ % 32 ;
if ( LocalZ < 0 )
{
LocalZ = 32 + LocalZ ;
}
unsigned ChunkLocation = ntohl ( m_Header [ LocalX + 32 * LocalZ ]);
unsigned ChunkOffset = ChunkLocation >> 8 ;
2014-09-06 13:32:16 +02:00
if ( ChunkOffset < 2 )
2014-09-02 22:40:24 +02:00
{
return false ;
}
2016-02-05 23:45:45 +02:00
2015-06-24 16:38:40 +02:00
m_File . Seek ( static_cast < int > ( ChunkOffset * 4096 ));
2016-02-05 23:45:45 +02:00
2015-06-24 16:38:40 +02:00
UInt32 ChunkSize = 0 ;
2012-06-14 13:06:06 +00:00
if ( m_File . Read ( & ChunkSize , 4 ) != 4 )
{
2015-07-28 15:53:28 +02:00
m_ParentSchema . ChunkLoadFailed ( a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , "Cannot read chunk size" , "" );
2012-06-14 13:06:06 +00:00
return false ;
}
2015-06-24 16:38:40 +02:00
ChunkSize = ntohl ( ChunkSize );
if ( ChunkSize < 1 )
{
// Chunk size too small
2015-07-28 15:53:28 +02:00
m_ParentSchema . ChunkLoadFailed ( a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , "Chunk size too small" , "" );
2015-06-24 16:38:40 +02:00
return false ;
}
2012-06-14 13:06:06 +00:00
char CompressionType = 0 ;
if ( m_File . Read ( & CompressionType , 1 ) != 1 )
{
2015-07-28 15:53:28 +02:00
m_ParentSchema . ChunkLoadFailed ( a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , "Cannot read chunk compression" , "" );
2012-06-14 13:06:06 +00:00
return false ;
}
2015-07-28 15:53:28 +02:00
ChunkSize -- ;
2016-02-05 23:45:45 +02:00
2015-07-28 15:53:28 +02:00
a_Data = m_File . Read ( ChunkSize );
if ( a_Data . size () != ChunkSize )
{
m_ParentSchema . ChunkLoadFailed ( a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , "Cannot read entire chunk data" , a_Data );
return false ;
}
2012-06-14 13:06:06 +00:00
if ( CompressionType != 2 )
{
// Chunk is in an unknown compression
2020-05-14 23:15:35 +01:00
m_ParentSchema . ChunkLoadFailed ( a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , Printf ( "Unknown chunk compression: %d" , CompressionType ), a_Data );
2012-06-14 13:06:06 +00:00
return false ;
}
2020-04-30 08:44:49 +02:00
return true ;
}
2012-06-14 13:06:06 +00:00
bool cWSSAnvil :: cMCAFile :: SetChunkData ( const cChunkCoords & a_Chunk , const AString & a_Data )
{
if ( ! OpenFile ( false ))
{
2013-03-09 14:35:43 +00:00
LOGWARNING ( "Cannot save chunk [%d, %d], opening file \" %s \" failed" , a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , GetFileName (). c_str ());
2012-06-14 13:06:06 +00:00
return false ;
}
int LocalX = a_Chunk . m_ChunkX % 32 ;
if ( LocalX < 0 )
{
LocalX = 32 + LocalX ;
}
int LocalZ = a_Chunk . m_ChunkZ % 32 ;
if ( LocalZ < 0 )
{
LocalZ = 32 + LocalZ ;
}
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
unsigned ChunkSector = FindFreeLocation ( LocalX , LocalZ , a_Data );
// Store the chunk data:
2015-07-29 09:04:03 -06:00
m_File . Seek ( static_cast < int > ( ChunkSector * 4096 ));
2016-08-24 21:45:03 +02:00
UInt32 ChunkSize = htonl ( static_cast < UInt32 > ( a_Data . size () + 1 ));
2012-06-14 13:06:06 +00:00
if ( m_File . Write ( & ChunkSize , 4 ) != 4 )
{
2013-03-09 14:35:43 +00:00
LOGWARNING ( "Cannot save chunk [%d, %d], writing(1) data to file \" %s \" failed" , a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , GetFileName (). c_str ());
2012-06-14 13:06:06 +00:00
return false ;
}
char CompressionType = 2 ;
if ( m_File . Write ( & CompressionType , 1 ) != 1 )
{
2013-03-09 14:35:43 +00:00
LOGWARNING ( "Cannot save chunk [%d, %d], writing(2) data to file \" %s \" failed" , a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , GetFileName (). c_str ());
2012-06-14 13:06:06 +00:00
return false ;
}
2015-07-29 09:04:03 -06:00
if ( m_File . Write ( a_Data . data (), a_Data . size ()) != static_cast < int > ( a_Data . size ()))
2012-06-14 13:06:06 +00:00
{
2013-03-09 14:35:43 +00:00
LOGWARNING ( "Cannot save chunk [%d, %d], writing(3) data to file \" %s \" failed" , a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , GetFileName (). c_str ());
2012-06-14 13:06:06 +00:00
return false ;
}
2016-02-05 23:45:45 +02:00
2014-05-01 18:00:10 +02:00
// Add padding to 4K boundary:
size_t BytesWritten = a_Data . size () + MCA_CHUNK_HEADER_LENGTH ;
2015-02-08 12:24:15 +01:00
if ( BytesWritten % 4096 != 0 )
{
static const char Padding [ 4095 ] = { 0 };
m_File . Write ( Padding , 4096 - ( BytesWritten % 4096 ));
}
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
// Store the header:
2016-08-24 21:45:03 +02:00
ChunkSize = ( static_cast < UInt32 > ( a_Data . size ()) + MCA_CHUNK_HEADER_LENGTH + 4095 ) / 4096 ; // Round data size up to nearest 4KB sector, make it a sector number
2014-09-04 14:00:54 +02:00
if ( ChunkSize > 255 )
{
LOGWARNING ( "Cannot save chunk [%d, %d], the data is too large (%u KiB, maximum is 1024 KiB). Remove some entities and retry." ,
2015-07-09 11:15:37 -06:00
a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , static_cast < unsigned > ( ChunkSize * 4 )
2014-09-04 14:00:54 +02:00
);
return false ;
}
2016-02-05 23:45:45 +02:00
2014-12-07 14:44:08 -08:00
// Store the header info in the table
2016-08-24 21:45:03 +02:00
m_Header [ LocalX + 32 * LocalZ ] = htonl ( static_cast < UInt32 > (( ChunkSector << 8 ) | ChunkSize ));
2014-12-07 14:44:08 -08:00
// Set the modification time
2016-08-24 21:45:03 +02:00
m_TimeStamps [ LocalX + 32 * LocalZ ] = htonl ( static_cast < UInt32 > ( time ( nullptr )));
2014-12-07 14:44:08 -08:00
2013-03-09 14:35:43 +00:00
if ( m_File . Seek ( 0 ) < 0 )
{
LOGWARNING ( "Cannot save chunk [%d, %d], seeking in file \" %s \" failed" , a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , GetFileName (). c_str ());
return false ;
}
2012-06-14 13:06:06 +00:00
if ( m_File . Write ( m_Header , sizeof ( m_Header )) != sizeof ( m_Header ))
{
2013-03-09 14:35:43 +00:00
LOGWARNING ( "Cannot save chunk [%d, %d], writing header to file \" %s \" failed" , a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , GetFileName (). c_str ());
2012-06-14 13:06:06 +00:00
return false ;
}
2014-12-08 11:23:19 -08:00
if ( m_File . Write ( m_TimeStamps , sizeof ( m_TimeStamps )) != sizeof ( m_TimeStamps ))
2014-12-07 14:44:08 -08:00
{
LOGWARNING ( "Cannot save chunk [%d, %d], writing timestamps to file \" %s \" failed" , a_Chunk . m_ChunkX , a_Chunk . m_ChunkZ , GetFileName (). c_str ());
return false ;
}
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
return true ;
}
unsigned cWSSAnvil :: cMCAFile :: FindFreeLocation ( int a_LocalX , int a_LocalZ , const AString & a_Data )
{
// See if it fits the current location:
unsigned ChunkLocation = ntohl ( m_Header [ a_LocalX + 32 * a_LocalZ ]);
unsigned ChunkLen = ChunkLocation & 0xff ;
if ( a_Data . size () + MCA_CHUNK_HEADER_LENGTH <= ( ChunkLen * 4096 ))
{
return ChunkLocation >> 8 ;
}
2016-02-05 23:45:45 +02:00
2012-06-14 13:06:06 +00:00
// Doesn't fit, append to the end of file (we're wasting a lot of space, TODO: fix this later)
unsigned MaxLocation = 2 << 8 ; // Minimum sector is #2 - after the headers
2013-12-20 16:01:34 +01:00
for ( size_t i = 0 ; i < ARRAYCOUNT ( m_Header ); i ++ )
2012-06-14 13:06:06 +00:00
{
ChunkLocation = ntohl ( m_Header [ i ]);
ChunkLocation = ChunkLocation + (( ChunkLocation & 0xff ) << 8 ); // Add the number of sectors used; don't care about the 4th byte
if ( MaxLocation < ChunkLocation )
{
MaxLocation = ChunkLocation ;
}
} // for i - m_Header[]
return MaxLocation >> 8 ;
}
2012-08-26 17:50:42 +00:00