diff options
author | Howaner <franzi.moos@googlemail.com> | 2014-08-30 12:21:39 +0200 |
---|---|---|
committer | Howaner <franzi.moos@googlemail.com> | 2014-08-30 12:21:39 +0200 |
commit | 80bd0a54579cbe5188d42bf8e1e51724d19a6fcf (patch) | |
tree | 39ded239dea2d76854283a47b3f29cc12b8801e1 | |
parent | Added speed entity effect. (diff) | |
parent | cChunk: Fixed the Coords param. (diff) | |
download | cuberite-80bd0a54579cbe5188d42bf8e1e51724d19a6fcf.tar cuberite-80bd0a54579cbe5188d42bf8e1e51724d19a6fcf.tar.gz cuberite-80bd0a54579cbe5188d42bf8e1e51724d19a6fcf.tar.bz2 cuberite-80bd0a54579cbe5188d42bf8e1e51724d19a6fcf.tar.lz cuberite-80bd0a54579cbe5188d42bf8e1e51724d19a6fcf.tar.xz cuberite-80bd0a54579cbe5188d42bf8e1e51724d19a6fcf.tar.zst cuberite-80bd0a54579cbe5188d42bf8e1e51724d19a6fcf.zip |
-rw-r--r-- | MCServer/crafting.txt | 2 | ||||
-rw-r--r-- | src/Blocks/BlockAnvil.h | 2 | ||||
-rw-r--r-- | src/Chunk.cpp | 10 | ||||
-rw-r--r-- | src/Chunk.h | 2 | ||||
-rw-r--r-- | src/CraftingRecipes.cpp | 7 | ||||
-rw-r--r-- | src/SetChunkData.cpp | 33 | ||||
-rw-r--r-- | src/SetChunkData.h | 3 | ||||
-rw-r--r-- | src/Simulator/VanillaFluidSimulator.cpp | 2 | ||||
-rw-r--r-- | src/StringUtils.h | 6 | ||||
-rw-r--r-- | src/World.cpp | 6 | ||||
-rw-r--r-- | src/WorldStorage/WSSAnvil.cpp | 438 | ||||
-rw-r--r-- | src/WorldStorage/WSSAnvil.h | 31 |
12 files changed, 319 insertions, 223 deletions
diff --git a/MCServer/crafting.txt b/MCServer/crafting.txt index 60cda0673..684294bb3 100644 --- a/MCServer/crafting.txt +++ b/MCServer/crafting.txt @@ -84,7 +84,7 @@ BrickSlab, 6 = BrickBlock, 1:1, 2:1, 3:1 StonebrickSlab, 6 = StoneBrick, 1:1, 2:1, 3:1 NetherbrickSlab, 6 = NetherBrick, 1:1, 2:1, 3:1 Quartzslab, 6 = QuartzBlock, 1:1, 2:1, 3:1 -snow, 6 = SnowBlock, 1:1, 2:1, 3:1 +snow, 6 = SnowBlock, 1:1, 2:1, 3:1 # Stairs: WoodStairs, 4 = Planks, 1:1, 1:2, 2:2, 1:3, 2:3, 3:3 diff --git a/src/Blocks/BlockAnvil.h b/src/Blocks/BlockAnvil.h index 376bf86a3..20514580e 100644 --- a/src/Blocks/BlockAnvil.h +++ b/src/Blocks/BlockAnvil.h @@ -40,7 +40,7 @@ public: ) override { a_BlockType = m_BlockType; - NIBBLETYPE Meta = a_Player->GetEquippedItem().m_ItemDamage; + NIBBLETYPE Meta = (NIBBLETYPE)a_Player->GetEquippedItem().m_ItemDamage; int Direction = (int)floor(a_Player->GetYaw() * 4.0 / 360.0 + 1.5) & 0x3; switch (Direction) diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 116c0f3a0..40ffff834 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -296,6 +296,16 @@ void cChunk::SetAllData(cSetChunkData & a_SetChunkData) } m_BlockEntities.clear(); std::swap(a_SetChunkData.GetBlockEntities(), m_BlockEntities); + + // Check that all block entities have a valid blocktype at their respective coords (DEBUG-mode only): + #ifdef _DEBUG + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + BLOCKTYPE EntityBlockType = (*itr)->GetBlockType(); + BLOCKTYPE WorldBlockType = GetBlock((*itr)->GetRelX(), (*itr)->GetPosY(), (*itr)->GetRelZ()); + ASSERT(EntityBlockType == WorldBlockType); + } // for itr - m_BlockEntities + #endif // _DEBUG // Set all block entities' World variable: for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) diff --git a/src/Chunk.h b/src/Chunk.h index 72a1f6c95..e5de22e3b 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -155,7 +155,7 @@ public: void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, bool a_SendToClients = true); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc. BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const; - BLOCKTYPE GetBlock(Vector3i a_cords) const { return GetBlock(a_cords.x, a_cords.y, a_cords.z);} + BLOCKTYPE GetBlock(const Vector3i & a_RelCoords) const { return GetBlock(a_RelCoords.x, a_RelCoords.y, a_RelCoords.z); } void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); void GetBlockInfo (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); diff --git a/src/CraftingRecipes.cpp b/src/CraftingRecipes.cpp index 2d80ecaf8..223184c64 100644 --- a/src/CraftingRecipes.cpp +++ b/src/CraftingRecipes.cpp @@ -1,4 +1,4 @@ - + // CraftingRecipes.cpp // Interfaces to the cCraftingRecipes class representing the storage of crafting recipes @@ -366,7 +366,10 @@ void cCraftingRecipes::ClearRecipes(void) void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine) { - AStringVector Sides = StringSplit(a_RecipeLine, "="); + AString RecipeLine(a_RecipeLine); + RecipeLine.erase(std::remove(RecipeLine.begin(), RecipeLine.end(), ' '), RecipeLine.end()); + + AStringVector Sides = StringSplit(RecipeLine, "="); if (Sides.size() != 2) { LOGWARNING("crafting.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1); diff --git a/src/SetChunkData.cpp b/src/SetChunkData.cpp index af6ad1251..97903074a 100644 --- a/src/SetChunkData.cpp +++ b/src/SetChunkData.cpp @@ -5,6 +5,7 @@ #include "Globals.h" #include "SetChunkData.h" +#include "BlockEntities/BlockEntity.h" @@ -116,3 +117,35 @@ void cSetChunkData::CalculateHeightMap(void) + +void cSetChunkData::RemoveInvalidBlockEntities(void) +{ + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end();) + { + BLOCKTYPE EntityBlockType = (*itr)->GetBlockType(); + BLOCKTYPE WorldBlockType = cChunkDef::GetBlock(m_BlockTypes, (*itr)->GetRelX(), (*itr)->GetPosY(), (*itr)->GetRelZ()); + if (EntityBlockType != WorldBlockType) + { + // Bad blocktype, remove the block entity: + LOGD("Block entity blocktype mismatch at {%d, %d, %d}: entity for blocktype %s(%d) in block %s(%d). Deleting the block entity.", + (*itr)->GetPosX(), (*itr)->GetPosY(), (*itr)->GetPosZ(), + ItemTypeToString(EntityBlockType).c_str(), EntityBlockType, + ItemTypeToString(WorldBlockType).c_str(), WorldBlockType + ); + cBlockEntityList::iterator itr2 = itr; + itr2++; + m_BlockEntities.erase(itr); + delete *itr; + itr = itr2; + } + else + { + // Good blocktype, keep the block entity: + ++itr; + } + } // for itr - m_BlockEntities[] +} + + + + diff --git a/src/SetChunkData.h b/src/SetChunkData.h index a2f776f6b..03e9ef4d9 100644 --- a/src/SetChunkData.h +++ b/src/SetChunkData.h @@ -92,6 +92,9 @@ public: /** Calculates the heightmap based on the contained blocktypes and marks it valid. */ void CalculateHeightMap(void); + /** Removes the block entities that don't have a proper blocktype at their corresponding coords. */ + void RemoveInvalidBlockEntities(void); + protected: int m_ChunkX; int m_ChunkZ; diff --git a/src/Simulator/VanillaFluidSimulator.cpp b/src/Simulator/VanillaFluidSimulator.cpp index 18d9b07e1..6df75eebb 100644 --- a/src/Simulator/VanillaFluidSimulator.cpp +++ b/src/Simulator/VanillaFluidSimulator.cpp @@ -98,7 +98,7 @@ int cVanillaFluidSimulator::CalculateFlowCost(cChunk * a_Chunk, int a_RelX, int } // Check if block below is passable - if (!a_Chunk->UnboundedRelGetBlock(a_RelX, a_RelY - 1, a_RelZ, BlockType, BlockMeta)) + if ((a_RelY > 0) && !a_Chunk->UnboundedRelGetBlock(a_RelX, a_RelY - 1, a_RelZ, BlockType, BlockMeta)) { return Cost; } diff --git a/src/StringUtils.h b/src/StringUtils.h index 4a4c267c7..72a90a8c2 100644 --- a/src/StringUtils.h +++ b/src/StringUtils.h @@ -101,7 +101,7 @@ extern void SetBEInt(char * a_Mem, Int32 a_Value); /// Parses any integer type. Checks bounds and returns errors out of band. template <class T> -bool StringToInteger(const AString& a_str, T& a_Num) +bool StringToInteger(const AString & a_str, T & a_Num) { size_t i = 0; bool positive = true; @@ -119,7 +119,7 @@ bool StringToInteger(const AString& a_str, T& a_Num) { for (size_t size = a_str.size(); i < size; i++) { - if ((a_str[i] <= '0') || (a_str[i] >= '9')) + if ((a_str[i] < '0') || (a_str[i] > '9')) { return false; } @@ -140,7 +140,7 @@ bool StringToInteger(const AString& a_str, T& a_Num) { for (size_t size = a_str.size(); i < size; i++) { - if ((a_str[i] <= '0') || (a_str[i] >= '9')) + if ((a_str[i] < '0') || (a_str[i] > '9')) { return false; } diff --git a/src/World.cpp b/src/World.cpp index 69d1217f1..b01e53638 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -3484,14 +3484,16 @@ void cWorld::cChunkGeneratorCallbacks::OnChunkGenerated(cChunkDesc & a_ChunkDesc cChunkDef::BlockNibbles BlockMetas; a_ChunkDesc.CompressBlockMetas(BlockMetas); - m_World->QueueSetChunkData(cSetChunkDataPtr(new cSetChunkData( + cSetChunkDataPtr SetChunkData(new cSetChunkData( a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(), a_ChunkDesc.GetBlockTypes(), BlockMetas, NULL, NULL, // We don't have lighting, chunk will be lighted when needed &a_ChunkDesc.GetHeightMap(), &a_ChunkDesc.GetBiomeMap(), a_ChunkDesc.GetEntities(), a_ChunkDesc.GetBlockEntities(), true - ))); + )); + SetChunkData->RemoveInvalidBlockEntities(); + m_World->QueueSetChunkData(SetChunkData); } diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index a9c9ae4b5..5978e5ef8 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -396,7 +396,7 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT } // for y //*/ - m_World->QueueSetChunkData(cSetChunkDataPtr(new cSetChunkData( + cSetChunkDataPtr SetChunkData(new cSetChunkData( a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, BlockTypes, MetaData, IsLightValid ? BlockLight : NULL, @@ -404,7 +404,8 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT NULL, Biomes, Entities, BlockEntities, false - ))); + )); + m_World->QueueSetChunkData(SetChunkData); return true; } @@ -581,64 +582,32 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con { continue; } - int sID = a_NBT.FindChildByName(Child, "id"); - if (sID < 0) + + // Get the BlockEntity's position + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, Child, x, y, z)) { + LOGWARNING("Bad block entity, missing the coords. Will be ignored."); continue; } - if (strncmp(a_NBT.GetData(sID), "Beacon", a_NBT.GetDataLength(sID)) == 0) - { - LoadBeaconFromNBT(a_BlockEntities, a_NBT, Child); - } - else if (strncmp(a_NBT.GetData(sID), "Chest", a_NBT.GetDataLength(sID)) == 0) - { - LoadChestFromNBT(a_BlockEntities, a_NBT, Child, E_BLOCK_CHEST); - } - else if (strncmp(a_NBT.GetData(sID), "Control", a_NBT.GetDataLength(sID)) == 0) - { - LoadCommandBlockFromNBT(a_BlockEntities, a_NBT, Child); - } - else if (strncmp(a_NBT.GetData(sID), "Dropper", a_NBT.GetDataLength(sID)) == 0) - { - LoadDropperFromNBT(a_BlockEntities, a_NBT, Child); - } - else if (strncmp(a_NBT.GetData(sID), "FlowerPot", a_NBT.GetDataLength(sID)) == 0) - { - LoadFlowerPotFromNBT(a_BlockEntities, a_NBT, Child); - } - else if (strncmp(a_NBT.GetData(sID), "Furnace", a_NBT.GetDataLength(sID)) == 0) - { - LoadFurnaceFromNBT(a_BlockEntities, a_NBT, Child, a_BlockTypes, a_BlockMetas); - } - else if (strncmp(a_NBT.GetData(sID), "Hopper", a_NBT.GetDataLength(sID)) == 0) - { - LoadHopperFromNBT(a_BlockEntities, a_NBT, Child); - } - else if (strncmp(a_NBT.GetData(sID), "Music", a_NBT.GetDataLength(sID)) == 0) - { - LoadNoteFromNBT(a_BlockEntities, a_NBT, Child); - } - else if (strncmp(a_NBT.GetData(sID), "RecordPlayer", a_NBT.GetDataLength(sID)) == 0) - { - LoadJukeboxFromNBT(a_BlockEntities, a_NBT, Child); - } - else if (strncmp(a_NBT.GetData(sID), "Sign", a_NBT.GetDataLength(sID)) == 0) - { - LoadSignFromNBT(a_BlockEntities, a_NBT, Child); - } - else if (strncmp(a_NBT.GetData(sID), "Skull", a_NBT.GetDataLength(sID)) == 0) - { - LoadMobHeadFromNBT(a_BlockEntities, a_NBT, Child); - } - else if (strncmp(a_NBT.GetData(sID), "Trap", a_NBT.GetDataLength(sID)) == 0) + int RelX = x, RelY = y, RelZ = z, ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(RelX, RelY, RelZ, ChunkX, ChunkZ); + if (RelY == 2) { - LoadDispenserFromNBT(a_BlockEntities, a_NBT, Child); + LOGD("HERE"); } - else if (strncmp(a_NBT.GetData(sID), "TrappedChest", a_NBT.GetDataLength(sID)) == 0) + + // Load the proper BlockEntity type based on the block type: + BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, RelX, RelY, RelZ); + NIBBLETYPE BlockMeta = cChunkDef::GetNibble(a_BlockMetas, RelX, RelY, RelZ); + std::auto_ptr<cBlockEntity> be(LoadBlockEntityFromNBT(a_NBT, Child, x, y, z, BlockType, BlockMeta)); + if (be.get() == NULL) { - LoadChestFromNBT(a_BlockEntities, a_NBT, Child, E_BLOCK_TRAPPED_CHEST); + continue; } - // TODO: Other block entities + + // Add the BlockEntity to the loaded data: + a_BlockEntities.push_back(be.release()); } // for Child - tag children } @@ -646,6 +615,52 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con +cBlockEntity * cWSSAnvil::LoadBlockEntityFromNBT(const cParsedNBT & a_NBT, int a_Tag, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + // Load the specific BlockEntity type: + switch (a_BlockType) + { + // Specific entity loaders: + case E_BLOCK_BEACON: return LoadBeaconFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ); + case E_BLOCK_CHEST: return LoadChestFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_CHEST); + case E_BLOCK_COMMAND_BLOCK: return LoadCommandBlockFromNBT(a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ); + case E_BLOCK_DISPENSER: return LoadDispenserFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ); + case E_BLOCK_DROPPER: return LoadDropperFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ); + case E_BLOCK_FLOWER_POT: return LoadFlowerPotFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ); + case E_BLOCK_FURNACE: return LoadFurnaceFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FURNACE, a_BlockMeta); + case E_BLOCK_HEAD: return LoadMobHeadFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ); + case E_BLOCK_HOPPER: return LoadHopperFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ); + case E_BLOCK_JUKEBOX: return LoadJukeboxFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ); + case E_BLOCK_LIT_FURNACE: return LoadFurnaceFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_LIT_FURNACE, a_BlockMeta); + case E_BLOCK_NOTE_BLOCK: return LoadNoteBlockFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ); + case E_BLOCK_SIGN_POST: return LoadSignFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SIGN_POST); + case E_BLOCK_TRAPPED_CHEST: return LoadChestFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_TRAPPED_CHEST); + case E_BLOCK_WALLSIGN: return LoadSignFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WALLSIGN); + + // Blocktypes that have block entities but don't load their contents from disk: + case E_BLOCK_ENDER_CHEST: return NULL; + } + + // 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), (size_t)a_NBT.GetDataLength(TagID)); + } + LOGINFO("WorldLoader(%s): Block entity mismatch: block type %s (%d), type \"%s\", at {%d, %d, %d}; the entity will be lost.", + m_World->GetName().c_str(), + ItemTypeToString(a_BlockType).c_str(), a_BlockType, TypeName.c_str(), + a_BlockX, a_BlockY, a_BlockZ + ); + return NULL; +} + + + + + bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx) { int Type = a_NBT.FindChildByName(a_TagIdx, "id"); @@ -656,7 +671,6 @@ bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_ a_Item.m_ItemType = a_NBT.GetShort(Type); if (a_Item.m_ItemType < 0) { - LOGD("Encountered an item with negative type (%d). Replacing with an empty item.", a_NBT.GetShort(Type)); a_Item.Empty(); return true; } @@ -754,16 +768,46 @@ void cWSSAnvil::LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a -void cWSSAnvil::LoadBeaconFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +bool cWSSAnvil::CheckBlockEntityType(const cParsedNBT & a_NBT, int a_TagIdx, const char * a_ExpectedType) { - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + // Check if the given tag is a compound: + if (a_NBT.GetType(a_TagIdx) != TAG_Compound) { - return; + return false; } - std::auto_ptr<cBeaconEntity> Beacon(new cBeaconEntity(x, y, z, m_World)); + // Get the "id" tag: + int TagID = a_NBT.FindChildByName(a_TagIdx, "id"); + if (TagID < 0) + { + return false; + } + + // Compare the value: + if (strncmp(a_NBT.GetData(TagID), a_ExpectedType, (size_t)a_NBT.GetDataLength(TagID)) == 0) + { + return true; + } + LOGWARNING("Block entity type mismatch: exp \"%s\", got \"%s\".", + a_ExpectedType, + AString(a_NBT.GetData(TagID), (size_t)a_NBT.GetDataLength(TagID)).c_str() + ); + return false; +} + + + + + +cBlockEntity * cWSSAnvil::LoadBeaconFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Check if the data has a proper type: + if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Beacon")) + { + return NULL; + } + + std::auto_ptr<cBeaconEntity> Beacon(new cBeaconEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); int CurrentLine = a_NBT.FindChildByName(a_TagIdx, "Levels"); if (CurrentLine >= 0) @@ -790,88 +834,128 @@ void cWSSAnvil::LoadBeaconFromNBT(cBlockEntityList & a_BlockEntities, const cPar LoadItemGridFromNBT(Beacon->GetContents(), a_NBT, Items); } - a_BlockEntities.push_back(Beacon.release()); + return Beacon.release(); } -void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_ChestType) +cBlockEntity * cWSSAnvil::LoadChestFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_ChestBlockType) { - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + // Check if the data has a proper type: + // TODO: Does vanilla use "TrappedChest" or not? MCWiki says no, but previous code says yes + // Ref.: http://minecraft.gamepedia.com/Trapped_Chest + // https://github.com/mc-server/MCServer/blob/d0551e2e0a98a28f31a88d489d17b408e4a7d38d/src/WorldStorage/WSSAnvil.cpp#L637 + if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Chest") && !CheckBlockEntityType(a_NBT, a_TagIdx, "TrappedChest")) { - return; + return NULL; } + 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 + return NULL; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this } - std::auto_ptr<cChestEntity> Chest(new cChestEntity(x, y, z, m_World, a_ChestType)); + std::auto_ptr<cChestEntity> Chest(new cChestEntity(a_BlockX, a_BlockY, a_BlockZ, m_World, a_ChestBlockType)); LoadItemGridFromNBT(Chest->GetContents(), a_NBT, Items); - a_BlockEntities.push_back(Chest.release()); + return Chest.release(); } -void cWSSAnvil::LoadDispenserFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +cBlockEntity * cWSSAnvil::LoadCommandBlockFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ) { - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + // Check if the data has a proper type: + if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Control")) { - return; + return NULL; + } + + std::auto_ptr<cCommandBlockEntity> CmdBlock(new cCommandBlockEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); + + 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) + { + CmdBlock->SetResult(a_NBT.GetInt(currentLine)); + } + + 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. + + return CmdBlock.release(); +} + + + + + +cBlockEntity * cWSSAnvil::LoadDispenserFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // Check if the data has a proper type: + if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Trap")) + { + return NULL; } + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) { - return; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this + return NULL; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this } - std::auto_ptr<cDispenserEntity> Dispenser(new cDispenserEntity(x, y, z, m_World)); + std::auto_ptr<cDispenserEntity> Dispenser(new cDispenserEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); LoadItemGridFromNBT(Dispenser->GetContents(), a_NBT, Items); - a_BlockEntities.push_back(Dispenser.release()); + return Dispenser.release(); } -void cWSSAnvil::LoadDropperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +cBlockEntity * cWSSAnvil::LoadDropperFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ) { - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + // Check if the data has a proper type: + if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Dropper")) { - return; + return NULL; } + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) { - return; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this + return NULL; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this } - std::auto_ptr<cDropperEntity> Dropper(new cDropperEntity(x, y, z, m_World)); + std::auto_ptr<cDropperEntity> Dropper(new cDropperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); LoadItemGridFromNBT(Dropper->GetContents(), a_NBT, Items); - a_BlockEntities.push_back(Dropper.release()); + return Dropper.release(); } -void cWSSAnvil::LoadFlowerPotFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +cBlockEntity * cWSSAnvil::LoadFlowerPotFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ) { - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + // Check if the data has a proper type: + if (!CheckBlockEntityType(a_NBT, a_TagIdx, "FlowerPot")) { - return; + return NULL; } - std::auto_ptr<cFlowerPotEntity> FlowerPot(new cFlowerPotEntity(x, y, z, m_World)); + + std::auto_ptr<cFlowerPotEntity> FlowerPot(new cFlowerPotEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); short ItemType = 0, ItemData = 0; int currentLine = a_NBT.FindChildByName(a_TagIdx, "Item"); @@ -887,37 +971,28 @@ void cWSSAnvil::LoadFlowerPotFromNBT(cBlockEntityList & a_BlockEntities, const c } FlowerPot->SetItem(cItem(ItemType, 1, ItemData)); - a_BlockEntities.push_back(FlowerPot.release()); + return FlowerPot.release(); } -void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas) +cBlockEntity * cWSSAnvil::LoadFurnaceFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + // Check if the data has a proper type: + if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Furnace")) { - return; + return NULL; } + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) { - return; // Make it an empty furnace - the chunk loader will provide an empty cFurnaceEntity for this + return NULL; // Make it an empty furnace - the chunk loader will provide an empty cFurnaceEntity for this } - // Convert coords to relative: - int RelX = x; - int RelZ = z; - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(RelX, y, RelZ, ChunkX, ChunkZ); - - // Create the furnace entity, with proper BlockType and BlockMeta info: - BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, RelX, y, RelZ); - NIBBLETYPE BlockMeta = cChunkDef::GetNibble(a_BlockMetas, RelX, y, RelZ); - std::auto_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(x, y, z, BlockType, BlockMeta, m_World)); + std::auto_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, m_World)); // Load slots: for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child)) @@ -954,184 +1029,147 @@ void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cPa // Restart cooking: Furnace->ContinueCooking(); - a_BlockEntities.push_back(Furnace.release()); + return Furnace.release(); } -void cWSSAnvil::LoadHopperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +cBlockEntity * cWSSAnvil::LoadHopperFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ) { - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + // Check if the data has a proper type: + if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Hopper")) { - return; + return NULL; } + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) { - return; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this + return NULL; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this } - std::auto_ptr<cHopperEntity> Hopper(new cHopperEntity(x, y, z, m_World)); + std::auto_ptr<cHopperEntity> Hopper(new cHopperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); LoadItemGridFromNBT(Hopper->GetContents(), a_NBT, Items); - a_BlockEntities.push_back(Hopper.release()); + return Hopper.release(); } -void cWSSAnvil::LoadJukeboxFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +cBlockEntity * cWSSAnvil::LoadJukeboxFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ) { - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + // Check if the data has a proper type: + if (!CheckBlockEntityType(a_NBT, a_TagIdx, "RecordPlayer")) { - return; + return NULL; } - std::auto_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(x, y, z, m_World)); + + std::auto_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); int Record = a_NBT.FindChildByName(a_TagIdx, "Record"); if (Record >= 0) { Jukebox->SetRecord(a_NBT.GetInt(Record)); } - a_BlockEntities.push_back(Jukebox.release()); -} - - - - - -void cWSSAnvil::LoadNoteFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) -{ - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) - { - return; - } - std::auto_ptr<cNoteEntity> Note(new cNoteEntity(x, y, z, m_World)); - int note = a_NBT.FindChildByName(a_TagIdx, "note"); - if (note >= 0) - { - Note->SetPitch(a_NBT.GetByte(note)); - } - a_BlockEntities.push_back(Note.release()); + return Jukebox.release(); } -void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +cBlockEntity * cWSSAnvil::LoadMobHeadFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ) { - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + // Check if the data has a proper type: + if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Skull")) { - return; + return NULL; } - std::auto_ptr<cSignEntity> Sign(new cSignEntity(E_BLOCK_SIGN_POST, x, y, z, m_World)); - int currentLine = a_NBT.FindChildByName(a_TagIdx, "Text1"); - if (currentLine >= 0) - { - Sign->SetLine(0, a_NBT.GetString(currentLine)); - } + std::auto_ptr<cMobHeadEntity> MobHead(new cMobHeadEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); - currentLine = a_NBT.FindChildByName(a_TagIdx, "Text2"); + int currentLine = a_NBT.FindChildByName(a_TagIdx, "SkullType"); if (currentLine >= 0) { - Sign->SetLine(1, a_NBT.GetString(currentLine)); + MobHead->SetType(static_cast<eMobHeadType>(a_NBT.GetByte(currentLine))); } - currentLine = a_NBT.FindChildByName(a_TagIdx, "Text3"); + currentLine = a_NBT.FindChildByName(a_TagIdx, "Rot"); if (currentLine >= 0) { - Sign->SetLine(2, a_NBT.GetString(currentLine)); + MobHead->SetRotation(static_cast<eMobHeadRotation>(a_NBT.GetByte(currentLine))); } - currentLine = a_NBT.FindChildByName(a_TagIdx, "Text4"); + currentLine = a_NBT.FindChildByName(a_TagIdx, "ExtraType"); if (currentLine >= 0) { - Sign->SetLine(3, a_NBT.GetString(currentLine)); + MobHead->SetOwner(a_NBT.GetString(currentLine)); } - a_BlockEntities.push_back(Sign.release()); + return MobHead.release(); } -void cWSSAnvil::LoadMobHeadFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +cBlockEntity * cWSSAnvil::LoadNoteBlockFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ) { - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) - { - return; - } - std::auto_ptr<cMobHeadEntity> MobHead(new cMobHeadEntity(x, y, z, m_World)); - - int currentLine = a_NBT.FindChildByName(a_TagIdx, "SkullType"); - if (currentLine >= 0) + // Check if the data has a proper type: + if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Music")) { - MobHead->SetType(static_cast<eMobHeadType>(a_NBT.GetByte(currentLine))); + return NULL; } - currentLine = a_NBT.FindChildByName(a_TagIdx, "Rot"); - if (currentLine >= 0) + std::auto_ptr<cNoteEntity> NoteBlock(new cNoteEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); + int note = a_NBT.FindChildByName(a_TagIdx, "note"); + if (note >= 0) { - MobHead->SetRotation(static_cast<eMobHeadRotation>(a_NBT.GetByte(currentLine))); + NoteBlock->SetPitch(a_NBT.GetByte(note)); } - - currentLine = a_NBT.FindChildByName(a_TagIdx, "ExtraType"); - if (currentLine >= 0) - { - MobHead->SetOwner(a_NBT.GetString(currentLine)); - } - - a_BlockEntities.push_back(MobHead.release()); + return NoteBlock.release(); } -void cWSSAnvil::LoadCommandBlockFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +cBlockEntity * cWSSAnvil::LoadSignFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) { - ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); - int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + // Check if the data has a proper type: + if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Sign")) { - return; + return NULL; } - std::auto_ptr<cCommandBlockEntity> CmdBlock(new cCommandBlockEntity(x, y, z, m_World)); - int currentLine = a_NBT.FindChildByName(a_TagIdx, "Command"); + std::auto_ptr<cSignEntity> Sign(new cSignEntity(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, m_World)); + + int currentLine = a_NBT.FindChildByName(a_TagIdx, "Text1"); if (currentLine >= 0) { - CmdBlock->SetCommand(a_NBT.GetString(currentLine)); + Sign->SetLine(0, a_NBT.GetString(currentLine)); } - currentLine = a_NBT.FindChildByName(a_TagIdx, "SuccessCount"); + currentLine = a_NBT.FindChildByName(a_TagIdx, "Text2"); if (currentLine >= 0) { - CmdBlock->SetResult(a_NBT.GetInt(currentLine)); + Sign->SetLine(1, a_NBT.GetString(currentLine)); } - currentLine = a_NBT.FindChildByName(a_TagIdx, "LastOutput"); + currentLine = a_NBT.FindChildByName(a_TagIdx, "Text3"); if (currentLine >= 0) { - CmdBlock->SetLastOutput(a_NBT.GetString(currentLine)); + Sign->SetLine(2, a_NBT.GetString(currentLine)); } - // TODO 2014-01-18 xdot: Figure out what TrackOutput is and parse it. + currentLine = a_NBT.FindChildByName(a_TagIdx, "Text4"); + if (currentLine >= 0) + { + Sign->SetLine(3, a_NBT.GetString(currentLine)); + } - a_BlockEntities.push_back(CmdBlock.release()); + return Sign.release(); } diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index a41268f4c..591ec6757 100644 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -125,6 +125,10 @@ protected: /// Loads the chunk's BlockEntities from NBT data (a_Tag is the Level\\TileEntities list tag; may be -1) void LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntitites, const cParsedNBT & a_NBT, int a_Tag, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas); + /** Loads the data for a block entity from the specified NBT tag. + Returns the loaded block entity, or NULL upon failure. */ + cBlockEntity * LoadBlockEntityFromNBT(const cParsedNBT & a_NBT, int a_Tag, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + /// Loads a cItem contents from the specified NBT tag; returns true if successful. Doesn't load the Slot tag bool LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx); @@ -134,18 +138,21 @@ protected: */ void LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int s_SlotOffset = 0); - void LoadBeaconFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_ChestType); - void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadFlowerPotFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas); - void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadMobHeadFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); - void LoadCommandBlockFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + /** Returns true iff the "id" child tag inside the specified tag equals the specified expected type. */ + bool CheckBlockEntityType(const cParsedNBT & a_NBT, int a_TagIdx, const char * a_ExpectedType); + + cBlockEntity * LoadBeaconFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ); + cBlockEntity * LoadChestFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_ChestBlockType); + cBlockEntity * LoadCommandBlockFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ); + cBlockEntity * LoadDispenserFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ); + cBlockEntity * LoadDropperFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ); + cBlockEntity * LoadFlowerPotFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ); + cBlockEntity * LoadFurnaceFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + cBlockEntity * LoadHopperFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ); + cBlockEntity * LoadJukeboxFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ); + cBlockEntity * LoadMobHeadFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ); + cBlockEntity * LoadNoteBlockFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ); + cBlockEntity * LoadSignFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_SignBlockType); void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, size_t a_IDTagLength); |