diff options
author | Tiger Wang <ziwei.tiger@hotmail.co.uk> | 2014-06-24 22:15:48 +0200 |
---|---|---|
committer | Tiger Wang <ziwei.tiger@hotmail.co.uk> | 2014-06-24 22:15:48 +0200 |
commit | bd5df3d3b975c448e3d8f4227d86a742d0ad3b12 (patch) | |
tree | 30193169848d8ea39e2e5a423bd9e1a8e189c5f6 | |
parent | Conforms to standards (diff) | |
parent | Merge pull request #1125 from Howaner/Entities (diff) | |
download | cuberite-bd5df3d3b975c448e3d8f4227d86a742d0ad3b12.tar cuberite-bd5df3d3b975c448e3d8f4227d86a742d0ad3b12.tar.gz cuberite-bd5df3d3b975c448e3d8f4227d86a742d0ad3b12.tar.bz2 cuberite-bd5df3d3b975c448e3d8f4227d86a742d0ad3b12.tar.lz cuberite-bd5df3d3b975c448e3d8f4227d86a742d0ad3b12.tar.xz cuberite-bd5df3d3b975c448e3d8f4227d86a742d0ad3b12.tar.zst cuberite-bd5df3d3b975c448e3d8f4227d86a742d0ad3b12.zip |
Diffstat (limited to '')
-rw-r--r-- | CONTRIBUTORS | 4 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/APIDesc.lua | 3 | ||||
-rw-r--r-- | MCServer/Plugins/Debuggers/Debuggers.lua | 4 | ||||
-rw-r--r-- | src/BlockEntities/ChestEntity.cpp | 13 | ||||
-rw-r--r-- | src/BlockEntities/HopperEntity.cpp | 77 | ||||
-rw-r--r-- | src/BlockInfo.cpp | 21 | ||||
-rw-r--r-- | src/Blocks/BlockDoor.cpp | 7 | ||||
-rw-r--r-- | src/Blocks/BlockFenceGate.h | 1 | ||||
-rw-r--r-- | src/Blocks/BlockHandler.cpp | 89 | ||||
-rw-r--r-- | src/Blocks/BlockLever.h | 11 | ||||
-rw-r--r-- | src/Blocks/BlockSign.h | 6 | ||||
-rw-r--r-- | src/Blocks/BlockSlab.h | 15 | ||||
-rw-r--r-- | src/Chunk.cpp | 6 | ||||
-rw-r--r-- | src/ClientHandle.cpp | 29 | ||||
-rw-r--r-- | src/ClientHandle.h | 11 | ||||
-rw-r--r-- | src/Generating/Prefab.cpp | 11 | ||||
-rw-r--r-- | src/Simulator/IncrementalRedstoneSimulator.cpp | 63 | ||||
-rw-r--r-- | src/VoronoiMap.cpp | 4 | ||||
-rw-r--r-- | src/WorldStorage/NBTChunkSerializer.cpp | 3 | ||||
-rw-r--r-- | src/WorldStorage/WSSAnvil.cpp | 21 |
20 files changed, 297 insertions, 102 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 50c8dbfb8..09515aa6b 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -1,6 +1,6 @@ Many people have contributed to MCServer, and this list attempts to broadcast at least some of them. -BasedDoge - Donated AlchemistVillage prefabs +BasedDoge (Donated AlchemistVillage prefabs) bearbin (Alexander Harkness) derouinw Diusrex @@ -27,6 +27,6 @@ tonibm19 UltraCoderRU worktycho xoft -Yeeeeezus - Donated AlchemistVillage prefabs +Yeeeeezus (Donated AlchemistVillage prefabs) Please add yourself to this list if you contribute to MCServer. diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 19ca971e2..06e333cf9 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -523,13 +523,16 @@ end Functions = { + GenerateOfflineUUID = { Params = "Username", Return = "string", Notes = "(STATIC) Generates an UUID based on the player name provided. This is used for the offline (non-auth) mode, when there's no UUID source. Each username generates a unique and constant UUID, so that when the player reconnects with the same name, their UUID is the same. Returns a 36-char UUID (with dashes)." }, GetLocale = { Params = "", Return = "Locale", Notes = "Returns the locale string that the client sends as part of the protocol handshake. Can be used to provide localized strings." }, GetPing = { Params = "", Return = "number", Notes = "Returns the ping time, in ms" }, GetPlayer = { Params = "", Return = "{{cPlayer|cPlayer}}", Notes = "Returns the player object connected to this client. Note that this may be nil, for example if the player object is not yet spawned." }, GetUniqueID = { Params = "", Return = "number", Notes = "Returns the UniqueID of the client used to identify the client in the server" }, + GetUUID = { Params = "", Return = "string", Notes = "Returns the authentication-based UUID of the client. This UUID should be used to identify the player when persisting any player-related data." }, GetUsername = { Params = "", Return = "string", Notes = "Returns the username that the client has provided" }, GetViewDistance = { Params = "", Return = "number", Notes = "Returns the viewdistance (number of chunks loaded for the player in each direction)" }, HasPluginChannel = { Params = "ChannelName", Return = "bool", Notes = "Returns true if the client has registered to receive messages on the specified plugin channel." }, + IsUUIDOnline = { Params = "UUID", Return = "bool", Notes = "(STATIC) Returns true if the UUID is generated by online auth, false if it is an offline-generated UUID. We use Version-3 UUIDs for offline UUIDs, online UUIDs are Version-4, thus we can tell them apart. Accepts both 32-char and 36-char UUIDs (with and without dashes). If the string given is not a valid UUID, returns false."}, Kick = { Params = "Reason", Return = "", Notes = "Kicks the user with the specified reason" }, SendPluginMessage = { Params = "Channel, Message", Return = "", Notes = "Sends the plugin message on the specified channel." }, SetLocale = { Params = "Locale", Return = "", Notes = "Sets the locale that MCServer keeps on record. Initially the locale is initialized in protocol handshake, this function allows plugins to override the stored value (but only server-side and only until the user disconnects)." }, diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index 534426d25..deb6a720b 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -346,7 +346,7 @@ end function OnUsingBlazeRod(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, CursorY, CursorZ) -- Magic rod of query: show block types and metas for both neighbors of the pointed face - local Type, Meta, Valid = Player:GetWorld():GetBlockTypeMeta(BlockX, BlockY, BlockZ, Type, Meta); + local Valid, Type, Meta = Player:GetWorld():GetBlockTypeMeta(BlockX, BlockY, BlockZ); if (Type == E_BLOCK_AIR) then Player:SendMessage(cChatColor.LightGray .. "Block {" .. BlockX .. ", " .. BlockY .. ", " .. BlockZ .. "}: air:" .. Meta); @@ -356,7 +356,7 @@ function OnUsingBlazeRod(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, Cur end local X, Y, Z = AddFaceDirection(BlockX, BlockY, BlockZ, BlockFace); - Valid, Type, Meta = Player:GetWorld():GetBlockTypeMeta(X, Y, Z, Type, Meta); + Valid, Type, Meta = Player:GetWorld():GetBlockTypeMeta(X, Y, Z); if (Type == E_BLOCK_AIR) then Player:SendMessage(cChatColor.LightGray .. "Block {" .. X .. ", " .. Y .. ", " .. Z .. "}: air:" .. Meta); else diff --git a/src/BlockEntities/ChestEntity.cpp b/src/BlockEntities/ChestEntity.cpp index dfbe6ae87..cb9cc89bf 100644 --- a/src/BlockEntities/ChestEntity.cpp +++ b/src/BlockEntities/ChestEntity.cpp @@ -122,6 +122,13 @@ void cChestEntity::UsedBy(cPlayer * a_Player) void cChestEntity::OpenNewWindow(void) { + // TODO: cats are an obstruction + if ((GetPosY() + 1 < cChunkDef::Height) && cBlockInfo::IsSolid(GetWorld()->GetBlock(GetPosX(), GetPosY() + 1, GetPosZ()))) + { + // Obstruction, don't open + return; + } + // Callback for opening together with neighbor chest: class cOpenDouble : public cChestCallback @@ -135,6 +142,12 @@ void cChestEntity::OpenNewWindow(void) virtual bool Item(cChestEntity * a_Chest) override { + if ((a_Chest->GetPosY() + 1 < cChunkDef::Height) && cBlockInfo::IsSolid(a_Chest->GetWorld()->GetBlock(a_Chest->GetPosX(), a_Chest->GetPosY() + 1, a_Chest->GetPosZ()))) + { + // Obstruction, don't open + return false; + } + // The primary chest should eb the one with lesser X or Z coord: cChestEntity * Primary = a_Chest; cChestEntity * Secondary = m_ThisChest; diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp index 7f001c739..5856f20d1 100644 --- a/src/BlockEntities/HopperEntity.cpp +++ b/src/BlockEntities/HopperEntity.cpp @@ -294,23 +294,24 @@ bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick) return false; } - int bx, by, bz; + // Get the coords of the block where to output items: + int OutX, OutY, OutZ; NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); - if (!GetOutputBlockPos(Meta, bx, by, bz)) + if (!GetOutputBlockPos(Meta, OutX, OutY, OutZ)) { // Not attached to another container return false; } - if (by < 0) + if (OutY < 0) { // Cannot output below the zero-th block level return false; } // Convert coords to relative: - int rx = bx - a_Chunk.GetPosX() * cChunkDef::Width; - int rz = bz - a_Chunk.GetPosZ() * cChunkDef::Width; - cChunk * DestChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(rx, rz); + int OutRelX = OutX - a_Chunk.GetPosX() * cChunkDef::Width; + int OutRelZ = OutZ - a_Chunk.GetPosZ() * cChunkDef::Width; + cChunk * DestChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(OutRelX, OutRelZ); if (DestChunk == NULL) { // The destination chunk has been unloaded, don't tick @@ -319,26 +320,32 @@ bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick) // Call proper moving function, based on the blocktype present at the coords: bool res = false; - switch (DestChunk->GetBlock(rx, by, rz)) + switch (DestChunk->GetBlock(OutRelX, OutY, OutRelZ)) { case E_BLOCK_CHEST: { // Chests have special handling because of double-chests - res = MoveItemsToChest(*DestChunk, bx, by, bz); + res = MoveItemsToChest(*DestChunk, OutX, OutY, OutZ); break; } case E_BLOCK_LIT_FURNACE: case E_BLOCK_FURNACE: { // Furnaces have special handling because of the direction-to-slot relation - res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); + res = MoveItemsToFurnace(*DestChunk, OutX, OutY, OutZ, Meta); break; } case E_BLOCK_DISPENSER: case E_BLOCK_DROPPER: case E_BLOCK_HOPPER: { - res = MoveItemsToGrid(*(cBlockEntityWithItems *)DestChunk->GetBlockEntity(bx, by, bz)); + cBlockEntityWithItems * BlockEntity = (cBlockEntityWithItems *)DestChunk->GetBlockEntity(OutX, OutY, OutZ); + if (BlockEntity == NULL) + { + LOGWARNING("%s: A block entity was not found where expected at {%d, %d, %d}", __FUNCTION__, OutX, OutY, OutZ); + return false; + } + res = MoveItemsToGrid(*BlockEntity); break; } } @@ -359,7 +366,13 @@ bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick) /// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed. bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk) { - if (MoveItemsFromGrid(*(cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))) + cChestEntity * Chest = (cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ); + if (Chest == NULL) + { + LOGWARNING("%s: A chest entity was not found where expected, at {%d, %d, %d}", __FUNCTION__, m_PosX, m_PosY + 1, m_PosZ); + return false; + } + if (MoveItemsFromGrid(*Chest)) { // Moved the item from the chest directly above the hopper return true; @@ -389,9 +402,17 @@ bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk) { continue; } - if (MoveItemsFromGrid(*(cChestEntity *)Neighbor->GetBlockEntity(x, m_PosY, z))) + Chest = (cChestEntity *)Neighbor->GetBlockEntity(m_PosX + Coords[i].x, m_PosY + 1, m_PosZ + Coords[i].z); + if (Chest == NULL) { - return true; + LOGWARNING("%s: A chest entity was not found where expected, at {%d, %d, %d}", __FUNCTION__, m_PosX + Coords[i].x, m_PosY + 1, m_PosZ + Coords[i].z); + } + else + { + if (MoveItemsFromGrid(*Chest)) + { + return true; + } } return false; } @@ -408,7 +429,11 @@ bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk) bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk) { cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ); - ASSERT(Furnace != NULL); + if (Furnace == NULL) + { + LOGWARNING("%s: A furnace entity was not found where expected, at {%d, %d, %d}", __FUNCTION__, m_PosX, m_PosY + 1, m_PosZ); + return false; + } // Try move from the output slot: if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsOutput, true)) @@ -517,7 +542,13 @@ bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_Sl bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ) { // Try the chest directly connected to the hopper: - if (MoveItemsToGrid(*(cChestEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))) + cChestEntity * Chest = (cChestEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); + if (Chest == NULL) + { + LOGWARNING("%s: A chest entity was not found where expected, at {%d, %d, %d}", __FUNCTION__, a_BlockX, a_BlockY, a_BlockZ); + return false; + } + if (MoveItemsToGrid(*Chest)) { return true; } @@ -534,19 +565,27 @@ bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_Block {0, 1}, {0, -1}, } ; + int RelX = a_BlockX - a_Chunk.GetPosX() * cChunkDef::Width; + int RelZ = a_BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width; for (size_t i = 0; i < ARRAYCOUNT(Coords); i++) { - int x = m_RelX + Coords[i].x; - int z = m_RelZ + Coords[i].z; + int x = RelX + Coords[i].x; + int z = RelZ + Coords[i].z; cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z); if ( (Neighbor == NULL) || - (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST) + (Neighbor->GetBlock(x, a_BlockY, z) != E_BLOCK_CHEST) ) { continue; } - if (MoveItemsToGrid(*(cChestEntity *)Neighbor->GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))) + Chest = (cChestEntity *)Neighbor->GetBlockEntity(a_BlockX + Coords[i].x, a_BlockY, a_BlockZ + Coords[i].z); + if (Chest == NULL) + { + LOGWARNING("%s: A chest entity was not found where expected, at {%d, %d, %d} (%d, %d)", __FUNCTION__, a_BlockX + Coords[i].x, a_BlockY, a_BlockZ + Coords[i].z, x, z); + continue; + } + if (MoveItemsToGrid(*Chest)) { return true; } diff --git a/src/BlockInfo.cpp b/src/BlockInfo.cpp index 32fdec905..b084d0915 100644 --- a/src/BlockInfo.cpp +++ b/src/BlockInfo.cpp @@ -9,6 +9,7 @@ cBlockInfo cBlockInfo::ms_Info[256]; +static bool g_IsBlockInfoInitialized = false; @@ -43,6 +44,11 @@ cBlockInfo::~cBlockInfo() cBlockInfo & cBlockInfo::Get(BLOCKTYPE a_Type) { + if (!g_IsBlockInfoInitialized) + { + cBlockInfo::Initialize(); + g_IsBlockInfoInitialized = true; + } return ms_Info[a_Type]; } @@ -448,18 +454,3 @@ void cBlockInfo::Initialize(void) - -// This is actually just some code that needs to run at program startup, so it is wrapped into a global var's constructor: -class cBlockInfoInitializer -{ -public: - cBlockInfoInitializer(void) - { - cBlockInfo::Initialize(); - } -} BlockInfoInitializer; - - - - - diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp index fb2d6f2dc..934a01994 100644 --- a/src/Blocks/BlockDoor.cpp +++ b/src/Blocks/BlockDoor.cpp @@ -45,9 +45,16 @@ void cBlockDoorHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldIn void cBlockDoorHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { + UNUSED(a_WorldInterface); + UNUSED(a_BlockFace); + UNUSED(a_CursorX); + UNUSED(a_CursorY); + UNUSED(a_CursorZ); + if (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WOODEN_DOOR) { ChangeDoor(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); + a_Player->GetWorld()->BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0, a_Player->GetClientHandle()); } } diff --git a/src/Blocks/BlockFenceGate.h b/src/Blocks/BlockFenceGate.h index e202c6610..e992870d4 100644 --- a/src/Blocks/BlockFenceGate.h +++ b/src/Blocks/BlockFenceGate.h @@ -45,6 +45,7 @@ public: // Standing aside - use last direction a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, OldMetaData); } + a_Player->GetWorld()->BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0, a_Player->GetClientHandle()); } diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp index a7b89fcb7..405c9bf43 100644 --- a/src/Blocks/BlockHandler.cpp +++ b/src/Blocks/BlockHandler.cpp @@ -85,6 +85,91 @@ +/* +// Tests the meta rotation and mirroring. +// Note that the cMetaRotator needs to have its assert paths disabled for this test to work! +static class cBlockHandlerRotationTester +{ +public: + cBlockHandlerRotationTester(void) + { + printf("Performing block handlers test...\n"); + for (BLOCKTYPE Type = 0; Type < E_BLOCK_MAX_TYPE_ID; Type++) + { + cBlockHandler * Handler = cBlockInfo::GetHandler(Type); + if (Handler == NULL) + { + printf("NULL handler for block type %d!\n", Type); + continue; + } + AString BlockName = ItemTypeToString(Type); + for (NIBBLETYPE Meta = 0; Meta < 16; Meta++) + { + // Test the CW / CCW rotations: + NIBBLETYPE TestMeta; + TestMeta = Handler->MetaRotateCW(Handler->MetaRotateCW(Handler->MetaRotateCW(Handler->MetaRotateCW(Meta)))); + if (TestMeta != Meta) + { + // 4 CW rotations should produce no change in the meta + printf("Handler for blocktype %d (%s) fails CW 4-rotation test for meta %d: got back %d\n", Type, BlockName.c_str(), Meta, TestMeta); + } + TestMeta = Handler->MetaRotateCCW(Handler->MetaRotateCCW(Handler->MetaRotateCCW(Handler->MetaRotateCCW(Meta)))); + if (TestMeta != Meta) + { + // 4 CCW rotations should produce no change in the meta + printf("Handler for blocktype %d (%s) fails CCW 4-rotation test for meta %d: got back %d\n", Type, BlockName.c_str(), Meta, TestMeta); + } + TestMeta = Handler->MetaRotateCCW(Handler->MetaRotateCW(Meta)); + if (TestMeta != Meta) + { + // CCW rotation of a CW rotation should produce no change in the meta + printf("Handler for blocktype %d (%s) fails CCW(CW) rotation test for meta %d: got back %d\n", Type, BlockName.c_str(), Meta, TestMeta); + } + TestMeta = Handler->MetaRotateCW(Handler->MetaRotateCCW(Meta)); + if (TestMeta != Meta) + { + // CW rotation of a CCW rotation should produce no change in the meta + printf("Handler for blocktype %d (%s) fails CW(CCW) rotation test for meta %d: got back %d\n", Type, BlockName.c_str(), Meta, TestMeta); + } + + // Test the mirroring: + TestMeta = Handler->MetaMirrorXY(Handler->MetaMirrorXY(Meta)); + if (TestMeta != Meta) + { + // Double-mirroring should produce the same meta: + printf("Handler for blocktype %d (%s) fails XY mirror test for meta %d: got back %d\n", Type, BlockName.c_str(), Meta, TestMeta); + } + TestMeta = Handler->MetaMirrorXZ(Handler->MetaMirrorXZ(Meta)); + if (TestMeta != Meta) + { + // Double-mirroring should produce the same meta: + printf("Handler for blocktype %d (%s) fails XZ mirror test for meta %d: got back %d\n", Type, BlockName.c_str(), Meta, TestMeta); + } + TestMeta = Handler->MetaMirrorYZ(Handler->MetaMirrorYZ(Meta)); + if (TestMeta != Meta) + { + // Double-mirroring should produce the same meta: + printf("Handler for blocktype %d (%s) fails YZ mirror test for meta %d: got back %d\n", Type, BlockName.c_str(), Meta, TestMeta); + } + + // Test mirror-rotating: + TestMeta = Handler->MetaRotateCW(Handler->MetaRotateCW(Handler->MetaMirrorXY(Handler->MetaMirrorYZ(Meta)))); + if (TestMeta != Meta) + { + // 2 CW rotations should be the same as XY, YZ mirroring: + printf("Handler for blocktype %d (%s) fails rotation-mirror test for meta %d: got back %d\n", Type, BlockName.c_str(), Meta, TestMeta); + } + } + } // for Type + printf("Block handlers test complete.\n"); + } +} g_BlockHandlerRotationTester; +//*/ + + + + + cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) { switch(a_BlockType) @@ -175,7 +260,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_QUARTZ_BLOCK: return new cBlockQuartzHandler (a_BlockType); case E_BLOCK_QUARTZ_STAIRS: return new cBlockStairsHandler (a_BlockType); case E_BLOCK_RAIL: return new cBlockRailHandler (a_BlockType); - case E_BLOCK_REDSTONE_LAMP_ON: return new cBlockRedstoneLampHandler (a_BlockType); // We need this to change pickups to an off lamp; else 1.7+ clients crash + case E_BLOCK_REDSTONE_LAMP_ON: return new cBlockRedstoneLampHandler (a_BlockType); case E_BLOCK_REDSTONE_ORE: return new cBlockOreHandler (a_BlockType); case E_BLOCK_REDSTONE_ORE_GLOWING: return new cBlockOreHandler (a_BlockType); case E_BLOCK_REDSTONE_REPEATER_OFF: return new cBlockRedstoneRepeaterHandler(a_BlockType); @@ -207,7 +292,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_TRAPDOOR: return new cBlockTrapdoorHandler (a_BlockType); case E_BLOCK_TNT: return new cBlockTNTHandler (a_BlockType); case E_BLOCK_VINES: return new cBlockVineHandler (a_BlockType); - case E_BLOCK_WALLSIGN: return new cBlockSignHandler (a_BlockType); + case E_BLOCK_WALLSIGN: return new cBlockSignHandler (a_BlockType); // TODO: This needs a special handler case E_BLOCK_WATER: return new cBlockFluidHandler (a_BlockType); case E_BLOCK_WOODEN_BUTTON: return new cBlockButtonHandler (a_BlockType); case E_BLOCK_WOODEN_DOOR: return new cBlockDoorHandler (a_BlockType); diff --git a/src/Blocks/BlockLever.h b/src/Blocks/BlockLever.h index ad2ae29e5..f1d3ff6d2 100644 --- a/src/Blocks/BlockLever.h +++ b/src/Blocks/BlockLever.h @@ -7,12 +7,13 @@ class cBlockLeverHandler : - public cMetaRotator<cBlockHandler, 0x07, 0x04, 0x02, 0x03, 0x01, false> + public cMetaRotator<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, false> { - typedef cMetaRotator<cBlockHandler, 0x07, 0x04, 0x02, 0x03, 0x01, false> super; + typedef cMetaRotator<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, false> super; + public: - cBlockLeverHandler(BLOCKTYPE a_BlockType) - : cMetaRotator<cBlockHandler, 0x07, 0x04, 0x02, 0x03, 0x01, false>(a_BlockType) + cBlockLeverHandler(BLOCKTYPE a_BlockType) : + super(a_BlockType) { } @@ -132,7 +133,7 @@ public: case 0x05: return 0x06; // Ground rotation case 0x06: return 0x05; - default: return super::MetaRotateCCW(a_Meta); // Wall Rotation + default: return super::MetaRotateCW(a_Meta); // Wall Rotation } } } ; diff --git a/src/Blocks/BlockSign.h b/src/Blocks/BlockSign.h index 9d6fede21..f5630bdb0 100644 --- a/src/Blocks/BlockSign.h +++ b/src/Blocks/BlockSign.h @@ -75,13 +75,13 @@ public: virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override { - return (++a_Meta) & 0x0F; + return (a_Meta + 4) & 0x0f; } virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override { - return (--a_Meta) & 0x0F; + return (a_Meta + 12) & 0x0f; } virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override @@ -90,7 +90,7 @@ public: // There are 16 meta values which correspond to different directions. // These values are equated to angles on a circle; 0x08 = 180 degrees. - return (a_Meta < 0x08) ? 0x08 + a_Meta : 0x08 - a_Meta; + return (a_Meta < 0x08) ? (0x08 + a_Meta) : (0x08 - a_Meta); } diff --git a/src/Blocks/BlockSlab.h b/src/Blocks/BlockSlab.h index f3f2366fd..6c861be86 100644 --- a/src/Blocks/BlockSlab.h +++ b/src/Blocks/BlockSlab.h @@ -124,6 +124,12 @@ public: return E_BLOCK_AIR; } + + virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override + { + // Toggle the 4th bit - up / down: + return (a_Meta ^ 0x08); + } } ; @@ -167,15 +173,6 @@ public: ASSERT(!"Unhandled double slab type!"); return ""; } - - - virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override - { - NIBBLETYPE OtherMeta = a_Meta & 0x07; // Contains unrelated meta data. - - // 8th bit is up/down. 1 right-side-up, 0 is up-side-down. - return (a_Meta & 0x08) ? 0x00 + OtherMeta : 0x01 + OtherMeta; - } } ; diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 0b8e2d1a9..2e14cf368 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -1616,6 +1616,12 @@ void cChunk::AddBlockEntity(cBlockEntity * a_BlockEntity) cBlockEntity * cChunk::GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ) { + // Check that the query coords are within chunk bounds: + ASSERT(a_BlockX >= m_PosX * cChunkDef::Width); + ASSERT(a_BlockX < m_PosX * cChunkDef::Width + cChunkDef::Width); + ASSERT(a_BlockZ >= m_PosZ * cChunkDef::Width); + ASSERT(a_BlockZ < m_PosZ * cChunkDef::Width + cChunkDef::Width); + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) { if ( diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 1c6c989c5..a39a87077 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -231,6 +231,9 @@ AString cClientHandle::FormatMessageType(bool ShouldAppendChatPrefixes, eMessage AString cClientHandle::GenerateOfflineUUID(const AString & a_Username) { + // Online UUIDs are always version 4 (random) + // We use Version 3 (MD5 hash) UUIDs for the offline UUIDs + // This guarantees that they will never collide with an online UUID and can be distinguished. // Proper format for a version 3 UUID is: // xxxxxxxx-xxxx-3xxx-yxxx-xxxxxxxxxxxx where x is any hexadecimal digit and y is one of 8, 9, A, or B @@ -253,6 +256,32 @@ AString cClientHandle::GenerateOfflineUUID(const AString & a_Username) +bool cClientHandle::IsUUIDOnline(const AString & a_UUID) +{ + // Online UUIDs are always version 4 (random) + // We use Version 3 (MD5 hash) UUIDs for the offline UUIDs + // This guarantees that they will never collide with an online UUID and can be distinguished. + // The version-specifying char is at pos #12 of raw UUID, pos #14 in dashed-UUID. + switch (a_UUID.size()) + { + case 32: + { + // This is the UUID format without dashes, the version char is at pos #12: + return (a_UUID[12] == '4'); + } + case 36: + { + // This is the UUID format with dashes, the version char is at pos #14: + return (a_UUID[14] == '4'); + } + } + return false; +} + + + + + void cClientHandle::Kick(const AString & a_Reason) { if (m_State >= csAuthenticating) // Don't log pings diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 99d10c3aa..d6e086bd2 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -63,7 +63,7 @@ public: const AString & GetIPString(void) const { return m_IPString; } - cPlayer* GetPlayer() { return m_Player; } // tolua_export + cPlayer * GetPlayer(void) { return m_Player; } // tolua_export const AString & GetUUID(void) const { return m_UUID; } // tolua_export void SetUUID(const AString & a_UUID) { m_UUID = a_UUID; } @@ -76,9 +76,16 @@ public: /** Generates an UUID based on the player name provided. This is used for the offline (non-auth) mode, when there's no UUID source. - Each username generates a unique and constant UUID, so that when the player reconnects with the same name, their UUID is the same. */ + Each username generates a unique and constant UUID, so that when the player reconnects with the same name, their UUID is the same. + Returns a 36-char UUID (with dashes). */ static AString GenerateOfflineUUID(const AString & a_Username); // tolua_export + /** Returns true if the UUID is generated by online auth, false if it is an offline-generated UUID. + We use Version-3 UUIDs for offline UUIDs, online UUIDs are Version-4, thus we can tell them apart. + Accepts both 32-char and 36-char UUIDs (with and without dashes). + If the string given is not a valid UUID, returns false. */ + static bool IsUUIDOnline(const AString & a_UUID); // tolua_export + /** Formats the type of message with the proper color and prefix for sending to the client. **/ static AString FormatMessageType(bool ShouldAppendChatPrefixes, eMessageType a_ChatPrefix, const AString & a_AdditionalData); diff --git a/src/Generating/Prefab.cpp b/src/Generating/Prefab.cpp index 2ab1455b9..7d876909a 100644 --- a/src/Generating/Prefab.cpp +++ b/src/Generating/Prefab.cpp @@ -211,6 +211,17 @@ void cPrefab::Draw(cChunkDesc & a_Dest, const Vector3i & a_Placement, int a_NumR int ChunkStartZ = a_Dest.GetChunkZ() * cChunkDef::Width; Placement.Move(-ChunkStartX, 0, -ChunkStartZ); const cBlockArea & Image = m_BlockArea[a_NumRotations]; + + // If the placement is outside this chunk, bail out: + if ( + (Placement.x > cChunkDef::Width) || (Placement.x + Image.GetSizeX() < 0) || + (Placement.z > cChunkDef::Width) || (Placement.z + Image.GetSizeZ() < 0) + ) + { + return; + } + + // Write the image: a_Dest.WriteBlockArea(Image, Placement.x, Placement.y, Placement.z, m_MergeStrategy); // If requested, draw the floor (from the bottom of the prefab down to the nearest non-air) diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp index 183c527ca..10446a879 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.cpp +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -59,21 +59,20 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, int RelZ = 0; BLOCKTYPE Block; NIBBLETYPE Meta; - cChunk * Chunk; + cChunk * OtherChunk = a_Chunk; if (a_OtherChunk != NULL) { RelX = a_BlockX - a_OtherChunk->GetPosX() * cChunkDef::Width; RelZ = a_BlockZ - a_OtherChunk->GetPosZ() * cChunkDef::Width; a_OtherChunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta); - Chunk = a_OtherChunk; + OtherChunk = a_OtherChunk; } else { RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; a_Chunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta); - Chunk = a_Chunk; } // Every time a block is changed (AddBlock called), we want to go through all lists and check to see if the coordiantes stored within are still valid @@ -92,7 +91,8 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, { LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list as it no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); itr = PoweredBlocks->erase(itr); - Chunk->SetIsRedstoneDirty(true); + a_Chunk->SetIsRedstoneDirty(true); + OtherChunk->SetIsRedstoneDirty(true); continue; } else if ( @@ -107,21 +107,23 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, { LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); itr = PoweredBlocks->erase(itr); - Chunk->SetIsRedstoneDirty(true); + a_Chunk->SetIsRedstoneDirty(true); + OtherChunk->SetIsRedstoneDirty(true); continue; } else if (Block == E_BLOCK_DAYLIGHT_SENSOR) { - if (!m_World.IsChunkLighted(Chunk->GetPosX(), Chunk->GetPosZ())) + if (!m_World.IsChunkLighted(OtherChunk->GetPosX(), OtherChunk->GetPosZ())) { - m_World.QueueLightChunk(Chunk->GetPosX(), Chunk->GetPosZ()); + m_World.QueueLightChunk(OtherChunk->GetPosX(), OtherChunk->GetPosZ()); } else { - if (Chunk->GetTimeAlteredLight(Chunk->GetSkyLight(RelX, a_BlockY + 1, RelZ)) <= 7) + if (OtherChunk->GetTimeAlteredLight(OtherChunk->GetSkyLight(RelX, a_BlockY + 1, RelZ)) <= 7) { itr = PoweredBlocks->erase(itr); - Chunk->SetIsRedstoneDirty(true); + a_Chunk->SetIsRedstoneDirty(true); + OtherChunk->SetIsRedstoneDirty(true); continue; } } @@ -139,7 +141,8 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, { LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); itr = LinkedPoweredBlocks->erase(itr); - Chunk->SetIsRedstoneDirty(true); + a_Chunk->SetIsRedstoneDirty(true); + OtherChunk->SetIsRedstoneDirty(true); continue; } else if ( @@ -153,7 +156,8 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, { LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); itr = LinkedPoweredBlocks->erase(itr); - Chunk->SetIsRedstoneDirty(true); + a_Chunk->SetIsRedstoneDirty(true); + OtherChunk->SetIsRedstoneDirty(true); continue; } } @@ -163,7 +167,8 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, { LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer powered through a valid middle block", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); itr = LinkedPoweredBlocks->erase(itr); - Chunk->SetIsRedstoneDirty(true); + a_Chunk->SetIsRedstoneDirty(true); + OtherChunk->SetIsRedstoneDirty(true); continue; } } @@ -343,6 +348,7 @@ void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int case E_BLOCK_REDSTONE_REPEATER_ON: { HandleRedstoneRepeater(dataitr->x, dataitr->y, dataitr->z, dataitr->Data); + break; } case E_BLOCK_REDSTONE_TORCH_OFF: case E_BLOCK_REDSTONE_TORCH_ON: @@ -520,8 +526,11 @@ void cIncrementalRedstoneSimulator::HandleFenceGate(int a_RelBlockX, int a_RelBl { if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true)) { - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MetaData | 0x4); - m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0); + if ((MetaData & 0x4) == 0) + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MetaData | 0x4); + m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0); + } SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true); } } @@ -529,8 +538,11 @@ void cIncrementalRedstoneSimulator::HandleFenceGate(int a_RelBlockX, int a_RelBl { if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false)) { - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MetaData & 0xFFFFFFFB); - m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0); + if ((MetaData & 0x4) != 0) + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MetaData & ~0x04); + m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0); + } SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false); } } @@ -758,7 +770,7 @@ void cIncrementalRedstoneSimulator::HandleRedstoneRepeater(int a_RelBlockX, int void cIncrementalRedstoneSimulator::HandleRedstoneRepeaterDelays() { - for (RepeatersDelayList::iterator itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end(); itr++) + for (RepeatersDelayList::iterator itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end();) { if (itr->a_ElapsedTicks >= itr->a_DelayTicks) // Has the elapsed ticks reached the target ticks? @@ -804,7 +816,7 @@ void cIncrementalRedstoneSimulator::HandleRedstoneRepeaterDelays() { m_Chunk->SetBlock(RelBlockX, RelBlockY, RelBlockZ, E_BLOCK_REDSTONE_REPEATER_OFF, Meta); } - m_RepeatersDelayList->erase(itr); + itr = m_RepeatersDelayList->erase(itr); } else { @@ -813,6 +825,7 @@ void cIncrementalRedstoneSimulator::HandleRedstoneRepeaterDelays() // I am confounded to say why. Perhaps optimisation failure. LOGD("Incremented a repeater @ {%i %i %i} | Elapsed ticks: %i | Target delay: %i", itr->a_RelBlockPos.x, itr->a_RelBlockPos.y, itr->a_RelBlockPos.z, itr->a_ElapsedTicks, itr->a_DelayTicks); itr->a_ElapsedTicks++; + itr++; } } } @@ -1072,19 +1085,17 @@ void cIncrementalRedstoneSimulator::HandleNoteBlock(int a_RelBlockX, int a_RelBl void cIncrementalRedstoneSimulator::HandleDaylightSensor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) { - int a_ChunkX, a_ChunkZ; - cChunkDef::BlockToChunk(a_RelBlockX, a_RelBlockZ, a_ChunkX, a_ChunkZ); + int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX, BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ); - if (!m_World.IsChunkLighted(a_ChunkX, a_ChunkZ)) + if (!m_World.IsChunkLighted(ChunkX, ChunkZ)) { - m_World.QueueLightChunk(a_ChunkX, a_ChunkZ); + m_World.QueueLightChunk(ChunkX, ChunkZ); } else { - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; - NIBBLETYPE SkyLight = m_Chunk->GetTimeAlteredLight(m_World.GetBlockSkyLight(BlockX, a_RelBlockY + 1, BlockZ)); - if (SkyLight > 8) + if (m_Chunk->GetTimeAlteredLight(m_World.GetBlockSkyLight(BlockX, a_RelBlockY + 1, BlockZ)) > 8) { SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); } diff --git a/src/VoronoiMap.cpp b/src/VoronoiMap.cpp index 9c3ca7e65..5efd09c01 100644 --- a/src/VoronoiMap.cpp +++ b/src/VoronoiMap.cpp @@ -14,7 +14,9 @@ cVoronoiMap::cVoronoiMap(int a_Seed, int a_CellSize) : m_Noise1(a_Seed + 1), m_Noise2(a_Seed + 2), m_Noise3(a_Seed + 3), - m_CellSize(a_CellSize) + m_CellSize(a_CellSize), + m_CurrentCellX(9999999), // Cell coords that are definitely out of the range for normal generator, so that the first query will overwrite them + m_CurrentCellZ(9999999) { } diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index d058a4583..93ade6448 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -345,6 +345,7 @@ void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_C m_Writer.AddDouble("", a_Entity->GetYaw()); m_Writer.AddDouble("", a_Entity->GetPitch()); m_Writer.EndList(); + m_Writer.AddShort("Health", a_Entity->GetHealth()); } @@ -576,7 +577,6 @@ void cNBTChunkSerializer::AddPickupEntity(cPickup * a_Pickup) m_Writer.BeginCompound(""); AddBasicEntity(a_Pickup, "Item"); AddItem(a_Pickup->GetItem(), -1, "Item"); - m_Writer.AddShort("Health", (Int16)(unsigned char)a_Pickup->GetHealth()); m_Writer.AddShort("Age", (Int16)a_Pickup->GetAge()); m_Writer.EndCompound(); } @@ -679,7 +679,6 @@ void cNBTChunkSerializer::AddExpOrbEntity(cExpOrb * a_ExpOrb) { m_Writer.BeginCompound(""); AddBasicEntity(a_ExpOrb, "XPOrb"); - m_Writer.AddShort("Health", (Int16)(unsigned char)a_ExpOrb->GetHealth()); m_Writer.AddShort("Age", (Int16)a_ExpOrb->GetAge()); m_Writer.AddShort("Value", (Int16)a_ExpOrb->GetReward()); m_Writer.EndCompound(); diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index a3259b769..f714c0414 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -1461,13 +1461,6 @@ void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a { return; } - - // Load health: - int Health = a_NBT.FindChildByName(a_TagIdx, "Health"); - if (Health > 0) - { - Pickup->SetHealth((int) (a_NBT.GetShort(Health) & 0xFF)); - } // Load age: int Age = a_NBT.FindChildByName(a_TagIdx, "Age"); @@ -1513,13 +1506,6 @@ void cWSSAnvil::LoadExpOrbFromNBT(cEntityList & a_Entities, const cParsedNBT & a return; } - // Load Health: - int Health = a_NBT.FindChildByName(a_TagIdx, "Health"); - if (Health > 0) - { - ExpOrb->SetHealth((int) (a_NBT.GetShort(Health) & 0xFF)); - } - // Load Age: int Age = a_NBT.FindChildByName(a_TagIdx, "Age"); if (Age > 0) @@ -2437,6 +2423,13 @@ bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_N } a_Entity.SetYaw(Rotation[0]); a_Entity.SetRoll(Rotation[1]); + + // Load health: + int Health = a_NBT.FindChildByName(a_TagIdx, "Health"); + if (Health > 0) + { + a_Entity.SetHealth(a_NBT.GetShort(Health)); + } return true; } |