diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Bindings/ManualBindings.cpp | 81 | ||||
-rw-r--r-- | src/Blocks/BlockComparator.h | 7 | ||||
-rw-r--r-- | src/Blocks/BlockDoor.cpp | 23 | ||||
-rw-r--r-- | src/Blocks/BlockDoor.h | 1 | ||||
-rw-r--r-- | src/Blocks/BlockFenceGate.h | 6 | ||||
-rw-r--r-- | src/Blocks/BlockHandler.cpp | 10 | ||||
-rw-r--r-- | src/Blocks/BlockHandler.h | 3 | ||||
-rw-r--r-- | src/Blocks/BlockRedstoneRepeater.h | 9 | ||||
-rw-r--r-- | src/Blocks/BlockTNT.h | 32 | ||||
-rw-r--r-- | src/Blocks/BlockTrapdoor.h | 6 | ||||
-rw-r--r-- | src/Blocks/WorldInterface.h | 3 | ||||
-rw-r--r-- | src/ClientHandle.cpp | 14 | ||||
-rw-r--r-- | src/Inventory.cpp | 6 | ||||
-rw-r--r-- | src/Scoreboard.cpp | 2 | ||||
-rw-r--r-- | src/Simulator/FloodyFluidSimulator.cpp | 91 | ||||
-rw-r--r-- | src/Simulator/FloodyFluidSimulator.h | 18 | ||||
-rw-r--r-- | src/Simulator/VanillaFluidSimulator.cpp | 150 | ||||
-rw-r--r-- | src/Simulator/VanillaFluidSimulator.h | 42 | ||||
-rw-r--r-- | src/World.cpp | 26 | ||||
-rw-r--r-- | src/World.h | 2 | ||||
-rw-r--r-- | src/WorldStorage/SchematicFileSerializer.cpp | 133 | ||||
-rw-r--r-- | src/WorldStorage/SchematicFileSerializer.h | 33 |
22 files changed, 633 insertions, 65 deletions
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index b094da5fc..a5247bbe6 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -2457,7 +2457,7 @@ static int tolua_cBlockArea_GetSize(lua_State * tolua_S) static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S) { // function cBlockArea::LoadFromSchematicFile - // Exported manually because function has been moved to SchematicFileSerilizer.cpp + // Exported manually because function has been moved to SchematicFileSerializer.cpp cLuaState L(tolua_S); if ( !L.CheckParamUserType(1, "cBlockArea") || @@ -2484,10 +2484,41 @@ static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S) +static int tolua_cBlockArea_LoadFromSchematicString(lua_State * tolua_S) +{ + // function cBlockArea::LoadFromSchematicString + // Exported manually because function has been moved to SchematicFileSerializer.cpp + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cBlockArea") || + !L.CheckParamString (2) || + !L.CheckParamEnd (3) + ) + { + return 0; + } + cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL); + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea::LoadFromSchematicFile'", NULL); + return 0; + } + + AString Data; + L.GetStackValue(2, Data); + bool res = cSchematicFileSerializer::LoadFromSchematicString(*self, Data); + tolua_pushboolean(tolua_S, res); + return 1; +} + + + + + static int tolua_cBlockArea_SaveToSchematicFile(lua_State * tolua_S) { // function cBlockArea::SaveToSchematicFile - // Exported manually because function has been moved to SchematicFileSerilizer.cpp + // Exported manually because function has been moved to SchematicFileSerializer.cpp cLuaState L(tolua_S); if ( !L.CheckParamUserType(1, "cBlockArea") || @@ -2513,6 +2544,38 @@ static int tolua_cBlockArea_SaveToSchematicFile(lua_State * tolua_S) +static int tolua_cBlockArea_SaveToSchematicString(lua_State * tolua_S) +{ + // function cBlockArea::SaveToSchematicString + // Exported manually because function has been moved to SchematicFileSerializer.cpp + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cBlockArea") || + !L.CheckParamEnd (2) + ) + { + return 0; + } + cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL); + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea::SaveToSchematicFile'", NULL); + return 0; + } + + AString Data; + if (cSchematicFileSerializer::SaveToSchematicString(*self, Data)) + { + L.Push(Data); + return 1; + } + return 0; +} + + + + + static int tolua_cCompositeChat_AddRunCommandPart(lua_State * tolua_S) { // function cCompositeChat:AddRunCommandPart(Message, Command, [Style]) @@ -2776,12 +2839,14 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cBlockArea"); - tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cBlockArea_GetBlockTypeMeta); - tolua_function(tolua_S, "GetOrigin", tolua_cBlockArea_GetOrigin); - tolua_function(tolua_S, "GetRelBlockTypeMeta", tolua_cBlockArea_GetRelBlockTypeMeta); - tolua_function(tolua_S, "GetSize", tolua_cBlockArea_GetSize); - tolua_function(tolua_S, "LoadFromSchematicFile", tolua_cBlockArea_LoadFromSchematicFile); - tolua_function(tolua_S, "SaveToSchematicFile", tolua_cBlockArea_SaveToSchematicFile); + tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cBlockArea_GetBlockTypeMeta); + tolua_function(tolua_S, "GetOrigin", tolua_cBlockArea_GetOrigin); + tolua_function(tolua_S, "GetRelBlockTypeMeta", tolua_cBlockArea_GetRelBlockTypeMeta); + tolua_function(tolua_S, "GetSize", tolua_cBlockArea_GetSize); + tolua_function(tolua_S, "LoadFromSchematicFile", tolua_cBlockArea_LoadFromSchematicFile); + tolua_function(tolua_S, "LoadFromSchematicString", tolua_cBlockArea_LoadFromSchematicString); + tolua_function(tolua_S, "SaveToSchematicFile", tolua_cBlockArea_SaveToSchematicFile); + tolua_function(tolua_S, "SaveToSchematicString", tolua_cBlockArea_SaveToSchematicString); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cCompositeChat"); diff --git a/src/Blocks/BlockComparator.h b/src/Blocks/BlockComparator.h index aba390d9d..cea35a864 100644 --- a/src/Blocks/BlockComparator.h +++ b/src/Blocks/BlockComparator.h @@ -26,6 +26,13 @@ public: } + virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override + { + UNUSED(a_ChunkInterface); + a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override { // Reset meta to 0 diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp index 2ff5c1c37..0acf04852 100644 --- a/src/Blocks/BlockDoor.cpp +++ b/src/Blocks/BlockDoor.cpp @@ -55,6 +55,29 @@ void cBlockDoorHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterfac +void cBlockDoorHandler::OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) +{ + UNUSED(a_ChunkInterface); + + a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); + NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + if (Meta & 8) + { + // Current block is top of the door + a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY - 1, a_BlockZ, a_Player); + } + else + { + // Current block is bottom of the door + a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, a_Player); + } +} + + + + + void cBlockDoorHandler::OnPlacedByPlayer( cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h index ef0dbb787..68be2c658 100644 --- a/src/Blocks/BlockDoor.h +++ b/src/Blocks/BlockDoor.h @@ -16,6 +16,7 @@ public: virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override; virtual void 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) override; + virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override; virtual const char * GetStepSound(void) override; diff --git a/src/Blocks/BlockFenceGate.h b/src/Blocks/BlockFenceGate.h index fb984f345..1abe1eecf 100644 --- a/src/Blocks/BlockFenceGate.h +++ b/src/Blocks/BlockFenceGate.h @@ -48,6 +48,12 @@ public: } + virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override + { + a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); + } + + virtual bool IsUseable(void) override { return true; diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp index 052f88f7a..c5165986c 100644 --- a/src/Blocks/BlockHandler.cpp +++ b/src/Blocks/BlockHandler.cpp @@ -57,6 +57,7 @@ #include "BlockRedstoneLamp.h" #include "BlockRedstoneRepeater.h" #include "BlockRedstoneTorch.h" +#include "BlockTNT.h" #include "BlockSand.h" #include "BlockSapling.h" #include "BlockSideways.h" @@ -185,6 +186,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_TALL_GRASS: return new cBlockTallGrassHandler (a_BlockType); case E_BLOCK_TORCH: return new cBlockTorchHandler (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_WATER: return new cBlockFluidHandler (a_BlockType); @@ -320,6 +322,14 @@ void cBlockHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & +void cBlockHandler::OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer *a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) +{ +} + + + + + void cBlockHandler::ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) { // Setting the meta to a_BlockMeta keeps most textures. The few other blocks have to override this. diff --git a/src/Blocks/BlockHandler.h b/src/Blocks/BlockHandler.h index c46a46045..50c2e2ad5 100644 --- a/src/Blocks/BlockHandler.h +++ b/src/Blocks/BlockHandler.h @@ -69,6 +69,9 @@ public: /// Called if the user right clicks the block and the block is useable virtual void 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); + /** Called when a Right Click to this Block is cancelled */ + virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace); + /// <summary>Called when the item is mined to convert it into pickups. Pickups may specify multiple items. Appends items to a_Pickups, preserves its original contents</summary> virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta); diff --git a/src/Blocks/BlockRedstoneRepeater.h b/src/Blocks/BlockRedstoneRepeater.h index eb0918acf..1e2a86949 100644 --- a/src/Blocks/BlockRedstoneRepeater.h +++ b/src/Blocks/BlockRedstoneRepeater.h @@ -29,7 +29,7 @@ public: a_BlockMeta = RepeaterRotationToMetaData(a_Player->GetYaw()); return true; } - + virtual void 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) override { @@ -37,6 +37,13 @@ public: } + virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override + { + UNUSED(a_ChunkInterface); + a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override { // Reset meta to 0 diff --git a/src/Blocks/BlockTNT.h b/src/Blocks/BlockTNT.h new file mode 100644 index 000000000..283a03730 --- /dev/null +++ b/src/Blocks/BlockTNT.h @@ -0,0 +1,32 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockTNTHandler : + public cBlockHandler +{ +public: + cBlockTNTHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual const char * GetStepSound(void) override + { + return "step.grass"; + } + + virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override + { + a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); + } +}; + + + + diff --git a/src/Blocks/BlockTrapdoor.h b/src/Blocks/BlockTrapdoor.h index a28861e69..9bae92c4d 100644 --- a/src/Blocks/BlockTrapdoor.h +++ b/src/Blocks/BlockTrapdoor.h @@ -42,6 +42,12 @@ public: World->BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0, a_Player->GetClientHandle()); } + virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override + { + UNUSED(a_ChunkInterface); + a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); + } + virtual bool GetPlacementBlockTypeMeta( cChunkInterface & a_ChunkInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, diff --git a/src/Blocks/WorldInterface.h b/src/Blocks/WorldInterface.h index b6f2f55a7..e59b00eff 100644 --- a/src/Blocks/WorldInterface.h +++ b/src/Blocks/WorldInterface.h @@ -27,4 +27,7 @@ public: /** Spawns a mob of the specified type. Returns the mob's EntityID if recognized and spawned, <0 otherwise */ virtual int SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eType a_MonsterType) = 0; + + /** Sends the block on those coords to the player */ + virtual void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, cPlayer * a_Player) = 0; }; diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 6982a6227..870568cdf 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -920,14 +920,22 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, ItemToFullString(a_HeldItem).c_str() ); + cWorld * World = m_Player->GetWorld(); + cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); if (PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) { // A plugin doesn't agree with the action, replace the block on the client and quit: + cChunkInterface ChunkInterface(World->GetChunkMap()); + BLOCKTYPE BlockType = World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + cBlockHandler * BlockHandler = cBlockInfo::GetHandler(BlockType); + BlockHandler->OnCancelRightClick(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + if (a_BlockFace > -1) { AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + World->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, m_Player); //2 block high things } return; } @@ -953,12 +961,10 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e if (a_BlockFace > -1) { AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); } return; } - - cWorld * World = m_Player->GetWorld(); BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; diff --git a/src/Inventory.cpp b/src/Inventory.cpp index 7f434adfd..c7c089d5f 100644 --- a/src/Inventory.cpp +++ b/src/Inventory.cpp @@ -204,6 +204,12 @@ void cInventory::SetSlot(int a_SlotNum, const cItem & a_Item) return; } Grid->SetSlot(GridSlotNum, a_Item); + + // Broadcast the Equipped Item, if the Slot is changed. + if ((Grid == &m_HotbarSlots) && (m_EquippedSlotNum == (a_SlotNum - invHotbarOffset))) + { + m_Owner.GetWorld()->BroadcastEntityEquipment(m_Owner, 0, a_Item, m_Owner.GetClientHandle()); + } } diff --git a/src/Scoreboard.cpp b/src/Scoreboard.cpp index c1da27086..8088e624b 100644 --- a/src/Scoreboard.cpp +++ b/src/Scoreboard.cpp @@ -506,7 +506,7 @@ bool cScoreboard::ForEachObjective(cObjectiveCallback& a_Callback) bool cScoreboard::ForEachTeam(cTeamCallback& a_Callback) { - cCSLock Lock(m_CSObjectives); + cCSLock Lock(m_CSTeams); for (cTeamMap::iterator it = m_Teams.begin(); it != m_Teams.end(); ++it) { diff --git a/src/Simulator/FloodyFluidSimulator.cpp b/src/Simulator/FloodyFluidSimulator.cpp index 95182345c..03e94e791 100644 --- a/src/Simulator/FloodyFluidSimulator.cpp +++ b/src/Simulator/FloodyFluidSimulator.cpp @@ -54,14 +54,23 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ) ); - NIBBLETYPE MyMeta = a_Chunk->GetMeta(a_RelX, a_RelY, a_RelZ); - if (!IsAnyFluidBlock(a_Chunk->GetBlock(a_RelX, a_RelY, a_RelZ))) + BLOCKTYPE MyBlock; NIBBLETYPE MyMeta; + a_Chunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, MyBlock, MyMeta); + + if (!IsAnyFluidBlock(MyBlock)) { // Can happen - if a block is scheduled for simulating and gets replaced in the meantime. FLOG(" BadBlockType exit"); return; } + // When in contact with water, lava should harden + if (HardenBlock(a_Chunk, a_RelX, a_RelY, a_RelZ, MyBlock, MyMeta)) + { + // Block was changed, bail out + return; + } + if (MyMeta != 0) { // Source blocks aren't checked for tributaries, others are. @@ -86,7 +95,12 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re { // Spread only down, possibly washing away what's there or turning lava to stone / cobble / obsidian: SpreadToNeighbor(a_Chunk, a_RelX, a_RelY - 1, a_RelZ, 8); - SpreadFurther = false; + + // Source blocks spread both downwards and sideways + if (MyMeta != 0) + { + SpreadFurther = false; + } } // If source creation is on, check for it here: else if ( @@ -105,10 +119,7 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re if (SpreadFurther && (NewMeta < 8)) { // Spread to the neighbors: - SpreadToNeighbor(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, NewMeta); - SpreadToNeighbor(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, NewMeta); - SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, NewMeta); - SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, NewMeta); + Spread(a_Chunk, a_RelX, a_RelY, a_RelZ, NewMeta); } // Mark as processed: @@ -119,6 +130,17 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re +void cFloodyFluidSimulator::Spread(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta) +{ + SpreadToNeighbor(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, a_NewMeta); + SpreadToNeighbor(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, a_NewMeta); + SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, a_NewMeta); + SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, a_NewMeta); +} + + + + bool cFloodyFluidSimulator::CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta) { // If we have a section above, check if there's fluid above this block that would feed it: @@ -296,6 +318,8 @@ void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, i a_NewMeta ); a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta); + + HardenBlock(a_NearChunk, a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta); } @@ -348,3 +372,56 @@ bool cFloodyFluidSimulator::CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX + +bool cFloodyFluidSimulator::HardenBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta) +{ + // Only lava blocks can harden + if (!IsBlockLava(a_BlockType)) + { + return false; + } + + bool ShouldHarden = false; + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + static const Vector3i Coords[] = + { + Vector3i( 1, 0, 0), + Vector3i(-1, 0, 0), + Vector3i( 0, 0, 1), + Vector3i( 0, 0, -1), + }; + for (size_t i = 0; i < ARRAYCOUNT(Coords); i++) + { + if (!a_Chunk->UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta)) + { + continue; + } + if (IsBlockWater(BlockType)) + { + ShouldHarden = true; + } + } // for i - Coords[] + + if (ShouldHarden) + { + if (a_Meta == 0) + { + // Source lava block + a_Chunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_OBSIDIAN, 0); + return true; + } + // Ignore last lava level + else if (a_Meta <= 4) + { + a_Chunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_COBBLESTONE, 0); + return true; + } + } + + return false; +} + + + diff --git a/src/Simulator/FloodyFluidSimulator.h b/src/Simulator/FloodyFluidSimulator.h index c4af2e246..632de3bb2 100644 --- a/src/Simulator/FloodyFluidSimulator.h +++ b/src/Simulator/FloodyFluidSimulator.h @@ -38,14 +38,26 @@ protected: // cDelayedFluidSimulator overrides: virtual void SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override; - /// Checks tributaries, if not fed, decreases the block's level and returns true + /** Checks tributaries, if not fed, decreases the block's level and returns true. */ bool CheckTributaries(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_MyMeta); - /// Spreads into the specified block, if the blocktype there allows. a_Area is for checking. + /** Spreads into the specified block, if the blocktype there allows. a_Area is for checking. */ void SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta); - /// Checks if there are enough neighbors to create a source at the coords specified; turns into source and returns true if so + /** Checks if there are enough neighbors to create a source at the coords specified; turns into source and returns true if so. */ bool CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ); + + /** Checks if the specified block should harden (Water/Lava interaction) and if so, converts it to a suitable block. + * + * Returns whether the block was changed or not. + */ + bool HardenBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta); + + /** Spread water to neighbors. + * + * May be overridden to provide more sophisticated algorithms. + */ + virtual void Spread(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta); } ; diff --git a/src/Simulator/VanillaFluidSimulator.cpp b/src/Simulator/VanillaFluidSimulator.cpp new file mode 100644 index 000000000..78aff9d68 --- /dev/null +++ b/src/Simulator/VanillaFluidSimulator.cpp @@ -0,0 +1,150 @@ + +// VanillaFluidSimulator.cpp + +#include "Globals.h" + +#include "VanillaFluidSimulator.h" +#include "../World.h" +#include "../Chunk.h" +#include "../BlockArea.h" +#include "../Blocks/BlockHandler.h" +#include "../BlockInServerPluginInterface.h" + + + + + +static const int InfiniteCost = 100; + + + + + +cVanillaFluidSimulator::cVanillaFluidSimulator( + cWorld & a_World, + BLOCKTYPE a_Fluid, + BLOCKTYPE a_StationaryFluid, + NIBBLETYPE a_Falloff, + int a_TickDelay, + int a_NumNeighborsForSource +) : super(a_World, a_Fluid, a_StationaryFluid, a_Falloff, a_TickDelay, a_NumNeighborsForSource) +{ +} + + + + + +void cVanillaFluidSimulator::Spread(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta) +{ + int Cost[4]; + Cost[0] = CalculateFlowCost(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, X_PLUS); + Cost[1] = CalculateFlowCost(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, X_MINUS); + Cost[2] = CalculateFlowCost(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, Z_PLUS); + Cost[3] = CalculateFlowCost(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, Z_MINUS); + + int MinCost = InfiniteCost; + for (unsigned int i = 0; i < ARRAYCOUNT(Cost); ++i) + { + if (Cost[i] < MinCost) + { + MinCost = Cost[i]; + } + } + + if (Cost[0] == MinCost) + { + SpreadToNeighbor(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, a_NewMeta); + } + if (Cost[1] == MinCost) + { + SpreadToNeighbor(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, a_NewMeta); + } + if (Cost[2] == MinCost) + { + SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, a_NewMeta); + } + if (Cost[3] == MinCost) + { + SpreadToNeighbor(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, a_NewMeta); + } +} + + + + + +int cVanillaFluidSimulator::CalculateFlowCost(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, Direction a_Dir, unsigned a_Iteration) +{ + int Cost = InfiniteCost; + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + + // Check if block is passable + if (!a_Chunk->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta)) + { + return Cost; + } + if (!IsPassableForFluid(BlockType) && !IsBlockLiquid(BlockType)) + { + return Cost; + } + + // Check if block below is passable + if (!a_Chunk->UnboundedRelGetBlock(a_RelX, a_RelY - 1, a_RelZ, BlockType, BlockMeta)) + { + return Cost; + } + if (IsPassableForFluid(BlockType) || IsBlockLiquid(BlockType)) + { + // Path found, exit + return a_Iteration; + } + + // 5 blocks away, bail out + if (a_Iteration > 3) + { + return Cost; + } + + // Recurse + if (a_Dir != X_MINUS) + { + int NextCost = CalculateFlowCost(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, X_PLUS, a_Iteration + 1); + if (NextCost < Cost) + { + Cost = NextCost; + } + } + if (a_Dir != X_PLUS) + { + int NextCost = CalculateFlowCost(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, X_MINUS, a_Iteration + 1); + if (NextCost < Cost) + { + Cost = NextCost; + } + } + if (a_Dir != Z_MINUS) + { + int NextCost = CalculateFlowCost(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, Z_PLUS, a_Iteration + 1); + if (NextCost < Cost) + { + Cost = NextCost; + } + } + if (a_Dir != Z_PLUS) + { + int NextCost = CalculateFlowCost(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, Z_MINUS, a_Iteration + 1); + if (NextCost < Cost) + { + Cost = NextCost; + } + } + + return Cost; +} + + + + diff --git a/src/Simulator/VanillaFluidSimulator.h b/src/Simulator/VanillaFluidSimulator.h new file mode 100644 index 000000000..a9ea98b5a --- /dev/null +++ b/src/Simulator/VanillaFluidSimulator.h @@ -0,0 +1,42 @@ + +// VanillaFluidSimulator.h + + + + + +#pragma once + +#include "FloodyFluidSimulator.h" + + + + + +// fwd: +class cBlockArea; + + + + + +class cVanillaFluidSimulator : + public cFloodyFluidSimulator +{ + typedef cFloodyFluidSimulator super; + +public: + cVanillaFluidSimulator(cWorld & a_World, BLOCKTYPE a_Fluid, BLOCKTYPE a_StationaryFluid, NIBBLETYPE a_Falloff, int a_TickDelay, int a_NumNeighborsForSource); + +protected: + // cFloodyFluidSimulator overrides: + virtual void Spread(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta) override; + + /** Recursively calculates the minimum number of blocks needed to descend a level. */ + int CalculateFlowCost(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, Direction a_Dir, unsigned a_Iteration = 0); + +} ; + + + + diff --git a/src/World.cpp b/src/World.cpp index ebc37f7b2..e208cfe91 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -34,6 +34,7 @@ #include "Simulator/NoopRedstoneSimulator.h" #include "Simulator/SandSimulator.h" #include "Simulator/IncrementalRedstoneSimulator.h" +#include "Simulator/VanillaFluidSimulator.h" #include "Simulator/VaporizeFluidSimulator.h" // Mobs: @@ -3062,8 +3063,8 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c AString SimulatorName = a_IniFile.GetValueSet("Physics", SimulatorNameKey, ""); if (SimulatorName.empty()) { - LOGWARNING("[Physics] %s not present or empty in %s, using the default of \"Floody\".", SimulatorNameKey.c_str(), GetIniFileName().c_str()); - SimulatorName = "Floody"; + LOGWARNING("[Physics] %s not present or empty in %s, using the default of \"Vanilla\".", SimulatorNameKey.c_str(), GetIniFileName().c_str()); + SimulatorName = "Vanilla"; } cFluidSimulator * res = NULL; @@ -3087,15 +3088,24 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c } else { - if (NoCaseCompare(SimulatorName, "floody") != 0) - { - // The simulator name doesn't match anything we have, issue a warning: - LOGWARNING("%s [Physics]:%s specifies an unknown simulator, using the default \"Floody\".", GetIniFileName().c_str(), SimulatorNameKey.c_str()); - } int Falloff = a_IniFile.GetValueSetI(SimulatorSectionName, "Falloff", IsWater ? 1 : 2); int TickDelay = a_IniFile.GetValueSetI(SimulatorSectionName, "TickDelay", IsWater ? 5 : 30); int NumNeighborsForSource = a_IniFile.GetValueSetI(SimulatorSectionName, "NumNeighborsForSource", IsWater ? 2 : -1); - res = new cFloodyFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock, Falloff, TickDelay, NumNeighborsForSource); + + if (NoCaseCompare(SimulatorName, "floody") == 0) + { + res = new cFloodyFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock, Falloff, TickDelay, NumNeighborsForSource); + } + else if (NoCaseCompare(SimulatorName, "vanilla") == 0) + { + res = new cVanillaFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock, Falloff, TickDelay, NumNeighborsForSource); + } + else + { + // The simulator name doesn't match anything we have, issue a warning: + LOGWARNING("%s [Physics]:%s specifies an unknown simulator, using the default \"Vanilla\".", GetIniFileName().c_str(), SimulatorNameKey.c_str()); + res = new cVanillaFluidSimulator(*this, a_SimulateBlock, a_StationaryBlock, Falloff, TickDelay, NumNeighborsForSource); + } } m_SimulatorManager->RegisterSimulator(res, Rate); diff --git a/src/World.h b/src/World.h index 517b6b4fa..6e550e06b 100644 --- a/src/World.h +++ b/src/World.h @@ -457,7 +457,7 @@ public: // tolua_begin bool DigBlock (int a_X, int a_Y, int a_Z); - void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player ); + virtual void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player); double GetSpawnX(void) const { return m_SpawnX; } double GetSpawnY(void) const { return m_SpawnY; } diff --git a/src/WorldStorage/SchematicFileSerializer.cpp b/src/WorldStorage/SchematicFileSerializer.cpp index 45fd967bd..b021aeb0c 100644 --- a/src/WorldStorage/SchematicFileSerializer.cpp +++ b/src/WorldStorage/SchematicFileSerializer.cpp @@ -1,10 +1,18 @@ +// SchematicFileSerializer.cpp + +// Implements the cSchematicFileSerializer class representing the interface to load and save cBlockArea to a .schematic file + #include "Globals.h" #include "OSSupport/GZipFile.h" #include "FastNBT.h" - #include "SchematicFileSerializer.h" +#include "../StringCompression.h" + + + + bool cSchematicFileSerializer::LoadFromSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName) { @@ -40,48 +48,51 @@ bool cSchematicFileSerializer::LoadFromSchematicFile(cBlockArea & a_BlockArea, c -bool cSchematicFileSerializer::SaveToSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName) +bool cSchematicFileSerializer::LoadFromSchematicString(cBlockArea & a_BlockArea, const AString & a_SchematicData) { - cFastNBTWriter Writer("Schematic"); - Writer.AddShort("Width", a_BlockArea.m_SizeX); - Writer.AddShort("Height", a_BlockArea.m_SizeY); - Writer.AddShort("Length", a_BlockArea.m_SizeZ); - Writer.AddString("Materials", "Alpha"); - if (a_BlockArea.HasBlockTypes()) + // Uncompress the data: + AString UngzippedData; + if (UncompressStringGZIP(a_SchematicData.data(), a_SchematicData.size(), UngzippedData) != Z_OK) { - Writer.AddByteArray("Blocks", (const char *)a_BlockArea.m_BlockTypes, a_BlockArea.GetBlockCount()); - } - else - { - AString Dummy(a_BlockArea.GetBlockCount(), 0); - Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size()); + LOG("%s: Cannot unGZip the schematic data.", __FUNCTION__); + return false; } - if (a_BlockArea.HasBlockMetas()) + + // Parse the NBT: + cParsedNBT NBT(UngzippedData.data(), UngzippedData.size()); + if (!NBT.IsValid()) { - Writer.AddByteArray("Data", (const char *)a_BlockArea.m_BlockMetas, a_BlockArea.GetBlockCount()); + LOG("%s: Cannot parse the NBT in the schematic data.", __FUNCTION__); + return false; } - else + + return LoadFromSchematicNBT(a_BlockArea, NBT); +} + + + + + +bool cSchematicFileSerializer::SaveToSchematicFile(const cBlockArea & a_BlockArea, const AString & a_FileName) +{ + // Serialize into NBT data: + AString NBT = SaveToSchematicNBT(a_BlockArea); + if (NBT.empty()) { - AString Dummy(a_BlockArea.GetBlockCount(), 0); - Writer.AddByteArray("Data", Dummy.data(), Dummy.size()); + LOG("%s: Cannot serialize the area into an NBT representation for file \"%s\".", __FUNCTION__, a_FileName.c_str()); + return false; } - // TODO: Save entities and block entities - Writer.BeginList("Entities", TAG_Compound); - Writer.EndList(); - Writer.BeginList("TileEntities", TAG_Compound); - Writer.EndList(); - Writer.Finish(); // Save to file cGZipFile File; if (!File.Open(a_FileName, cGZipFile::fmWrite)) { - LOG("Cannot open file \"%s\" for writing.", a_FileName.c_str()); + LOG("%s: Cannot open file \"%s\" for writing.", __FUNCTION__, a_FileName.c_str()); return false; } - if (!File.Write(Writer.GetResult())) + if (!File.Write(NBT)) { - LOG("Cannot write data to file \"%s\".", a_FileName.c_str()); + LOG("%s: Cannot write data to file \"%s\".", __FUNCTION__, a_FileName.c_str()); return false; } return true; @@ -92,6 +103,30 @@ bool cSchematicFileSerializer::SaveToSchematicFile(cBlockArea & a_BlockArea, con +bool cSchematicFileSerializer::SaveToSchematicString(const cBlockArea & a_BlockArea, AString & a_Out) +{ + // Serialize into NBT data: + AString NBT = SaveToSchematicNBT(a_BlockArea); + if (NBT.empty()) + { + LOG("%s: Cannot serialize the area into an NBT representation.", __FUNCTION__); + return false; + } + + // Gzip the data: + int res = CompressStringGZIP(NBT.data(), NBT.size(), a_Out); + if (res != Z_OK) + { + LOG("%s: Cannot Gzip the area data NBT representation: %d", __FUNCTION__, res); + return false; + } + return true; +} + + + + + bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cParsedNBT & a_NBT) { int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials"); @@ -170,3 +205,45 @@ bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cP } + + + +AString cSchematicFileSerializer::SaveToSchematicNBT(const cBlockArea & a_BlockArea) +{ + cFastNBTWriter Writer("Schematic"); + Writer.AddShort("Width", a_BlockArea.m_SizeX); + Writer.AddShort("Height", a_BlockArea.m_SizeY); + Writer.AddShort("Length", a_BlockArea.m_SizeZ); + Writer.AddString("Materials", "Alpha"); + if (a_BlockArea.HasBlockTypes()) + { + Writer.AddByteArray("Blocks", (const char *)a_BlockArea.m_BlockTypes, a_BlockArea.GetBlockCount()); + } + else + { + AString Dummy(a_BlockArea.GetBlockCount(), 0); + Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size()); + } + if (a_BlockArea.HasBlockMetas()) + { + Writer.AddByteArray("Data", (const char *)a_BlockArea.m_BlockMetas, a_BlockArea.GetBlockCount()); + } + else + { + AString Dummy(a_BlockArea.GetBlockCount(), 0); + Writer.AddByteArray("Data", Dummy.data(), Dummy.size()); + } + + // TODO: Save entities and block entities + Writer.BeginList("Entities", TAG_Compound); + Writer.EndList(); + Writer.BeginList("TileEntities", TAG_Compound); + Writer.EndList(); + Writer.Finish(); + + return Writer.GetResult(); +} + + + + diff --git a/src/WorldStorage/SchematicFileSerializer.h b/src/WorldStorage/SchematicFileSerializer.h index 9be2e5b57..05b6c74f4 100644 --- a/src/WorldStorage/SchematicFileSerializer.h +++ b/src/WorldStorage/SchematicFileSerializer.h @@ -1,4 +1,12 @@ +// SchematicFileSerializer.h + +// Declares the cSchematicFileSerializer class representing the interface to load and save cBlockArea to a .schematic file + + + + + #pragma once #include "../BlockArea.h" @@ -13,17 +21,34 @@ class cParsedNBT; + class cSchematicFileSerializer { public: - /// Loads an area from a .schematic file. Returns true if successful + /** Loads an area from a .schematic file. Returns true if successful. */ static bool LoadFromSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName); - /// Saves the area into a .schematic file. Returns true if successful - static bool SaveToSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName); + /** Loads an area from a string containing the .schematic file data. Returns true if successful. */ + static bool LoadFromSchematicString(cBlockArea & a_BlockArea, const AString & a_SchematicData); + + /** Saves the area into a .schematic file. Returns true if successful. */ + static bool SaveToSchematicFile(const cBlockArea & a_BlockArea, const AString & a_FileName); + + /** Saves the area into a string containing the .schematic file data. + Returns true if successful, false on failure. The data is stored into a_Out. */ + static bool SaveToSchematicString(const cBlockArea & a_BlockArea, AString & a_Out); private: - /// Loads the area from a schematic file uncompressed and parsed into a NBT tree. Returns true if successful. + /** Loads the area from a schematic file uncompressed and parsed into a NBT tree. + Returns true if successful. */ static bool LoadFromSchematicNBT(cBlockArea & a_BlockArea, cParsedNBT & a_NBT); + + /** Saves the area into a NBT representation and returns the NBT data as a string. + Returns an empty string if failed. */ + static AString SaveToSchematicNBT(const cBlockArea & a_BlockArea); }; + + + + |