From ecfe6ab65bd1fc2c7f5733fe6ef4e6ddaac44a26 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Sun, 23 Sep 2012 22:09:57 +0000 Subject: Source files cleanup: The rest of the files renamed. git-svn-id: http://mc-server.googlecode.com/svn/trunk@887 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- VC2008/MCServer.vcproj | 474 +++--- VC2008/WebServer.vcproj | 4 +- WebServer/Events.cpp | 125 ++ WebServer/Events.h | 18 + WebServer/WebServer.cpp | 2 +- WebServer/cEvents.cpp | 125 -- WebServer/cEvents.h | 18 - source/Authenticator.cpp | 284 ++++ source/Authenticator.h | 90 ++ source/Bindings.cpp | 56 +- source/BlockArea.cpp | 2 +- source/BlockEntity.h | 78 + source/BlockID.cpp | 2 +- source/ChatColor.cpp | 39 + source/ChatColor.h | 43 + source/ChestEntity.cpp | 225 +++ source/ChestEntity.h | 65 + source/Chunk.cpp | 1852 +++++++++++++++++++++++ source/Chunk.h | 321 ++++ source/Chunk.inl.h | 34 + source/ChunkMap.cpp | 1874 +++++++++++++++++++++++ source/ChunkMap.h | 345 +++++ source/ChunkSender.cpp | 4 +- source/ClientHandle.cpp | 1651 ++++++++++++++++++++ source/ClientHandle.h | 254 ++++ source/CraftingRecipes.cpp | 4 +- source/CraftingRecipes.h | 2 +- source/Cuboid.cpp | 19 + source/Cuboid.h | 40 + source/Doors.h | 61 + source/Entity.cpp | 344 +++++ source/Entity.h | 172 +++ source/FireSimulator.cpp | 133 ++ source/FireSimulator.h | 47 + source/FluidSimulator.cpp | 692 +++++++++ source/FluidSimulator.h | 55 + source/FurnaceEntity.cpp | 417 +++++ source/FurnaceEntity.h | 78 + source/FurnaceRecipe.cpp | 241 +++ source/FurnaceRecipe.h | 46 + source/Generating/BioGen.h | 2 +- source/Generating/Caves.h | 2 +- source/Generating/ChunkGenerator.cpp | 8 +- source/Generating/CompoGen.h | 2 +- source/Generating/FinishGen.cpp | 2 +- source/Generating/FinishGen.h | 2 +- source/Generating/HeiGen.h | 2 +- source/Generating/Ravines.h | 2 +- source/Generating/StructGen.h | 2 +- source/Generating/Trees.h | 2 +- source/Globals.h | 2 +- source/Group.cpp | 37 + source/Group.h | 40 + source/GroupManager.cpp | 108 ++ source/GroupManager.h | 17 + source/HeartBeat.cpp | 143 ++ source/HeartBeat.h | 28 + source/Inventory.cpp | 427 ++++++ source/Inventory.h | 88 ++ source/Item.cpp | 58 + source/Item.h | 146 ++ source/Ladder.h | 43 + source/LavaSimulator.cpp | 20 + source/LavaSimulator.h | 12 + source/LightingThread.cpp | 4 +- source/Log.cpp | 168 +++ source/Log.h | 30 + source/LuaChunk.cpp | 4 + source/LuaChunk.h | 139 ++ source/LuaCommandBinder.cpp | 122 ++ source/LuaCommandBinder.h | 42 + source/LuaFunctions.h | 2 +- source/LuaItems.h | 2 +- source/MCLogger.cpp | 191 +++ source/MCLogger.h | 60 + source/ManualBindings.cpp | 20 +- source/Mobs/AggressiveMonster.cpp | 2 +- source/Mobs/Monster.cpp | 14 +- source/Mobs/Monster.h | 6 +- source/Mobs/PassiveAggressiveMonster.cpp | 2 +- source/MonsterConfig.cpp | 114 ++ source/MonsterConfig.h | 17 + source/Noise.cpp | 377 +++++ source/Noise.h | 94 ++ source/Noise.inc | 124 ++ source/NoteEntity.cpp | 159 ++ source/NoteEntity.h | 52 + source/Pawn.cpp | 230 +++ source/Pawn.h | 66 + source/Pickup.cpp | 275 ++++ source/Pickup.h | 63 + source/Piston.cpp | 152 ++ source/Piston.h | 60 + source/Player.cpp | 1019 +++++++++++++ source/Player.h | 171 +++ source/Plugin.cpp | 308 ++++ source/Plugin.h | 111 ++ source/PluginManager.cpp | 883 +++++++++++ source/PluginManager.h | 149 ++ source/Plugin_Lua.cpp | 98 ++ source/Plugin_Lua.h | 38 + source/Plugin_NewLua.cpp | 794 ++++++++++ source/Plugin_NewLua.h | 76 + source/Plugin_Squirrel.cpp | 387 +++++ source/Plugin_Squirrel.h | 53 + source/Protocol/Protocol125.cpp | 14 +- source/Protocol/Protocol132.cpp | 10 +- source/Protocol/ProtocolRecognizer.cpp | 8 +- source/Redstone.cpp | 374 +++++ source/Redstone.h | 122 ++ source/RedstoneSimulator.cpp | 701 +++++++++ source/RedstoneSimulator.h | 90 ++ source/ReferenceManager.cpp | 43 + source/ReferenceManager.h | 34 + source/Root.cpp | 524 +++++++ source/Root.h | 120 ++ source/SandSimulator.cpp | 91 ++ source/SandSimulator.h | 40 + source/Server.cpp | 714 +++++++++ source/Server.h | 141 ++ source/Sign.h | 32 + source/SignEntity.cpp | 130 ++ source/SignEntity.h | 41 + source/Simulator.cpp | 33 + source/Simulator.h | 20 + source/SimulatorManager.cpp | 67 + source/SimulatorManager.h | 39 + source/SquirrelCommandBinder.cpp | 98 ++ source/SquirrelCommandBinder.h | 35 + source/Stairs.h | 28 + source/StringMap.cpp | 23 + source/StringMap.h | 29 + source/Torch.h | 77 + source/Tracer.cpp | 350 +++++ source/Tracer.h | 34 + source/UI/SlotArea.cpp | 10 +- source/UI/SlotArea.h | 2 +- source/UI/Window.cpp | 14 +- source/UI/Window.h | 2 +- source/UI/WindowOwner.h | 4 +- source/Vine.h | 23 + source/WaterSimulator.cpp | 22 + source/WaterSimulator.h | 11 + source/WebAdmin.cpp | 365 +++++ source/WebAdmin.h | 67 + source/WebPlugin.cpp | 109 ++ source/WebPlugin.h | 47 + source/World.cpp | 2097 ++++++++++++++++++++++++++ source/World.h | 477 ++++++ source/WorldStorage/WSSAnvil.cpp | 14 +- source/WorldStorage/WSSCompact.cpp | 10 +- source/WorldStorage/WorldStorage.cpp | 6 +- source/blocks/Block.cpp | 445 ------ source/blocks/Block.h | 90 -- source/blocks/BlockCactus.h | 2 +- source/blocks/BlockChest.h | 6 +- source/blocks/BlockCloth.h | 2 +- source/blocks/BlockCrops.h | 4 +- source/blocks/BlockDirt.h | 4 +- source/blocks/BlockDispenser.h | 6 +- source/blocks/BlockDoor.cpp | 8 +- source/blocks/BlockDoor.h | 2 +- source/blocks/BlockEntity.h | 2 +- source/blocks/BlockFire.h | 2 +- source/blocks/BlockFlower.h | 2 +- source/blocks/BlockFluid.h | 2 +- source/blocks/BlockFurnace.h | 6 +- source/blocks/BlockGlowstone.h | 2 +- source/blocks/BlockGravel.h | 2 +- source/blocks/BlockHandler.cpp | 445 ++++++ source/blocks/BlockHandler.h | 90 ++ source/blocks/BlockIce.h | 4 +- source/blocks/BlockLadder.h | 6 +- source/blocks/BlockLeaves.h | 4 +- source/blocks/BlockMelon.h | 2 +- source/blocks/BlockMushroom.h | 2 +- source/blocks/BlockNote.h | 2 +- source/blocks/BlockOre.h | 4 +- source/blocks/BlockPiston.cpp | 10 +- source/blocks/BlockPiston.h | 2 +- source/blocks/BlockRedstone.cpp | 8 +- source/blocks/BlockRedstone.h | 4 +- source/blocks/BlockRedstoneRepeater.cpp | 8 +- source/blocks/BlockRedstoneRepeater.h | 4 +- source/blocks/BlockRedstoneTorch.h | 2 +- source/blocks/BlockSand.h | 2 +- source/blocks/BlockSapling.h | 4 +- source/blocks/BlockSign.h | 8 +- source/blocks/BlockSlab.h | 2 +- source/blocks/BlockSnow.h | 2 +- source/blocks/BlockStairs.h | 4 +- source/blocks/BlockStems.h | 4 +- source/blocks/BlockStone.h | 4 +- source/blocks/BlockSugarcane.h | 2 +- source/blocks/BlockTallGrass.h | 2 +- source/blocks/BlockTorch.h | 6 +- source/blocks/BlockVine.h | 4 +- source/blocks/BlockWood.h | 2 +- source/blocks/BlockWorkbench.h | 4 +- source/cAuthenticator.cpp | 284 ---- source/cAuthenticator.h | 90 -- source/cBlockEntity.h | 78 - source/cChatColor.cpp | 39 - source/cChatColor.h | 43 - source/cChestEntity.cpp | 225 --- source/cChestEntity.h | 65 - source/cChunk.cpp | 1852 ----------------------- source/cChunk.h | 321 ---- source/cChunk.inl.h | 34 - source/cChunkMap.cpp | 1874 ----------------------- source/cChunkMap.h | 345 ----- source/cClientHandle.cpp | 1651 -------------------- source/cClientHandle.h | 254 ---- source/cCuboid.cpp | 19 - source/cCuboid.h | 40 - source/cDoors.h | 61 - source/cEntity.cpp | 344 ----- source/cEntity.h | 172 --- source/cFileFormatUpdater.cpp | 143 -- source/cFileFormatUpdater.h | 11 - source/cFireSimulator.cpp | 133 -- source/cFireSimulator.h | 47 - source/cFluidSimulator.cpp | 692 --------- source/cFluidSimulator.h | 55 - source/cFurnaceEntity.cpp | 417 ----- source/cFurnaceEntity.h | 78 - source/cFurnaceRecipe.cpp | 241 --- source/cFurnaceRecipe.h | 46 - source/cGroup.cpp | 37 - source/cGroup.h | 40 - source/cGroupManager.cpp | 108 -- source/cGroupManager.h | 17 - source/cHeartBeat.cpp | 143 -- source/cHeartBeat.h | 28 - source/cInventory.cpp | 427 ------ source/cInventory.h | 88 -- source/cItem.cpp | 58 - source/cItem.h | 146 -- source/cLadder.h | 43 - source/cLavaSimulator.cpp | 20 - source/cLavaSimulator.h | 12 - source/cLog.cpp | 168 --- source/cLog.h | 30 - source/cLuaChunk.cpp | 4 - source/cLuaChunk.h | 139 -- source/cLuaCommandBinder.cpp | 122 -- source/cLuaCommandBinder.h | 42 - source/cMCLogger.cpp | 191 --- source/cMCLogger.h | 60 - source/cMonsterConfig.cpp | 114 -- source/cMonsterConfig.h | 17 - source/cNoise.cpp | 377 ----- source/cNoise.h | 94 -- source/cNoise.inc | 124 -- source/cNoteEntity.cpp | 159 -- source/cNoteEntity.h | 52 - source/cPawn.cpp | 230 --- source/cPawn.h | 66 - source/cPickup.cpp | 275 ---- source/cPickup.h | 63 - source/cPiston.cpp | 152 -- source/cPiston.h | 60 - source/cPlayer.cpp | 1019 ------------- source/cPlayer.h | 171 --- source/cPlugin.cpp | 308 ---- source/cPlugin.h | 111 -- source/cPluginManager.cpp | 883 ----------- source/cPluginManager.h | 149 -- source/cPlugin_Lua.cpp | 98 -- source/cPlugin_Lua.h | 38 - source/cPlugin_NewLua.cpp | 794 ---------- source/cPlugin_NewLua.h | 76 - source/cPlugin_Squirrel.cpp | 387 ----- source/cPlugin_Squirrel.h | 53 - source/cRedstone.cpp | 374 ----- source/cRedstone.h | 122 -- source/cRedstoneSimulator.cpp | 701 --------- source/cRedstoneSimulator.h | 90 -- source/cReferenceManager.cpp | 43 - source/cReferenceManager.h | 34 - source/cRoot.cpp | 527 ------- source/cRoot.h | 120 -- source/cSandSimulator.cpp | 91 -- source/cSandSimulator.h | 40 - source/cServer.cpp | 714 --------- source/cServer.h | 141 -- source/cSign.h | 32 - source/cSignEntity.cpp | 130 -- source/cSignEntity.h | 41 - source/cSimulator.cpp | 33 - source/cSimulator.h | 20 - source/cSimulatorManager.cpp | 67 - source/cSimulatorManager.h | 39 - source/cSquirrelCommandBinder.cpp | 98 -- source/cSquirrelCommandBinder.h | 35 - source/cStairs.h | 28 - source/cStringMap.cpp | 23 - source/cStringMap.h | 29 - source/cTorch.h | 77 - source/cTracer.cpp | 350 ----- source/cTracer.h | 34 - source/cVine.h | 23 - source/cWaterSimulator.cpp | 22 - source/cWaterSimulator.h | 11 - source/cWebAdmin.cpp | 365 ----- source/cWebAdmin.h | 67 - source/cWebPlugin.cpp | 109 -- source/cWebPlugin.h | 47 - source/cWorld.cpp | 2097 -------------------------- source/cWorld.h | 477 ------ source/items/Item.cpp | 418 ----- source/items/Item.h | 83 - source/items/ItemBucket.h | 5 +- source/items/ItemCloth.h | 2 +- source/items/ItemDoor.h | 5 +- source/items/ItemDye.h | 8 +- source/items/ItemFood.h | 3 +- source/items/ItemHandler.cpp | 418 +++++ source/items/ItemHandler.h | 83 + source/items/ItemHoe.h | 8 +- source/items/ItemLeaves.h | 2 +- source/items/ItemLighter.h | 6 +- source/items/ItemPickaxe.h | 8 +- source/items/ItemRedstoneDust.h | 3 +- source/items/ItemRedstoneRepeater.h | 3 +- source/items/ItemSapling.h | 2 +- source/items/ItemSeeds.h | 4 +- source/items/ItemShears.h | 7 +- source/items/ItemShovel.h | 14 +- source/items/ItemSign.h | 5 +- source/items/ItemSlab.h | 4 +- source/items/ItemSugarcane.h | 4 +- source/items/ItemSword.h | 8 +- source/items/ItemWood.h | 2 +- source/main.cpp | 2 +- source/squirrelbindings/SquirrelBaseClass.h | 41 + source/squirrelbindings/SquirrelBindings.cpp | 4 +- source/squirrelbindings/cSquirrelBaseClass.h | 41 - 338 files changed, 26255 insertions(+), 26421 deletions(-) create mode 100644 WebServer/Events.cpp create mode 100644 WebServer/Events.h delete mode 100644 WebServer/cEvents.cpp delete mode 100644 WebServer/cEvents.h create mode 100644 source/Authenticator.cpp create mode 100644 source/Authenticator.h create mode 100644 source/BlockEntity.h create mode 100644 source/ChatColor.cpp create mode 100644 source/ChatColor.h create mode 100644 source/ChestEntity.cpp create mode 100644 source/ChestEntity.h create mode 100644 source/Chunk.cpp create mode 100644 source/Chunk.h create mode 100644 source/Chunk.inl.h create mode 100644 source/ChunkMap.cpp create mode 100644 source/ChunkMap.h create mode 100644 source/ClientHandle.cpp create mode 100644 source/ClientHandle.h create mode 100644 source/Cuboid.cpp create mode 100644 source/Cuboid.h create mode 100644 source/Doors.h create mode 100644 source/Entity.cpp create mode 100644 source/Entity.h create mode 100644 source/FireSimulator.cpp create mode 100644 source/FireSimulator.h create mode 100644 source/FluidSimulator.cpp create mode 100644 source/FluidSimulator.h create mode 100644 source/FurnaceEntity.cpp create mode 100644 source/FurnaceEntity.h create mode 100644 source/FurnaceRecipe.cpp create mode 100644 source/FurnaceRecipe.h create mode 100644 source/Group.cpp create mode 100644 source/Group.h create mode 100644 source/GroupManager.cpp create mode 100644 source/GroupManager.h create mode 100644 source/HeartBeat.cpp create mode 100644 source/HeartBeat.h create mode 100644 source/Inventory.cpp create mode 100644 source/Inventory.h create mode 100644 source/Item.cpp create mode 100644 source/Item.h create mode 100644 source/Ladder.h create mode 100644 source/LavaSimulator.cpp create mode 100644 source/LavaSimulator.h create mode 100644 source/Log.cpp create mode 100644 source/Log.h create mode 100644 source/LuaChunk.cpp create mode 100644 source/LuaChunk.h create mode 100644 source/LuaCommandBinder.cpp create mode 100644 source/LuaCommandBinder.h create mode 100644 source/MCLogger.cpp create mode 100644 source/MCLogger.h create mode 100644 source/MonsterConfig.cpp create mode 100644 source/MonsterConfig.h create mode 100644 source/Noise.cpp create mode 100644 source/Noise.h create mode 100644 source/Noise.inc create mode 100644 source/NoteEntity.cpp create mode 100644 source/NoteEntity.h create mode 100644 source/Pawn.cpp create mode 100644 source/Pawn.h create mode 100644 source/Pickup.cpp create mode 100644 source/Pickup.h create mode 100644 source/Piston.cpp create mode 100644 source/Piston.h create mode 100644 source/Player.cpp create mode 100644 source/Player.h create mode 100644 source/Plugin.cpp create mode 100644 source/Plugin.h create mode 100644 source/PluginManager.cpp create mode 100644 source/PluginManager.h create mode 100644 source/Plugin_Lua.cpp create mode 100644 source/Plugin_Lua.h create mode 100644 source/Plugin_NewLua.cpp create mode 100644 source/Plugin_NewLua.h create mode 100644 source/Plugin_Squirrel.cpp create mode 100644 source/Plugin_Squirrel.h create mode 100644 source/Redstone.cpp create mode 100644 source/Redstone.h create mode 100644 source/RedstoneSimulator.cpp create mode 100644 source/RedstoneSimulator.h create mode 100644 source/ReferenceManager.cpp create mode 100644 source/ReferenceManager.h create mode 100644 source/Root.cpp create mode 100644 source/Root.h create mode 100644 source/SandSimulator.cpp create mode 100644 source/SandSimulator.h create mode 100644 source/Server.cpp create mode 100644 source/Server.h create mode 100644 source/Sign.h create mode 100644 source/SignEntity.cpp create mode 100644 source/SignEntity.h create mode 100644 source/Simulator.cpp create mode 100644 source/Simulator.h create mode 100644 source/SimulatorManager.cpp create mode 100644 source/SimulatorManager.h create mode 100644 source/SquirrelCommandBinder.cpp create mode 100644 source/SquirrelCommandBinder.h create mode 100644 source/Stairs.h create mode 100644 source/StringMap.cpp create mode 100644 source/StringMap.h create mode 100644 source/Torch.h create mode 100644 source/Tracer.cpp create mode 100644 source/Tracer.h create mode 100644 source/Vine.h create mode 100644 source/WaterSimulator.cpp create mode 100644 source/WaterSimulator.h create mode 100644 source/WebAdmin.cpp create mode 100644 source/WebAdmin.h create mode 100644 source/WebPlugin.cpp create mode 100644 source/WebPlugin.h create mode 100644 source/World.cpp create mode 100644 source/World.h delete mode 100644 source/blocks/Block.cpp delete mode 100644 source/blocks/Block.h create mode 100644 source/blocks/BlockHandler.cpp create mode 100644 source/blocks/BlockHandler.h delete mode 100644 source/cAuthenticator.cpp delete mode 100644 source/cAuthenticator.h delete mode 100644 source/cBlockEntity.h delete mode 100644 source/cChatColor.cpp delete mode 100644 source/cChatColor.h delete mode 100644 source/cChestEntity.cpp delete mode 100644 source/cChestEntity.h delete mode 100644 source/cChunk.cpp delete mode 100644 source/cChunk.h delete mode 100644 source/cChunk.inl.h delete mode 100644 source/cChunkMap.cpp delete mode 100644 source/cChunkMap.h delete mode 100644 source/cClientHandle.cpp delete mode 100644 source/cClientHandle.h delete mode 100644 source/cCuboid.cpp delete mode 100644 source/cCuboid.h delete mode 100644 source/cDoors.h delete mode 100644 source/cEntity.cpp delete mode 100644 source/cEntity.h delete mode 100644 source/cFileFormatUpdater.cpp delete mode 100644 source/cFileFormatUpdater.h delete mode 100644 source/cFireSimulator.cpp delete mode 100644 source/cFireSimulator.h delete mode 100644 source/cFluidSimulator.cpp delete mode 100644 source/cFluidSimulator.h delete mode 100644 source/cFurnaceEntity.cpp delete mode 100644 source/cFurnaceEntity.h delete mode 100644 source/cFurnaceRecipe.cpp delete mode 100644 source/cFurnaceRecipe.h delete mode 100644 source/cGroup.cpp delete mode 100644 source/cGroup.h delete mode 100644 source/cGroupManager.cpp delete mode 100644 source/cGroupManager.h delete mode 100644 source/cHeartBeat.cpp delete mode 100644 source/cHeartBeat.h delete mode 100644 source/cInventory.cpp delete mode 100644 source/cInventory.h delete mode 100644 source/cItem.cpp delete mode 100644 source/cItem.h delete mode 100644 source/cLadder.h delete mode 100644 source/cLavaSimulator.cpp delete mode 100644 source/cLavaSimulator.h delete mode 100644 source/cLog.cpp delete mode 100644 source/cLog.h delete mode 100644 source/cLuaChunk.cpp delete mode 100644 source/cLuaChunk.h delete mode 100644 source/cLuaCommandBinder.cpp delete mode 100644 source/cLuaCommandBinder.h delete mode 100644 source/cMCLogger.cpp delete mode 100644 source/cMCLogger.h delete mode 100644 source/cMonsterConfig.cpp delete mode 100644 source/cMonsterConfig.h delete mode 100644 source/cNoise.cpp delete mode 100644 source/cNoise.h delete mode 100644 source/cNoise.inc delete mode 100644 source/cNoteEntity.cpp delete mode 100644 source/cNoteEntity.h delete mode 100644 source/cPawn.cpp delete mode 100644 source/cPawn.h delete mode 100644 source/cPickup.cpp delete mode 100644 source/cPickup.h delete mode 100644 source/cPiston.cpp delete mode 100644 source/cPiston.h delete mode 100644 source/cPlayer.cpp delete mode 100644 source/cPlayer.h delete mode 100644 source/cPlugin.cpp delete mode 100644 source/cPlugin.h delete mode 100644 source/cPluginManager.cpp delete mode 100644 source/cPluginManager.h delete mode 100644 source/cPlugin_Lua.cpp delete mode 100644 source/cPlugin_Lua.h delete mode 100644 source/cPlugin_NewLua.cpp delete mode 100644 source/cPlugin_NewLua.h delete mode 100644 source/cPlugin_Squirrel.cpp delete mode 100644 source/cPlugin_Squirrel.h delete mode 100644 source/cRedstone.cpp delete mode 100644 source/cRedstone.h delete mode 100644 source/cRedstoneSimulator.cpp delete mode 100644 source/cRedstoneSimulator.h delete mode 100644 source/cReferenceManager.cpp delete mode 100644 source/cReferenceManager.h delete mode 100644 source/cRoot.cpp delete mode 100644 source/cRoot.h delete mode 100644 source/cSandSimulator.cpp delete mode 100644 source/cSandSimulator.h delete mode 100644 source/cServer.cpp delete mode 100644 source/cServer.h delete mode 100644 source/cSign.h delete mode 100644 source/cSignEntity.cpp delete mode 100644 source/cSignEntity.h delete mode 100644 source/cSimulator.cpp delete mode 100644 source/cSimulator.h delete mode 100644 source/cSimulatorManager.cpp delete mode 100644 source/cSimulatorManager.h delete mode 100644 source/cSquirrelCommandBinder.cpp delete mode 100644 source/cSquirrelCommandBinder.h delete mode 100644 source/cStairs.h delete mode 100644 source/cStringMap.cpp delete mode 100644 source/cStringMap.h delete mode 100644 source/cTorch.h delete mode 100644 source/cTracer.cpp delete mode 100644 source/cTracer.h delete mode 100644 source/cVine.h delete mode 100644 source/cWaterSimulator.cpp delete mode 100644 source/cWaterSimulator.h delete mode 100644 source/cWebAdmin.cpp delete mode 100644 source/cWebAdmin.h delete mode 100644 source/cWebPlugin.cpp delete mode 100644 source/cWebPlugin.h delete mode 100644 source/cWorld.cpp delete mode 100644 source/cWorld.h delete mode 100644 source/items/Item.cpp delete mode 100644 source/items/Item.h create mode 100644 source/items/ItemHandler.cpp create mode 100644 source/items/ItemHandler.h create mode 100644 source/squirrelbindings/SquirrelBaseClass.h delete mode 100644 source/squirrelbindings/cSquirrelBaseClass.h diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj index cafb18701..dfbe8380a 100644 --- a/VC2008/MCServer.vcproj +++ b/VC2008/MCServer.vcproj @@ -281,184 +281,204 @@ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" > + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1056,51 +990,75 @@ Name="Physics" > + + + + + + + + + + + + @@ -1429,91 +1387,91 @@ > - - - - @@ -1787,6 +1737,14 @@ RelativePath="..\source\blocks\BlockGravel.h" > + + + + @@ -1908,27 +1866,31 @@ Name="Items" > + + + + @@ -1967,6 +1933,10 @@ RelativePath="..\source\items\ItemShovel.h" > + + diff --git a/VC2008/WebServer.vcproj b/VC2008/WebServer.vcproj index 460f0920c..078100bfd 100644 --- a/VC2008/WebServer.vcproj +++ b/VC2008/WebServer.vcproj @@ -222,11 +222,11 @@ > + + + + + +#define DEFAULT_AUTH_SERVER "session.minecraft.net" +#define DEFAULT_AUTH_ADDRESS "/game/checkserver.jsp?user=%USERNAME%&serverId=%SERVERID%" +#define MAX_REDIRECTS 10 + + + + + +cAuthenticator::cAuthenticator(void) : + super("cAuthenticator"), + m_Server(DEFAULT_AUTH_SERVER), + m_Address(DEFAULT_AUTH_ADDRESS), + m_ShouldAuthenticate(true) +{ + ReadINI(); +} + + + + + +cAuthenticator::~cAuthenticator() +{ + Stop(); +} + + + + + +/// Read custom values from INI +void cAuthenticator::ReadINI(void) +{ + cIniFile IniFile("settings.ini"); + if (!IniFile.ReadFile()) + { + return; + } + + m_Server = IniFile.GetValue("Authentication", "Server"); + m_Address = IniFile.GetValue("Authentication", "Address"); + m_ShouldAuthenticate = IniFile.GetValueB("Authentication", "Authenticate", true); + bool bSave = false; + + if (m_Server.length() == 0) + { + m_Server = DEFAULT_AUTH_SERVER; + IniFile.SetValue("Authentication", "Server", m_Server); + bSave = true; + } + if (m_Address.length() == 0) + { + m_Address = DEFAULT_AUTH_ADDRESS; + IniFile.SetValue("Authentication", "Address", m_Address); + bSave = true; + } + + if (bSave) + { + IniFile.SetValueB("Authentication", "Authenticate", m_ShouldAuthenticate); + IniFile.WriteFile(); + } +} + + + + + +/// Queues a request for authenticating a user. If the auth fails, the user is kicked +void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash) +{ + if (!m_ShouldAuthenticate) + { + cRoot::Get()->AuthenticateUser(a_ClientID); + return; + } + + cCSLock Lock(m_CS); + m_Queue.push_back(cUser(a_ClientID, a_UserName, a_ServerHash)); + m_QueueNonempty.Set(); +} + + + + + +void cAuthenticator::Stop(void) +{ + m_ShouldTerminate = true; + m_QueueNonempty.Set(); + Wait(); +} + + + + + +void cAuthenticator::Execute(void) +{ + for (;;) + { + cCSLock Lock(m_CS); + while (!m_ShouldTerminate && (m_Queue.size() == 0)) + { + cCSUnlock Unlock(Lock); + m_QueueNonempty.Wait(); + } + if (m_ShouldTerminate) + { + return; + } + ASSERT(!m_Queue.empty()); + + int ClientID = m_Queue.front().m_ClientID; + AString UserName = m_Queue.front().m_Name; + AString ActualAddress = m_Address; + ReplaceString(ActualAddress, "%USERNAME%", UserName); + ReplaceString(ActualAddress, "%SERVERID%", m_Queue.front().m_ServerID); + m_Queue.pop_front(); + Lock.Unlock(); + + if (!AuthFromAddress(m_Server, ActualAddress, UserName)) + { + cRoot::Get()->KickUser(ClientID, "Failed to authenticate account!"); + } + else + { + cRoot::Get()->AuthenticateUser(ClientID); + } + } // for (-ever) +} + + + + + +bool cAuthenticator::AuthFromAddress(const AString & a_Server, const AString & a_Address, const AString & a_UserName, int a_Level /* = 1 */) +{ + // Returns true if the user authenticated okay, false on error; iLevel is the recursion deptht (bails out if too deep) + + cBlockingTCPLink Link; + if (!Link.Connect(a_Server.c_str(), 80)) + { + LOGERROR("cAuthenticator: cannot connect to auth server \"%s\", kicking user \"%s\"", a_Server.c_str(), a_Server.c_str()); + return false; + } + + Link.SendMessage( AString( "GET " + a_Address + " HTTP/1.1\r\n" ).c_str()); + Link.SendMessage( AString( "User-Agent: MCServer\r\n" ).c_str()); + Link.SendMessage( AString( "Host: " + a_Server + "\r\n" ).c_str()); + //Link.SendMessage( AString( "Host: session.minecraft.net\r\n" ).c_str()); + Link.SendMessage( AString( "Accept: */*\r\n" ).c_str()); + Link.SendMessage( AString( "Connection: close\r\n" ).c_str()); //Close so we donīt have to mess with the Content-Length :) + Link.SendMessage( AString( "\r\n" ).c_str()); + AString DataRecvd; + Link.ReceiveData(DataRecvd); + Link.CloseSocket(); + + std::stringstream ss(DataRecvd); + + // Parse the data received: + std::string temp; + ss >> temp; + bool bRedirect = false; + bool bOK = false; + if ((temp.compare("HTTP/1.1") == 0) || (temp.compare("HTTP/1.0") == 0)) + { + int code; + ss >> code; + if (code == 302) + { + // redirect blabla + LOGINFO("Need to redirect!"); + if (a_Level > MAX_REDIRECTS) + { + LOGERROR("cAuthenticator: received too many levels of redirection from auth server \"%s\" for user \"%s\", bailing out and kicking the user", a_Server.c_str(), a_UserName.c_str()); + return false; + } + bRedirect = true; + } + else if (code == 200) + { + LOGINFO("Got 200 OK :D"); + bOK = true; + } + } + else + { + LOGERROR("cAuthenticator: cannot parse auth reply from server \"%s\" for user \"%s\", kicking the user.", a_Server.c_str(), a_UserName.c_str()); + return false; + } + + if( bRedirect ) + { + AString Location; + // Search for "Location:" + bool bFoundLocation = false; + while( !bFoundLocation && ss.good() ) + { + char c = 0; + while( c != '\n' ) + { + ss.get( c ); + } + AString Name; + ss >> Name; + if (Name.compare("Location:") == 0) + { + bFoundLocation = true; + ss >> Location; + } + } + if (!bFoundLocation) + { + LOGERROR("cAuthenticator: received invalid redirection from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str()); + return false; + } + + Location = Location.substr(strlen("http://"), std::string::npos); // Strip http:// + std::string Server = Location.substr( 0, Location.find( "/" ) ); // Only leave server address + Location = Location.substr( Server.length(), std::string::npos); + return AuthFromAddress(Server, Location, a_UserName, a_Level + 1); + } + + if (!bOK) + { + LOGERROR("cAuthenticator: received an error from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str()); + return false; + } + + // Header says OK, so receive the rest. + // Go past header, double \n means end of headers + char c = 0; + while (ss.good()) + { + while (c != '\n') + { + ss.get(c); + } + ss.get(c); + if( c == '\n' || c == '\r' || ss.peek() == '\r' || ss.peek() == '\n' ) + break; + } + if (!ss.good()) + { + LOGERROR("cAuthenticator: error while parsing response body from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str()); + return false; + } + + std::string Result; + ss >> Result; + LOGINFO("Got result: %s", Result.c_str()); + //if (Result.compare("3") == 0) // FIXME: Quick and dirty hack to support auth + //Lapayo: Wtf 3? + if (Result.compare("YES") == 0) //Works well + { + LOGINFO("Result was \"YES\", so player is authenticated!"); + return true; + } + + + LOGINFO("Result was \"%s\", so player is NOT authenticated!", Result.c_str()); + return false; +} + + + + diff --git a/source/Authenticator.h b/source/Authenticator.h new file mode 100644 index 000000000..c9e647329 --- /dev/null +++ b/source/Authenticator.h @@ -0,0 +1,90 @@ + +// cAuthenticator.h + +// Interfaces to the cAuthenticator class representing the thread that authenticates users against the official MC server +// Authentication prevents "hackers" from joining with an arbitrary username (possibly impersonating the server admins) +// For more info, see http://wiki.vg/Session#Server_operation +// In MCS, authentication is implemented as a single thread that receives queued auth requests and dispatches them one by one. + + + + + +#pragma once +#ifndef CAUTHENTICATOR_H_INCLUDED +#define CAUTHENTICATOR_H_INCLUDED + +#include "OSSupport/IsThread.h" + + + + + +// fwd: "cRoot.h" +class cRoot; + + + + + +class cAuthenticator : + public cIsThread +{ + typedef cIsThread super; + +public: + cAuthenticator(void); + ~cAuthenticator(); + + /// (Re-)read server and address from INI: + void ReadINI(void); + + /// Queues a request for authenticating a user. If the auth fails, the user is kicked + void Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash); + + // Stops the authenticator thread + void Stop(void); + +private: + + class cUser + { + public: + int m_ClientID; + AString m_Name; + AString m_ServerID; + + cUser(int a_ClientID, const AString & a_Name, const AString & a_ServerID) : + m_ClientID(a_ClientID), + m_Name(a_Name), + m_ServerID(a_ServerID) + { + } + } ; + + typedef std::deque cUserList; + + cCriticalSection m_CS; + cUserList m_Queue; + cEvent m_QueueNonempty; + + AString m_Server; + AString m_Address; + bool m_ShouldAuthenticate; + + // cIsThread override: + virtual void Execute(void) override; + + // Returns true if the user authenticated okay, false on error; iLevel is the recursion deptht (bails out if too deep) + bool AuthFromAddress(const AString & a_Server, const AString & a_Address, const AString & a_UserName, int a_Level = 1); +}; + + + + + +#endif // CAUTHENTICATOR_H_INCLUDED + + + + diff --git a/source/Bindings.cpp b/source/Bindings.cpp index 8b9718386..e835fa1a6 100644 --- a/source/Bindings.cpp +++ b/source/Bindings.cpp @@ -16,44 +16,44 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S); #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "tolua_base.h" #include "ChunkDef.h" -#include "cTorch.h" -#include "cStairs.h" -#include "cLadder.h" -#include "cVine.h" +#include "Torch.h" +#include "Stairs.h" +#include "Ladder.h" +#include "Vine.h" #include "../iniFile/iniFile.h" #include "BlockID.h" #include "Defines.h" #include "LuaFunctions.h" -#include "cStringMap.h" -#include "cChatColor.h" -#include "cClientHandle.h" -#include "cEntity.h" -#include "cPawn.h" -#include "cPlayer.h" -#include "cPluginManager.h" -#include "cPlugin.h" -#include "cPlugin_NewLua.h" -#include "cPlugin_Lua.h" -#include "cServer.h" -#include "cWorld.h" -#include "cInventory.h" -#include "cItem.h" -#include "cChestEntity.h" -#include "cWebAdmin.h" -#include "cWebPlugin.h" -#include "cPickup.h" -#include "cRoot.h" +#include "StringMap.h" +#include "ChatColor.h" +#include "ClientHandle.h" +#include "Entity.h" +#include "Pawn.h" +#include "Player.h" +#include "PluginManager.h" +#include "Plugin.h" +#include "Plugin_NewLua.h" +#include "Plugin_Lua.h" +#include "Server.h" +#include "World.h" +#include "Inventory.h" +#include "Item.h" +#include "ChestEntity.h" +#include "WebAdmin.h" +#include "WebPlugin.h" +#include "Pickup.h" +#include "Root.h" #include "OSSupport/TCPLink.h" #include "Vector3f.h" #include "Vector3d.h" #include "Vector3i.h" #include "Matrix4f.h" -#include "cCuboid.h" -#include "cMCLogger.h" -#include "cTracer.h" -#include "cGroup.h" +#include "Cuboid.h" +#include "MCLogger.h" +#include "Tracer.h" +#include "Group.h" #include "BlockArea.h" -#include "cLuaChunk.h" +#include "LuaChunk.h" #include "CraftingRecipes.h" #include "LuaItems.h" diff --git a/source/BlockArea.cpp b/source/BlockArea.cpp index 7750b94fb..caeaf609e 100644 --- a/source/BlockArea.cpp +++ b/source/BlockArea.cpp @@ -6,7 +6,7 @@ #include "Globals.h" #include "BlockArea.h" -#include "cWorld.h" +#include "World.h" diff --git a/source/BlockEntity.h b/source/BlockEntity.h new file mode 100644 index 000000000..b7d1fe6c4 --- /dev/null +++ b/source/BlockEntity.h @@ -0,0 +1,78 @@ + +#pragma once + +#include "ClientHandle.h" +#include "World.h" + + + + + +#ifndef _WIN32 +#include "BlockID.h" +#else +enum ENUM_BLOCK_ID; +#endif + + + + + +namespace Json +{ + class Value; +}; + +class cPlayer; +class cWorld; +class cPacket; + + + + + +class cBlockEntity +{ +protected: + cBlockEntity(ENUM_BLOCK_ID a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) + : m_PosX( a_BlockX ) + , m_PosY( a_BlockY ) + , m_PosZ( a_BlockZ ) + , m_BlockType( a_BlockType ) + , m_World( a_World ) + {} +public: + virtual ~cBlockEntity() {}; + virtual void Destroy() {}; + + // Position, in absolute block coordinates: + int GetPosX() { return m_PosX; } + int GetPosY() { return m_PosY; } + int GetPosZ() { return m_PosZ; } + + ENUM_BLOCK_ID GetBlockType() { return m_BlockType; } + + cWorld * GetWorld(void) const {return m_World; } + + virtual void SaveToJson (Json::Value & a_Value ) = 0; + + virtual void UsedBy( cPlayer * a_Player ) = 0; + + /** Sends the packet defining the block entity to the client specified. + To send to all eligible clients, use cWorld::BroadcastBlockEntity() + */ + virtual void SendTo(cClientHandle & a_Client) = 0; + +protected: + int m_PosX; // Position in absolute block coordinates + int m_PosY; + int m_PosZ; + + ENUM_BLOCK_ID m_BlockType; + + cWorld * m_World; +}; + + + + diff --git a/source/BlockID.cpp b/source/BlockID.cpp index 9cdc05c58..6553b612e 100644 --- a/source/BlockID.cpp +++ b/source/BlockID.cpp @@ -6,7 +6,7 @@ #include "Globals.h" #include "BlockID.h" #include "../iniFile/iniFile.h" -#include "cItem.h" +#include "Item.h" diff --git a/source/ChatColor.cpp b/source/ChatColor.cpp new file mode 100644 index 000000000..2b223ee76 --- /dev/null +++ b/source/ChatColor.cpp @@ -0,0 +1,39 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ChatColor.h" + +const std::string cChatColor::Color = "\xc2\xa7"; // or in other words: "§" in UTF-8 +const std::string cChatColor::Delimiter = "\xc2\xa7"; // or in other words: "§" in UTF-8 +const std::string cChatColor::Black = cChatColor::Color + "0"; +const std::string cChatColor::Navy = cChatColor::Color + "1"; +const std::string cChatColor::Green = cChatColor::Color + "2"; +const std::string cChatColor::Blue = cChatColor::Color + "3"; +const std::string cChatColor::Red = cChatColor::Color + "4"; +const std::string cChatColor::Purple = cChatColor::Color + "5"; +const std::string cChatColor::Gold = cChatColor::Color + "6"; +const std::string cChatColor::LightGray = cChatColor::Color + "7"; +const std::string cChatColor::Gray = cChatColor::Color + "8"; +const std::string cChatColor::DarkPurple = cChatColor::Color + "9"; +const std::string cChatColor::LightGreen = cChatColor::Color + "a"; +const std::string cChatColor::LightBlue = cChatColor::Color + "b"; +const std::string cChatColor::Rose = cChatColor::Color + "c"; +const std::string cChatColor::LightPurple = cChatColor::Color + "d"; +const std::string cChatColor::Yellow = cChatColor::Color + "e"; +const std::string cChatColor::White = cChatColor::Color + "f"; + +const std::string cChatColor::Random = cChatColor::Color + "k"; +const std::string cChatColor::Bold = cChatColor::Color + "l"; +const std::string cChatColor::Strikethrough = cChatColor::Color + "m"; +const std::string cChatColor::Underlined = cChatColor::Color + "n"; +const std::string cChatColor::Italic = cChatColor::Color + "o"; +const std::string cChatColor::Plain = cChatColor::Color + "r"; + +const std::string cChatColor::MakeColor( char a_Color ) +{ + return cChatColor::Color + a_Color; +} + + + + diff --git a/source/ChatColor.h b/source/ChatColor.h new file mode 100644 index 000000000..85b10f400 --- /dev/null +++ b/source/ChatColor.h @@ -0,0 +1,43 @@ + +#pragma once + + + + + +// tolua_begin +class cChatColor +{ +public: + static const std::string Color; + static const std::string Delimiter; + + static const std::string Black; + static const std::string Navy; + static const std::string Green; + static const std::string Blue; + static const std::string Red; + static const std::string Purple; + static const std::string Gold; + static const std::string LightGray; + static const std::string Gray; + static const std::string DarkPurple; + static const std::string LightGreen; + static const std::string LightBlue; + static const std::string Rose; + static const std::string LightPurple; + static const std::string Yellow; + static const std::string White; + + // Styles ( source: http://wiki.vg/Chat ) + static const std::string Random; + static const std::string Bold; + static const std::string Strikethrough; + static const std::string Underlined; + static const std::string Italic; + static const std::string Plain; + + static const std::string MakeColor( char a_Color ); +}; + +// tolua_end diff --git a/source/ChestEntity.cpp b/source/ChestEntity.cpp new file mode 100644 index 000000000..262217da8 --- /dev/null +++ b/source/ChestEntity.cpp @@ -0,0 +1,225 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ChestEntity.h" +#include "Item.h" +#include "ClientHandle.h" +#include "Player.h" +#include "UI/Window.h" +#include "World.h" +#include "Root.h" +#include "Pickup.h" +#include + + + + + +class cWorld; +class cRoot; + + + + + +cChestEntity::cChestEntity(int a_X, int a_Y, int a_Z, cWorld * a_World) + : cBlockEntity( E_BLOCK_CHEST, a_X, a_Y, a_Z, a_World) + , m_TopChest( false ) + , m_JoinedChest( NULL ) +{ + m_Content = new cItem[ c_ChestHeight * c_ChestWidth ]; + SetBlockEntity(this); // cBlockEntityWindowOwner +} + + + + + +cChestEntity::~cChestEntity() +{ + if( GetWindow() ) + { + GetWindow()->OwnerDestroyed(); + } + + if( m_Content ) + { + delete [] m_Content; + } +} + + + + + +void cChestEntity::Destroy() +{ + // Drop items + cItems Pickups; + for( int i = 0; i < c_ChestHeight * c_ChestWidth; ++i ) + { + if( !m_Content[i].IsEmpty() ) + { + Pickups.push_back(m_Content[i]); + m_Content[i].Empty(); + } + } + m_World->SpawnItemPickups(Pickups, m_PosX, m_PosY, m_PosZ); + if (m_JoinedChest) + { + m_JoinedChest->RemoveJoinedChest(this); + } +} + + + + + +const cItem * cChestEntity::GetSlot( int a_Slot ) const +{ + if( a_Slot > -1 && a_Slot < c_ChestHeight*c_ChestWidth ) + { + return &m_Content[ a_Slot ]; + } + return 0; +} + + + + + +void cChestEntity::SetSlot(int a_Slot, const cItem & a_Item) +{ + if ((a_Slot > -1) && (a_Slot < c_ChestHeight * c_ChestWidth)) + { + m_Content[a_Slot] = a_Item; + } +} + + + + + +#define READ(File, Var) \ + if (File.Read(&Var, sizeof(Var)) != sizeof(Var)) \ + { \ + LOGERROR("ERROR READING cChestEntity %s FROM FILE (line %d)", #Var, __LINE__); \ + return false; \ + } + + + + + + +bool cChestEntity::LoadFromJson( const Json::Value& a_Value ) +{ + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + Json::Value AllSlots = a_Value.get("Slots", 0); + int SlotIdx = 0; + for( Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr ) + { + cItem Item; + Item.FromJson( *itr ); + SetSlot( SlotIdx, Item ); + SlotIdx++; + } + return true; +} + + + + + +void cChestEntity::SaveToJson( Json::Value& a_Value ) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + unsigned int NumSlots = c_ChestHeight*c_ChestWidth; + Json::Value AllSlots; + for(unsigned int i = 0; i < NumSlots; i++) + { + Json::Value Slot; + const cItem * Item = GetSlot( i ); + if( Item ) Item->GetJson( Slot ); + AllSlots.append( Slot ); + } + a_Value["Slots"] = AllSlots; +} + + + + + +void cChestEntity::SendTo(cClientHandle & a_Client) +{ + // The chest entity doesn't need anything sent to the client when it's created / gets in the viewdistance + // All the actual handling is in the cWindow UI code that gets called when the chest is rclked + + UNUSED(a_Client); +} + + + + + +void cChestEntity::UsedBy(cPlayer * a_Player) +{ + if (GetWindow() == NULL) + { + OpenWindow(new cChestWindow(m_PosX, m_PosY, m_PosZ, this)); + } + if (GetWindow()) + { + if( a_Player->GetWindow() != GetWindow() ) + { + a_Player->OpenWindow( GetWindow() ); + GetWindow()->SendWholeWindow(*a_Player->GetClientHandle()); + } + } + + // This is rather a hack + // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now + // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first. + // The few false positives aren't much to worry about + int ChunkX, ChunkY = 0, ChunkZ; + cChunkDef::BlockToChunk(m_PosX, m_PosY, m_PosZ, ChunkX, ChunkZ); + m_World->MarkChunkDirty(ChunkX, ChunkY, ChunkZ); +} + + + + + +cItem * cChestEntity::GetContents(bool a_OnlyThis) +{ + if (m_JoinedChest && !a_OnlyThis) + { + // TODO: "Combined" memory leaks here + cItem * Combined = new cItem[GetChestHeight() * c_ChestWidth]; + cItem * first = (m_TopChest) ? GetContents(true) : m_JoinedChest->GetContents(true); + cItem * second = (!m_TopChest) ? GetContents(true) : m_JoinedChest->GetContents(true); + for (int i = 0; i < GetChestHeight() * c_ChestWidth; i++) + { + int index = i % (c_ChestHeight * c_ChestWidth); + if (i < c_ChestHeight * c_ChestWidth) + Combined[index] = first[index]; + else + Combined[index] = second[index]; + } + return Combined; + } + else + { + return m_Content; + } +} + + + + diff --git a/source/ChestEntity.h b/source/ChestEntity.h new file mode 100644 index 000000000..2269e6614 --- /dev/null +++ b/source/ChestEntity.h @@ -0,0 +1,65 @@ + +#pragma once + +#include "BlockEntity.h" +#include "UI/WindowOwner.h" + + + + + +namespace Json +{ + class Value; +}; + +class cClientHandle; +class cServer; +class cItem; +class cNBTData; + + + + + +class cChestEntity : // tolua_export + public cBlockEntity, // tolua_export + public cBlockEntityWindowOwner // tolua_export +{ // tolua_export +public: + cChestEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); + virtual ~cChestEntity(); + virtual void Destroy(); + + void HandleData( cNBTData* a_NBTData ); + + const cItem * GetSlot( int a_Slot ) const; //tolua_export + void SetSlot(int a_Slot, const cItem & a_Item ); //tolua_export + + bool LoadFromJson( const Json::Value& a_Value ); + virtual void SaveToJson(Json::Value& a_Value ) override; + + virtual void SendTo(cClientHandle & a_Client) override; + + virtual void UsedBy( cPlayer * a_Player ); //tolua_export + + cChestEntity * GetJoinedChest() { return m_JoinedChest; } // NOTE: Is this a safe function? Should it be exported to Lua? + void SetJoinedChest(cChestEntity *a_Chest) { m_JoinedChest = a_Chest; } + void RemoveJoinedChest(cChestEntity *a_Chest) { if (m_JoinedChest && m_JoinedChest == a_Chest) { m_JoinedChest = NULL; m_TopChest = false; } } + + int GetChestHeight() { return ((m_JoinedChest) ? c_ChestHeight * 2 : c_ChestHeight); } //tolua_export + cItem * GetContents(bool a_OnlyThis = false); + + static const int c_ChestWidth = 9; + static const int c_ChestHeight = 3; + +private: + + cItem * m_Content; + bool m_TopChest; + cChestEntity * m_JoinedChest; +}; //tolua_export + + + + diff --git a/source/Chunk.cpp b/source/Chunk.cpp new file mode 100644 index 000000000..7cd3b961f --- /dev/null +++ b/source/Chunk.cpp @@ -0,0 +1,1852 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#ifndef _WIN32 + #include +#endif + + +#include "Chunk.h" +#include "World.h" +#include "WaterSimulator.h" +#include "LavaSimulator.h" +#include "ClientHandle.h" +#include "Server.h" +#include "zlib.h" +#include "Defines.h" +#include "ChestEntity.h" +#include "FurnaceEntity.h" +#include "SignEntity.h" +#include "NoteEntity.h" +#include "Torch.h" +#include "Ladder.h" +#include "Pickup.h" +#include "Redstone.h" +#include "Item.h" +#include "Noise.h" +#include "Root.h" +#include "MersenneTwister.h" +#include "Player.h" +#include "BlockArea.h" +#include "PluginManager.h" +#include "blocks/BlockHandler.h" + +#include + + + + + + + + + +extern bool g_bWaterPhysics; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// sSetBlock: + +sSetBlock::sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) // absolute block position + : x( a_BlockX ) + , y( a_BlockY ) + , z( a_BlockZ ) + , BlockType( a_BlockType ) + , BlockMeta( a_BlockMeta ) +{ + cChunkDef::AbsoluteToRelative(x, y, z, ChunkX, ChunkZ); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cChunk: + +cChunk::cChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkMap * a_ChunkMap, cWorld * a_World) + : m_PosX( a_ChunkX ) + , m_PosY( a_ChunkY ) + , m_PosZ( a_ChunkZ ) + , m_BlockTickX( 0 ) + , m_BlockTickY( 0 ) + , m_BlockTickZ( 0 ) + , m_World( a_World ) + , m_ChunkMap(a_ChunkMap) + , m_IsValid(false) + , m_IsLightValid(false) + , m_IsDirty(false) + , m_IsSaving(false) + , m_StayCount(0) +{ + // LOGINFO("### new cChunk (%i, %i) at %p, thread 0x%x ###", a_X, a_Z, this, GetCurrentThreadId()); +} + + + + + +cChunk::~cChunk() +{ + // LOGINFO("### delete cChunk() (%i, %i) from %p, thread 0x%x ###", m_PosX, m_PosZ, this, GetCurrentThreadId() ); + + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + delete *itr; + } + m_BlockEntities.clear(); + + // Remove and destroy all entities that are not players: + cEntityList Entities; + for (cEntityList::const_iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) + { + if ((*itr)->GetEntityType() != cEntity::eEntityType_Player) + { + Entities.push_back(*itr); + } + } + for (cEntityList::iterator itr = Entities.begin(); itr != Entities.end(); ++itr) + { + (*itr)->RemoveFromChunk(); + (*itr)->Destroy(); + } + m_Entities.clear(); +} + + + + + +void cChunk::SetValid(void) +{ + m_IsValid = true; + + m_World->GetChunkMap()->ChunkValidated(); +} + + + + + +void cChunk::MarkRegenerating(void) +{ + // Tell all clients attached to this chunk that they want this chunk: + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) + { + (*itr)->AddWantedChunk(m_PosX, m_PosZ); + } // for itr - m_LoadedByClient[] +} + + + + + +bool cChunk::CanUnload(void) +{ + return m_LoadedByClient.empty() && !m_IsDirty && (m_StayCount == 0); +} + + + + + +void cChunk::MarkSaving(void) +{ + m_IsSaving = true; +} + + + + + +void cChunk::MarkSaved(void) +{ + if (!m_IsSaving) + { + return; + } + m_IsDirty = false; +} + + + + + +void cChunk::MarkLoaded(void) +{ + m_IsDirty = false; + SetValid(); +} + + + + + +void cChunk::MarkLoadFailed(void) +{ + if (m_IsValid) + { + return; + } + + m_HasLoadFailed = true; +} + + + + + +void cChunk::GetAllData(cChunkDataCallback & a_Callback) +{ + a_Callback.HeightMap (&m_HeightMap); + a_Callback.BiomeData (&m_BiomeMap); + a_Callback.BlockTypes (m_BlockTypes); + a_Callback.BlockMeta (m_BlockMeta); + a_Callback.LightIsValid (m_IsLightValid); + a_Callback.BlockLight (m_BlockLight); + a_Callback.BlockSkyLight(m_BlockSkyLight); + + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) + { + a_Callback.Entity(*itr); + } + + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + a_Callback.BlockEntity(*itr); + } +} + + + + + +void cChunk::SetAllData( + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const HeightMap * a_HeightMap, + const BiomeMap & a_BiomeMap, + cEntityList & a_Entities, + cBlockEntityList & a_BlockEntities +) +{ + memcpy(m_BiomeMap, a_BiomeMap, sizeof(m_BiomeMap)); + + if (a_HeightMap != NULL) + { + memcpy(m_HeightMap, a_HeightMap, sizeof(m_HeightMap)); + } + + memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes)); + memcpy(m_BlockMeta, a_BlockMeta, sizeof(m_BlockMeta)); + if (a_BlockLight != NULL) + { + memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight)); + } + if (a_BlockSkyLight != NULL) + { + memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight)); + } + + m_IsLightValid = (a_BlockLight != NULL) && (a_BlockSkyLight != NULL); + + if (a_HeightMap == NULL) + { + CalculateHeightmap(); + } + + // Append entities to current entity list: + m_Entities.splice(m_Entities.end(), a_Entities); + + // Clear the block entities present - either the loader / saver has better, or we'll create empty ones: + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + delete *itr; + } + std::swap(a_BlockEntities, m_BlockEntities); + + // Create block entities that the loader didn't load; fill them with defaults + CreateBlockEntities(); + + m_HasLoadFailed = false; +} + + + + + +void cChunk::SetLight( + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight +) +{ + // TODO: We might get cases of wrong lighting when a chunk changes in the middle of a lighting calculation. + // Postponing until we see how bad it is :) + memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight)); + memcpy(m_BlockSkyLight, a_SkyLight, sizeof(m_BlockSkyLight)); + m_IsLightValid = true; +} + + + + + +void cChunk::GetBlockTypes(BLOCKTYPE * a_BlockTypes) +{ + memcpy(a_BlockTypes, m_BlockTypes, NumBlocks); +} + + + + + +void cChunk::GetBlockData(BLOCKTYPE * a_BlockData) +{ + memcpy(a_BlockData, m_BlockTypes, NumBlocks); + memcpy(a_BlockData + MetaOffset, m_BlockMeta, NumBlocks / 2); + memcpy(a_BlockData + LightOffset, m_BlockLight, NumBlocks / 2); + memcpy(a_BlockData + SkyLightOffset, m_BlockSkyLight, NumBlocks / 2); +} + + + + + +/// Returns true if there is a block entity at the coords specified +bool cChunk::HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if ( + ((*itr)->GetPosX() == a_BlockX) && + ((*itr)->GetPosY() == a_BlockY) && + ((*itr)->GetPosZ() == a_BlockZ) + ) + { + return true; + } + } // for itr - m_BlockEntities[] + return false; +} + + + + + +/// Sets or resets the internal flag that prevents chunk from being unloaded +void cChunk::Stay(bool a_Stay) +{ + m_StayCount += (a_Stay ? 1 : -1); + ASSERT(m_StayCount >= 0); +} + + + + + +void cChunk::Tick(float a_Dt, MTRand & a_TickRandom) +{ + BroadcastPendingBlockChanges(); + + // Unload the chunk from all clients that have queued unloading: + for (cClientHandleList::iterator itr = m_UnloadQuery.begin(), end = m_UnloadQuery.end(); itr != end; ++itr) + { + (*itr)->SendUnloadChunk(m_PosX, m_PosZ); + } + m_UnloadQuery.clear(); + + CheckBlocks(); + + TickBlocks(a_TickRandom); + + // Tick block entities (furnaces) + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if ((*itr)->GetBlockType() == E_BLOCK_FURNACE) + { + m_IsDirty = ((cFurnaceEntity *)(*itr))->Tick( a_Dt ) | m_IsDirty; + } + } +} + + + + + +void cChunk::BroadcastPendingBlockChanges(void) +{ + sSetBlockVector Changes; + { + cCSLock Lock(m_CSBlockLists); + if (m_PendingSendBlocks.empty()) + { + return; + } + Changes.reserve(m_PendingSendBlocks.size()); + for (std::vector::iterator itr = m_PendingSendBlocks.begin(), end = m_PendingSendBlocks.end(); itr != end; ++itr) + { + unsigned int index = *itr; + Vector3i RelPos = IndexToCoordinate(index); + Changes.push_back(sSetBlock(m_PosX, m_PosZ, RelPos.x, RelPos.y, RelPos.z, GetBlock(index), GetMeta(index))); + } // for itr - m_PendingSendBlocks[] + m_PendingSendBlocks.clear(); + } + + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) + { + (*itr)->SendBlockChanges(m_PosX, m_PosZ, Changes); + } +} + + + + + +void cChunk::CheckBlocks(void) +{ + cCSLock Lock2(m_CSBlockLists); + unsigned int NumTickBlocks = m_ToTickBlocks.size(); + Lock2.Unlock(); + + if (NumTickBlocks == 0) + { + return; + } + + Lock2.Lock(); + std::deque< unsigned int > ToTickBlocks = m_ToTickBlocks; + m_ToTickBlocks.clear(); + Lock2.Unlock(); + + for (std::deque< unsigned int >::const_iterator itr = ToTickBlocks.begin(); itr != ToTickBlocks.end(); ++itr) + { + unsigned int index = (*itr); + Vector3i BlockPos = IndexToCoordinate(index); + Vector3i WorldPos = PositionToWorldPosition( BlockPos ); + + cBlockHandler * Handler = BlockHandler(GetBlock(index)); + if(!Handler->CanBeAt(m_World, WorldPos.x, WorldPos.y, WorldPos.z)) + { + if(Handler->DropOnUnsuitable()) + { + Handler->DropBlock(m_World, WorldPos.x, WorldPos.y, WorldPos.z); + } + + m_World->SetBlock(WorldPos.x, WorldPos.y, WorldPos.z, E_BLOCK_AIR, 0); + } + } // for itr - ToTickBlocks[] +} + + + + + +void cChunk::TickBlocks(MTRand & a_TickRandom) +{ + // Tick dem blocks + // _X: We must limit the random number or else we get a nasty int overflow bug ( http://forum.mc-server.org/showthread.php?tid=457 ) + int RandomX = a_TickRandom.randInt(0x00ffffff); + int RandomY = a_TickRandom.randInt(0x00ffffff); + int RandomZ = a_TickRandom.randInt(0x00ffffff); + int TickX = m_BlockTickX; + int TickY = m_BlockTickY; + int TickZ = m_BlockTickZ; + + // This for loop looks disgusting, but it actually does a simple thing - first processes m_BlockTick, then adds random to it + // This is so that SetNextBlockTick() works + for (int i = 0; i < 50; i++, + + // This weird construct (*2, then /2) is needed, + // otherwise the blocktick distribution is too biased towards even coords! + + TickX = (TickX + RandomX) % (Width * 2), + TickY = (TickY + RandomY) % (Height * 2), + TickZ = (TickZ + RandomZ) % (Width * 2), + m_BlockTickX = TickX / 2, + m_BlockTickY = TickY / 2, + m_BlockTickZ = TickZ / 2 + ) + { + + if (m_BlockTickY > cChunkDef::GetHeight(m_HeightMap, m_BlockTickX, m_BlockTickZ)) + { + continue; // It's all air up here + } + + unsigned int Index = MakeIndexNoCheck( m_BlockTickX, m_BlockTickY, m_BlockTickZ ); + BLOCKTYPE ID = m_BlockTypes[Index]; + switch( ID ) + { + case E_BLOCK_CROPS: + { + NIBBLETYPE Meta = GetMeta(Index); + if (Meta < 7) + { + FastSetBlock(m_BlockTickX, m_BlockTickY, m_BlockTickZ, E_BLOCK_CROPS, ++Meta); + } + break; + } + + case E_BLOCK_PUMPKIN_STEM: + case E_BLOCK_MELON_STEM: TickMelonPumpkin(m_BlockTickX, m_BlockTickY, m_BlockTickZ, Index, ID, a_TickRandom); break; + case E_BLOCK_FARMLAND: TickFarmland (m_BlockTickX, m_BlockTickY, m_BlockTickZ); break; + case E_BLOCK_SUGARCANE: GrowSugarcane (m_BlockTickX, m_BlockTickY, m_BlockTickZ, 1); break; + case E_BLOCK_CACTUS: GrowCactus (m_BlockTickX, m_BlockTickY, m_BlockTickZ, 1); break; + + + default: + { + cBlockHandler * Handler = BlockHandler(ID); + ASSERT(Handler != NULL); // Happenned on server restart, FS #243 + if (Handler->NeedsRandomTicks()) + { + Handler->OnUpdate(m_World, m_BlockTickX + m_PosX * Width, m_BlockTickY, m_BlockTickZ + m_PosZ * Width); + } + break; + } + } + } +} + + + + + +void cChunk::TickMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, int a_BlockIdx, BLOCKTYPE a_BlockType, MTRand & a_TickRandom) +{ + NIBBLETYPE Meta = GetMeta(a_BlockIdx); + if (Meta < 7) + { + FastSetBlock(m_BlockTickX, m_BlockTickY, m_BlockTickZ, a_BlockType, ++Meta); + return; + } + GrowMelonPumpkin(a_RelX, a_RelY, a_RelZ, a_BlockType, a_TickRandom); +} + + + + + +void cChunk::TickFarmland(int a_RelX, int a_RelY, int a_RelZ) +{ + // TODO: Rain hydrates blocks, too. Check world weather, don't search for water if raining. + // NOTE: The desert biomes do not get precipitation, so another check needs to be made. + + // Search for water in a close proximity: + // Ref.: http://www.minecraftwiki.net/wiki/Farmland#Hydrated_Farmland_Tiles + bool Found = false; + for (int y = a_RelY; y <= a_RelY + 1; y++) + { + for (int z = a_RelZ - 4; z <= a_RelZ + 4; z++) + { + for (int x = a_RelX - 4; x <= a_RelX + 4; x++) + { + BLOCKTYPE BlockType; + NIBBLETYPE Meta; // unused + + if (!UnboundedRelGetBlock(x, y, z, BlockType, Meta)) + { + // Too close to an unloaded chunk, we might miss a water block there, so don't tick at all + return; + } + if ( + (BlockType == E_BLOCK_WATER) || + (BlockType == E_BLOCK_STATIONARY_WATER) + ) + { + Found = true; + break; + } + } // for x + if (Found) + { + break; + } + } // for z + if (Found) + { + break; + } + } // for y + + NIBBLETYPE BlockMeta = GetMeta(a_RelX, a_RelY, a_RelZ); + + if (Found) + { + // Water was found, hydrate the block until hydration reaches 7: + if (BlockMeta < 7) + { + FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_FARMLAND, ++BlockMeta); + } + return; + } + + // Water wasn't found, de-hydrate block: + if (BlockMeta > 0) + { + FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_FARMLAND, --BlockMeta); + return; + } + + // Farmland too dry. If nothing is growing on top, turn back to dirt: + + switch (GetBlock(a_RelX, a_RelY + 1, a_RelZ)) + { + case E_BLOCK_CROPS: + case E_BLOCK_MELON_STEM: + case E_BLOCK_PUMPKIN_STEM: + break; + default: + FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_DIRT, 0); + break; + } +} + + + + + + +void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_TickRandom) +{ + // Convert the stem BlockType into produce BlockType + BLOCKTYPE ProduceType; + switch (a_BlockType) + { + case E_BLOCK_MELON_STEM: ProduceType = E_BLOCK_MELON; break; + case E_BLOCK_PUMPKIN_STEM: ProduceType = E_BLOCK_PUMPKIN; break; + default: + { + ASSERT(!"Unhandled blocktype in TickMelonPumpkin()"); + return; + } + } + + // Check if there's another melon / pumpkin around that stem, if so, abort: + bool IsValid; + BLOCKTYPE BlockType[4]; + NIBBLETYPE BlockMeta; // unused + IsValid = UnboundedRelGetBlock(a_RelX + 1, a_RelY, a_RelZ, BlockType[0], BlockMeta); + IsValid = IsValid && UnboundedRelGetBlock(a_RelX - 1, a_RelY, a_RelZ, BlockType[1], BlockMeta); + IsValid = IsValid && UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ + 1, BlockType[2], BlockMeta); + IsValid = IsValid && UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ - 1, BlockType[3], BlockMeta); + if ( + !IsValid || + (BlockType[0] == ProduceType) || + (BlockType[1] == ProduceType) || + (BlockType[2] == ProduceType) || + (BlockType[3] == ProduceType) + ) + { + // Neighbors not valid or already taken by the same produce + return; + } + + // Pick a direction in which to place the produce: + int x = 0, z = 0; + int CheckType = a_TickRandom.randInt(3); // The index to the neighbors array which should be checked for emptiness + switch (CheckType) + { + case 0: x = 1; break; + case 1: x = -1; break; + case 2: z = 1; break; + case 3: z = -1; break; + } + + // Check that the block in that direction is empty: + switch (BlockType[CheckType]) + { + case E_BLOCK_AIR: + case E_BLOCK_SNOW: + case E_BLOCK_TALL_GRASS: + case E_BLOCK_DEAD_BUSH: + { + break; + } + default: return; + } + + // Check if there's soil under the neighbor. We already know the neighbors are valid. Place produce if ok + BLOCKTYPE Soil; + UnboundedRelGetBlock(a_RelX + x, a_RelY - 1, a_RelZ + z, Soil, BlockMeta); + switch (Soil) + { + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + case E_BLOCK_FARMLAND: + { + // Place a randomly-facing produce: + UnboundedRelFastSetBlock(a_RelX + x, a_RelY, a_RelZ + z, ProduceType, (NIBBLETYPE)(a_TickRandom.randInt(4) % 4)); + break; + } + } +} + + + + + +void cChunk::GrowSugarcane(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) +{ + // Check the total height of the sugarcane blocks here: + int Top = a_RelY + 1; + while ( + (Top < cChunkDef::Height) && + (GetBlock(a_RelX, Top, a_RelZ) == E_BLOCK_SUGARCANE) + ) + { + ++Top; + } + int Bottom = a_RelY - 1; + while ( + (Bottom > 0) && + (GetBlock(a_RelX, Bottom, a_RelZ) == E_BLOCK_SUGARCANE) + ) + { + --Bottom; + } + + // Grow by at most a_NumBlocks, but no more than max height: + int ToGrow = std::min(a_NumBlocks, m_World->GetMaxSugarcaneHeight() + 1 - (Top - Bottom)); + for (int i = 0; i < ToGrow; i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (UnboundedRelGetBlock(a_RelX, Top + i, a_RelZ, BlockType, BlockMeta) && (BlockType == E_BLOCK_AIR)) + { + UnboundedRelFastSetBlock(a_RelX, Top + i, a_RelZ, E_BLOCK_SUGARCANE, 0); + } + else + { + break; + } + } // for i +} + + + + + +void cChunk::GrowCactus(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) +{ + // Check the total height of the sugarcane blocks here: + int Top = a_RelY + 1; + while ( + (Top < cChunkDef::Height) && + (GetBlock(a_RelX, Top, a_RelZ) == E_BLOCK_CACTUS) + ) + { + ++Top; + } + int Bottom = a_RelY - 1; + while ( + (Bottom > 0) && + (GetBlock(a_RelX, Bottom, a_RelZ) == E_BLOCK_CACTUS) + ) + { + --Bottom; + } + + // Grow by at most a_NumBlocks, but no more than max height: + int ToGrow = std::min(a_NumBlocks, m_World->GetMaxCactusHeight() + 1 - (Top - Bottom)); + for (int i = 0; i < ToGrow; i++) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (UnboundedRelGetBlock(a_RelX, Top + i, a_RelZ, BlockType, BlockMeta) && (BlockType == E_BLOCK_AIR)) + { + // TODO: Check the surrounding blocks, if they aren't air, break the cactus block into pickups (and continue breaking blocks above in the next loop iterations) + UnboundedRelFastSetBlock(a_RelX, Top + i, a_RelZ, E_BLOCK_CACTUS, 0); + } + else + { + break; + } + } // for i +} + + + + + +bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + if ((a_RelY < 0) || (a_RelY > cChunkDef::Height)) + { + return false; + } + + if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)) + { + int BlockIdx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); + a_BlockType = GetBlock(BlockIdx); + a_BlockMeta = GetMeta(BlockIdx); + return true; + } + return m_ChunkMap->LockedGetBlock( + m_PosX * cChunkDef::Width + a_RelX, + ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, + m_PosZ * cChunkDef::Width + a_RelZ, + a_BlockType, a_BlockMeta + ); +} + + + + + +bool cChunk::UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)) + { + SetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + return true; + } + return m_ChunkMap->LockedSetBlock( + m_PosX * cChunkDef::Width + a_RelX, + ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, + m_PosZ * cChunkDef::Width + a_RelZ, + a_BlockType, a_BlockMeta + ); +} + + + + + +bool cChunk::UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)) + { + FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); + return true; + } + return m_ChunkMap->LockedFastSetBlock( + m_PosX * cChunkDef::Width + a_RelX, + ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, + m_PosZ * cChunkDef::Width + a_RelZ, + a_BlockType, a_BlockMeta + ); +} + + + + + +int cChunk::GetHeight( int a_X, int a_Z ) +{ + ASSERT((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width)); + + if ((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width)) + { + return m_HeightMap[a_X + a_Z * Width]; + } + return 0; +} + + + + + +void cChunk::CreateBlockEntities(void) +{ + for (int x = 0; x < Width; x++) + { + for (int z = 0; z < Width; z++) + { + for (int y = 0; y < Height; y++) + { + ENUM_BLOCK_ID BlockType = (ENUM_BLOCK_ID)cChunkDef::GetBlock(m_BlockTypes, x, y, z); + switch ( BlockType ) + { + case E_BLOCK_CHEST: + { + if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) + { + m_BlockEntities.push_back( new cChestEntity( x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) ); + } + break; + } + + case E_BLOCK_FURNACE: + { + if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) + { + m_BlockEntities.push_back( new cFurnaceEntity( x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) ); + } + break; + } + + case E_BLOCK_SIGN_POST: + case E_BLOCK_WALLSIGN: + { + if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) + { + m_BlockEntities.push_back( new cSignEntity( BlockType, x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) ); + } + break; + } + + case E_BLOCK_NOTE_BLOCK: + { + if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) + { + m_BlockEntities.push_back(new cNoteEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) ); + } + break; + } + } // switch (BlockType) + } // for y + } // for z + } // for x +} + + + + + +void cChunk::CalculateHeightmap() +{ + for (int x = 0; x < Width; x++) + { + for (int z = 0; z < Width; z++) + { + for (int y = Height - 1; y > -1; y--) + { + int index = MakeIndex( x, y, z ); + if (m_BlockTypes[index] != E_BLOCK_AIR) + { + m_HeightMap[x + z * Width] = (unsigned char)y; + break; + } + } // for y + } // for z + } // for x +} + + + + + +void cChunk::SetBlock( int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) +{ + if (a_RelX < 0 || a_RelX >= Width || a_RelY < 0 || a_RelY >= Height || a_RelZ < 0 || a_RelZ >= Width) + { + return; // Clip + } + + ASSERT(IsValid()); // Is this chunk loaded / generated? + + int index = MakeIndexNoCheck( a_RelX, a_RelY, a_RelZ ); + BLOCKTYPE OldBlockMeta = GetNibble( m_BlockMeta, index ); + BLOCKTYPE OldBlockType = m_BlockTypes[index]; + m_BlockTypes[index] = a_BlockType; + + SetNibble( m_BlockMeta, index, a_BlockMeta ); + + if ((OldBlockType == a_BlockType) && (OldBlockMeta == a_BlockMeta)) + { + return; + } + + MarkDirty(); + + { + cCSLock Lock(m_CSBlockLists); + m_PendingSendBlocks.push_back( index ); + } + + // ONLY recalculate lighting if it's necessary! + if( + (g_BlockLightValue[ OldBlockType ] != g_BlockLightValue[ a_BlockType ]) || + (g_BlockSpreadLightFalloff[ OldBlockType ] != g_BlockSpreadLightFalloff[ a_BlockType ]) || + (g_BlockTransparent[ OldBlockType ] != g_BlockTransparent[ a_BlockType ] ) + ) + { + m_IsLightValid = false; + } + + // Update heightmap, if needed: + if (a_RelY >= m_HeightMap[a_RelX + a_RelZ * Width]) + { + if (a_BlockType != E_BLOCK_AIR) + { + SetHeight(m_HeightMap, a_RelX, a_RelZ, a_RelY); + } + else + { + for (int y = a_RelY - 1; y > 0; --y) + { + if (cChunkDef::GetBlock(m_BlockTypes, a_RelX, y, a_RelZ) != E_BLOCK_AIR) + { + SetHeight(m_HeightMap, a_RelX, a_RelZ, y); + break; + } + } // for y - column in m_BlockData + } + } + + m_ToTickBlocks.push_back( MakeIndex( a_RelX, a_RelY, a_RelZ ) ); + m_ToTickBlocks.push_back( MakeIndex( a_RelX + 1, a_RelY, a_RelZ ) ); + m_ToTickBlocks.push_back( MakeIndex( a_RelX - 1, a_RelY, a_RelZ ) ); + m_ToTickBlocks.push_back( MakeIndex( a_RelX, a_RelY + 1, a_RelZ ) ); + m_ToTickBlocks.push_back( MakeIndex( a_RelX, a_RelY - 1, a_RelZ ) ); + m_ToTickBlocks.push_back( MakeIndex( a_RelX, a_RelY, a_RelZ + 1 ) ); + m_ToTickBlocks.push_back( MakeIndex( a_RelX, a_RelY, a_RelZ - 1 ) ); + + Vector3i WorldPos = PositionToWorldPosition( a_RelX, a_RelY, a_RelZ ); + cBlockEntity* BlockEntity = GetBlockEntity( WorldPos ); + if( BlockEntity ) + { + BlockEntity->Destroy(); + RemoveBlockEntity( BlockEntity ); + delete BlockEntity; + } + switch( a_BlockType ) + { + case E_BLOCK_CHEST: + { + AddBlockEntity( new cChestEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World) ); + break; + } + case E_BLOCK_FURNACE: + { + AddBlockEntity( new cFurnaceEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World) ); + break; + } + case E_BLOCK_SIGN_POST: + case E_BLOCK_WALLSIGN: + { + AddBlockEntity( new cSignEntity( (ENUM_BLOCK_ID)a_BlockType, WorldPos.x, WorldPos.y, WorldPos.z, m_World) ); + break; + } + case E_BLOCK_NOTE_BLOCK: + { + AddBlockEntity(new cNoteEntity(WorldPos.x, WorldPos.y, WorldPos.z, m_World)); + break; + } + } // switch (a_BlockType) +} + + + + + +void cChunk::FastSetBlock( int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta) +{ + ASSERT(!((a_X < 0 || a_X >= Width || a_Y < 0 || a_Y >= Height || a_Z < 0 || a_Z >= Width))); + + ASSERT(IsValid()); + + const int index = MakeIndexNoCheck( a_X, a_Y, a_Z ); + const BLOCKTYPE OldBlock = m_BlockTypes[index]; + const BLOCKTYPE OldBlockMeta = GetNibble( m_BlockMeta, index ); + if ((OldBlock == a_BlockType) && (OldBlockMeta == a_BlockMeta)) + { + return; + } + + MarkDirty(); + + m_BlockTypes[index] = a_BlockType; + + { + cCSLock Lock(m_CSBlockLists); + m_PendingSendBlocks.push_back( index ); + } + + SetNibble( m_BlockMeta, index, a_BlockMeta ); + + // ONLY recalculate lighting if it's necessary! + if( + (g_BlockLightValue[ OldBlock ] != g_BlockLightValue[ a_BlockType ]) || + (g_BlockSpreadLightFalloff[ OldBlock ] != g_BlockSpreadLightFalloff[ a_BlockType ]) || + (g_BlockTransparent[ OldBlock ] != g_BlockTransparent[ a_BlockType ] ) + ) + { + m_IsLightValid = false; + } + + // Update heightmap, if needed: + if (a_Y >= m_HeightMap[a_X + a_Z * Width]) + { + if (a_BlockType != E_BLOCK_AIR) + { + m_HeightMap[a_X + a_Z * Width] = (unsigned char)a_Y; + } + else + { + for (int y = a_Y - 1; y > 0; --y) + { + if (m_BlockTypes[MakeIndexNoCheck(a_X, y, a_Z)] != E_BLOCK_AIR) + { + m_HeightMap[a_X + a_Z * Width] = (unsigned char)y; + break; + } + } // for y - column in m_BlockData + } + } +} + + + + + +void cChunk::SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client) +{ + unsigned int index = MakeIndex(a_RelX, a_RelY, a_RelZ); + if (index == INDEX_OUT_OF_RANGE) + { + LOGWARN("cChunk::SendBlockTo Index out of range!"); + return; + } + + if (a_Client == NULL) + { + // Queue the block for all clients in the chunk (will be sent in Tick()) + m_PendingSendBlocks.push_back(index); + return; + } + + Vector3i wp = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ); + a_Client->SendBlockChange(wp.x, wp.y, wp.z, GetBlock(index), GetMeta(index)); +} + + + + + +void cChunk::AddBlockEntity( cBlockEntity* a_BlockEntity ) +{ + cCSLock Lock(m_CSBlockLists); + m_BlockEntities.push_back( a_BlockEntity ); +} + + + + + +cBlockEntity * cChunk::GetBlockEntity(int a_X, int a_Y, int a_Z) +{ + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if ( + ((*itr)->GetPosX() == a_X) && + ((*itr)->GetPosY() == a_Y) && + ((*itr)->GetPosZ() == a_Z) + ) + { + return *itr; + } + } // for itr - m_BlockEntities[] + + return NULL; +} + + + + + +void cChunk::UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z) +{ + cBlockEntity * be = GetBlockEntity(a_X, a_Y, a_Z); + if (be != NULL) + { + be->UsedBy(a_Player); + } +} + + + + + +void cChunk::CollectPickupsByPlayer(cPlayer * a_Player) +{ + double PosX = a_Player->GetPosX(); + double PosY = a_Player->GetPosY(); + double PosZ = a_Player->GetPosZ(); + + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) + { + if ( (*itr)->GetEntityType() != cEntity::eEntityType_Pickup ) + { + continue; // Only pickups + } + float DiffX = (float)((*itr)->GetPosX() - PosX ); + float DiffY = (float)((*itr)->GetPosY() - PosY ); + float DiffZ = (float)((*itr)->GetPosZ() - PosZ ); + float SqrDist = DiffX * DiffX + DiffY * DiffY + DiffZ * DiffZ; + if (SqrDist < 1.5f * 1.5f) // 1.5 block + { + MarkDirty(); + (reinterpret_cast(*itr))->CollectedBy( a_Player ); + } + } +} + + + + + +void cChunk::UpdateSign(int a_PosX, int a_PosY, int a_PosZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) +{ + // Also sends update packets to all clients in the chunk + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if ( + ((*itr)->GetPosX() == a_PosX) && + ((*itr)->GetPosY() == a_PosY) && + ((*itr)->GetPosZ() == a_PosZ) && + ( + ((*itr)->GetBlockType() == E_BLOCK_WALLSIGN) || + ((*itr)->GetBlockType() == E_BLOCK_SIGN_POST) + ) + ) + { + MarkDirty(); + (reinterpret_cast(*itr))->SetLines(a_Line1, a_Line2, a_Line3, a_Line4); + m_World->BroadcastBlockEntity(a_PosX, a_PosY, a_PosZ); + } + } // for itr - m_BlockEntities[] +} + + + + + +void cChunk::RemoveBlockEntity( cBlockEntity* a_BlockEntity ) +{ + cCSLock Lock(m_CSBlockLists); + MarkDirty(); + m_BlockEntities.remove( a_BlockEntity ); +} + + + + + +bool cChunk::AddClient(cClientHandle* a_Client) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) + { + if (a_Client == *itr) + { + // Already there, nothing needed + return false; + } + } + m_LoadedByClient.push_back( a_Client ); + + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr ) + { + LOGD("cChunk: Entity #%d (%s) at [%i, %i, %i] spawning for player \"%s\"", (*itr)->GetUniqueID(), (*itr)->GetClass(), m_PosX, m_PosY, m_PosZ, a_Client->GetUsername().c_str()); + (*itr)->SpawnOn(*a_Client); + } + return true; +} + + + + + +void cChunk::RemoveClient( cClientHandle* a_Client ) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) + { + if (*itr != a_Client) + { + continue; + } + + m_LoadedByClient.erase(itr); + + if (!a_Client->IsDestroyed()) + { + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr ) + { + LOGD("chunk [%i, %i] destroying entity #%i for player \"%s\"", m_PosX, m_PosZ, (*itr)->GetUniqueID(), a_Client->GetUsername().c_str() ); + a_Client->SendDestroyEntity(*(*itr)); + } + } + return; + } // for itr - m_LoadedByClient[] +} + + + + + +bool cChunk::HasClient( cClientHandle* a_Client ) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) + { + if ((*itr) == a_Client) + { + return true; + } + } + return false; +} + + + + + +bool cChunk::HasAnyClients(void) +{ + return !m_LoadedByClient.empty(); +} + + + + + +void cChunk::AddEntity( cEntity * a_Entity) +{ + if (a_Entity->GetEntityType() != cEntity::eEntityType_Player) + { + MarkDirty(); + } + m_Entities.push_back( a_Entity ); +} + + + + + +void cChunk::RemoveEntity(cEntity * a_Entity) +{ + size_t SizeBefore = m_Entities.size(); + m_Entities.remove(a_Entity); + size_t SizeAfter = m_Entities.size(); + + if (SizeBefore != SizeAfter) + { + // Mark as dirty if it was a server-generated entity: + if (a_Entity->GetEntityType() != cEntity::eEntityType_Player) + { + MarkDirty(); + } + } +} + + + + + +bool cChunk::ForEachEntity(cEntityCallback & a_Callback) +{ + // The entity list is locked by the parent chunkmap's CS + for (cEntityList::iterator itr = m_Entities.begin(), itr2 = itr; itr != m_Entities.end(); itr = itr2) + { + ++itr2; + if (a_Callback.Item(*itr)) + { + return false; + } + } // for itr - m_Entitites[] + return true; +} + + + + + +bool cChunk::ForEachChest(cChestCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if ((*itr)->GetBlockType() != E_BLOCK_CHEST) + { + continue; + } + if (a_Callback.Item((cChestEntity *)*itr)) + { + return false; + } + } // for itr - m_BlockEntitites[] + return true; +} + + + + + +bool cChunk::ForEachFurnace(cFurnaceCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + switch ((*itr)->GetBlockType()) + { + case E_BLOCK_FURNACE: + case E_BLOCK_LIT_FURNACE: + { + break; + } + default: + { + continue; + } + } + if (a_Callback.Item((cFurnaceEntity *)*itr)) + { + return false; + } + } // for itr - m_BlockEntitites[] + return true; +} + + + + + +bool cChunk::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + if ((*itr)->GetBlockType() != E_BLOCK_CHEST) + { + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } + + // The correct block entity is here + if (a_Callback.Item((cChestEntity *)*itr)) + { + return false; + } + return true; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + +bool cChunk::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + switch ((*itr)->GetBlockType()) + { + case E_BLOCK_FURNACE: + case E_BLOCK_LIT_FURNACE: + { + break; + } + default: + { + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } + } // switch (BlockType) + + // The correct block entity is here, + if (a_Callback.Item((cFurnaceEntity *)*itr)) + { + return false; + } + return true; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + +bool cChunk::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + switch ((*itr)->GetBlockType()) + { + case E_BLOCK_WALLSIGN: + case E_BLOCK_SIGN_POST: + { + a_Line1 = ((cSignEntity *)*itr)->GetLine(0); + a_Line2 = ((cSignEntity *)*itr)->GetLine(1); + a_Line3 = ((cSignEntity *)*itr)->GetLine(2); + a_Line4 = ((cSignEntity *)*itr)->GetLine(3); + return true; + } + } // switch (BlockType) + + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + +BLOCKTYPE cChunk::GetBlock( int a_X, int a_Y, int a_Z ) +{ + if ((a_X < 0) || (a_X >= Width) || (a_Y < 0) || (a_Y >= Height) || (a_Z < 0) || (a_Z >= Width)) return 0; // Clip + + return m_BlockTypes[ MakeIndexNoCheck( a_X, a_Y, a_Z ) ]; +} + + + + + +BLOCKTYPE cChunk::GetBlock( int a_BlockIdx ) +{ + if( a_BlockIdx < 0 || a_BlockIdx >= NumBlocks ) return 0; + return m_BlockTypes[ a_BlockIdx ]; +} + + + + + +void cChunk::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); + a_BlockType = cChunkDef::GetBlock (m_BlockTypes, a_RelX, a_RelY, a_RelZ); + a_BlockMeta = cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cChunk::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendPlayerAnimation(a_Player, a_Animation); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntLook(a_Entity); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntHeadLook(a_Entity); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendDestroyEntity(a_Entity); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendEntityStatus(a_Entity, a_Status); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastMetadata(const cPawn & a_Pawn, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendMetadata(a_Pawn); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastSpawn(cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + a_Entity.SpawnOn(*(*itr)); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendCollectPickup(a_Pickup, a_Player); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastChunkData(cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendChunkData(m_PosX, m_PosZ, a_Serializer); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + // We can operate on entity pointers, we're inside the ChunkMap's CS lock which guards the list + cBlockEntity * Entity = GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); + if (Entity == NULL) + { + return; + } + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + Entity->SendTo(*(*itr)); + } // for itr - LoadedByClient[] +} + + + + + +void cChunk::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client) +{ + cBlockEntity * Entity = GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); + if (Entity == NULL) + { + return; + } + Entity->SendTo(a_Client); +} + + + + + +void cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ, int & a_BlockX, int & a_BlockY, int & a_BlockZ) +{ + a_BlockY = a_RelY; + a_BlockX = m_PosX * Width + a_RelX; + a_BlockZ = m_PosZ * Width + a_RelZ; +} + + + + + +Vector3i cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ) +{ + return Vector3i(m_PosX * Width + a_RelX, m_PosY * Height + a_RelY, m_PosZ * Width + a_RelZ); +} + + + + + +#if !C_CHUNK_USE_INLINE +# include "cChunk.inl.h" +#endif + + + + diff --git a/source/Chunk.h b/source/Chunk.h new file mode 100644 index 000000000..c9d1e0899 --- /dev/null +++ b/source/Chunk.h @@ -0,0 +1,321 @@ + +#pragma once + +#include "Entity.h" +#include "ChunkDef.h" + + + + + +#define C_CHUNK_USE_INLINE 1 + +// Do not touch +#if C_CHUNK_USE_INLINE + #define __C_CHUNK_INLINE__ inline +#else + #define __C_CHUNK_INLINE__ +#endif + + + + + +namespace Json +{ + class Value; +}; + + + + + +class cWorld; +class cFurnaceEntity; +class cClientHandle; +class cServer; +class MTRand; +class cPlayer; +class cChunkMap; +class cChestEntity; +class cFurnaceEntity; +class cBlockArea; +class cPawn; +class cPickup; +class cChunkDataSerializer; + +typedef std::list cClientHandleList; +typedef cItemCallback cEntityCallback; +typedef cItemCallback cChestCallback; +typedef cItemCallback cFurnaceCallback; + + + + +// This class is not to be used directly +// Instead, call actions on cChunkMap (such as cChunkMap::SetBlock() etc.) +class cChunk : + public cChunkDef // The inheritance is "misused" here only to inherit the functions and constants defined in cChunkDef +{ +public: + cChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkMap * a_ChunkMap, cWorld * a_World); + ~cChunk(); + + bool IsValid(void) const {return m_IsValid; } // Returns true if the chunk block data is valid (loaded / generated) + void SetValid(void); // Also wakes up any calls to cChunkMap::GetHeight() + void MarkRegenerating(void); // Marks all clients attached to this chunk as wanting this chunk + bool IsDirty(void) const {return m_IsDirty; } // Returns true if the chunk has changed since it was last saved + bool HasLoadFailed(void) const {return m_HasLoadFailed; } // Returns true if the chunk failed to load and hasn't been generated since then + bool CanUnload(void); + + bool IsLightValid(void) const {return m_IsLightValid; } + + /* + To save a chunk, the WSSchema must: + 1. Mark the chunk as being saved (MarkSaving() ) + 2. Get the chunk's data using GetAllData() + 3. Mark the chunk as saved (MarkSaved() ) + If anywhere inside this sequence another thread mmodifies the chunk, the chunk will not get marked as saved in MarkSaved() + */ + void MarkSaving(void); // Marks the chunk as being saved. + void MarkSaved(void); // Marks the chunk as saved, if it didn't change from the last call to MarkSaving() + void MarkLoaded(void); // Marks the chunk as freshly loaded. Fails if the chunk is already valid + void MarkLoadFailed(void); // Marks the chunk as failed to load. Ignored is the chunk is already valid + + /// Gets all chunk data, calls the a_Callback's methods for each data type + void GetAllData(cChunkDataCallback & a_Callback); + + /// Sets all chunk data + void SetAllData( + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const cChunkDef::HeightMap * a_HeightMap, + const cChunkDef::BiomeMap & a_BiomeMap, + cEntityList & a_Entities, + cBlockEntityList & a_BlockEntities + ); + + void SetLight( + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight + ); + + /// Copies m_BlockData into a_BlockTypes, only the block types + void GetBlockTypes(BLOCKTYPE * a_BlockTypes); + + /// Copies entire block data into a_BlockData, the entire 4 arrays (Type, Meta, Light, SkyLight) + void GetBlockData(BLOCKTYPE * a_BlockData); + + /// Returns true if there is a block entity at the coords specified + bool HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Sets or resets the internal flag that prevents chunk from being unloaded + void Stay(bool a_Stay = true); + + void Tick(float a_Dt, MTRand & a_TickRandom); + + int GetPosX() { return m_PosX; } + int GetPosY() { return m_PosY; } + int GetPosZ() { return m_PosZ; } + cWorld * GetWorld() { return m_World; } + + // OBSOLETE void SendTo( cClientHandle * a_Client ); + + void SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ); + // SetBlock() does a lot of work (heightmap, tickblocks, blockentities) so a BlockIdx version doesn't make sense + void SetBlock( const Vector3i & a_RelBlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) { SetBlock( a_RelBlockPos.x, a_RelBlockPos.y, a_RelBlockPos.z, a_BlockType, a_BlockMeta ); } + void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta ); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc. + BLOCKTYPE GetBlock( int a_X, int a_Y, int a_Z ); + BLOCKTYPE GetBlock( int a_BlockIdx ); + void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); + + EMCSBiome GetBiomeAt(int a_RelX, int a_RelZ) const {return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ); } + + void CollectPickupsByPlayer(cPlayer * a_Player); + void UpdateSign(int a_PosX, int a_PosY, int a_PosZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); // Also sends update packets to all clients in the chunk + + int GetHeight( int a_X, int a_Z ); + + void SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client); + + /// Adds a client to the chunk; returns true if added, false if already there + bool AddClient (cClientHandle* a_Client ); + + void RemoveClient (cClientHandle* a_Client ); + bool HasClient (cClientHandle* a_Client ); + bool HasAnyClients(void); // Returns true if theres any client in the chunk; false otherwise + + void AddEntity( cEntity * a_Entity); + void RemoveEntity( cEntity * a_Entity); + + /// Calls the callback for each entity; returns true if all entities processed, false if the callback aborted by returning true + bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible + + /// Calls the callback for each chest; returns true if all chests processed, false if the callback aborted by returning true + bool ForEachChest(cChestCallback & a_Callback); // Lua-accessible + + /// Calls the callback for each furnace; returns true if all furnaces processed, false if the callback aborted by returning true + bool ForEachFurnace(cFurnaceCallback & a_Callback); // Lua-accessible + + /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found + bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible + + /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords, true if found + bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Lua-accessible + + /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found + bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible + + void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); // [x, y, z] in world block coords + + void CalculateLighting(); // Recalculate right now + void CalculateHeightmap(); + + void BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); + void BroadcastEntRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + void BroadcastEntRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + void BroadcastEntLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntHeadLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL); + void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); + void BroadcastMetadata (const cPawn & a_Pawn, const cClientHandle * a_Exclude = NULL); + void BroadcastSpawn (cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); + void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); + void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); + void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8 + void BroadcastChunkData (cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); + + void SendBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); + + Vector3i PositionToWorldPosition(const Vector3i & a_RelPos) + { + return PositionToWorldPosition(a_RelPos.x, a_RelPos.y, a_RelPos.z); + } + + void PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ, int & a_BlockX, int & a_BlockY, int & a_BlockZ); + Vector3i PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ ); + + inline void MarkDirty(void) + { + m_IsDirty = true; + m_IsSaving = false; + } + + /// Sets the blockticking to start at the specified block. Only one blocktick may be set, second call overwrites the first call + inline void SetNextBlockTick(int a_RelX, int a_RelY, int a_RelZ) + { + m_BlockTickX = a_RelX; + m_BlockTickY = a_RelY; + m_BlockTickZ = a_RelZ; + } + + inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) {return cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); } + inline NIBBLETYPE GetMeta(int a_BlockIdx) {return cChunkDef::GetNibble(m_BlockMeta, a_BlockIdx); } + inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ, a_Meta); } + + inline NIBBLETYPE GetLight(int a_RelX, int a_RelY, int a_RelZ) {return cChunkDef::GetNibble(m_BlockLight, a_RelX, a_RelY, a_RelZ); } + inline NIBBLETYPE GetSkyLight(int a_RelX, int a_RelY, int a_RelZ) {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ); } + +private: + + friend class cChunkMap; + + bool m_IsValid; // True if the chunk is loaded / generated + bool m_IsLightValid; // True if the blocklight and skylight are calculated + bool m_IsDirty; // True if the chunk has changed since it was last saved + bool m_IsSaving; // True if the chunk is being saved + bool m_HasLoadFailed; // True if chunk failed to load and hasn't been generated yet since then + + cCriticalSection m_CSBlockLists; + std::deque< unsigned int > m_ToTickBlocks; + std::vector< unsigned int > m_PendingSendBlocks; + + // A critical section is not needed, because all chunk access is protected by its parent ChunkMap's csLayers + cClientHandleList m_LoadedByClient; + cClientHandleList m_UnloadQuery; + cEntityList m_Entities; + cBlockEntityList m_BlockEntities; + + /// Number of times the chunk has been requested to stay (by various cChunkStay objects); if zero, the chunk can be unloaded + int m_StayCount; + + int m_PosX, m_PosY, m_PosZ; + cWorld * m_World; + cChunkMap * m_ChunkMap; + + // TODO: Make these pointers and don't allocate what isn't needed + BLOCKTYPE m_BlockTypes [cChunkDef::NumBlocks]; + NIBBLETYPE m_BlockMeta [cChunkDef::NumBlocks / 2]; + NIBBLETYPE m_BlockLight [cChunkDef::NumBlocks / 2]; + NIBBLETYPE m_BlockSkyLight[cChunkDef::NumBlocks / 2]; + + cChunkDef::HeightMap m_HeightMap; + cChunkDef::BiomeMap m_BiomeMap; + + int m_BlockTickX, m_BlockTickY, m_BlockTickZ; + + void RemoveBlockEntity( cBlockEntity* a_BlockEntity ); + void AddBlockEntity( cBlockEntity* a_BlockEntity ); + cBlockEntity * GetBlockEntity( int a_X, int a_Y, int a_Z ); + cBlockEntity * GetBlockEntity( const Vector3i & a_BlockPos ) { return GetBlockEntity( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z ); } + + void SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff); + + void CreateBlockEntities(void); + + // Makes a copy of the list + cClientHandleList GetAllClients(void) const {return m_LoadedByClient; } + + /// Sends m_PendingSendBlocks to all clients + void BroadcastPendingBlockChanges(void); + + /// Checks the block scheduled for checking in m_ToTickBlocks[] + void CheckBlocks(void); + + void TickBlocks (MTRand & a_TickRandom); + void TickGrass (int a_RelX, int a_RelY, int a_RelZ, MTRand & a_TickRandom); + void TickMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, int a_BlockIdx, BLOCKTYPE a_BlockType, MTRand & a_TickRandom); + void TickFarmland (int a_RelX, int a_RelY, int a_RelZ); + + /// Grows sugarcane by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking) + void GrowSugarcane (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks); + + /// Grows cactus by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking) + void GrowCactus (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks); + + /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem) + void GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_Random); + + /// Checks if a leaves block at the specified coords has a log up to 4 blocks away connected by other leaves blocks (false if no log) + bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Same as GetBlock(), but relative coords needn't be in this chunk (uses m_ChunkMap in such a case); returns true on success; only usable in Tick() + bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); + + /// Same as SetBlock(), but relative coords needn't be in this chunk (uses m_ChunkMap in such a case); returns true on success; only usable in Tick() + bool UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_ChunkMap in such a case); returns true on success; only usable in Tick() + bool UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); +}; + +typedef cChunk * cChunkPtr; + +typedef std::list cChunkPtrList; + + + + + +#if C_CHUNK_USE_INLINE + #include "Chunk.inl.h" +#endif + + + + diff --git a/source/Chunk.inl.h b/source/Chunk.inl.h new file mode 100644 index 000000000..fb9c4dad1 --- /dev/null +++ b/source/Chunk.inl.h @@ -0,0 +1,34 @@ + +#ifndef __C_CHUNK_INL_H__ +#define __C_CHUNK_INL_H__ + +#ifndef MAX +# define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + + + + + +__C_CHUNK_INLINE__ +void cChunk::SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff) +{ + unsigned char CurrentLight = cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z ); + cChunkDef::SetNibble( a_LightBuffer, a_X-1, a_Y, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X-1, a_Y, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); + cChunkDef::SetNibble( a_LightBuffer, a_X+1, a_Y, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X+1, a_Y, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); + cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y-1, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y-1, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); + cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y+1, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y+1, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); + cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y, a_Z-1, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z-1 ), MAX(0,CurrentLight-a_Falloff) ) ); + cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y, a_Z+1, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z+1 ), MAX(0,CurrentLight-a_Falloff) ) ); + MarkDirty(); +} + + + + + +#endif + + + + diff --git a/source/ChunkMap.cpp b/source/ChunkMap.cpp new file mode 100644 index 000000000..508d5ae11 --- /dev/null +++ b/source/ChunkMap.cpp @@ -0,0 +1,1874 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ChunkMap.h" +#include "World.h" +#include "Root.h" +#include "Player.h" +#include "BlockID.h" +#include "Item.h" +#include "Pickup.h" +#include "Chunk.h" +#include "Generating/Trees.h" // used in cChunkMap::ReplaceTreeBlocks() for tree block discrimination + +#ifndef _WIN32 + #include // abs +#endif + +#include "zlib.h" +#include + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cChunkMap: + +cChunkMap::cChunkMap(cWorld * a_World ) + : m_World( a_World ) +{ +} + + + + + +cChunkMap::~cChunkMap() +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + delete *itr; + } // for itr - m_Layers[] +} + + + + + +void cChunkMap::RemoveLayer( cChunkLayer* a_Layer ) +{ + cCSLock Lock(m_CSLayers); + m_Layers.remove(a_Layer); +} + + + + + +cChunkMap::cChunkLayer * cChunkMap::GetLayer(int a_LayerX, int a_LayerZ) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + if (((*itr)->GetX() == a_LayerX) && ((*itr)->GetZ() == a_LayerZ)) + { + return *itr; + } + } + + // Not found, create new: + cChunkLayer * Layer = new cChunkLayer(a_LayerX, a_LayerZ, this); + if (Layer == NULL) + { + LOGERROR("cChunkMap: Cannot create new layer, server out of memory?"); + return NULL; + } + m_Layers.push_back(Layer); + return Layer; +} + + + + + +cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk( int a_ChunkX, int a_ChunkZ ) +{ + const int LayerX = (int)(floorf((float)a_ChunkX / (float)(LAYER_SIZE))); + const int LayerZ = (int)(floorf((float)a_ChunkZ / (float)(LAYER_SIZE))); + return GetLayer( LayerX, LayerZ ); +} + + + + + +cChunkPtr cChunkMap::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) +{ + // No need to lock m_CSLayers, since it's already locked by the operation that called us + cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); + if (Layer == NULL) + { + // An error must have occurred, since layers are automatically created if they don't exist + return NULL; + } + + cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); + if (Chunk == NULL) + { + return NULL; + } + if (!(Chunk->IsValid())) + { + m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ, true); + } + return Chunk; +} + + + + + +cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) +{ + // No need to lock m_CSLayers, since it's already locked by the operation that called us + cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); + if (Layer == NULL) + { + // An error must have occurred, since layers are automatically created if they don't exist + return NULL; + } + + cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); + if (Chunk == NULL) + { + return NULL; + } + if (!(Chunk->IsValid())) + { + m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ, false); + } + + return Chunk; +} + + + + + +cChunkPtr cChunkMap::GetChunkNoLoad( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) +{ + // No need to lock m_CSLayers, since it's already locked by the operation that called us + cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); + if (Layer == NULL) + { + // An error must have occurred, since layers are automatically created if they don't exist + return NULL; + } + + return Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +bool cChunkMap::LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + // We already have m_CSLayers locked since this can be called only from within the tick thread + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return false; + } + + int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ); + a_BlockType = Chunk->GetBlock(Index); + a_BlockMeta = Chunk->GetMeta(Index); + return true; +} + + + + + +bool cChunkMap::LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + // We already have m_CSLayers locked since this can be called only from within the tick thread + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return false; + } + + Chunk->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); + return true; +} + + + + + +bool cChunkMap::LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + // We already have m_CSLayers locked since this can be called only from within the tick thread + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return false; + } + + Chunk->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); + return true; +} + + + + + +void cChunkMap::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Player.GetChunkX(), a_Player.GetChunkY(), a_Player.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastPlayerAnimation(a_Player, a_Animation, a_Exclude); +} + + + + + +void cChunkMap::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkY(), a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityEquipment(a_Entity, a_SlotNum, a_Item, a_Exclude); +} + + + + + + +void cChunkMap::BroadcastEntRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkY(), a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); +} + + + + + + +void cChunkMap::BroadcastEntRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkY(), a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntRelMove(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); +} + + + + + + +void cChunkMap::BroadcastEntLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkY(), a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntLook(a_Entity, a_Exclude); +} + + + + + + +void cChunkMap::BroadcastEntHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkY(), a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntHeadLook(a_Entity, a_Exclude); +} + + + + + + +void cChunkMap::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int x, y, z, ChunkX, ChunkZ; + x = a_BlockX; + y = a_BlockY; + z = a_BlockZ; + cChunkDef::BlockToChunk(x, y, z, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType, a_Exclude); +} + + + + + + +void cChunkMap::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkY(), a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastDestroyEntity(a_Entity, a_Exclude); +} + + + + + + +void cChunkMap::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkY(), a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastEntityStatus(a_Entity, a_Status, a_Exclude); +} + + + + + + +void cChunkMap::BroadcastMetadata(const cPawn & a_Pawn, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Pawn.GetChunkX(), a_Pawn.GetChunkY(), a_Pawn.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastMetadata(a_Pawn, a_Exclude); +} + + + + + +void cChunkMap::BroadcastSpawn(cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkY(), a_Entity.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastSpawn(a_Entity, a_Exclude); +} + + + + + +void cChunkMap::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_Pickup.GetChunkX(), a_Pickup.GetChunkY(), a_Pickup.GetChunkZ()); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastCollectPickup(a_Pickup, a_Player, a_Exclude); +} + + + + + +void cChunkMap::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); +} + + + + + +void cChunkMap::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + + cChunkDef::BlockToChunk(a_SrcX / 8, a_SrcY / 8, a_SrcZ / 8, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch, a_Exclude); +} + + + + + +void cChunkMap::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, 0, a_ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastChunkData(a_Serializer, a_Exclude); +} + + + + + +void cChunkMap::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->BroadcastBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); +} + + + + + +void cChunkMap::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->SendBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Client); +} + + + + + +void cChunkMap::UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) +{ + // a_Player rclked block entity at the coords specified, handle it + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cChunkMap::MarkChunkDirty (int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->MarkDirty(); +} + + + + + +void cChunkMap::MarkChunkSaving(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->MarkSaving(); +} + + + + + +void cChunkMap::MarkChunkSaved (int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->MarkSaved(); +} + + + + + +void cChunkMap::SetChunkData( + int a_ChunkX, int a_ChunkY, int a_ChunkZ, + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const cChunkDef::HeightMap * a_HeightMap, + const cChunkDef::BiomeMap & a_BiomeMap, + cEntityList & a_Entities, + cBlockEntityList & a_BlockEntities, + bool a_MarkDirty +) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, a_ChunkZ); + if (Chunk == NULL) + { + return; + } + Chunk->SetAllData(a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, a_HeightMap, a_BiomeMap, a_Entities, a_BlockEntities); + Chunk->SetValid(); + + if (a_MarkDirty) + { + Chunk->MarkDirty(); + } +} + + + + + +void cChunkMap::ChunkLighted( + int a_ChunkX, int a_ChunkZ, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight +) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + return; + } + Chunk->SetLight(a_BlockLight, a_SkyLight); + Chunk->MarkDirty(); +} + + + + + +bool cChunkMap::GetChunkData(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkDataCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return false; + } + Chunk->GetAllData(a_Callback); + return true; +} + + + + + +bool cChunkMap::GetChunkBlockTypes(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockTypes) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return false; + } + Chunk->GetBlockTypes(a_BlockTypes); + return true; +} + + + + + +bool cChunkMap::GetChunkBlockData(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return false; + } + Chunk->GetBlockData(a_BlockData); + return true; +} + + + + + +bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, a_ChunkZ); + return (Chunk != NULL) && Chunk->IsValid(); +} + + + + + +bool cChunkMap::HasChunkAnyClients(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); + return (Chunk != NULL) && Chunk->HasAnyClients(); +} + + + + + +int cChunkMap::GetHeight(int a_BlockX, int a_BlockZ) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ, BlockY = 0; + cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk == NULL) + { + return 0; + } + + // Wait for the chunk to become valid: + while (!Chunk->IsValid()) + { + GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); // Re-queue (in case it managed to get unloaded before we caught it + cCSUnlock Unlock(Lock); + m_evtChunkValid.Wait(); + } + + return Chunk->GetHeight(a_BlockX, a_BlockZ); +} + + + + + +void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList) +{ + sSetBlockList Failed; + + // Process all items from a_BlockList, either successfully or by placing into Failed + while (!a_BlockList.empty()) + { + int ChunkX = a_BlockList.front().ChunkX; + int ChunkZ = a_BlockList.front().ChunkZ; + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();) + { + if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) + { + Chunk->FastSetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + itr = a_BlockList.erase(itr); + } + else + { + ++itr; + } + } // for itr - a_BlockList[] + } + else + { + // The chunk is not valid, move all blocks within this chunk to Failed + for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();) + { + if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) + { + Failed.push_back(*itr); + itr = a_BlockList.erase(itr); + } + else + { + ++itr; + } + } // for itr - a_BlockList[] + } + } + + // Return the failed: + std::swap(Failed, a_BlockList); +} + + + + + +void cChunkMap::CollectPickupsByPlayer(cPlayer * a_Player) +{ + int BlockX = (int)(a_Player->GetPosX()); // Truncating doesn't matter much; we're scanning entire chunks anyway + int BlockY = (int)(a_Player->GetPosY()); + int BlockZ = (int)(a_Player->GetPosZ()); + int ChunkX, ChunkZ, ChunkY = ZERO_CHUNK_Y; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + int OtherChunkX = ChunkX + ((BlockX > 8) ? 1 : -1); + int OtherChunkZ = ChunkZ + ((BlockZ > 8) ? 1 : -1); + + // We suppose that each player keeps their chunks in memory, therefore it makes little sense to try to re-load or even generate them. + // The only time the chunks are not valid is when the player is downloading the initial world and they should not call this at that moment + + cCSLock Lock(m_CSLayers); + GetChunkNoLoad(ChunkX, ChunkY, ChunkZ)->CollectPickupsByPlayer(a_Player); + + // Check the neighboring chunks as well: + GetChunkNoLoad(OtherChunkX, ChunkY, ChunkZ )->CollectPickupsByPlayer(a_Player); + GetChunkNoLoad(OtherChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player); + GetChunkNoLoad(ChunkX, ChunkY, ChunkZ )->CollectPickupsByPlayer(a_Player); + GetChunkNoLoad(ChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player); +} + + + + + +BLOCKTYPE cChunkMap::GetBlock(int a_X, int a_Y, int a_Z) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + return Chunk->GetBlock(a_X, a_Y, a_Z); + } + return 0; +} + + + + + +BLOCKTYPE cChunkMap::GetBlockMeta(int a_X, int a_Y, int a_Z) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid() ) + { + return Chunk->GetMeta(a_X, a_Y, a_Z); + } + return 0; +} + + + + + +BLOCKTYPE cChunkMap::GetBlockSkyLight(int a_X, int a_Y, int a_Z) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid() ) + { + return Chunk->GetSkyLight( a_X, a_Y, a_Z ); + } + return 0; +} + + + + + +void cChunkMap::SetBlockMeta(int a_X, int a_Y, int a_Z, NIBBLETYPE a_BlockMeta) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid() ) + { + Chunk->SetMeta(a_X, a_Y, a_Z, a_BlockMeta); + Chunk->MarkDirty(); + Chunk->SendBlockTo( a_X, a_Y, a_Z, NULL ); + } +} + + + + + +void cChunkMap::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta) +{ + int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; + cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + Chunk->SetBlock(X, Y, Z, a_BlockType, a_BlockMeta ); + } +} + + + + + +void cChunkMap::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; + cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + Chunk->GetBlockTypeMeta(X, Y, Z, a_BlockType, a_BlockMeta); + } +} + + + + + +void cChunkMap::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType) +{ + cCSLock Lock(m_CSLayers); + for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) + { + cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + continue; + } + if (Chunk->GetBlock(itr->x, itr->y, itr->z) == a_FilterBlockType) + { + Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + } + } +} + + + + + +void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks) +{ + cCSLock Lock(m_CSLayers); + for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) + { + cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + continue; + } + switch (Chunk->GetBlock(itr->x, itr->y, itr->z)) + { + CASE_TREE_OVERWRITTEN_BLOCKS: + { + Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + break; + } + case E_BLOCK_LEAVES: + { + if (itr->BlockType == E_BLOCK_LOG) + { + Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + } + break; + } + } + } // for itr - a_Blocks[] +} + + + + + +EMCSBiome cChunkMap::GetBiomeAt (int a_BlockX, int a_BlockZ) +{ + int ChunkX, ChunkZ, X = a_BlockX, Y = 0, Z = a_BlockZ; + cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + return Chunk->GetBiomeAt(X, Z); + } + else + { + return m_World->GetGenerator().GetBiomeAt(a_BlockX, a_BlockZ); + } +} + + + + + +bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) +{ + bool res = true; + cCSLock Lock(m_CSLayers); + for (sSetBlockVector::iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) + { + cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + if (!a_ContinueOnFailure) + { + return false; + } + res = false; + continue; + } + int idx = cChunkDef::MakeIndexNoCheck(itr->x, itr->y, itr->z); + itr->BlockType = Chunk->GetBlock(idx); + itr->BlockMeta = Chunk->GetMeta(idx); + } + return res; +} + + + + + +bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z) +{ + int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkZ; + + cChunkDef::AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkZ ); + + { + cCSLock Lock(m_CSLayers); + cChunkPtr DestChunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + if ((DestChunk == NULL) || !DestChunk->IsValid()) + { + return false; + } + + DestChunk->SetBlock(PosX, PosY, PosZ, E_BLOCK_AIR, 0 ); + } + + m_World->GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z); + + return true; +} + + + + + +void cChunkMap::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_X, a_Y, a_Z, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk->IsValid()) + { + Chunk->SendBlockTo(a_X, a_Y, a_Z, a_Player->GetClientHandle()); + } +} + + + + + +void cChunkMap::CompareChunkClients(int a_ChunkX1, int a_ChunkY1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkY2, int a_ChunkZ2, cClientDiffCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk1 = GetChunkNoGen(a_ChunkX1, a_ChunkY1, a_ChunkZ1); + if (Chunk1 == NULL) + { + return; + } + cChunkPtr Chunk2 = GetChunkNoGen(a_ChunkX2, a_ChunkY2, a_ChunkZ2); + if (Chunk2 == NULL) + { + return; + } + + cClientHandleList Clients1(Chunk1->GetAllClients()); + cClientHandleList Clients2(Chunk2->GetAllClients()); + + // Find "removed" clients: + for (cClientHandleList::iterator itr1 = Clients1.begin(); itr1 != Clients1.end(); ++itr1) + { + bool Found = false; + for (cClientHandleList::iterator itr2 = Clients2.begin(); itr2 != Clients2.end(); ++itr2) + { + if (*itr1 == *itr2) + { + Found = true; + break; + } + } // for itr2 - Clients2[] + if (!Found) + { + a_Callback.Removed(*itr1); + } + } // for itr1 - Clients1[] + + // Find "added" clients: + for (cClientHandleList::iterator itr2 = Clients2.begin(); itr2 != Clients2.end(); ++itr2) + { + bool Found = false; + for (cClientHandleList::iterator itr1 = Clients1.begin(); itr1 != Clients1.end(); ++itr1) + { + if (*itr1 == *itr2) + { + Found = true; + break; + } + } // for itr1 - Clients1[] + if (!Found) + { + a_Callback.Added(*itr2); + } + } // for itr2 - Clients2[] +} + + + + + +bool cChunkMap::AddChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); + if (Chunk == NULL) + { + return false; + } + return Chunk->AddClient(a_Client); +} + + + + + +void cChunkMap::RemoveChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); + if (Chunk == NULL) + { + return; + } + Chunk->RemoveClient(a_Client); +} + + + + + +void cChunkMap::RemoveClientFromChunks(cClientHandle * a_Client) +{ + cCSLock Lock(m_CSLayers); + + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->RemoveClient(a_Client); + } // for itr - m_Layers[] +} + + + + + +void cChunkMap::MoveEntityToChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr OldChunk = GetChunkNoGen(a_Entity->GetChunkX(), a_Entity->GetChunkY(), a_Entity->GetChunkZ()); + if (OldChunk != NULL) + { + OldChunk->RemoveEntity(a_Entity); + } + cChunkPtr NewChunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); + if (NewChunk != NULL) + { + NewChunk->AddEntity(a_Entity); + } +} + + + + + +void cChunkMap::RemoveEntityFromChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return; + } + Chunk->RemoveEntity(a_Entity); +} + + + + + +bool cChunkMap::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachEntity(a_Callback); +} + + + + + +bool cChunkMap::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachChest(a_Callback); +} + + + + + +bool cChunkMap::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachFurnace(a_Callback); +} + + + + + +bool cChunkMap::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithChestAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cChunkMap::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithFurnaceAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cChunkMap::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->GetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); +} + + + + + +void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +/// Loads the chunk synchronously, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) +bool cChunkMap::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + { + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); + if (Chunk == NULL) + { + // Internal error + return false; + } + if (Chunk->IsValid()) + { + // Already loaded + return true; + } + if (Chunk->HasLoadFailed()) + { + // Already tried loading and it failed + return false; + } + } + return m_World->GetStorage().LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +/// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() +void cChunkMap::LoadChunks(const cChunkCoordsList & a_Chunks) +{ + for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr) + { + LoadChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); + } // for itr - a_Chunks[] +} + + + + + +void cChunkMap::ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, a_ChunkZ); + if (Chunk == NULL) + { + return; + } + Chunk->MarkLoadFailed(); +} + + + + + +void cChunkMap::UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) +{ + cCSLock Lock(m_CSLayers); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(a_X, a_Y, a_Z, ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->UpdateSign(a_X, a_Y, a_Z, a_Line1, a_Line2, a_Line3, a_Line4); +} + + + + + +void cChunkMap::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay) +{ + cCSLock Lock(m_CSLayers); + for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr) + { + cChunkPtr Chunk = GetChunkNoLoad(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); + if (Chunk == NULL) + { + continue; + } + Chunk->Stay(a_Stay); + } +} + + + + + +void cChunkMap::MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + // Not present + return; + } + Chunk->MarkRegenerating(); +} + + + + + +bool cChunkMap::IsChunkLighted(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk == NULL) + { + // Not present + return false; + } + return Chunk->IsLightValid(); +} + + + + + +bool cChunkMap::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback) +{ + bool Result = true; + cCSLock Lock(m_CSLayers); + for (int z = a_MinChunkZ; z <= a_MaxChunkZ; z++) + { + for (int x = a_MinChunkX; x <= a_MaxChunkX; x++) + { + cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z); + if ((Chunk == NULL) || (!Chunk->IsValid())) + { + // Not present / not valid + Result = false; + continue; + } + if (!a_Callback.Coords(x, z)) + { + continue; + } + Chunk->GetAllData(a_Callback); + } + } + return Result; +} + + + + + +void cChunkMap::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) +{ + a_NumChunksValid = 0; + a_NumChunksDirty = 0; + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + int NumValid = 0, NumDirty = 0; + (*itr)->GetChunkStats(NumValid, NumDirty); + a_NumChunksValid += NumValid; + a_NumChunksDirty += NumDirty; + } // for itr - m_Layers[] +} + + + + + +void cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk != NULL) + { + Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Rand); + } +} + + + + + +void cChunkMap::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk != NULL) + { + Chunk->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); + } +} + + + + + +void cChunkMap::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk != NULL) + { + Chunk->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); + } +} + + + + + +void cChunkMap::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if (Chunk != NULL) + { + Chunk->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ); + } +} + + + + + +void cChunkMap::Tick( float a_Dt, MTRand & a_TickRandom ) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->Tick(a_Dt, a_TickRandom); + } // for itr - m_Layers +} + + + + + +void cChunkMap::UnloadUnusedChunks() +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->UnloadUnusedChunks(); + } // for itr - m_Layers +} + + + + + +void cChunkMap::SaveAllChunks(void) +{ + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + (*itr)->Save(); + } // for itr - m_Layers[] +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cChunkMap::cChunkLayer: + +cChunkMap::cChunkLayer::cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent) + : m_LayerX( a_LayerX ) + , m_LayerZ( a_LayerZ ) + , m_Parent( a_Parent ) + , m_NumChunksLoaded( 0 ) +{ + memset(m_Chunks, 0, sizeof(m_Chunks)); +} + + + + + +cChunkMap::cChunkLayer::~cChunkLayer() +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); ++i) + { + delete m_Chunks[i]; + } // for i - m_Chunks[] +} + + + + + +cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) +{ + // Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check + + const int LocalX = a_ChunkX - m_LayerX * LAYER_SIZE; + const int LocalZ = a_ChunkZ - m_LayerZ * LAYER_SIZE; + + if (!((LocalX < LAYER_SIZE) && (LocalZ < LAYER_SIZE) && (LocalX > -1) && (LocalZ > -1))) + { + ASSERT(!"Asking a cChunkLayer for a chunk that doesn't belong to it!"); + return NULL; + } + + int Index = LocalX + LocalZ * LAYER_SIZE; + if (m_Chunks[Index] == NULL) + { + m_Chunks[Index] = new cChunk(a_ChunkX, 0, a_ChunkZ, m_Parent, m_Parent->GetWorld()); + } + return m_Chunks[Index]; +} + + + + + +void cChunkMap::cChunkLayer::Tick(float a_Dt, MTRand & a_TickRand) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + // Only tick chunks that are valid and have clients: + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) + { + m_Chunks[i]->Tick(a_Dt, a_TickRand); + } + } // for i - m_Chunks[] +} + + + + + +void cChunkMap::cChunkLayer::RemoveClient(cClientHandle * a_Client) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + if (m_Chunks[i] != NULL) + { + m_Chunks[i]->RemoveClient(a_Client); + } + } // for i - m_Chunks[] +} + + + + + +int cChunkMap::cChunkLayer::GetNumChunksLoaded(void) const +{ + int NumChunks = 0; + for ( int i = 0; i < ARRAYCOUNT(m_Chunks); ++i ) + { + if (m_Chunks[i] != NULL) + { + NumChunks++; + } + } // for i - m_Chunks[] + return NumChunks; +} + + + + + +void cChunkMap::cChunkLayer::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) const +{ + int NumValid = 0; + int NumDirty = 0; + for ( int i = 0; i < ARRAYCOUNT(m_Chunks); ++i ) + { + if (m_Chunks[i] == NULL) + { + continue; + } + NumValid++; + if (m_Chunks[i]->IsDirty()) + { + NumDirty++; + } + } // for i - m_Chunks[] + a_NumChunksValid = NumValid; + a_NumChunksDirty = NumDirty; +} + + + + + +void cChunkMap::cChunkLayer::Save(void) +{ + cWorld * World = m_Parent->GetWorld(); + for (int i = 0; i < ARRAYCOUNT(m_Chunks); ++i) + { + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->IsDirty()) + { + World->GetStorage().QueueSaveChunk(m_Chunks[i]->GetPosX(), m_Chunks[i]->GetPosY(), m_Chunks[i]->GetPosZ()); + } + } // for i - m_Chunks[] +} + + + + + +void cChunkMap::cChunkLayer::UnloadUnusedChunks(void) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) + { + if ((m_Chunks[i] != NULL) && (m_Chunks[i]->CanUnload())) + { + // The cChunk destructor calls our GetChunk() while removing its entities + // so we still need to be able to return the chunk. Therefore we first delete, then NULLify + // Doing otherwise results in bug http://forum.mc-server.org/showthread.php?tid=355 + delete m_Chunks[i]; + m_Chunks[i] = NULL; + } + } // for i - m_Chunks[] +} + + + + + +int cChunkMap::GetNumChunks(void) +{ + cCSLock Lock(m_CSLayers); + int NumChunks = 0; + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) + { + NumChunks += (*itr)->GetNumChunksLoaded(); + } + return NumChunks; +} + + + + + +void cChunkMap::ChunkValidated(void) +{ + m_evtChunkValid.Set(); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cChunkStay: + +cChunkStay::cChunkStay(cWorld * a_World) : + m_World(a_World), + m_IsEnabled(false) +{ +} + + + + + +cChunkStay::~cChunkStay() +{ + Clear(); +} + + + + + +void cChunkStay::Clear(void) +{ + if (m_IsEnabled) + { + Disable(); + } + m_Chunks.clear(); +} + + + + + +void cChunkStay::Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + ASSERT(!m_IsEnabled); + + for (cChunkCoordsList::const_iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr) + { + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ)) + { + // Already present + return; + } + } // for itr - Chunks[] + m_Chunks.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); +} + + + + + +void cChunkStay::Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + ASSERT(!m_IsEnabled); + + for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr) + { + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ)) + { + // Found, un-"stay" + m_Chunks.erase(itr); + return; + } + } // for itr - m_Chunks[] +} + + + + + +void cChunkStay::Enable(void) +{ + ASSERT(!m_IsEnabled); + + m_World->ChunksStay(*this, true); + m_IsEnabled = true; +} + + + + + +void cChunkStay::Load(void) +{ + for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr) + { + m_World->TouchChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); + } // for itr - m_Chunks[] +} + + + + + +void cChunkStay::Disable(void) +{ + ASSERT(m_IsEnabled); + + m_World->ChunksStay(*this, false); + m_IsEnabled = false; +} + + + + diff --git a/source/ChunkMap.h b/source/ChunkMap.h new file mode 100644 index 000000000..4165be089 --- /dev/null +++ b/source/ChunkMap.h @@ -0,0 +1,345 @@ + +// cChunkMap.h + +// Interfaces to the cChunkMap class representing the chunk storage for a single world + +#pragma once + +#include "ChunkDef.h" + + + + + +class cWorld; +class cItem; +class MTRand; +class cChunkStay; +class cChunk; +class cPlayer; +class cChestEntity; +class cFurnaceEntity; +class cPawn; +class cPickup; +class cChunkDataSerializer; + +typedef std::list cClientHandleList; +typedef cChunk * cChunkPtr; +typedef cItemCallback cEntityCallback; +typedef cItemCallback cChestCallback; +typedef cItemCallback cFurnaceCallback; + + + + + +class cChunkMap +{ +public: + + static const int LAYER_SIZE = 32; + + cChunkMap(cWorld* a_World ); + ~cChunkMap(); + + /// Broadcasts an a_Player's animation to all clients in the chunk where a_Player is + void BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL); + + /// Broadcasts an entity equipment change to all clients in the chunk where a_Entity is + void BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); + + /// Broadcasts a RelEntMoveLook packet to all clients in the chunk where a_Entity is + void BroadcastEntRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + + /// Broadcasts a RelEntMove packet to all clients in the chunk where a_Entity is + void BroadcastEntRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + + /// Broadcasts a EntLook packet to all clients in the chunk where a_Entity is + void BroadcastEntLook(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + + /// Broadcasts a EntHeadLook packet to all clients in the chunk where a_Entity is + void BroadcastEntHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + + /// Broadcasts a BlockAction packet to all clients who are in the specified chunk + void BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL); + + void BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + + void BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); + + void BroadcastMetadata(const cPawn & a_Pawn, const cClientHandle * a_Exclude = NULL); + + void BroadcastSpawn(cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + + void BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); + + void BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); + + void BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8 + + void BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); + + /// Broadcasts the block entity, if it is at the coords specified, to all clients except a_Exclude + void BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude); + + /// Sends the block entity, if it is at the coords specified, to a_Client + void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); + + /// a_Player rclked block entity at the coords specified, handle it + void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); + + void MarkChunkDirty (int a_ChunkX, int a_ChunkY, int a_ChunkZ); + void MarkChunkSaving (int a_ChunkX, int a_ChunkY, int a_ChunkZ); + void MarkChunkSaved (int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /** Sets the chunk data as either loaded from the storage or generated. + a_BlockLight and a_BlockSkyLight are optional, if not present, chunk will be marked as unlighted. + a_BiomeMap is optional, if not present, biomes will be calculated by the generator + a_HeightMap is optional, if not present, will be calculated. + If a_MarkDirty is set, the chunk is set as dirty (used after generating) + */ + void SetChunkData( + int a_ChunkX, int a_ChunkY, int a_ChunkZ, + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const cChunkDef::HeightMap * a_HeightMap, + const cChunkDef::BiomeMap & a_BiomeMap, + cEntityList & a_Entities, + cBlockEntityList & a_BlockEntities, + bool a_MarkDirty + ); + + void ChunkLighted( + int a_ChunkX, int a_ChunkZ, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight + ); + + bool GetChunkData (int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkDataCallback & a_Callback); + + /// Gets the chunk's blocks, only the block types + bool GetChunkBlockTypes (int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_Blocks); + + /// Gets the chunk's block data, the entire 4 arrays (Types, Meta, Light, SkyLight) + bool GetChunkBlockData (int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData); + + bool IsChunkValid (int a_ChunkX, int a_ChunkY, int a_ChunkZ); + bool HasChunkAnyClients (int a_ChunkX, int a_ChunkY, int a_ChunkZ); + int GetHeight (int a_BlockX, int a_BlockZ); + void FastSetBlocks (sSetBlockList & a_BlockList); + void CollectPickupsByPlayer(cPlayer * a_Player); + BLOCKTYPE GetBlock (int a_X, int a_Y, int a_Z); + BLOCKTYPE GetBlockMeta (int a_X, int a_Y, int a_Z); + BLOCKTYPE GetBlockSkyLight (int a_X, int a_Y, int a_Z); + void SetBlockMeta (int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockMeta); + void SetBlock (int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta); + void GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); + + /// Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType + void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType); + + /// Special function used for growing trees, replaces only blocks that tree may overwrite + void ReplaceTreeBlocks(const sSetBlockVector & a_Blocks); + + EMCSBiome GetBiomeAt (int a_BlockX, int a_BlockZ); + + /// Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. + bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure); + + 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); + + /// Compares clients of two chunks, calls the callback accordingly + void CompareChunkClients(int a_ChunkX1, int a_ChunkY1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkY2, int a_ChunkZ2, cClientDiffCallback & a_Callback); + + /// Adds client to a chunk, if not already present; returns true if added, false if present + bool AddChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes the client from the chunk + void RemoveChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes the client from all chunks it is present in + void RemoveClientFromChunks(cClientHandle * a_Client); + + /// Moves the entity from its current chunk to the new chunk specified + void MoveEntityToChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Removes the entity from the chunk specified + void RemoveEntityFromChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true + bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Lua-accessible + + /// Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true + bool ForEachChestInChunk (int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Lua-accessible + + /// Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true + bool ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback); // Lua-accessible + + /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found + bool DoWithChestAt (int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible + + /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords, true if found + bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Lua-accessible + + /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found + bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible + + /// Touches the chunk, causing it to be loaded or generated + void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) + bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() + void LoadChunks(const cChunkCoordsList & a_Chunks); + + /// Marks the chunk as failed-to-load: + void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + void UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); + + /// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable; to be used only by cChunkStay! + void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true); + + /// Marks the chunk as being regenerated - all its clients want that chunk again (used by cWorld::RegenerateChunk() ) + void MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ); + + bool IsChunkLighted(int a_ChunkX, int a_ChunkZ); + + /// Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully + bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback); + + /// Returns the number of valid chunks and the number of dirty chunks + void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty); + + /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem) + void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand); + + /// Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height of 3 + void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); + + /// Grows a cactus present at the block specified by the amount of blocks specified, up to the max height of 3 + void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); + + /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call + void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); + + void Tick( float a_Dt, MTRand & a_TickRand ); + + void UnloadUnusedChunks(); + void SaveAllChunks(); + + cWorld * GetWorld() { return m_World; } + + int GetNumChunks(void); + + void ChunkValidated(void); // Called by chunks that have become valid + +private: + + friend class cChunk; // The chunks can manipulate neighbors while in their Tick() method, using LockedGetBlock() and LockedSetBlock() + + class cChunkLayer + { + public: + cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent); + ~cChunkLayer(); + + /// Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check + cChunkPtr GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ); + + int GetX(void) const {return m_LayerX; } + int GetZ(void) const {return m_LayerZ; } + + int GetNumChunksLoaded(void) const ; + + void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) const; + + void Save(void); + void UnloadUnusedChunks(void); + + void Tick( float a_Dt, MTRand & a_TickRand ); + + void RemoveClient(cClientHandle * a_Client); + + protected: + + cChunkPtr m_Chunks[LAYER_SIZE * LAYER_SIZE]; + int m_LayerX; + int m_LayerZ; + cChunkMap * m_Parent; + int m_NumChunksLoaded; + }; + + typedef std::list cChunkLayerList; + // TODO: Use smart pointers for cChunkLayerList as well, so that ticking and saving needn't lock the entire layerlist + // This however means that cChunkLayer needs to interlock its m_Chunks[] + + cChunkLayer * GetLayerForChunk( int a_ChunkX, int a_ChunkZ ); // Creates the layer if it doesn't already exist + cChunkLayer * GetLayer( int a_LayerX, int a_LayerZ ); // Creates the layer if it doesn't already exist + void RemoveLayer( cChunkLayer* a_Layer ); + + cCriticalSection m_CSLayers; + cChunkLayerList m_Layers; + cEvent m_evtChunkValid; // Set whenever any chunk becomes valid, via ChunkValidated() + + cWorld * m_World; + + cChunkPtr GetChunk (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading / generating if not valid + cChunkPtr GetChunkNoGen (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading if not valid; doesn't generate + cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Doesn't load, doesn't generate + + /// Gets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) + bool LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); + + /// Sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) + bool LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /// Fast-sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) + bool LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); +}; + + + + + +/** Makes chunks stay loaded until this object is cleared or destroyed +Works by setting internal flags in the cChunk that it should not be unloaded. +To optimize for speed, cChunkStay has an Enabled flag, it will "stay" the chunks only when enabled and it will refuse manipulations when enabled +The object itself is not made thread-safe, it's supposed to be used from a single thread only. +*/ +class cChunkStay +{ +public: + cChunkStay(cWorld * a_World); + ~cChunkStay(); + + void Clear(void); + + void Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + void Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + void Enable(void); + void Disable(void); + + /// Queues each chunk in m_Chunks[] for loading / generating + void Load(void); + + // Allow cChunkStay be passed to functions expecting a const cChunkCoordsList & + operator const cChunkCoordsList(void) const {return m_Chunks; } + +protected: + + cWorld * m_World; + + bool m_IsEnabled; + + cChunkCoordsList m_Chunks; +} ; + + + + diff --git a/source/ChunkSender.cpp b/source/ChunkSender.cpp index 922d77ad6..ea5ed91ea 100644 --- a/source/ChunkSender.cpp +++ b/source/ChunkSender.cpp @@ -9,8 +9,8 @@ #include "Globals.h" #include "ChunkSender.h" -#include "cWorld.h" -#include "cBlockEntity.h" +#include "World.h" +#include "BlockEntity.h" #include "Protocol/ChunkDataSerializer.h" diff --git a/source/ClientHandle.cpp b/source/ClientHandle.cpp new file mode 100644 index 000000000..a080b4ed6 --- /dev/null +++ b/source/ClientHandle.cpp @@ -0,0 +1,1651 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ClientHandle.h" +#include "Server.h" +#include "World.h" +#include "Pickup.h" +#include "PluginManager.h" +#include "Player.h" +#include "Inventory.h" +#include "ChestEntity.h" +#include "SignEntity.h" +#include "UI/Window.h" +#include "Item.h" +#include "Torch.h" +#include "Doors.h" +#include "Ladder.h" +#include "Vine.h" +#include "Sign.h" +#include "Redstone.h" +#include "Piston.h" +#include "Mobs/Monster.h" +#include "ChatColor.h" +#include "OSSupport/Socket.h" +#include "OSSupport/Timer.h" +#include "items/ItemHandler.h" +#include "blocks/BlockHandler.h" + +#include "Tracer.h" +#include "Vector3f.h" +#include "Vector3d.h" + +#include "Root.h" + +#include "Authenticator.h" +#include "MersenneTwister.h" + +#include "Protocol/ProtocolRecognizer.h" + + + + + +#define AddPistonDir(x, y, z, dir, amount) switch (dir) { case 0: (y)-=(amount); break; case 1: (y)+=(amount); break;\ + case 2: (z)-=(amount); break; case 3: (z)+=(amount); break;\ + case 4: (x)-=(amount); break; case 5: (x)+=(amount); break; } + + + + + +/// If the number of queued outgoing packets reaches this, the client will be kicked +#define MAX_OUTGOING_PACKETS 2000 + + + + + +#define RECI_RAND_MAX (1.f/RAND_MAX) +inline int fRadRand(MTRand & r1, int a_BlockCoord) +{ + return a_BlockCoord * 32 + (int)(16 * ((float)r1.rand() * RECI_RAND_MAX) * 16 - 8); +} + + + + + +int cClientHandle::s_ClientCount = 0; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cClientHandle: + +cClientHandle::cClientHandle(const cSocket & a_Socket, int a_ViewDistance) + : m_ViewDistance(a_ViewDistance) + , m_Socket(a_Socket) + , m_OutgoingData(64 KiB) + , m_bDestroyed(false) + , m_Player(NULL) + , m_bKicking(false) + , m_TimeLastPacket(cWorld::GetTime()) + , m_bKeepThreadGoing(true) + , m_Ping(1000) + , m_PingID(1) + , m_State(csConnected) + , m_LastStreamedChunkX(0x7fffffff) // bogus chunk coords to force streaming upon login + , m_LastStreamedChunkZ(0x7fffffff) + , m_ShouldCheckDownloaded(false) + , m_UniqueID(0) +{ + m_Protocol = new cProtocolRecognizer(this); + + s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread + m_UniqueID = s_ClientCount; + + cTimer t1; + m_LastPingTime = t1.GetNowTime(); + + LOGD("New ClientHandle created at %p", this); +} + + + + + +cClientHandle::~cClientHandle() +{ + LOGD("Deleting client \"%s\" at %p", GetUsername().c_str(), this); + + // Remove from cSocketThreads, we're not to be called anymore: + cRoot::Get()->GetServer()->ClientDestroying(this); + + { + cCSLock Lock(m_CSChunkLists); + m_LoadedChunks.clear(); + m_ChunksToSend.clear(); + } + + if (m_Player != NULL) + { + cWorld * World = m_Player->GetWorld(); + if (!m_Username.empty() && (World != NULL)) + { + // Send the Offline PlayerList packet: + World->BroadcastPlayerListItem(*m_Player, false, this); + + // Send the Chat packet: + AString Left(m_Username + " left the game!"); + World->BroadcastChat(Left, this); + } + if (World != NULL) + { + World->RemovePlayer(m_Player); + } + } + + if (m_Socket.IsValid()) + { + if (!m_bKicking) + { + SendDisconnect("Server shut down? Kthnxbai"); + } + } + + if (m_Player != NULL) + { + m_Player->Destroy(); + m_Player = NULL; + } + + // Queue all remaining outgoing packets to cSocketThreads: + { + cCSLock Lock(m_CSOutgoingData); + AString Data; + m_OutgoingData.ReadAll(Data); + m_OutgoingData.CommitRead(); + cRoot::Get()->GetServer()->WriteToClient(&m_Socket, Data); + } + + // Queue the socket to close as soon as it sends all outgoing data: + cRoot::Get()->GetServer()->QueueClientClose(&m_Socket); + + // We need to remove the socket from SocketThreads because we own it and it gets destroyed after this destructor finishes + // TODO: The socket needs to stay alive, someone else has to own it + cRoot::Get()->GetServer()->RemoveClient(&m_Socket); + + delete m_Protocol; + m_Protocol = NULL; + + LOGD("ClientHandle at %p deleted", this); +} + + + + + +void cClientHandle::Destroy() +{ + // Setting m_bDestroyed was moved to the bottom of Destroy(), + // otherwise the destructor may be called within another thread before the client is removed from chunks + // http://forum.mc-server.org/showthread.php?tid=366 + + if ((m_Player != NULL) && (m_Player->GetWorld() != NULL)) + { + RemoveFromAllChunks(); + m_Player->GetWorld()->RemoveClientFromChunkSender(this); + } + + m_bDestroyed = true; +} + + + + + +void cClientHandle::Kick(const AString & a_Reason) +{ + if (m_State >= csAuthenticating) // Don't log pings + { + LOG("Kicking user \"%s\" for \"%s\"", m_Username.c_str(), a_Reason.c_str()); + } + SendDisconnect(a_Reason); + m_bKicking = true; +} + + + + + +void cClientHandle::Authenticate(void) +{ + if (m_State != csAuthenticating) + { + return; + } + + ASSERT( m_Player == NULL ); + + // Spawn player (only serversided, so data is loaded) + m_Player = new cPlayer(this, GetUsername()); + + cWorld * World = cRoot::Get()->GetWorld(m_Player->GetLoadedWorldName()); + if (World == NULL) + { + World = cRoot::Get()->GetDefaultWorld(); + } + + if (m_Player->GetGameMode() == eGameMode_NotSet) + { + m_Player->LoginSetGameMode(World->GetGameMode()); + } + + m_Player->SetIP (m_Socket.GetIPString()); + + cRoot::Get()->GetPluginManager()->CallHook(cPluginManager::HOOK_PLAYER_SPAWN, 1, m_Player); + + m_ConfirmPosition = m_Player->GetPosition(); + + // Return a server login packet + m_Protocol->SendLogin(*m_Player, *World); + + // Send Weather if raining: + if ((World->GetWeather() == 1) || (World->GetWeather() == 2)) + { + m_Protocol->SendWeather(World->GetWeather()); + } + + // Send time + m_Protocol->SendTimeUpdate(World->GetWorldTime()); + + // Send inventory + m_Player->GetInventory().SendWholeInventory(*this); + + // Send health + m_Player->SendHealth(); + + m_Player->Initialize(World); + StreamChunks(); + m_State = csDownloadingWorld; + + // Broadcast this player's spawning to all other players in the same chunk + m_Player->GetWorld()->BroadcastSpawn(*m_Player, this); +} + + + + + +void cClientHandle::StreamChunks(void) +{ + if (m_State < csAuthenticating) + { + return; + } + + ASSERT(m_Player != NULL); + + int ChunkPosX = FAST_FLOOR_DIV(m_Player->GetPosX(), cChunkDef::Width); + int ChunkPosZ = FAST_FLOOR_DIV(m_Player->GetPosZ(), cChunkDef::Width); + if ((ChunkPosX == m_LastStreamedChunkX) && (ChunkPosZ == m_LastStreamedChunkZ)) + { + // Already streamed for this position + return; + } + m_LastStreamedChunkX = ChunkPosX; + m_LastStreamedChunkZ = ChunkPosZ; + + LOGD("Streaming chunks centered on [%d, %d], view distance %d", ChunkPosX, ChunkPosZ, m_ViewDistance); + + cWorld * World = m_Player->GetWorld(); + ASSERT(World != NULL); + + // Remove all loaded chunks that are no longer in range; deferred to out-of-CS: + cChunkCoordsList RemoveChunks; + { + cCSLock Lock(m_CSChunkLists); + for (cChunkCoordsList::iterator itr = m_LoadedChunks.begin(); itr != m_LoadedChunks.end();) + { + int RelX = (*itr).m_ChunkX - ChunkPosX; + int RelZ = (*itr).m_ChunkZ - ChunkPosZ; + if ((RelX > m_ViewDistance) || (RelX < -m_ViewDistance) || (RelZ > m_ViewDistance) || (RelZ < -m_ViewDistance)) + { + RemoveChunks.push_back(*itr); + itr = m_LoadedChunks.erase(itr); + } + else + { + ++itr; + } + } // for itr - m_LoadedChunks[] + for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end();) + { + int RelX = (*itr).m_ChunkX - ChunkPosX; + int RelZ = (*itr).m_ChunkZ - ChunkPosZ; + if ((RelX > m_ViewDistance) || (RelX < -m_ViewDistance) || (RelZ > m_ViewDistance) || (RelZ < -m_ViewDistance)) + { + itr = m_ChunksToSend.erase(itr); + } + else + { + ++itr; + } + } // for itr - m_ChunksToSend[] + } + for (cChunkCoordsList::iterator itr = RemoveChunks.begin(); itr != RemoveChunks.end(); ++itr) + { + World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ, this); + m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ); + } // for itr - RemoveChunks[] + + // Add all chunks that are in range and not yet in m_LoadedChunks: + // Queue these smartly - from the center out to the edge + for (int d = 0; d <= m_ViewDistance; ++d) // cycle through (square) distance, from nearest to furthest + { + // For each distance add chunks in a hollow square centered around current position: + for (int i = -d; i <= d; ++i) + { + StreamChunk(ChunkPosX + d, ZERO_CHUNK_Y, ChunkPosZ + i); + StreamChunk(ChunkPosX - d, ZERO_CHUNK_Y, ChunkPosZ + i); + } // for i + for (int i = -d + 1; i < d; ++i) + { + StreamChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ + d); + StreamChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ - d); + } // for i + } // for d + + // Touch chunks GENERATEDISTANCE ahead to let them generate: + for (int d = m_ViewDistance + 1; d <= m_ViewDistance + GENERATEDISTANCE; ++d) // cycle through (square) distance, from nearest to furthest + { + // For each distance touch chunks in a hollow square centered around current position: + for (int i = -d; i <= d; ++i) + { + World->TouchChunk(ChunkPosX + d, ZERO_CHUNK_Y, ChunkPosZ + i); + World->TouchChunk(ChunkPosX - d, ZERO_CHUNK_Y, ChunkPosZ + i); + } // for i + for (int i = -d + 1; i < d; ++i) + { + World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ + d); + World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ - d); + } // for i + } // for d +} + + + + +void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cWorld * World = m_Player->GetWorld(); + ASSERT(World != NULL); + + if (World->AddChunkClient(a_ChunkX, a_ChunkY, a_ChunkZ, this)) + { + { + cCSLock Lock(m_CSChunkLists); + m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); + m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); + } + World->SendChunkTo(a_ChunkX, a_ChunkY, a_ChunkZ, this); + } +} + + + + + +// Removes the client from all chunks. Used when switching worlds or destroying the player +void cClientHandle::RemoveFromAllChunks() +{ + cWorld * World = m_Player->GetWorld(); + if (World != NULL) + { + World->RemoveClientFromChunks(this); + } + + { + cCSLock Lock(m_CSChunkLists); + m_LoadedChunks.clear(); + m_ChunksToSend.clear(); + } +} + + + + + +void cClientHandle::HandlePing(void) +{ + // Somebody tries to retrieve information about the server + AString Reply; + Printf(Reply, "%s%s%i%s%i", + cRoot::Get()->GetDefaultWorld()->GetDescription().c_str(), + cChatColor::Delimiter.c_str(), + cRoot::Get()->GetDefaultWorld()->GetNumPlayers(), + cChatColor::Delimiter.c_str(), + cRoot::Get()->GetDefaultWorld()->GetMaxPlayers() + ); + Kick(Reply.c_str()); +} + + + + + +bool cClientHandle::HandleLogin(int a_ProtocolVersion, const AString & a_Username) +{ + LOGD("LOGIN %s", a_Username.c_str()); + m_Username = a_Username; + + if (cRoot::Get()->GetPluginManager()->CallHookLogin(this, a_ProtocolVersion, a_Username)) + { + Destroy(); + return false; + } + + // Schedule for authentication; until then, let them wait (but do not block) + m_State = csAuthenticating; + cRoot::Get()->GetAuthenticator().Authenticate(GetUniqueID(), GetUsername(), m_Protocol->GetAuthServerID()); + return true; +} + + + + + +void cClientHandle::HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem) +{ + // This is for creative Inventory changes + if (m_Player->GetGameMode() != eGameMode_Creative) + { + LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in creative mode. Ignoring.", m_Username.c_str()); + return; + } + if (m_Player->GetWindow()->GetWindowType() != cWindow::Inventory) + { + LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in the inventory window. Ignoring.", m_Username.c_str()); + return; + } + + m_Player->GetWindow()->Clicked(*m_Player, 0, a_SlotNum, false, false, a_HeldItem); +} + + + + + +void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround) +{ + /* + // TODO: Invalid stance check + if ((a_PosY >= a_Stance) || (a_Stance > a_PosY + 1.65)) + { + LOGD("Invalid stance"); + SendPlayerMoveLook(); + return; + } + */ + + // LOGD("recv player pos: {%0.2f %0.2f %0.2f}, ground: %d", a_PosX, a_PosY, a_PosZ, a_IsOnGround ? 1 : 0); + Vector3d Pos(a_PosX, a_PosY, a_PosZ); + if ((m_Player->GetPosition() - Pos).SqrLength() > 100 * 100) + { + LOGD("Too far away (%0.2f), \"repairing\" the client", (m_Player->GetPosition() - Pos).Length()); + SendPlayerMoveLook(); + return; + } + m_Player->MoveTo(Pos); + m_Player->SetStance(a_Stance); + m_Player->SetTouchGround(a_IsOnGround); +} + + + + + +void cClientHandle::HandleBlockDig(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) +{ + if (!CheckBlockInteractionsRate()) + { + return; + } + + LOGD("OnBlockDig: {%i, %i, %i}; Face: %i; Stat: %i", + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status + ); + + // Do we want plugins to disable tossing items? Probably no, so toss item before asking plugins for permission + if (a_Status == DIG_STATUS_DROP_HELD) // Drop held item + { + m_Player->TossItem(false); + return; + } + + if (a_Status == DIG_STATUS_SHOOT_EAT) + { + LOGINFO("BlockDig: Status SHOOT/EAT not implemented"); + return; + } + + cWorld * World = m_Player->GetWorld(); + BLOCKTYPE OldBlock; + NIBBLETYPE OldMeta; + World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, OldBlock, OldMeta); + + if (cRoot::Get()->GetPluginManager()->CallHookBlockDig(m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status, OldBlock, OldMeta)) + { + // The plugin doesn't agree with the digging, replace the block on the client and quit: + World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + return; + } + + bool bBroken = ( + (a_Status == DIG_STATUS_FINISHED) || + (g_BlockOneHitDig[(int)OldBlock]) || + ((a_Status == DIG_STATUS_STARTED) && (m_Player->GetGameMode() == 1)) + ); + + cItem & Equipped = m_Player->GetInventory().GetEquippedItem(); + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Equipped.m_ItemID); + + if (bBroken) + { + ItemHandler->OnBlockDestroyed(World, m_Player, &Equipped, a_BlockX, a_BlockY, a_BlockZ); + + BlockHandler(OldBlock)->OnDestroyedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); + World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); + } + else + { + cBlockHandler * Handler = cBlockHandler::GetBlockHandler(OldBlock); + Handler->OnDigging(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); + + ItemHandler->OnDiggingBlock(World, m_Player, &Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + + // Check for clickthrough-blocks: + int pX = a_BlockX; + int pY = a_BlockY; + int pZ = a_BlockZ; + AddDirection(pX, pY, pZ, a_BlockFace); + + Handler = cBlockHandler::GetBlockHandler(World->GetBlock(pX, pY, pZ)); + if (Handler->IsClickedThrough()) + { + Handler->OnDigging(World, m_Player, pX, pY, pZ); + } + } +} + + + + + +void cClientHandle::HandleBlockPlace(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) +{ + LOGD("HandleBlockPlace: {%d, %d, %d}, face %d, itemtype: %d", + a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_HeldItem.m_ItemType + ); + + if (!CheckBlockInteractionsRate()) + { + return; + } + + cItem & Equipped = m_Player->GetInventory().GetEquippedItem(); + + if ((Equipped.m_ItemType != a_HeldItem.m_ItemType) && (a_HeldItem.m_ItemType != -1)) + { + // Only compare ItemType, not meta (torches have different metas) + // The -1 check is there because sometimes the client sends -1 instead of the held item + // ( http://forum.mc-server.org/showthread.php?tid=549&pid=4502#pid4502 ) + LOGWARN("Player %s tried to place a block that was not equipped (exp %d, got %d)", + m_Username.c_str(), Equipped.m_ItemType, a_HeldItem.m_ItemType + ); + + // Let's send the current world block to the client, so that it can immediately "let the user know" that they haven't placed the block + if (a_BlockFace > -1) + { + AddDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + } + return; + } + + if (cRoot::Get()->GetPluginManager()->CallHookBlockPlace(m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, Equipped)) + { + if (a_BlockFace > -1) + { + AddDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + } + return; + } + + cWorld * World = m_Player->GetWorld(); + + cBlockHandler *Handler = cBlockHandler::GetBlockHandler(World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)); + if (Handler->IsUseable()) + { + Handler->OnUse(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); + } + else + { + cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Equipped.m_ItemID); + + if (ItemHandler->OnItemUse(World, m_Player, &Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) + { + // Nothing here :P + } + else if (ItemHandler->IsPlaceable()) + { + if (a_BlockFace < 0) + { + // clicked in air + return; + } + + BLOCKTYPE ClickedBlock = World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + cBlockHandler *Handler = cBlockHandler::GetBlockHandler(ClickedBlock); + + if(Handler->IgnoreBuildCollision()) + { + Handler->OnDestroyedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); + // World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + } + else + { + AddDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + // Check for Blocks not allowing placement on top + if ((a_BlockFace == BLOCK_FACE_TOP) && !Handler->AllowBlockOnTop()) + { + // Resend the old block + // Some times the client still places the block O.o + + World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); + return; + } + + + BLOCKTYPE PlaceBlock = m_Player->GetWorld()->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + if (!BlockHandler(PlaceBlock)->IgnoreBuildCollision()) + { + // Tried to place a block *into* another? + return; // Happens when you place a block aiming at side of block like torch or stem + } + } + + cBlockHandler * NewBlock = BlockHandler(ItemHandler->GetBlockType()); + + // Cannot be placed on the side of an other block + if ((a_BlockFace != BLOCK_FACE_TOP) && !NewBlock->CanBePlacedOnSide()) + { + return; + } + + if (NewBlock->CanBePlacedAt(World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) + { + ItemHandler->PlaceBlock(World, m_Player, &m_Player->GetInventory().GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + // Step sound with 0.8f pitch is used as block placement sound + World->BroadcastSoundEffect(NewBlock->GetStepSound(),a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 0.8f); + } + else + { + World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); // Send the old block back to the player + return; + } + + } + else if (ItemHandler->IsFood()) + { + cItem Item; + Item.m_ItemID = Equipped.m_ItemID; + Item.m_ItemCount = 1; + if (ItemHandler->EatItem(m_Player, &Item)) + { + ItemHandler->OnFoodEaten(World, m_Player, &Item); + m_Player->GetInventory().RemoveItem(Item); + return; + } + } + } +} + + + + + +void cClientHandle::HandleChat(const AString & a_Message) +{ + if (!cRoot::Get()->GetServer()->Command(*this, a_Message)) + { + AString Msg; + Printf(Msg, "<%s%s%s> %s", + m_Player->GetColor().c_str(), + m_Player->GetName().c_str(), + cChatColor::White.c_str(), + a_Message.c_str() + ); + m_Player->GetWorld()->BroadcastChat(Msg); + } +} + + + + + +void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsOnGround) +{ + m_Player->SetRotation (a_Rotation); + m_Player->SetPitch (a_Pitch); + m_Player->SetTouchGround(a_IsOnGround); + m_Player->WrapRotation(); +} + + + + + +void cClientHandle::HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround) +{ + /* + // TODO: Invalid stance check + if ((a_PosY >= a_Stance) || (a_Stance > a_PosY + 1.65)) + { + LOGD("Invalid stance"); + SendPlayerMoveLook(); + return; + } + */ + switch (m_State) + { + case csPlaying: + { + m_Player->MoveTo(Vector3d(a_PosX, a_PosY, a_PosZ)); + m_Player->SetStance (a_Stance); + m_Player->SetTouchGround(a_IsOnGround); + m_Player->SetRotation (a_Rotation); + m_Player->SetPitch (a_Pitch); + m_Player->WrapRotation(); + break; + } + + case csDownloadingWorld: + { + Vector3d ReceivedPosition = Vector3d(a_PosX, a_PosY, a_PosZ); + // LOGD("Received MoveLook confirmation: {%0.2f %0.2f %0.2f}", a_PosX, a_PosY, a_PosZ); + + // Test the distance between points with a small/large enough value instead of comparing directly. Floating point inaccuracies might screw stuff up + double Dist = (ReceivedPosition - m_ConfirmPosition).SqrLength(); + if (Dist < 1.0) + { + if (ReceivedPosition.Equals(m_ConfirmPosition)) + { + LOGINFO("Exact position confirmed by client!"); + } + m_State = csPlaying; + } + else + { + LOGWARNING("Player \"%s\" sent a weird position confirmation %.2f blocks away, retrying", m_Username.c_str(), sqrt(Dist)); + LOGD(" Expected pos: {%0.2f, %0.2f, %0.2f}", m_ConfirmPosition.x, m_ConfirmPosition.y, m_ConfirmPosition.z); + LOGD(" Received pos: {%0.2f, %0.2f, %0.2f}", a_PosX, a_PosY, a_PosZ); + m_ConfirmPosition = m_Player->GetPosition(); + SendPlayerMoveLook(); + } + break; + } + } +} + + + + + +void cClientHandle::HandleAnimation(char a_Animation) +{ + m_Player->GetWorld()->BroadcastPlayerAnimation(*m_Player, a_Animation, this); +} + + + + + +void cClientHandle::HandleSlotSelected(short a_SlotNum) +{ + m_Player->GetInventory().SetEquippedSlot(a_SlotNum); + m_Player->GetWorld()->BroadcastEntityEquipment(*m_Player, 0, m_Player->GetInventory().GetEquippedItem(), this); +} + + + + + +void cClientHandle::HandleWindowClose(char a_WindowID) +{ + m_Player->CloseWindow(a_WindowID); +} + + + + + +void cClientHandle::HandleWindowClick(char a_WindowID, short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_HeldItem) +{ + LOGD("WindowClick: WinID %d, SlotNum %d, IsRclk %d, IsShift %d, Item %s x %d", + a_WindowID, a_SlotNum, a_IsRightClick, a_IsShiftPressed, + ItemToString(a_HeldItem).c_str(), a_HeldItem.m_ItemCount + ); + + cWindow * Window = m_Player->GetWindow(); + if (Window == NULL) + { + LOGWARNING("Player \"%s\" clicked in a non-existent window. Ignoring", m_Username.c_str()); + return; + } + + Window->Clicked(*m_Player, a_WindowID, a_SlotNum, a_IsRightClick, a_IsShiftPressed, a_HeldItem); +} + + + + + +void cClientHandle::HandleUpdateSign( + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, + const AString & a_Line3, const AString & a_Line4 +) +{ + cWorld * World = m_Player->GetWorld(); + World->UpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, m_Player); +} + + + + + +void cClientHandle::HandleUseEntity(int a_TargetEntityID, bool a_IsLeftClick) +{ + if (!a_IsLeftClick) + { + // TODO: we don't handle right-clicking yet + return; + } + + class cDamageEntity : public cEntityCallback + { + virtual bool Item(cEntity * a_Entity) override + { + if (a_Entity->IsA("cPawn")) + { + reinterpret_cast(a_Entity)->TakeDamage(Damage, Instigator); + } + return true; + } + public: + int Damage; + cEntity * Instigator; + } Callback; + + Callback.Damage = 1; // TODO: Find proper damage from current item equipped + Callback.Instigator = m_Player; + + cWorld * World = m_Player->GetWorld(); + World->DoWithEntityByID(a_TargetEntityID, Callback); +} + + + + + +void cClientHandle::HandleRespawn(void) +{ + m_Player->Respawn(); +} + + + + + +void cClientHandle::HandleDisconnect(const AString & a_Reason) +{ + LOGD("Received d/c packet from \"%s\" with reason \"%s\"", m_Username.c_str(), a_Reason.c_str()); + if (!cRoot::Get()->GetPluginManager()->CallHookDisconnect(m_Player, a_Reason)) + { + AString DisconnectMessage; + Printf(DisconnectMessage, "%s disconnected: %s", m_Username.c_str(), a_Reason.c_str()); + m_Player->GetWorld()->BroadcastChat(DisconnectMessage, this); + } + Destroy(); +} + + + + + +void cClientHandle::HandleKeepAlive(int a_KeepAliveID) +{ + if (a_KeepAliveID == m_PingID) + { + cTimer t1; + m_Ping = (short)((t1.GetNowTime() - m_PingStartTime) / 2); + } +} + + + + + +bool cClientHandle::HandleHandshake(const AString & a_Username) +{ + if (!cRoot::Get()->GetPluginManager()->CallHookHandshake(this, a_Username)) + { + if (cRoot::Get()->GetDefaultWorld()->GetNumPlayers() >= cRoot::Get()->GetDefaultWorld()->GetMaxPlayers()) + { + Kick("The server is currently full :(-- Try again later"); + return false; + } + } + return true; +} + + + + + +void cClientHandle::SendData(const char * a_Data, int a_Size) +{ + { + cCSLock Lock(m_CSOutgoingData); + + // _X 2012_09_06: We need an overflow buffer, usually when streaming the initial chunks + if (m_OutgoingDataOverflow.empty()) + { + // No queued overflow data; if this packet fits into the ringbuffer, put it in, otherwise put it in the overflow buffer: + int CanFit = m_OutgoingData.GetFreeSpace(); + if (CanFit > a_Size) + { + CanFit = a_Size; + } + if (CanFit > 0) + { + m_OutgoingData.Write(a_Data, CanFit); + } + if (a_Size > CanFit) + { + m_OutgoingDataOverflow.append(a_Data + CanFit, a_Size - CanFit); + } + } + else + { + // There is a queued overflow. Append to it, then send as much from its front as possible + m_OutgoingDataOverflow.append(a_Data, a_Size); + int CanFit = m_OutgoingData.GetFreeSpace(); + if (CanFit > 128) + { + // No point in moving the data over if it's not large enough - too much effort for too little an effect + m_OutgoingData.Write(m_OutgoingDataOverflow.data(), CanFit); + m_OutgoingDataOverflow.erase(0, CanFit); + } + } + } // Lock(m_CSOutgoingData) + + // Notify SocketThreads that we have something to write: + cRoot::Get()->GetServer()->NotifyClientWrite(this); +} + + + + + +bool cClientHandle::CheckBlockInteractionsRate(void) +{ + ASSERT(m_Player != NULL); + ASSERT(m_Player->GetWorld() != NULL); + int LastActionCnt = m_Player->GetLastBlockActionCnt(); + if ((m_Player->GetWorld()->GetTime() - m_Player->GetLastBlockActionTime()) < 0.1) + { + // Limit the number of block interactions per tick + m_Player->SetLastBlockActionTime(); //Player tried to interact with a block. Reset last block interation time. + m_Player->SetLastBlockActionCnt(LastActionCnt + 1); + if (m_Player->GetLastBlockActionCnt() > MAXBLOCKCHANGEINTERACTIONS) + { + // Kick if more than MAXBLOCKCHANGEINTERACTIONS per tick + LOGWARN("Player %s tried to interact with a block too quickly! (could indicate bot) Was Kicked.", m_Username.c_str()); + Kick("You're a baaaaaad boy!"); + return false; + } + } + else + { + m_Player->SetLastBlockActionCnt(0); // Reset count + m_Player->SetLastBlockActionTime(); // Player tried to interact with a block. Reset last block interation time. + } + return true; +} + + + + + +void cClientHandle::Tick(float a_Dt) +{ + (void)a_Dt; + if (cWorld::GetTime() - m_TimeLastPacket > 30.f) // 30 seconds time-out + { + SendDisconnect("Nooooo!! You timed out! D: Come back!"); + + // TODO: Cannot sleep in the tick thread! + cSleep::MilliSleep(1000); // Give packet some time to be received + + Destroy(); + } + + if ((m_State == csDownloadingWorld) && m_ShouldCheckDownloaded) + { + CheckIfWorldDownloaded(); + m_ShouldCheckDownloaded = false; + } + + cTimer t1; + // Send ping packet + if ( + (m_Player != NULL) && // Is logged in? + (m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime()) + ) + { + m_PingID++; + m_PingStartTime = t1.GetNowTime(); + m_Protocol->SendKeepAlive(m_PingID); + m_LastPingTime = m_PingStartTime; + } +} + + + + + +void cClientHandle::SendDisconnect(const AString & a_Reason) +{ + LOGD("Sending a DC: \"%s\"", a_Reason.c_str()); + m_Protocol->SendDisconnect(a_Reason); +} + + + + + +void cClientHandle::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) +{ + m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item); +} + + + + + +void cClientHandle::SendChat(const AString & a_Message) +{ + m_Protocol->SendChat(a_Message); +} + + + + + +void cClientHandle::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) +{ + m_Protocol->SendPlayerAnimation(a_Player, a_Animation); +} + + + + + +void cClientHandle::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) +{ + m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); +} + + + + + +void cClientHandle::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) +{ + m_Protocol->SendWindowOpen(a_WindowID, a_WindowType, a_WindowTitle, a_NumSlots); +} + + + + + +void cClientHandle::SendWindowClose(char a_WindowID) +{ + m_Protocol->SendWindowClose(a_WindowID); +} + + + + + +void cClientHandle::SendWholeInventory(const cInventory & a_Inventory) +{ + m_Protocol->SendWholeInventory(a_Inventory); +} + + + + + +void cClientHandle::SendWholeInventory(const cWindow & a_Window) +{ + m_Protocol->SendWholeInventory(a_Window); +} + + + + + +void cClientHandle::SendTeleportEntity(const cEntity & a_Entity) +{ + m_Protocol->SendTeleportEntity(a_Entity); +} + + + + + +void cClientHandle::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) +{ + m_Protocol->SendPlayerListItem(a_Player, a_IsOnline); +} + + + + + +void cClientHandle::SendPlayerPosition(void) +{ + m_Protocol->SendPlayerPosition(); +} + + + + + +void cClientHandle::SendEntRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self + + m_Protocol->SendEntRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cClientHandle::SendEntRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) +{ + ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self + + m_Protocol->SendEntRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); +} + + + + + +void cClientHandle::SendEntLook(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self + + m_Protocol->SendEntLook(a_Entity); +} + + + + + +void cClientHandle::SendEntHeadLook(const cEntity & a_Entity) +{ + ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self + + m_Protocol->SendEntHeadLook(a_Entity); +} + + + + + +void cClientHandle::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) +{ + m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); +} + + + + + +void cClientHandle::SendHealth(void) +{ + m_Protocol->SendHealth(); +} + + + + + +void cClientHandle::SendRespawn(void) +{ + m_Protocol->SendRespawn(); +} + + + + + +void cClientHandle::SendGameMode(eGameMode a_GameMode) +{ + m_Protocol->SendGameMode(a_GameMode); +} + + + + + +void cClientHandle::SendDestroyEntity(const cEntity & a_Entity) +{ + m_Protocol->SendDestroyEntity(a_Entity); +} + + + + + +void cClientHandle::SendPlayerMoveLook(void) +{ + /* + LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d", + m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0 + ); + */ + m_Protocol->SendPlayerMoveLook(); +} + + + + + +void cClientHandle::SendEntityStatus(const cEntity & a_Entity, char a_Status) +{ + m_Protocol->SendEntityStatus(a_Entity, a_Status); +} + + + + + +void cClientHandle::SendMetadata(const cPawn & a_Pawn) +{ + m_Protocol->SendMetadata(a_Pawn); +} + + + + + +void cClientHandle::SendInventoryProgress(char a_WindowID, short a_ProgressBar, short a_Value) +{ + m_Protocol->SendInventoryProgress(a_WindowID, a_ProgressBar, a_Value); +} + + + + + +void cClientHandle::SendPlayerSpawn(const cPlayer & a_Player) +{ + if (a_Player.GetUniqueID() == m_Player->GetUniqueID()) + { + // Do NOT send this packet to myself + return; + } + + LOG("Spawning player \"%s\" on client \"%s\" @ %s", + a_Player.GetName().c_str(), GetPlayer()->GetName().c_str(), GetIPString().c_str() + ); + + m_Protocol->SendPlayerSpawn(a_Player); +} + + + + + +void cClientHandle::SendPickupSpawn(const cPickup & a_Pickup) +{ + m_Protocol->SendPickupSpawn(a_Pickup); +} + + + + + +void cClientHandle::SendSpawnMob(const cMonster & a_Mob) +{ + m_Protocol->SendSpawnMob(a_Mob); +} + + + + + +void cClientHandle::SendUpdateSign( + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 +) +{ + m_Protocol->SendUpdateSign( + a_BlockX, a_BlockY, a_BlockZ, + a_Line1, a_Line2, a_Line3, a_Line4 + ); +} + + + + + +void cClientHandle::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +{ + m_Protocol->SendCollectPickup(a_Pickup, a_Player); +} + + + + + +void cClientHandle::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); +} + + + + + +void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) +{ + m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes); +} + + + + + +void cClientHandle::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) +{ + m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ); +} + + + + + +void cClientHandle::SendWeather(eWeather a_Weather) +{ + m_Protocol->SendWeather(a_Weather); +} + + + + + +void cClientHandle::SendTimeUpdate(Int64 a_WorldTime) +{ + m_Protocol->SendTimeUpdate(a_WorldTime); +} + + + + + +void cClientHandle::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cClientHandle::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +{ + m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); +} + + + + + +void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + // Check chunks being sent, erase them from m_ChunksToSend: + bool Found = false; + { + cCSLock Lock(m_CSChunkLists); + for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end(); ++itr) + { + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ)) + { + m_ChunksToSend.erase(itr); + + // Make the tick thread check if all the needed chunks have been downloaded + // -- needed to offload this from here due to a deadlock possibility + m_ShouldCheckDownloaded = true; + + Found = true; + break; + } + } // for itr - m_ChunksToSend[] + } + if (!Found) + { + // This just sometimes happens. If you have a reliably replicatable situation for this, go ahead and fix it + // It's not a big issue anyway, just means that some chunks may be compressed several times + // LOGD("Refusing to send chunk [%d, %d] to client \"%s\" at [%d, %d].", ChunkX, ChunkZ, m_Username.c_str(), m_Player->GetChunkX(), m_Player->GetChunkZ()); + return; + } + + m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer); +} + + + + + +void cClientHandle::CheckIfWorldDownloaded(void) +{ + if (m_State != csDownloadingWorld) + { + return; + } + + bool ShouldSendConfirm = false; + { + cCSLock Lock(m_CSChunkLists); + ShouldSendConfirm = m_ChunksToSend.empty(); + } + + if (ShouldSendConfirm) + { + SendConfirmPosition(); + } +} + + + + + +void cClientHandle::SendConfirmPosition(void) +{ + LOG("Spawning player \"%s\" at {%.2f, %.2f, %.2f}", + m_Username.c_str(), m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ() + ); + + m_State = csConfirmingPos; + + if (!cRoot::Get()->GetPluginManager()->CallHook(cPluginManager::HOOK_PLAYER_JOIN, 1, m_Player)) + { + // Broadcast that this player has joined the game! Yay~ + cRoot::Get()->GetServer()->BroadcastChat(m_Username + " joined the game!", this); + } + + SendPlayerMoveLook(); +} + + + + + +const AString & cClientHandle::GetUsername(void) const +{ + return m_Username; +} + + + + + +void cClientHandle::SetViewDistance(int a_ViewDistance) +{ + if (a_ViewDistance < MIN_VIEW_DISTANCE) + { + a_ViewDistance = MIN_VIEW_DISTANCE; + } + if (a_ViewDistance > MAX_VIEW_DISTANCE) + { + a_ViewDistance = MAX_VIEW_DISTANCE; + } + m_ViewDistance = a_ViewDistance; + + // Need to re-stream chunks for the change to become apparent: + StreamChunks(); +} + + + + + +bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + cCSLock Lock(m_CSChunkLists); + return (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)) != m_ChunksToSend.end()); +} + + + + + +void cClientHandle::AddWantedChunk(int a_ChunkX, int a_ChunkZ) +{ + LOGD("Adding chunk [%d, %d] to wanted chunks for client %p", a_ChunkX, a_ChunkZ, this); + cCSLock Lock(m_CSChunkLists); + if (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)) == m_ChunksToSend.end()) + { + m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); + } +} + + + + + +void cClientHandle::PacketBufferFull(void) +{ + // Too much data in the incoming queue, the server is probably too busy, kick the client: + LOGERROR("Too much data in queue for client \"%s\" @ %s, kicking them.", m_Username.c_str(), m_Socket.GetIPString().c_str()); + SendDisconnect("Server busy"); + // TODO: QueueDestroy(); + cSleep::MilliSleep(1000); // Give packet some time to be received + Destroy(); +} + + + + + +void cClientHandle::PacketUnknown(unsigned char a_PacketType) +{ + LOGERROR("Unknown packet type 0x%02x from client \"%s\" @ %s", a_PacketType, m_Username.c_str(), m_Socket.GetIPString().c_str()); + + AString Reason; + Printf(Reason, "[C->S] Unknown PacketID: 0x%02x", a_PacketType); + SendDisconnect(Reason); + // TODO: QueueDestroy(); + cSleep::MilliSleep(1000); // Give packet some time to be received + Destroy(); +} + + + + + +void cClientHandle::PacketError(unsigned char a_PacketType) +{ + LOGERROR("Protocol error while parsing packet type 0x%02x; disconnecting client \"%s\"", a_PacketType, m_Username.c_str()); + SendDisconnect("Protocol error"); + // TODO: QueueDestroy(); + cSleep::MilliSleep(1000); // Give packet some time to be received + Destroy(); +} + + + + + +void cClientHandle::DataReceived(const char * a_Data, int a_Size) +{ + // Data is received from the client, hand it off to the protocol: + m_Protocol->DataReceived(a_Data, a_Size); + m_TimeLastPacket = cWorld::GetTime(); +} + + + + + +void cClientHandle::GetOutgoingData(AString & a_Data) +{ + // Data can be sent to client + { + cCSLock Lock(m_CSOutgoingData); + m_OutgoingData.ReadAll(a_Data); + m_OutgoingData.CommitRead(); + a_Data.append(m_OutgoingDataOverflow); + m_OutgoingDataOverflow.clear(); + } + + // Disconnect player after all packets have been sent + if (m_bKicking && a_Data.empty()) + { + Destroy(); + } +} + + + + + +void cClientHandle::SocketClosed(void) +{ + // The socket has been closed for any reason + + // TODO + /* + self->Destroy(); + LOG("Client \"%s\" disconnected", GetLogName().c_str()); + */ +} + + + + + + diff --git a/source/ClientHandle.h b/source/ClientHandle.h new file mode 100644 index 000000000..83d0fd9c0 --- /dev/null +++ b/source/ClientHandle.h @@ -0,0 +1,254 @@ + +// cClientHandle.h + +// Interfaces to the cClientHandle class representing a client connected to this server. The client need not be a player yet + + + + + +#pragma once +#ifndef CCLIENTHANDLE_H_INCLUDED +#define CCLIENTHANDLE_H_INCLUDED + +#include "Defines.h" +#include "Vector3d.h" +#include "OSSupport/SocketThreads.h" +#include "ChunkDef.h" +#include "ByteBuffer.h" + + + + + +class cChunkDataSerializer; +class cInventory; +class cMonster; +class cPawn; +class cPickup; +class cPlayer; +class cProtocol; +class cRedstone; +class cWindow; + + + + +class cClientHandle : // tolua_export + public cSocketThreads::cCallback +{ // tolua_export +public: + enum ENUM_PRIORITY + { + E_PRIORITY_LOW, + E_PRIORITY_NORMAL + }; + + static const int MAXBLOCKCHANGEINTERACTIONS = 20; // 5 didn't help, 10 still doesn't work in Creative, 20 seems to have done the trick + + static const int DEFAULT_VIEW_DISTANCE = 9; // The default ViewDistance (used when no value is set in Settings.ini) + static const int MAX_VIEW_DISTANCE = 10; + static const int MIN_VIEW_DISTANCE = 4; + + cClientHandle(const cSocket & a_Socket, int a_ViewDistance); + ~cClientHandle(); + + cSocket & GetSocket (void) { return m_Socket; } + const AString & GetIPString(void) const { return m_Socket.GetIPString(); } + + cPlayer* GetPlayer() { return m_Player; } // tolua_export + + void Kick(const AString & a_Reason); //tolua_export + void Authenticate(void); // Called by cAuthenticator when the user passes authentication + + void StreamChunks(void); + + // Removes the client from all chunks. Used when switching worlds or destroying the player + void RemoveFromAllChunks(void); + + inline bool IsLoggedIn(void) const { return m_State >= csAuthenticating; } + + void Tick(float a_Dt); + + bool IsDestroyed() { return m_bDestroyed; } + void Destroy(); + + bool IsPlaying(void) const {return (m_State == csPlaying); } + + void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType); + void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes); + void SendChat (const AString & a_Message); + void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer); + void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player); + void SendDestroyEntity (const cEntity & a_Entity); + void SendDisconnect (const AString & a_Reason); + void SendEntHeadLook (const cEntity & a_Entity); + void SendEntLook (const cEntity & a_Entity); + void SendEntRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ); + void SendEntRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ); + void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item); + void SendEntityStatus (const cEntity & a_Entity, char a_Status); + void SendGameMode (eGameMode a_GameMode); + void SendHealth (void); + void SendInventoryProgress(char a_WindowID, short a_Progressbar, short a_Value); + void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item); + void SendMetadata (const cPawn & a_Entity); + void SendPickupSpawn (const cPickup & a_Pickup); + void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation); + void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline); + void SendPlayerMoveLook (void); + void SendPlayerPosition (void); + void SendPlayerSpawn (const cPlayer & a_Player); + void SendRespawn (void); + void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch); // a_Src coords are Block * 8 + void SendSpawnMob (const cMonster & a_Mob); + void SendTeleportEntity (const cEntity & a_Entity); + void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ); + void SendTimeUpdate (Int64 a_WorldTime); + void SendUnloadChunk (int a_ChunkX, int a_ChunkZ); + void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); + void SendWeather (eWeather a_Weather); + void SendWholeInventory (const cInventory & a_Inventory); + void SendWholeInventory (const cWindow & a_Window); + void SendWindowClose (char a_WindowID); + void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots); + + const AString & GetUsername(void) const; //tolua_export + + inline short GetPing() const { return m_Ping; } //tolua_export + + void SetViewDistance(int a_ViewDistance); //tolua_export + int GetViewDistance() { return m_ViewDistance; }//tolua_export + + int GetUniqueID() const { return m_UniqueID; } //tolua_export + + /// Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend) + bool WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend) + void AddWantedChunk(int a_ChunkX, int a_ChunkZ); + + // Calls that cProtocol descendants use to report state: + void PacketBufferFull(void); + void PacketUnknown(unsigned char a_PacketType); + void PacketError(unsigned char a_PacketType); + + // Calls that cProtocol descendants use for handling packets: + void HandlePing (void); + void HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem); + void HandlePlayerPos (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround); + void HandleBlockDig (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status); + void HandleBlockPlace (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem); + void HandleChat (const AString & a_Message); + void HandlePlayerLook (float a_Rotation, float a_Pitch, bool a_IsOnGround); + void HandlePlayerMoveLook (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround); // While m_bPositionConfirmed (normal gameplay) + void HandleAnimation (char a_Animation); + void HandleSlotSelected (short a_SlotNum); + void HandleWindowClose (char a_WindowID); + void HandleWindowClick (char a_WindowID, short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_HeldItem); + void HandleUpdateSign ( + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, + const AString & a_Line3, const AString & a_Line4 + ); + void HandleUseEntity (int a_TargetEntityID, bool a_IsLeftClick); + void HandleRespawn (void); + void HandleDisconnect (const AString & a_Reason); + void HandleKeepAlive (int a_KeepAliveID); + bool HandleHandshake (const AString & a_Username); + + /** Called when the protocol has finished logging the user in. + Return true to allow the user in; false to kick them. + */ + bool HandleLogin(int a_ProtocolVersion, const AString & a_Username); + + void SendData(const char * a_Data, int a_Size); +private: + + int m_ViewDistance; // Number of chunks the player can see in each direction; 4 is the minimum ( http://wiki.vg/Protocol_FAQ#.E2.80.A6all_connecting_clients_spasm_and_jerk_uncontrollably.21 ) + + static const int GENERATEDISTANCE = 2; // Server generates this many chunks AHEAD of player sight. 2 is the minimum, since foliage is generated 1 step behind chunk terrain generation + + int m_ProtocolVersion; + AString m_Username; + AString m_Password; + + cCriticalSection m_CSChunkLists; + cChunkCoordsList m_LoadedChunks; // Chunks that the player belongs to + cChunkCoordsList m_ChunksToSend; // Chunks that need to be sent to the player (queued because they weren't generated yet or there's not enough time to send them) + + cSocket m_Socket; + cProtocol * m_Protocol; + + cCriticalSection m_CSOutgoingData; + cByteBuffer m_OutgoingData; + AString m_OutgoingDataOverflow; //< For data that didn't fit into the m_OutgoingData ringbuffer temporarily + + cCriticalSection m_CriticalSection; + + Vector3d m_ConfirmPosition; + + bool m_bDestroyed; + cPlayer * m_Player; + bool m_bKicking; + + // Chunk position when the last StreamChunks() was called; used to avoid re-streaming while in the same chunk + int m_LastStreamedChunkX; + int m_LastStreamedChunkZ; + + float m_TimeLastPacket; + + short m_Ping; + int m_PingID; + long long m_PingStartTime; + long long m_LastPingTime; + static const unsigned short PING_TIME_MS = 1000; //minecraft sends 1 per 20 ticks (1 second or every 1000 ms) + + enum eState + { + csConnected, // The client has just connected, waiting for their handshake / login + csAuthenticating, // The client has logged in, waiting for external authentication + csDownloadingWorld, // The client is waiting for chunks, we're waiting for the loader to provide and send them + csConfirmingPos, // The client has been sent the position packet, waiting for them to repeat the position back + csPlaying, // Normal gameplay + + // TODO: Add Kicking and Destroyed here as well + } ; + + eState m_State; + + bool m_bKeepThreadGoing; + + /// If set to true during csDownloadingWorld, the tick thread calls CheckIfWorldDownloaded() + bool m_ShouldCheckDownloaded; + + /// Returns true if the rate block interactions is within a reasonable limit (bot protection) + bool CheckBlockInteractionsRate(void); + + /// Checks whether all loaded chunks have been sent to the client; if so, sends the position to confirm + void CheckIfWorldDownloaded(void); + + /// Sends the PlayerMoveLook packet that the client needs to reply to for the game to start + void SendConfirmPosition(void); + + /// Adds a single chunk to be streamed to the client; used by StreamChunks() + void StreamChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + // cSocketThreads::cCallback overrides: + virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client + virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client + virtual void SocketClosed (void) override; // The socket has been closed for any reason + + static int s_ClientCount; + int m_UniqueID; +}; // tolua_export + + + + +#endif // CCLIENTHANDLE_H_INCLUDED + + + + diff --git a/source/CraftingRecipes.cpp b/source/CraftingRecipes.cpp index f75434e75..4cf503b3a 100644 --- a/source/CraftingRecipes.cpp +++ b/source/CraftingRecipes.cpp @@ -5,8 +5,8 @@ #include "Globals.h" #include "CraftingRecipes.h" -#include "cRoot.h" -#include "cPluginManager.h" +#include "Root.h" +#include "PluginManager.h" diff --git a/source/CraftingRecipes.h b/source/CraftingRecipes.h index 74d103f4d..9d92cbfab 100644 --- a/source/CraftingRecipes.h +++ b/source/CraftingRecipes.h @@ -8,7 +8,7 @@ #pragma once -#include "cItem.h" +#include "Item.h" diff --git a/source/Cuboid.cpp b/source/Cuboid.cpp new file mode 100644 index 000000000..7b3b24528 --- /dev/null +++ b/source/Cuboid.cpp @@ -0,0 +1,19 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Cuboid.h" + + + + + +void cCuboid::Sort() +{ + if( p1.x > p2.x ) std::swap( p1.x, p2.x ); + if( p1.y > p2.y ) std::swap( p1.y, p2.y ); + if( p1.z > p2.z ) std::swap( p1.z, p2.z ); +} + + + + diff --git a/source/Cuboid.h b/source/Cuboid.h new file mode 100644 index 000000000..a07d665fc --- /dev/null +++ b/source/Cuboid.h @@ -0,0 +1,40 @@ +#pragma once + +#include "Vector3i.h" +#include "Vector3d.h" + +class cCuboid //tolua_export +{ //tolua_export +public: //tolua_export + cCuboid() {} //tolua_export + cCuboid( const cCuboid & a_Cuboid ) : p1( a_Cuboid.p1 ), p2( a_Cuboid.p2 ) {} //tolua_export + cCuboid( const Vector3i & a_p1, const Vector3i & a_p2 ) : p1( a_p1 ), p2( a_p2 ) {} //tolua_export + + + Vector3i p1, p2; //tolua_export + + void Sort(); //tolua_export + + bool IsInside( const Vector3i & v ) const //tolua_export + { //tolua_export + if( v.x >= p1.x && v.x <= p2.x + && v.y >= p1.y && v.y <= p2.y + && v.z >= p1.z && v.z <= p2.z ) + { + return true; + } + return false; + } //tolua_export + + bool IsInside( const Vector3d & v ) const //tolua_export + { //tolua_export + if( v.x >= p1.x && v.x <= p2.x + && v.y >= p1.y && v.y <= p2.y + && v.z >= p1.z && v.z <= p2.z ) + { + return true; + } + return false; + } //tolua_export + +}; //tolua_export \ No newline at end of file diff --git a/source/Doors.h b/source/Doors.h new file mode 100644 index 000000000..71fec0d39 --- /dev/null +++ b/source/Doors.h @@ -0,0 +1,61 @@ +#pragma once + + +class cDoors //tolua_export +{ //tolua_export +public: + static char RotationToMetaData( float a_Rotation ) //tolua_export + { //tolua_export + a_Rotation += 90 + 45; // So its not aligned with axis + if( a_Rotation > 360.f ) a_Rotation -= 360.f; + if( a_Rotation >= 0.f && a_Rotation < 90.f ) + return 0x0; + else if( a_Rotation >= 180 && a_Rotation < 270 ) + return 0x2; + else if( a_Rotation >= 90 && a_Rotation < 180 ) + return 0x1; + else + return 0x3; + } //tolua_export + + static char ChangeStateMetaData( char a_MetaData ) //tolua_export + { //tolua_export + + a_MetaData ^= 4; //XOR bit 2 aka 3. bit (Door open state) + + return a_MetaData; + } //tolua_export + + static void ChangeDoor(cWorld *a_World, int a_X, int a_Y, int a_Z) //tolua_export + { //tolua_export + char OldMetaData = a_World->GetBlockMeta(a_X, a_Y, a_Z); + + a_World->SetBlockMeta(a_X, a_Y, a_Z, ChangeStateMetaData ( OldMetaData ) ); + + + if (OldMetaData & 8) + { //current block is top of the door + char BottomBlock = a_World->GetBlock(a_X, a_Y - 1, a_Z); + char BottomMeta = a_World->GetBlockMeta(a_X, a_Y - 1, a_Z); + + if (IsDoor(BottomBlock) && !(BottomMeta & 8)) + { + a_World->SetBlockMeta(a_X, a_Y - 1, a_Z, ChangeStateMetaData ( BottomMeta ) ); + } + } else { //current block is bottom of the door + char TopBlock = a_World->GetBlock(a_X, a_Y + 1, a_Z); + char TopMeta = a_World->GetBlockMeta(a_X, a_Y + 1, a_Z); + + if (IsDoor(TopBlock) && (TopMeta & 8)) + { + a_World->SetBlockMeta(a_X, a_Y + 1, a_Z, ChangeStateMetaData ( TopMeta ) ); + } + } + } //tolua_export + + inline static bool IsDoor(char a_Block) + { + return (a_Block == E_BLOCK_WOODEN_DOOR || a_Block == E_BLOCK_IRON_DOOR); + } + +}; //tolua_export \ No newline at end of file diff --git a/source/Entity.cpp b/source/Entity.cpp new file mode 100644 index 000000000..6bc5dfc39 --- /dev/null +++ b/source/Entity.cpp @@ -0,0 +1,344 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Entity.h" +#include "World.h" +#include "Server.h" +#include "Root.h" +#include "Vector3d.h" +#include "Vector3f.h" +#include "Matrix4f.h" +#include "ReferenceManager.h" +#include "ClientHandle.h" + + + + + +int cEntity::m_EntityCount = 0; +cCriticalSection cEntity::m_CSCount; + + + + + +cEntity::cEntity(const double & a_X, const double & a_Y, const double & a_Z) + : m_UniqueID( 0 ) + , m_Referencers( new cReferenceManager( cReferenceManager::RFMNGR_REFERENCERS ) ) + , m_References( new cReferenceManager( cReferenceManager::RFMNGR_REFERENCES ) ) + , m_ChunkX( 0 ) + , m_ChunkY( 0 ) + , m_ChunkZ( 0 ) + , m_Pos( a_X, a_Y, a_Z ) + , m_bDirtyPosition( true ) + , m_bDirtyOrientation( true ) + , m_bDestroyed( false ) + , m_EntityType( eEntityType_Entity ) + , m_World( 0 ) + , m_bRemovedFromChunk( false ) + , m_FireDamageInterval(0.f) + , m_BurnPeriod(0.f) +{ + cCSLock Lock(m_CSCount); + m_EntityCount++; + m_UniqueID = m_EntityCount; +} + + + + + +cEntity::~cEntity() +{ + LOG("Deleting entity %d at pos {%.2f, %.2f} ~ [%d, %d]; ptr %p", + m_UniqueID, + m_Pos.x, m_Pos.z, + (int)(m_Pos.x / cChunkDef::Width), (int)(m_Pos.z / cChunkDef::Width), + this + ); + + if( !m_bDestroyed || !m_bRemovedFromChunk ) + { + LOGERROR("ERROR: Entity deallocated without being destroyed %i or unlinked %i", m_bDestroyed, m_bRemovedFromChunk ); + ASSERT(!"Entity deallocated without being destroyed or unlinked"); + } + delete m_Referencers; + delete m_References; +} + + + + + +CLASS_DEF_GETCLASS(cEntity); + + + + + +void cEntity::Initialize(cWorld * a_World) +{ + m_World = a_World; + m_World->AddEntity( this ); + + MoveToCorrectChunk(true); +} + + + + + +void cEntity::WrapRotation() +{ + while (m_Rot.x > 180.f) m_Rot.x-=360.f; // Wrap it + while (m_Rot.x < -180.f) m_Rot.x+=360.f; + while (m_Rot.y > 180.f) m_Rot.y-=360.f; + while (m_Rot.y < -180.f) m_Rot.y+=360.f; +} + + + + + +void cEntity::MoveToCorrectChunk(bool a_bIgnoreOldChunk) +{ + ASSERT(m_World != NULL); // Entity needs a world to move to a chunk + if( !m_World ) return; + + int ChunkX = 0, ChunkY = 0, ChunkZ = 0; + cWorld::BlockToChunk( (int)m_Pos.x, (int)m_Pos.y, (int)m_Pos.z, ChunkX, ChunkY, ChunkZ ); + if (!a_bIgnoreOldChunk && (m_ChunkX == ChunkX) && (m_ChunkY == ChunkY) && (m_ChunkZ == ChunkZ)) + { + return; + } + + class cMover : + public cClientDiffCallback + { + virtual void Removed(cClientHandle * a_Client) override + { + if (m_IgnoreOldChunk) + { + return; + } + a_Client->SendDestroyEntity(*m_Entity); + } + + virtual void Added(cClientHandle * a_Client) override + { + m_Entity->SpawnOn(*a_Client); + } + + bool m_IgnoreOldChunk; + cEntity * m_Entity; + + public: + cMover(cEntity * a_Entity, bool a_IgnoreOldChunk) : + m_IgnoreOldChunk(a_IgnoreOldChunk), + m_Entity(a_Entity) + {} + } Mover(this, a_bIgnoreOldChunk); + + m_World->CompareChunkClients(m_ChunkX, m_ChunkY, m_ChunkZ, ChunkX, ChunkY, ChunkZ, Mover); + m_World->MoveEntityToChunk(this, ChunkX, ChunkY, ChunkZ); + + m_ChunkX = ChunkX; + m_ChunkY = ChunkY; + m_ChunkZ = ChunkZ; +} + + + + + +void cEntity::Destroy() +{ + if (m_bDestroyed) + { + return; + } + if (!m_bRemovedFromChunk) + { + RemoveFromChunk(); + } + + m_World->BroadcastDestroyEntity(*this); + + m_bDestroyed = true; + + Destroyed(); +} + + + + + +void cEntity::RemoveFromChunk(void) +{ + if (m_World == NULL) + { + return; + } + + m_World->RemoveEntityFromChunk(this, m_ChunkX, m_ChunkY, m_ChunkZ); + m_bRemovedFromChunk = true; +} + + + + + +bool cEntity::IsA( const char* a_EntityType ) +{ + //LOG("IsA( cEntity ) : %s", a_EntityType); + if( strcmp( a_EntityType, "cEntity" ) == 0 ) return true; + return false; +} + + + + + +////////////////////////////////////////////////////////////////////////// +// Set orientations +void cEntity::SetRot( const Vector3f & a_Rot ) +{ + m_Rot = a_Rot; + m_bDirtyOrientation = true; +} + + + + + +void cEntity::SetRotation( float a_Rotation ) +{ + m_Rot.x = a_Rotation; + m_bDirtyOrientation = true; +} + + + + + +void cEntity::SetPitch( float a_Pitch ) +{ + m_Rot.y = a_Pitch; + m_bDirtyOrientation = true; +} + + + + + +void cEntity::SetRoll( float a_Roll ) +{ + m_Rot.z = a_Roll; + m_bDirtyOrientation = true; +} + + + + + +////////////////////////////////////////////////////////////////////////// +// Get look vector (this is NOT a rotation!) +Vector3f cEntity::GetLookVector() +{ + Matrix4f m; + m.Init( Vector3f(), 0, m_Rot.x, -m_Rot.y ); + Vector3f Look = m.Transform( Vector3f(0, 0, 1) ); + LOG("Look: %0.1f %0.1f %0.1f", Look.x, Look.y, Look.z ); + return Look; +} + + + + + +////////////////////////////////////////////////////////////////////////// +// Set position +void cEntity::SetPosition( const Vector3d & a_Pos ) +{ + m_Pos = a_Pos; + MoveToCorrectChunk(); + m_bDirtyPosition = true; +} + + + + + +void cEntity::SetPosition( const double & a_PosX, const double & a_PosY, const double & a_PosZ ) +{ + m_Pos.Set( a_PosX, a_PosY, a_PosZ ); + MoveToCorrectChunk(); + m_bDirtyPosition = true; +} + + + + + +void cEntity::SetPosX( const double & a_PosX ) +{ + m_Pos.x = a_PosX; + MoveToCorrectChunk(); + m_bDirtyPosition = true; +} + + + + + +void cEntity::SetPosY( const double & a_PosY ) +{ + m_Pos.y = a_PosY; + MoveToCorrectChunk(); + m_bDirtyPosition = true; +} + + + + + +void cEntity::SetPosZ( const double & a_PosZ ) +{ + m_Pos.z = a_PosZ; + MoveToCorrectChunk(); + m_bDirtyPosition = true; +} + + + + + +////////////////////////////////////////////////////////////////////////// +// Reference stuffs +void cEntity::AddReference( cEntity*& a_EntityPtr ) +{ + m_References->AddReference( a_EntityPtr ); + a_EntityPtr->ReferencedBy( a_EntityPtr ); +} + + + + + +void cEntity::ReferencedBy( cEntity*& a_EntityPtr ) +{ + m_Referencers->AddReference( a_EntityPtr ); +} + + + + + +void cEntity::Dereference( cEntity*& a_EntityPtr ) +{ + m_Referencers->Dereference( a_EntityPtr ); +} + + + + diff --git a/source/Entity.h b/source/Entity.h new file mode 100644 index 000000000..15c351a09 --- /dev/null +++ b/source/Entity.h @@ -0,0 +1,172 @@ + +#pragma once + + + + +#include "Vector3d.h" +#include "Vector3f.h" + + + + + +#define CLASS_PROT_ISA() virtual bool IsA( const char* a_EntityType ); +#define CLASS_PROT_GETCLASS() virtual const char* GetClass(); + +/* Can't use this (yet) because of tolua */ +#define CLASS_PROTOTYPE() \ + CLASS_PROT_ISA(); \ + CLASS_PROT_GETCLASS(); + +#define CLASS_DEF_ISA( classname, superclass ) \ + bool classname::IsA( const char* a_EntityType ) \ + { \ + if( strcmp( a_EntityType, #classname ) == 0 ) return true; \ + return superclass::IsA( a_EntityType ); \ + } + +#define CLASS_DEF_GETCLASS( classname ) \ + const char* classname::GetClass() \ + { \ + return #classname; \ + } + +#define CLASS_DEFINITION( classname, superclass ) \ + CLASS_DEF_ISA( classname, superclass ) \ + CLASS_DEF_GETCLASS( classname ) + + + + + +class cWorld; +class cReferenceManager; +class cClientHandle; + + + + +// tolua_begin +class cEntity +{ +public: + enum + { + ENTITY_STATUS_HURT = 2, + ENTITY_STATUS_DEAD = 3, + ENTITY_STATUS_WOLF_TAMING = 6, + ENTITY_STATUS_WOLF_TAMED = 7, + ENTITY_STATUS_WOLF_SHAKING = 8, + ENTITY_STATUS_EATING_ACCEPTED = 9, + ENTITY_STATUS_SHEEP_EATING = 10, + } ; + + cEntity(const double & a_X, const double & a_Y, const double & a_Z); + virtual ~cEntity(); + + virtual void Initialize(cWorld * a_World); + + enum eEntityType + { + eEntityType_Entity, + eEntityType_Player, + eEntityType_Pickup + }; + + virtual unsigned int GetEntityType() { return m_EntityType; } + virtual bool IsA( const char* a_EntityType ); + virtual const char* GetClass(); + // tolua_end + + cWorld * GetWorld(void) const { return m_World; } //tolua_export + + const Vector3d & GetPosition(void) const {return m_Pos; } //tolua_export + const double & GetPosX (void) const {return m_Pos.x; } //tolua_export + const double & GetPosY (void) const {return m_Pos.y; } //tolua_export + const double & GetPosZ (void) const {return m_Pos.z; } //tolua_export + const Vector3f & GetRot (void) const {return m_Rot; } //tolua_export + float GetRotation(void) const {return m_Rot.x; } //tolua_export + float GetPitch (void) const {return m_Rot.y; } //tolua_export + float GetRoll (void) const {return m_Rot.z; } //tolua_export + Vector3f GetLookVector(); //tolua_export + + int GetChunkX(void) const {return m_ChunkX; } //tolua_export + int GetChunkY(void) const {return m_ChunkY; } //tolua_export + int GetChunkZ(void) const {return m_ChunkZ; } //tolua_export + + void SetPosX( const double & a_PosX ); //tolua_export + void SetPosY( const double & a_PosY ); //tolua_export + void SetPosZ( const double & a_PosZ ); //tolua_export + void SetPosition( const double & a_PosX, const double & a_PosY, const double & a_PosZ );//tolua_export + void SetPosition( const Vector3d & a_Pos ); //tolua_export + void SetRot( const Vector3f & a_Rot ); //tolua_export + void SetRotation( float a_Rotation ); //tolua_export + void SetPitch( float a_Pitch ); //tolua_export + void SetRoll( float a_Roll ); //tolua_export + + inline int GetUniqueID(void) const { return m_UniqueID; } //tolua_export + inline bool IsDestroyed(void) const { return m_bDestroyed; } //tolua_export + + void Destroy(); //tolua_export + void RemoveFromChunk(void); // for internal use in cChunk + + virtual void Tick(float a_Dt) = 0; //tolua_export + + /** Descendants override this function to send a command to the specified client to spawn the entity on the client. + To spawn on all eligible clients, use cChunkMap::BroadcastSpawnEntity() + Needs to have a default implementation due to Lua bindings. + */ + virtual void SpawnOn(cClientHandle & a_Client) {ASSERT(!"SpawnOn() unimplemented!"); } // tolua_export + + void WrapRotation(); + + // Metadata flags; descendants may override the defaults: + virtual bool IsOnFire (void) const {return (m_BurnPeriod > 0); } + virtual bool IsCrouched (void) const {return false; } + virtual bool IsRiding (void) const {return false; } + virtual bool IsSprinting(void) const {return false; } + virtual bool IsRclking (void) const {return false; } + +protected: + virtual void Destroyed() {} // Called after the entity has been destroyed + + void SetWorld( cWorld* a_World ) { m_World = a_World; } + void MoveToCorrectChunk(bool a_bIgnoreOldChunk = false); + + friend class cReferenceManager; + void AddReference( cEntity*& a_EntityPtr ); + void ReferencedBy( cEntity*& a_EntityPtr ); + void Dereference( cEntity*& a_EntityPtr ); + + static cCriticalSection m_CSCount; + static int m_EntityCount; + + int m_UniqueID; + + cReferenceManager* m_Referencers; + cReferenceManager* m_References; + + int m_ChunkX, m_ChunkY, m_ChunkZ; + Vector3d m_Pos; + bool m_bDirtyPosition; + + Vector3f m_Rot; + bool m_bDirtyOrientation; + + bool m_bDestroyed; + bool m_bRemovedFromChunk; + + eEntityType m_EntityType; + + cWorld* m_World; + + float m_FireDamageInterval; + float m_BurnPeriod; +}; //tolua_export + +typedef std::list cEntityList; + + + + diff --git a/source/FireSimulator.cpp b/source/FireSimulator.cpp new file mode 100644 index 000000000..9a08524ef --- /dev/null +++ b/source/FireSimulator.cpp @@ -0,0 +1,133 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "FireSimulator.h" +#include "World.h" +#include "Vector3i.h" +#include "BlockID.h" +#include "Defines.h" + +cFireSimulator::cFireSimulator( cWorld* a_World ) + : cSimulator(a_World) + , m_Blocks(new BlockList) + , m_Buffer(new BlockList) + , m_BurningBlocks(new BlockList) +{ + +} + +cFireSimulator::~cFireSimulator() +{ + delete m_Buffer; + delete m_Blocks; + delete m_BurningBlocks; +} + +void cFireSimulator::Simulate( float a_Dt ) +{ + m_Buffer->clear(); + std::swap( m_Blocks, m_Buffer ); + + for( BlockList::iterator itr = m_Buffer->begin(); itr != m_Buffer->end(); ++itr ) + { + Vector3i Pos = *itr; + + char BlockID = m_World->GetBlock(Pos.x, Pos.y, Pos.z); + + if(!IsAllowedBlock(BlockID)) //Check wheather the block is still burning + continue; + + if(BurnBlockAround(Pos.x, Pos.y, Pos.z)) //Burn single block and if there was one -> next time again + _AddBlock(Pos.x, Pos.y, Pos.z); + else + if(!IsForeverBurnable(m_World->GetBlock(Pos.x, Pos.y - 1, Pos.z)) && !FiresForever(BlockID)) + m_World->SetBlock(Pos.x, Pos.y, Pos.z, E_BLOCK_AIR, 0); + + } + +} + + +bool cFireSimulator::IsAllowedBlock( char a_BlockID ) +{ + return a_BlockID == E_BLOCK_FIRE + || IsBlockLava(a_BlockID); +} + +void cFireSimulator::AddBlock(int a_X, int a_Y, int a_Z) +{ + char BlockID = m_World->GetBlock(a_X, a_Y, a_Z); + if(!IsAllowedBlock(BlockID)) //This should save very much time because it doesnīt have to iterate through all blocks + return; + + //check for duplicates + for( BlockList::iterator itr = m_Blocks->begin(); itr != m_Blocks->end(); ++itr ) + { + Vector3i Pos = *itr; + if( Pos.x == a_X && Pos.y == a_Y && Pos.z == a_Z ) + return; + } + + _AddBlock(a_X, a_Y, a_Z); + +} + +void cFireSimulator::_AddBlock(int a_X, int a_Y, int a_Z) +{ + m_Blocks->push_back( Vector3i(a_X, a_Y, a_Z) ); + +} + +bool cFireSimulator::IsForeverBurnable( char a_BlockID ) +{ + return a_BlockID == E_BLOCK_BLOODSTONE; +} + +bool cFireSimulator::IsBurnable( char a_BlockID ) +{ + return a_BlockID == E_BLOCK_PLANKS + || a_BlockID == E_BLOCK_LEAVES + || a_BlockID == E_BLOCK_LOG + || a_BlockID == E_BLOCK_WOOL + || a_BlockID == E_BLOCK_BOOKCASE + || a_BlockID == E_BLOCK_FENCE + || a_BlockID == E_BLOCK_TNT + || a_BlockID == E_BLOCK_VINES; +} + +bool cFireSimulator::FiresForever( char a_BlockID ) +{ + return a_BlockID != E_BLOCK_FIRE; +} + +bool cFireSimulator::BurnBlockAround(int a_X, int a_Y, int a_Z) +{ + return BurnBlock(a_X + 1, a_Y, a_Z) + || BurnBlock(a_X - 1, a_Y, a_Z) + || BurnBlock(a_X, a_Y + 1, a_Z) + || BurnBlock(a_X, a_Y - 1, a_Z) + || BurnBlock(a_X, a_Y, a_Z + 1) + || BurnBlock(a_X, a_Y, a_Z - 1); +} + +bool cFireSimulator::BurnBlock(int a_X, int a_Y, int a_Z) +{ + char BlockID = m_World->GetBlock(a_X, a_Y, a_Z); + if(IsBurnable(BlockID)) + { + m_World->SetBlock(a_X, a_Y, a_Z, E_BLOCK_FIRE, 0); + return true; + } + if(IsForeverBurnable(BlockID)) + { + char BlockAbove = m_World->GetBlock(a_X, a_Y + 1, a_Z); + if(BlockAbove == E_BLOCK_AIR) + { + m_World->SetBlock(a_X, a_Y + 1, a_Z, E_BLOCK_FIRE, 0); //Doesnīt notify the simulator so it wonīt go off + return true; + } + return false; + } + + return false; +} \ No newline at end of file diff --git a/source/FireSimulator.h b/source/FireSimulator.h new file mode 100644 index 000000000..863fa9835 --- /dev/null +++ b/source/FireSimulator.h @@ -0,0 +1,47 @@ + +#pragma once + +#include "Simulator.h" +#include "BlockEntity.h" + + + + + +class Vector3i; +class cWorld; + + + + + +class cFireSimulator : public cSimulator +{ +public: + cFireSimulator( cWorld* a_World ); + ~cFireSimulator(); + + virtual void Simulate( float a_Dt ); + + virtual bool IsAllowedBlock( char a_BlockID ); + + virtual bool IsBurnable( char a_BlockID ); + virtual bool IsForeverBurnable( char a_BlockID ); + virtual bool FiresForever( char a_BlockID ); + +protected: + virtual void AddBlock(int a_X, int a_Y, int a_Z); + virtual void _AddBlock(int a_X, int a_Y, int a_Z); + virtual bool BurnBlockAround(int a_X, int a_Y, int a_Z); + virtual bool BurnBlock(int a_X, int a_Y, int a_Z); + + typedef std::list BlockList; + BlockList *m_Blocks; + BlockList *m_Buffer; + + BlockList *m_BurningBlocks; +}; + + + + diff --git a/source/FluidSimulator.cpp b/source/FluidSimulator.cpp new file mode 100644 index 000000000..804db9d36 --- /dev/null +++ b/source/FluidSimulator.cpp @@ -0,0 +1,692 @@ +#include "Globals.h" + +#include +#include + +#include "FluidSimulator.h" +#include "World.h" +#include "Vector3i.h" +#include "BlockID.h" +#include "Defines.h" +#include "Item.h" +#include "blocks/BlockHandler.h" + + + + + +//#define DEBUG_FLUID +#ifdef DEBUG_FLUID +#define LOG_FLUID(...) LOGWARN( __VA_ARGS__ ) +#else +#define LOG_FLUID(...) +#endif + + + + + +class cFluidSimulator::FluidData +{ +public: + FluidData( cWorld* a_World, cFluidSimulator *a_Simulator ) + : m_ActiveFluid( new std::set < Vector3i >() ) + , m_Simulator (a_Simulator) + , m_Buffer( new std::set< Vector3i >() ) + , m_World( a_World ) + {} + + ~FluidData() + { + delete m_Buffer; + delete m_ActiveFluid; + } + + void UpdateWave(Vector3i a_LeftCorner, Vector3i a_CurBlock) + { + Vector3i LevelPoints [] = { + Vector3i( a_CurBlock.x-1, a_CurBlock.y, a_CurBlock.z ), + Vector3i( a_CurBlock.x+1, a_CurBlock.y, a_CurBlock.z ), + Vector3i( a_CurBlock.x, a_CurBlock.y, a_CurBlock.z-1 ), + Vector3i( a_CurBlock.x, a_CurBlock.y, a_CurBlock.z+1 ), + }; + + for(int i=0; i<4; i++) + { + Vector3i cur = LevelPoints[i]; + switch(m_Relief[cur.x][cur.z]) + { + case E_HOLE: + { + m_StartSide[cur.x][cur.z] = m_StartSide[a_CurBlock.x][a_CurBlock.z]; + m_CurResult|=m_StartSide[cur.x][cur.z]; + m_NearestHole = m_WayLength[a_CurBlock.x][a_CurBlock.z] + 1; + LOG_FLUID("Hole found: %d \t curResult: %d", int(m_StartSide[cur.x][cur.z]), int(m_CurResult) ); + LOG_FLUID("Coordinates: (%d, %d)", cur.x, cur.z); + }break; + + case E_BLOCK: + {}break; + + case E_PLAIN: + { + if (m_WayLength[cur.x][cur.z] > m_WayLength[a_CurBlock.x][a_CurBlock.z] + 1) + { + m_WayLength[cur.x][cur.z] = m_WayLength[a_CurBlock.x][a_CurBlock.z] + 1; + m_StartSide[cur.x][cur.z] = m_StartSide[a_CurBlock.x][a_CurBlock.z]; + m_WaveQueue.push(cur); + } + else if(m_WayLength[cur.x][cur.z] == m_WayLength[a_CurBlock.x][a_CurBlock.z] + 1) + { + m_StartSide[cur.x][cur.z] |= m_StartSide[a_CurBlock.x][a_CurBlock.z]; + } + LOG_FLUID("Plain step: (%d, %d) from %d", cur.x, cur.z, m_StartSide[cur.x][cur.z]); + } + } + } + } + + std::vector< Vector3i > GetLowestPoints( int a_X, int a_Y, int a_Z ) + { + + std::vector< Vector3i > Points; //result + + Vector3i CornerGlobal(a_X - AREA_WIDTH/2, a_Y, a_Z - AREA_WIDTH/2); + + //TODO: rewrite without relief, get blocks directly in algorithm + for(int x=0; xGetBlock( CornerGlobal.x + x, CornerGlobal.y, CornerGlobal.z + z ); + char DownBlock = m_World->GetBlock( CornerGlobal.x + x, CornerGlobal.y-1, CornerGlobal.z + z ); + + if(m_Simulator->IsSolidBlock(UpperBlock)||(m_Simulator->IsStationaryBlock(UpperBlock))) + { + m_Relief[x][z] = E_BLOCK; + } + else if(m_Simulator->IsSolidBlock(DownBlock)) + { + m_Relief[x][z] = E_PLAIN; + } + else + { + m_Relief[x][z] = E_HOLE; + } + m_WayLength[x][z] = 255; + m_StartSide[x][z] = E_SIDE_NONE; + } + LOG_FLUID("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", m_Relief[x][0], m_Relief[x][1], m_Relief[x][2], m_Relief[x][3], m_Relief[x][4], m_Relief[x][5], m_Relief[x][6], m_Relief[x][7], m_Relief[x][8], m_Relief[x][9], m_Relief[x][10]); + } + + m_NearestHole = 5; + m_CurResult = 0; + while(!m_WaveQueue.empty()) m_WaveQueue.pop(); + + int left = AREA_WIDTH/2 - 1; + int right = AREA_WIDTH/2 + 1; + int center = AREA_WIDTH/2; + + Vector3i r(right, 0, center); //right block + Vector3i l(left, 0, center); //left block + Vector3i f(center, 0, right); //front block + Vector3i b(center, 0, left); //back block + Vector3i c(center, 0, center); //center block + + m_WayLength[c.x][c.z] = 0; + + Vector3i Nearest[] = {r, l, f, b}; + unsigned char Sides[] = {E_SIDE_RIGHT, E_SIDE_LEFT, E_SIDE_FRONT, E_SIDE_BACK}; + + for(int i=0; i<4; i++) + { + Vector3i cur = Nearest[i]; + switch(m_Relief[cur.x][cur.z]) + { + case E_HOLE: + { + m_StartSide[cur.x][cur.z] = Sides[i]; + m_CurResult |= m_StartSide[cur.x][cur.z]; + m_NearestHole = 1; + LOG_FLUID("Hole found: %d \t curResult: %d", int(Sides[i]), int(m_CurResult) ); + }break; + + case E_BLOCK: + {}break; + + case E_PLAIN: + { + m_WaveQueue.push(cur); + m_StartSide[cur.x][cur.z] = Sides[i]; + m_WayLength[cur.x][cur.z] = 1; + LOG_FLUID("Plain found: %d", int(Sides[i])); + } + } + } + + Vector3i curBlock; + + bool bContinue = !m_WaveQueue.empty(); + + if(!m_WaveQueue.empty()) + { + curBlock = m_WaveQueue.front(); + bContinue = (m_WayLength[curBlock.x][curBlock.z] < m_NearestHole); + } + + while(bContinue) + { + LOG_FLUID("while iteration" ); + curBlock = m_WaveQueue.front(); + UpdateWave(CornerGlobal, curBlock); + m_WaveQueue.pop(); + + bContinue = ( (!m_WaveQueue.empty()) && (m_WayLength[m_WaveQueue.front().x][m_WaveQueue.front().z] < m_NearestHole) ); + } + + + + if(m_CurResult & E_SIDE_LEFT) Points.push_back(Vector3i( a_X-1, a_Y, a_Z )); + if(m_CurResult & E_SIDE_RIGHT) Points.push_back(Vector3i( a_X+1, a_Y, a_Z )); + if(m_CurResult & E_SIDE_FRONT) Points.push_back(Vector3i( a_X, a_Y, a_Z+1 )); + if(m_CurResult & E_SIDE_BACK) Points.push_back(Vector3i( a_X, a_Y, a_Z-1 )); + + if(Points.empty()) + { + Vector3i LevelPoints [] = { + Vector3i( a_X-1, a_Y, a_Z ), + Vector3i( a_X+1, a_Y, a_Z ), + Vector3i( a_X, a_Y, a_Z-1 ), + Vector3i( a_X, a_Y, a_Z+1 ), + }; + for( int i = 0; i < 4; ++i ) + { + char Block = m_World->GetBlock( LevelPoints[i].x, a_Y, LevelPoints[i].z ); + if( m_Simulator->IsPassableForFluid(Block) ) + Points.push_back( LevelPoints[i] ); + } + } + + return Points; + } + + std::set< Vector3i >* m_ActiveFluid; + std::set< Vector3i >* m_Buffer; + cWorld* m_World; + cFluidSimulator *m_Simulator; + + const static int AREA_WIDTH = 11; + + const static unsigned char E_SIDE_RIGHT = 0x10; + const static unsigned char E_SIDE_LEFT = 0x20; + const static unsigned char E_SIDE_FRONT = 0x40; + const static unsigned char E_SIDE_BACK = 0x80; + const static unsigned char E_SIDE_NONE = 0x00; + + enum eRelief {E_HOLE = 0, E_PLAIN = 1, E_BLOCK = 2}; + + eRelief m_Relief[AREA_WIDTH][AREA_WIDTH]; + unsigned char m_WayLength[AREA_WIDTH][AREA_WIDTH]; + unsigned char m_StartSide[AREA_WIDTH][AREA_WIDTH]; + + std::queue m_WaveQueue; + + int m_NearestHole; + unsigned char m_CurResult; +}; + + + + + +cFluidSimulator::cFluidSimulator( cWorld* a_World ) + : cSimulator(a_World) + , m_Data(0) +{ + m_Data = new FluidData(a_World, this); +} + + + + + +cFluidSimulator::~cFluidSimulator() +{ + delete m_Data; +} + + + + + +void cFluidSimulator::AddBlock( int a_X, int a_Y, int a_Z ) +{ + char BlockType = m_World->GetBlock(a_X, a_Y, a_Z); + if (!IsAllowedBlock(BlockType)) //This should save very much time because it doesnīt have to iterate through all blocks + { + return; + } + + std::set< Vector3i > & ActiveFluid = *m_Data->m_ActiveFluid; + ActiveFluid.insert( Vector3i( a_X, a_Y, a_Z ) ); +} + + + + + +char cFluidSimulator::GetHighestLevelAround( int a_X, int a_Y, int a_Z ) +{ + char Max = m_MaxHeight + m_FlowReduction; + +#define __HIGHLEVEL_CHECK__( x, y, z ) \ + if( IsAllowedBlock( m_World->GetBlock( x, y, z ) ) ) \ + { \ + char Meta; \ + if( (Meta = m_World->GetBlockMeta( x, y, z ) ) < Max ) Max = Meta; \ + else if( Meta == m_MaxHeight + m_FlowReduction ) Max = 0; \ + if( Max == 0 ) return 0; \ + } + + __HIGHLEVEL_CHECK__( a_X-1, a_Y, a_Z ); + __HIGHLEVEL_CHECK__( a_X+1, a_Y, a_Z ); + __HIGHLEVEL_CHECK__( a_X, a_Y, a_Z-1 ); + __HIGHLEVEL_CHECK__( a_X, a_Y, a_Z+1 ); + + return Max; +} + + + + + +void cFluidSimulator::Simulate( float a_Dt ) +{ + m_Timer += a_Dt; + + if (m_Data->m_ActiveFluid->empty()) //Nothing to do if there is no active fluid ;) saves very little time ;D + { + return; + } + + std::swap( m_Data->m_ActiveFluid, m_Data->m_Buffer ); // Swap so blocks can be added to empty ActiveFluid array + m_Data->m_ActiveFluid->clear(); + + std::set< Vector3i > & FluidBlocks = *m_Data->m_Buffer; + for( std::set< Vector3i >::iterator itr = FluidBlocks.begin(); itr != FluidBlocks.end(); ++itr ) + { + const Vector3i & pos = *itr; + + if(UniqueSituation(pos)) + { + continue; + } + + char BlockID = m_World->GetBlock( pos.x, pos.y, pos.z ); + if( IsAllowedBlock( BlockID ) ) // only care about own fluid + { + bool bIsFed = false; + char Meta = m_World->GetBlockMeta( pos.x, pos.y, pos.z ); + char Feed = Meta; + if( BlockID == m_StationaryFluidBlock) Meta = 0; + if( Meta == 8 ) // Falling fluid + { + if( IsAllowedBlock( m_World->GetBlock(pos.x, pos.y+1, pos.z) ) ) // Block above is fluid + { + bIsFed = true; + Meta = 0; // Make it a full block + } + } + else if( Meta < m_FlowReduction ) // It's a full block, so it's always fed + { + bIsFed = true; + } + else + { + if( (Feed = GetHighestLevelAround( pos.x, pos.y, pos.z )) < Meta ) + bIsFed = true; + } + + + if( bIsFed ) + { + char DownID = m_World->GetBlock( pos.x, pos.y-1, pos.z ); + bool bWashedAwayItem = CanWashAway( DownID ); + if( (IsPassableForFluid(DownID) || bWashedAwayItem) && !IsStationaryBlock(DownID) ) // free for fluid + { + if( bWashedAwayItem ) + { + cBlockHandler * Handler = BlockHandler(DownID); + if(Handler->DropOnUnsuitable()) + { + Handler->DropBlock(m_World, pos.x, pos.y - 1, pos.z); + } + } + if (pos.y > 0) + { + m_World->FastSetBlock( pos.x, pos.y-1, pos.z, m_FluidBlock, 8 ); // falling + AddBlock( pos.x, pos.y-1, pos.z ); + ApplyUniqueToNearest(pos - Vector3i(0, 1, 0)); + } + } + if(IsSolidBlock(DownID)||( BlockID == m_StationaryFluidBlock)) // Not falling + { + if( Feed + m_FlowReduction < Meta ) + { + m_World->FastSetBlock( pos.x, pos.y, pos.z, m_FluidBlock, Feed + m_FlowReduction ); + AddBlock( pos.x, pos.y, pos.z ); + ApplyUniqueToNearest(pos); + } + else if(( Meta < m_MaxHeight )||( BlockID == m_StationaryFluidBlock)) // max is the lowest, so it cannot spread + { + std::vector< Vector3i > Points = m_Data->GetLowestPoints( pos.x, pos.y, pos.z ); + for( std::vector< Vector3i >::iterator itr = Points.begin(); itr != Points.end(); ++itr ) + { + Vector3i & p = *itr; + char BlockID = m_World->GetBlock( p.x, p.y, p.z ); + bool bWashedAwayItem = CanWashAway( BlockID ); + + if (!IsPassableForFluid(BlockID)) continue; + + if (!IsAllowedBlock(BlockID)) + { + if (bWashedAwayItem) + { + cBlockHandler * Handler = BlockHandler(DownID); + if(Handler->DropOnUnsuitable()) + { + Handler->DropBlock(m_World, p.x, p.y, p.z); + } + } + + if( p.y == pos.y ) + { + m_World->FastSetBlock(p.x, p.y, p.z, m_FluidBlock, Meta + m_FlowReduction); + } + else + { + m_World->FastSetBlock(p.x, p.y, p.z, m_FluidBlock, 8); + } + AddBlock( p.x, p.y, p.z ); + ApplyUniqueToNearest(p); + } + else // it's fluid + { + char PointMeta = m_World->GetBlockMeta( p.x, p.y, p.z ); + if( PointMeta > Meta + m_FlowReduction ) + { + AddBlock( p.x, p.y, p.z ); + ApplyUniqueToNearest(p); + } + } + } + } + } + } + else// not fed + { + m_World->FastSetBlock( pos.x, pos.y, pos.z, E_BLOCK_AIR, 0 ); + WakeUp( pos.x, pos.y, pos.z ); + } + } + } +} + + + + + +bool cFluidSimulator::IsPassableForFluid(char a_BlockID) +{ + return a_BlockID == E_BLOCK_AIR + || a_BlockID == E_BLOCK_FIRE + || IsAllowedBlock(a_BlockID) + || CanWashAway(a_BlockID); +} + + + + + +bool cFluidSimulator::IsStationaryBlock (char a_BlockID) +{ + return a_BlockID == m_StationaryFluidBlock; +} + + + + + +bool cFluidSimulator::CanWashAway( char a_BlockID ) +{ + switch( a_BlockID ) + { + case E_BLOCK_YELLOW_FLOWER: + case E_BLOCK_RED_ROSE: + case E_BLOCK_RED_MUSHROOM: + case E_BLOCK_BROWN_MUSHROOM: + case E_BLOCK_CACTUS: + return true; + default: + return false; + }; +} + + + + + +bool cFluidSimulator::IsSolidBlock( char a_BlockID ) +{ + return !(a_BlockID == E_BLOCK_AIR + || a_BlockID == E_BLOCK_FIRE + || IsBlockLava(a_BlockID) + || IsBlockWater(a_BlockID) + || CanWashAway(a_BlockID)); +} + + + + + +//TODO Not working very well yet :s +Direction cFluidSimulator::GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a_Over) +{ + char BlockID = m_World->GetBlock(a_X, a_Y, a_Z); + if(!IsAllowedBlock(BlockID)) //No Fluid -> No Flowing direction :D + return NONE; + + + /* + Disabled because of causing problems and beeing useless atm + char BlockBelow = m_World->GetBlock(a_X, a_Y - 1, a_Z); //If there is nothing or fluid below it -> dominating flow is down :D + if(BlockBelow == E_BLOCK_AIR || IsAllowedBlock(BlockBelow)) + return Y_MINUS; + */ + + char LowestPoint = m_World->GetBlockMeta(a_X, a_Y, a_Z); //Current Block Meta so only lower points will be counted + int X = 0, Y = 0, Z = 0; //Lowest Pos will be stored here + + if(IsAllowedBlock(m_World->GetBlock(a_X, a_Y + 1, a_Z)) && a_Over) //check for upper block to flow because this also affects the flowing direction + { + return GetFlowingDirection(a_X, a_Y + 1, a_Z, false); + } + + std::vector< Vector3i * > Points; + + Points.reserve(4); //Already allocate 4 places :D + + //add blocks around the checking pos + Points.push_back(new Vector3i(a_X - 1, a_Y, a_Z)); + Points.push_back(new Vector3i(a_X + 1, a_Y, a_Z)); + Points.push_back(new Vector3i(a_X, a_Y, a_Z + 1)); + Points.push_back(new Vector3i(a_X, a_Y, a_Z - 1)); + + for(std::vector::iterator it = Points.begin(); it < Points.end(); it++) + { + Vector3i *Pos = (*it); + char BlockID = m_World->GetBlock(Pos->x, Pos->y, Pos->z); + if(IsAllowedBlock(BlockID)) + { + char Meta = m_World->GetBlockMeta(Pos->x, Pos->y, Pos->z); + + if(Meta > LowestPoint) + { + LowestPoint = Meta; + X = Pos->x; + Y = Pos->y; + Z = Pos->z; + } + }else if(BlockID == E_BLOCK_AIR) + { + LowestPoint = 9; //This always dominates + X = Pos->x; + Y = Pos->y; + Z = Pos->z; + + } + delete Pos; + } + + if(LowestPoint == m_World->GetBlockMeta(a_X, a_Y, a_Z)) + return NONE; + + if(a_X - X > 0) + { + return X_MINUS; + } + + if(a_X - X < 0) + { + return X_PLUS; + } + + if(a_Z - Z > 0) + { + return Z_MINUS; + } + + if(a_Z - Z < 0) + { + return Z_PLUS; + } + + return NONE; +} + + + + + +bool cFluidSimulator::UniqueSituation(Vector3i a_Pos) +{ + bool result = false; + + char BlockId = m_World->GetBlock( a_Pos.x, a_Pos.y, a_Pos.z ); + char Meta = m_World->GetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z ); + + if(IsBlockWater(BlockId)) + { + + char UpperBlock = m_World->GetBlock( a_Pos.x, a_Pos.y + 1, a_Pos.z ); + if(IsBlockLava(UpperBlock)) + { + m_World->SetBlock(a_Pos.x, a_Pos.y, a_Pos.z, E_BLOCK_STONE, 0); + } + + + if(BlockId != E_BLOCK_STATIONARY_WATER) + { + char DownBlockId = m_World->GetBlock( a_Pos.x, a_Pos.y-1, a_Pos.z ); + if(IsSolidBlock(DownBlockId)) + { + Vector3i LevelPoints [] = { + Vector3i( a_Pos.x-1, a_Pos.y, a_Pos.z ), + Vector3i( a_Pos.x+1, a_Pos.y, a_Pos.z ), + Vector3i( a_Pos.x, a_Pos.y, a_Pos.z-1 ), + Vector3i( a_Pos.x, a_Pos.y, a_Pos.z+1 ), + }; + int SourceBlocksCount = 0; + for(int i=0; i<4; i++) + { + if (m_World->GetBlock(LevelPoints[i].x, LevelPoints[i].y, LevelPoints[i].z)==E_BLOCK_STATIONARY_WATER) + { + SourceBlocksCount++; + } + } + if(SourceBlocksCount>=2) + { + m_World->SetBlock(a_Pos.x, a_Pos.y, a_Pos.z, E_BLOCK_STATIONARY_WATER, 0); + } + } + + } + } + + if(IsBlockLava(BlockId)) + { + bool bWater = false; + + char UpperBlock = m_World->GetBlock( a_Pos.x, a_Pos.y + 1, a_Pos.z ); + if (IsBlockWater(UpperBlock)) + { + bWater = true; + } + else + { + Vector3i LevelPoints [] = { + Vector3i( a_Pos.x-1, a_Pos.y, a_Pos.z ), + Vector3i( a_Pos.x+1, a_Pos.y, a_Pos.z ), + Vector3i( a_Pos.x, a_Pos.y, a_Pos.z-1 ), + Vector3i( a_Pos.x, a_Pos.y, a_Pos.z+1 ), + }; + + for(int i=0; i<4; i++) + { + if (IsBlockWater(m_World->GetBlock(LevelPoints[i].x, LevelPoints[i].y, LevelPoints[i].z))) + { + bWater = true; + } + } + } + + + if(bWater) + { + if(BlockId == E_BLOCK_STATIONARY_LAVA) + { + m_World->SetBlock(a_Pos.x, a_Pos.y, a_Pos.z, E_BLOCK_OBSIDIAN, 0); + } + else if (MetaSetBlock(a_Pos.x, a_Pos.y, a_Pos.z, E_BLOCK_COBBLESTONE, 0); + } + } + } + + return result; +} + + + + + +void cFluidSimulator::ApplyUniqueToNearest(Vector3i a_Pos) +{ + Vector3i NearPoints [] = { + Vector3i( a_Pos.x-1, a_Pos.y, a_Pos.z ), + Vector3i( a_Pos.x+1, a_Pos.y, a_Pos.z ), + Vector3i( a_Pos.x, a_Pos.y, a_Pos.z-1 ), + Vector3i( a_Pos.x, a_Pos.y, a_Pos.z+1 ), + Vector3i( a_Pos.x, a_Pos.y-1, a_Pos.z ) + }; + + for(int i=0; i<5; i++) + { + UniqueSituation(NearPoints[i]); + } +} + + + + diff --git a/source/FluidSimulator.h b/source/FluidSimulator.h new file mode 100644 index 000000000..1ec254595 --- /dev/null +++ b/source/FluidSimulator.h @@ -0,0 +1,55 @@ +#pragma once + +#include "Simulator.h" +#include "Vector3i.h" + + +//TODO This definitly needs a better naming :D but how? +enum Direction +{ + X_PLUS, + X_MINUS, + Y_PLUS, + Y_MINUS, + Z_PLUS, + Z_MINUS, + NONE +}; + +class Vector3i; +class cWorld; +class cFluidSimulator : public cSimulator +{ +public: + cFluidSimulator( cWorld* a_World ); + ~cFluidSimulator(); + + virtual void Simulate( float a_Dt ); + + //Gets the flowing direction. if a_Over is true also the block over the current block affects the direction (standard) + Direction GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a_Over = true); + + virtual bool IsAllowedBlock( char a_BlockID ) = 0; + virtual bool IsStationaryBlock( char a_BlockID); + virtual bool IsPassableForFluid( char a_BlockID ); + bool CanWashAway( char a_BlockID ); + bool IsSolidBlock(char a_BlockID); +protected: + virtual void AddBlock( int a_X, int a_Y, int a_Z); + char GetHighestLevelAround( int a_X, int a_Y, int a_Z ); + + bool UniqueSituation(Vector3i a_Pos); //Applys special for this fluid rules like generation of water betwin sources, returns false if it is necessary to apply general rules + void ApplyUniqueToNearest(Vector3i a_Pos); + + float m_Timer; + + class FluidData; + FluidData* m_Data; + + //Customize + char m_FluidBlock; + char m_StationaryFluidBlock; + char m_MaxHeight; + char m_FlowReduction; + +}; \ No newline at end of file diff --git a/source/FurnaceEntity.cpp b/source/FurnaceEntity.cpp new file mode 100644 index 000000000..15298cbdd --- /dev/null +++ b/source/FurnaceEntity.cpp @@ -0,0 +1,417 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "FurnaceEntity.h" +#include "BlockID.h" +#include "Item.h" +#include "UI/Window.h" +#include "Player.h" +#include "World.h" +#include "ClientHandle.h" +#include "FurnaceRecipe.h" +#include "Server.h" +#include "Pickup.h" +#include "Root.h" +#include + + + + + + +enum +{ + PROGRESSBAR_SMELTING = 0, + PROGRESSBAR_FUEL = 1, +} ; + + + + + +cFurnaceEntity::cFurnaceEntity(int a_X, int a_Y, int a_Z, cWorld * a_World) + : cBlockEntity( E_BLOCK_FURNACE, a_X, a_Y, a_Z, a_World ) + , m_Items( new cItem[3] ) + , m_CookingItem( 0 ) + , m_CookTime( 0 ) + , m_TimeCooked( 0 ) + , m_BurnTime( 0 ) + , m_TimeBurned( 0 ) +{ + SetBlockEntity(this); // cBlockEntityWindowOwner +} + + + + + +cFurnaceEntity::~cFurnaceEntity() +{ + // Tell window its owner is destroyed + if( GetWindow() ) + { + GetWindow()->OwnerDestroyed(); + } + + // Clean up items + if( m_Items ) + { + delete [] m_Items; + } +} + + + + + +void cFurnaceEntity::Destroy() +{ + // Drop items + cItems Pickups; + for( int i = 0; i < 3; i++) + { + if( !m_Items[i].IsEmpty() ) + { + Pickups.push_back(m_Items[i]); + m_Items[i].Empty(); + } + } + m_World->SpawnItemPickups(Pickups, m_PosX, m_PosY, m_PosZ); +} + + + + + +void cFurnaceEntity::UsedBy(cPlayer * a_Player) +{ + if (GetWindow() == NULL) + { + OpenWindow(new cFurnaceWindow(m_PosX, m_PosY, m_PosZ, this)); + } + if (GetWindow() != NULL) + { + if (a_Player->GetWindow() != GetWindow()) + { + a_Player->OpenWindow(GetWindow()); + GetWindow()->SendWholeWindow(*a_Player->GetClientHandle()); + } + } +} + + + + + +bool cFurnaceEntity::Tick( float a_Dt ) +{ + /* + // DEBUG: + Int16 BurnTime = (Int16)(GetTimeToBurn() / 50.0); + Int16 CookTime = (Int16)(GetTimeCooked() / 50.0); + LOGD("Furnace: BurnTime %d, CookTime %d", BurnTime, CookTime); + */ + + if (m_BurnTime <= 0) + { + if (m_TimeCooked > 0) + { + // We have just finished smelting, reset the progress bar: + BroadcastProgress(PROGRESSBAR_SMELTING, 0); + m_TimeCooked = 0; + } + // There is no fuel and no flame, no need to tick at all + return false; + } + + // DEBUG: LOGD("Furnace: Left: %0.1f Burned: %0.1f Burn time: %0.1f", m_CookTime - m_TimeCooked, m_TimeBurned, m_BurnTime ); + + short SmeltingProgress = 0; + if ((m_CookingItem != NULL) && ((m_TimeBurned < m_BurnTime) || (m_TimeCooked + a_Dt >= m_CookTime))) + { + if (m_CookingItem->IsEqual(m_Items[2]) || m_Items[2].IsEmpty()) + { + m_TimeCooked += a_Dt; + if ( m_TimeCooked >= m_CookTime ) + { + m_Items[0].m_ItemCount--; + if( m_Items[0].IsEmpty() ) m_Items[0].Empty(); + + m_Items[2].m_ItemHealth = m_CookingItem->m_ItemHealth; + m_Items[2].m_ItemID = m_CookingItem->m_ItemID; + m_Items[2].m_ItemCount += m_CookingItem->m_ItemCount; + delete m_CookingItem; + m_CookingItem = NULL; + + cWindow * Window = GetWindow(); + if (Window != NULL) + { + Window->BroadcastWholeWindow(); + } + + m_TimeCooked -= m_CookTime; + StartCooking(); + } + SmeltingProgress = (short)( m_TimeCooked * (180.f / m_CookTime)); + if (SmeltingProgress > 180) SmeltingProgress = 180; + if (SmeltingProgress < 0) SmeltingProgress = 0; + } + } + BroadcastProgress(PROGRESSBAR_SMELTING, SmeltingProgress); + + m_TimeBurned += a_Dt; + + cWindow * Window = GetWindow(); + if (m_TimeBurned >= m_BurnTime) + { + m_TimeBurned -= m_BurnTime; + m_BurnTime = 0; + if (StartCooking() && (Window != NULL)) + { + Window->BroadcastWholeWindow(); + } + } + short Value = 0; + if (m_BurnTime > 0.f) + { + Value = 250 - (short)( m_TimeBurned * (250.f / m_BurnTime)); + if (Value > 250) Value = 250; + if (Value < 0) Value = 0; + } + BroadcastProgress(PROGRESSBAR_FUEL, Value); + + return ((m_CookingItem != NULL) || (m_TimeBurned < m_BurnTime)) && (m_BurnTime > 0.0); // Keep on ticking, if there's more to cook, or if it's cooking +} + + + + + +bool cFurnaceEntity::StartCooking(void) +{ + cFurnaceRecipe* FR = cRoot::Get()->GetFurnaceRecipe(); + float BurnTime = FR->GetBurnTime( m_Items[1] ); + if( (m_TimeBurned < m_BurnTime) || BurnTime > 0.f ) // burnable material + { + const cFurnaceRecipe::Recipe* R = FR->GetRecipeFrom( m_Items[0] ); + if (R != NULL) // cook able ingredient + { + if (m_Items[2].IsEqual(*R->Out) || m_Items[2].IsEmpty()) + { + // good to go + + if( m_TimeBurned >= m_BurnTime ) // burn new material + { + m_Items[1].m_ItemCount--; + if( m_Items[1].m_ItemCount <= 0 ) m_Items[1].Empty(); + m_TimeBurned = 0; + m_BurnTime = BurnTime; + } + + if( !m_CookingItem ) // Only cook new item if not already cooking + { + m_CookingItem = new cItem( *R->Out ); // Resulting item + m_TimeCooked = 0.f; + m_CookTime = R->CookTime; + } + return true; + } + } + } + return false; +} + + + + + +bool cFurnaceEntity::ContinueCooking(void) +{ + cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe(); + float BurnTime = FR->GetBurnTime( m_Items[1] ); + if( (m_TimeBurned < m_BurnTime) || BurnTime > 0.f ) // burnable material + { + const cFurnaceRecipe::Recipe * R = FR->GetRecipeFrom( m_Items[0] ); + if (R != NULL) // cook able ingredient + { + if (m_Items[2].IsEqual(*R->Out) || m_Items[2].IsEmpty()) + { + // good to go + if (m_CookingItem == NULL) // Only cook new item if not already cooking + { + m_CookingItem = new cItem( *R->Out ); // Resulting item + } + return true; + } + } + } + return false; +} + + + + + +void cFurnaceEntity::ResetCookTimer() +{ + delete m_CookingItem; + m_CookingItem = NULL; + m_TimeCooked = 0.f; + m_CookTime = 0.f; +} + + + + + +void cFurnaceEntity::SetSlot(int a_Slot, const cItem & a_Item) +{ + if ((a_Slot < 0) || (a_Slot >= 3)) + { + ASSERT(!"Furnace: slot number out of range"); + return; + } + m_Items[a_Slot] = a_Item; +} + + + + + +#define READ(File, Var) \ + if (File.Read(&Var, sizeof(Var)) != sizeof(Var)) \ + { \ + LOGERROR("ERROR READING cFurnaceEntity %s FROM FILE (line %d)", #Var, __LINE__); \ + return false; \ + } + +bool cFurnaceEntity::LoadFromFile(cFile & f) +{ + READ(f, m_PosX); + READ(f, m_PosY); + READ(f, m_PosZ); + + unsigned int NumSlots = 0; + READ(f, NumSlots); + m_Items = new cItem[ NumSlots ]; + for(unsigned int i = 0; i < NumSlots; i++) + { + cItem & Item = m_Items[i]; + READ(f, Item.m_ItemID); + READ(f, Item.m_ItemCount); + READ(f, Item.m_ItemHealth); + } + cItem CookingItem; + READ(f, CookingItem.m_ItemID); + READ(f, CookingItem.m_ItemCount); + READ(f, CookingItem.m_ItemHealth); + if (!CookingItem.IsEmpty()) + { + m_CookingItem = new cItem(CookingItem); + } + + READ(f, m_CookTime); + READ(f, m_TimeCooked); + READ(f, m_BurnTime); + READ(f, m_TimeBurned); + + return true; +} + + + + + +bool cFurnaceEntity::LoadFromJson( const Json::Value& a_Value ) +{ + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + Json::Value AllSlots = a_Value.get("Slots", 0); + int SlotIdx = 0; + for( Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr ) + { + m_Items[ SlotIdx ].FromJson( *itr ); + SlotIdx++; + } + + // Get currently cooking item + Json::Value JsonItem = a_Value.get("Cooking", Json::nullValue ); + if( !JsonItem.empty() ) + { + cItem Item; + Item.FromJson( JsonItem ); + if( !Item.IsEmpty() ) + { + m_CookingItem = new cItem( Item ); + } + } + + m_CookTime = (float)a_Value.get("CookTime", 0).asDouble(); + m_TimeCooked = (float)a_Value.get("TimeCooked", 0).asDouble(); + m_BurnTime = (float)a_Value.get("BurnTime", 0).asDouble(); + m_TimeBurned = (float)a_Value.get("TimeBurned", 0).asDouble(); + + return true; +} + + + + + +void cFurnaceEntity::SaveToJson( Json::Value& a_Value ) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + Json::Value AllSlots; + for(unsigned int i = 0; i < 3; i++) + { + Json::Value Slot; + m_Items[ i ].GetJson( Slot ); + AllSlots.append( Slot ); + } + a_Value["Slots"] = AllSlots; + + // Currently cooking item + if( m_CookingItem ) + { + Json::Value JsonItem; + m_CookingItem->GetJson( JsonItem ); + a_Value["Cooking"] = JsonItem; + } + + a_Value["CookTime"] = m_CookTime; + a_Value["TimeCooked"] = m_TimeCooked; + a_Value["BurnTime"] = m_BurnTime; + a_Value["TimeBurned"] = m_TimeBurned; +} + + + + + +void cFurnaceEntity::SendTo(cClientHandle & a_Client) +{ + // Nothing needs to be sent + UNUSED(a_Client); +} + + + + + +void cFurnaceEntity::BroadcastProgress(int a_ProgressbarID, short a_Value) +{ + cWindow * Window = GetWindow(); + if (Window != NULL) + { + Window->BroadcastInventoryProgress(a_ProgressbarID, a_Value); + } +} + + + + diff --git a/source/FurnaceEntity.h b/source/FurnaceEntity.h new file mode 100644 index 000000000..1c417c739 --- /dev/null +++ b/source/FurnaceEntity.h @@ -0,0 +1,78 @@ + +#pragma once + +#include "BlockEntity.h" +#include "UI/WindowOwner.h" +#include "Item.h" + + + + + +namespace Json +{ + class Value; +} + +class cClientHandle; +class cServer; + + + + + +class cFurnaceEntity : + public cBlockEntity, + public cBlockEntityWindowOwner +{ +public: + cFurnaceEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); + virtual ~cFurnaceEntity(); + virtual void Destroy(); + + bool LoadFromFile(cFile & a_File); // deprecated format + + bool LoadFromJson(const Json::Value& a_Value ); + virtual void SaveToJson(Json::Value& a_Value ) override; + + virtual void SendTo(cClientHandle & a_Client) override; + + // Returns true if there's any change, forcing the chunk to go dirty. + bool Tick( float a_Dt ); + + virtual void UsedBy( cPlayer * a_Player ) override; + + bool StartCooking(); + + /// Restarts cooking. Used after the furnace is loaded from storage to set up the internal variables so that cooking continues, if it was active. Returns true if cooking. + bool ContinueCooking(void); + + void ResetCookTimer(); + + const cItem * GetSlot(int i) const { return &(m_Items[i]); } + + void SetSlot(int a_Slot, const cItem & a_Item); + + float GetTimeCooked(void) const {return m_TimeCooked; } + float GetTimeToBurn(void) const {return m_BurnTime - m_TimeBurned; } + + void SetBurnTimes(float a_BurnTime, float a_TimeBurned) {m_BurnTime = a_BurnTime; m_TimeBurned = 0; } + void SetCookTimes(float a_CookTime, float a_TimeCooked) {m_CookTime = a_CookTime; m_TimeCooked = a_TimeCooked; } + +private: + + cItem * m_Items; + cItem * m_CookingItem; + + // All timers are in 1 ms + float m_CookTime; // Amount of time needed to fully cook current item + float m_TimeCooked; // Amount of time that the current item has been cooking + float m_BurnTime; // Amount of time that the current fuel can burn (in total); zero if no fuel burning + float m_TimeBurned; // Amount of time that the current fuel has been burning + + void BroadcastProgress(int a_ProgressbarID, short a_Value); +}; + + + + diff --git a/source/FurnaceRecipe.cpp b/source/FurnaceRecipe.cpp new file mode 100644 index 000000000..444d46f74 --- /dev/null +++ b/source/FurnaceRecipe.cpp @@ -0,0 +1,241 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "FurnaceRecipe.h" +#include "Item.h" + +#include +#include + + + + + +typedef std::list< cFurnaceRecipe::Recipe > RecipeList; +typedef std::list< cFurnaceRecipe::Fuel > FuelList; + + + + + +struct cFurnaceRecipe::sFurnaceRecipeState +{ + RecipeList Recipes; + FuelList Fuel; +}; + + + + + +cFurnaceRecipe::cFurnaceRecipe() + : m_pState( new sFurnaceRecipeState ) +{ + ReloadRecipes(); +} + + + + + +cFurnaceRecipe::~cFurnaceRecipe() +{ + ClearRecipes(); + delete m_pState; +} + + + + + +void cFurnaceRecipe::ReloadRecipes() +{ + ClearRecipes(); + LOG("-- Loading furnace recipes --"); + + std::ifstream f; + char a_File[] = "furnace.txt"; + f.open(a_File, std::ios::in); + std::string input; + + if( !f.good() ) + { + f.close(); + LOG("Could not open file for recipes: %s", a_File); + return; + } + + bool bSyntaxError = false; + while( f.good() ) + { + char c; + + ////////////////////////////////////////////////////////////////////////// + // comments + f >> c; + f.unget(); + if( c == '#' ) + { + while( f.good() && c != '\n' ) + { + f.get( c ); + } + continue; + } + + + ////////////////////////////////////////////////////////////////////////// + // Line breaks + f.get( c ); + while( f.good() && ( c == '\n' || c == '\r' ) ) { f.get( c ); } + if( f.eof() ) break; + f.unget(); + + ////////////////////////////////////////////////////////////////////////// + // Check for fuel + f >> c; + if( c == '!' ) // It's fuel :) + { + // Read item + int IItemID = 0, IItemCount = 0, IItemHealth = 0; + f >> IItemID; + f >> c; if( c != ':' ) { bSyntaxError = true; break; } + f >> IItemCount; + + // Optional health + f >> c; + if( c != ':' ) + f.unget(); + else + { + f >> IItemHealth; + } + + // Burn time + float BurnTime; + f >> c; if( c != '=' ) { bSyntaxError = true; break; } + f >> BurnTime; + + // Add to fuel list + Fuel F; + F.In = new cItem( (ENUM_ITEM_ID) IItemID, (char)IItemCount, (short)IItemHealth ); + F.BurnTime = BurnTime; + m_pState->Fuel.push_back( F ); + continue; + } + f.unget(); + + ////////////////////////////////////////////////////////////////////////// + // Read items + int IItemID = 0, IItemCount = 0, IItemHealth = 0; + f >> IItemID; + f >> c; if( c != ':' ) { bSyntaxError = true; break; } + f >> IItemCount; + + // Optional health + f >> c; + if( c != ':' ) + f.unget(); + else + { + f >> IItemHealth; + } + + float CookTime; + f >> c; if( c != '@' ) { bSyntaxError = true; break; } + f >> CookTime; + + int OItemID = 0, OItemCount = 0, OItemHealth = 0; + f >> c; if( c != '=' ) { bSyntaxError = true; break; } + f >> OItemID; + f >> c; if( c != ':' ) { bSyntaxError = true; break; } + f >> OItemCount; + + // Optional health + f >> c; + if( c != ':' ) + f.unget(); + else + { + f >> OItemHealth; + } + + // Add to recipe list + Recipe R; + R.In = new cItem( (ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth ); + R.Out = new cItem( (ENUM_ITEM_ID)OItemID, (char)OItemCount, (short)OItemHealth ); + R.CookTime = CookTime; + m_pState->Recipes.push_back( R ); + } + if( bSyntaxError ) + { + LOGERROR("ERROR: FurnaceRecipe, syntax error" ); + } + LOG("Got %i furnace recipes, and %i fuels.", m_pState->Recipes.size(), m_pState->Fuel.size() ); + + LOG("-- Done loading furnace recipes --"); +} + + + + + +void cFurnaceRecipe::ClearRecipes() +{ + for( RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr ) + { + Recipe R = *itr; + delete R.In; + delete R.Out; + } + m_pState->Recipes.clear(); + + for( FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr ) + { + Fuel F = *itr; + delete F.In; + } + m_pState->Fuel.clear(); +} + +const cFurnaceRecipe::Recipe* cFurnaceRecipe::GetRecipeFrom( const cItem & a_Ingredient ) const +{ + const Recipe* BestRecipe = 0; + for( RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr ) + { + const Recipe & R = *itr; + if( (R.In->m_ItemID == a_Ingredient.m_ItemID) && (R.In->m_ItemCount <= a_Ingredient.m_ItemCount ) ) + { + if( BestRecipe && (BestRecipe->In->m_ItemCount > R.In->m_ItemCount) ) + { + continue; + } + else + { + BestRecipe = &R; + } + } + } + return BestRecipe; +} + +float cFurnaceRecipe::GetBurnTime( const cItem & a_Fuel ) const +{ + float BestFuel = 0.f; + for( FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr ) + { + const Fuel & F = *itr; + if( (F.In->m_ItemID == a_Fuel.m_ItemID) && (F.In->m_ItemCount <= a_Fuel.m_ItemCount ) ) + { + if( BestFuel > 0.f && (BestFuel > F.BurnTime ) ) + { + continue; + } + else + { + BestFuel = F.BurnTime; + } + } + } + return BestFuel; +} \ No newline at end of file diff --git a/source/FurnaceRecipe.h b/source/FurnaceRecipe.h new file mode 100644 index 000000000..ccb008604 --- /dev/null +++ b/source/FurnaceRecipe.h @@ -0,0 +1,46 @@ + +#pragma once + + + + + +class cItem; + + + + + +class cFurnaceRecipe +{ +public: + cFurnaceRecipe(); + ~cFurnaceRecipe(); + + void ReloadRecipes(); + + struct Fuel + { + cItem* In; + float BurnTime; + }; + + struct Recipe + { + cItem* In; + cItem* Out; + float CookTime; + }; + const Recipe* GetRecipeFrom( const cItem & a_Ingredient ) const; + float GetBurnTime( const cItem & a_Fuel ) const; + +private: + void ClearRecipes(); + + struct sFurnaceRecipeState; + sFurnaceRecipeState* m_pState; +}; + + + + diff --git a/source/Generating/BioGen.h b/source/Generating/BioGen.h index 6cd78b7e0..d268c1de6 100644 --- a/source/Generating/BioGen.h +++ b/source/Generating/BioGen.h @@ -15,7 +15,7 @@ Interfaces to the various biome generators: #pragma once #include "ChunkGenerator.h" -#include "../cNoise.h" +#include "../Noise.h" diff --git a/source/Generating/Caves.h b/source/Generating/Caves.h index cc3f00a52..2e66c909c 100644 --- a/source/Generating/Caves.h +++ b/source/Generating/Caves.h @@ -13,7 +13,7 @@ #pragma once #include "ChunkGenerator.h" -#include "../cNoise.h" +#include "../Noise.h" diff --git a/source/Generating/ChunkGenerator.cpp b/source/Generating/ChunkGenerator.cpp index 5862c0f5f..eca24c6a7 100644 --- a/source/Generating/ChunkGenerator.cpp +++ b/source/Generating/ChunkGenerator.cpp @@ -2,16 +2,16 @@ #include "Globals.h" #include "ChunkGenerator.h" -#include "../cWorld.h" +#include "../World.h" #include "../../iniFile/iniFile.h" #include "BioGen.h" #include "HeiGen.h" #include "CompoGen.h" #include "StructGen.h" #include "FinishGen.h" -#include "../cRoot.h" -#include "../cPluginManager.h" -#include "../cLuaChunk.h" +#include "../Root.h" +#include "../PluginManager.h" +#include "../LuaChunk.h" #include "Ravines.h" #include "Caves.h" diff --git a/source/Generating/CompoGen.h b/source/Generating/CompoGen.h index 235b9dafd..f6dfa6131 100644 --- a/source/Generating/CompoGen.h +++ b/source/Generating/CompoGen.h @@ -15,7 +15,7 @@ #pragma once #include "ChunkGenerator.h" -#include "../cNoise.h" +#include "../Noise.h" diff --git a/source/Generating/FinishGen.cpp b/source/Generating/FinishGen.cpp index c3aedd1a0..6d0811891 100644 --- a/source/Generating/FinishGen.cpp +++ b/source/Generating/FinishGen.cpp @@ -10,7 +10,7 @@ #include "Globals.h" #include "FinishGen.h" -#include "../cNoise.h" +#include "../Noise.h" #include "../BlockID.h" diff --git a/source/Generating/FinishGen.h b/source/Generating/FinishGen.h index f9a539572..b6d3c3935 100644 --- a/source/Generating/FinishGen.h +++ b/source/Generating/FinishGen.h @@ -14,7 +14,7 @@ #include "ChunkGenerator.h" -#include "../cNoise.h" +#include "../Noise.h" diff --git a/source/Generating/HeiGen.h b/source/Generating/HeiGen.h index 42e70f2a5..101179bfc 100644 --- a/source/Generating/HeiGen.h +++ b/source/Generating/HeiGen.h @@ -15,7 +15,7 @@ Interfaces to the various height generators: #pragma once #include "ChunkGenerator.h" -#include "../cNoise.h" +#include "../Noise.h" diff --git a/source/Generating/Ravines.h b/source/Generating/Ravines.h index 623986f29..d2d2b9391 100644 --- a/source/Generating/Ravines.h +++ b/source/Generating/Ravines.h @@ -10,7 +10,7 @@ #pragma once #include "ChunkGenerator.h" -#include "../cNoise.h" +#include "../Noise.h" diff --git a/source/Generating/StructGen.h b/source/Generating/StructGen.h index 8ddfdadba..fafd0f85d 100644 --- a/source/Generating/StructGen.h +++ b/source/Generating/StructGen.h @@ -14,7 +14,7 @@ #pragma once #include "ChunkGenerator.h" -#include "../cNoise.h" +#include "../Noise.h" diff --git a/source/Generating/Trees.h b/source/Generating/Trees.h index d3e76a78c..f5148ad6f 100644 --- a/source/Generating/Trees.h +++ b/source/Generating/Trees.h @@ -18,7 +18,7 @@ logs can overwrite others(leaves), but others shouldn't overwrite logs. This is #pragma once #include "../ChunkDef.h" -#include "../cNoise.h" +#include "../Noise.h" diff --git a/source/Globals.h b/source/Globals.h index f8d3a7bbc..b9892c933 100644 --- a/source/Globals.h +++ b/source/Globals.h @@ -169,7 +169,7 @@ typedef short Int16; #include "OSSupport/Event.h" #include "OSSupport/Thread.h" #include "OSSupport/File.h" -#include "cMCLogger.h" +#include "MCLogger.h" diff --git a/source/Group.cpp b/source/Group.cpp new file mode 100644 index 000000000..448d29d87 --- /dev/null +++ b/source/Group.cpp @@ -0,0 +1,37 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Group.h" + +void cGroup::AddCommand( std::string a_Command ) +{ + m_Commands[ a_Command ] = true; +} + +void cGroup::AddPermission( std::string a_Permission ) +{ + m_Permissions[ a_Permission ] = true; +} + +bool cGroup::HasCommand( std::string a_Command ) +{ + if( m_Commands.find("*") != m_Commands.end() ) return true; + + CommandMap::iterator itr = m_Commands.find( a_Command ); + if( itr != m_Commands.end() ) + { + if( itr->second ) return true; + } + + for( GroupList::iterator itr = m_Inherits.begin(); itr != m_Inherits.end(); ++itr ) + { + if( (*itr)->HasCommand( a_Command ) ) return true; + } + return false; +} + +void cGroup::InheritFrom( cGroup* a_Group ) +{ + m_Inherits.remove( a_Group ); + m_Inherits.push_back( a_Group ); +} \ No newline at end of file diff --git a/source/Group.h b/source/Group.h new file mode 100644 index 000000000..447802e88 --- /dev/null +++ b/source/Group.h @@ -0,0 +1,40 @@ + +#pragma once + + + + + +class cGroup //tolua_export +{ //tolua_export +public: //tolua_export + cGroup() {} + ~cGroup() {} + + void SetName( std::string a_Name ) { m_Name = a_Name; } //tolua_export + const std::string & GetName() const { return m_Name; } //tolua_export + void SetColor( std::string a_Color ) { m_Color = a_Color; } //tolua_export + void AddCommand( std::string a_Command ); //tolua_export + void AddPermission( std::string a_Permission ); //tolua_export + void InheritFrom( cGroup* a_Group ); //tolua_export + + bool HasCommand( std::string a_Command ); //tolua_export + + typedef std::map< std::string, bool > PermissionMap; + const PermissionMap & GetPermissions() const { return m_Permissions; } + + typedef std::map< std::string, bool > CommandMap; + const CommandMap & GetCommands() const { return m_Commands; } + + const AString & GetColor() const { return m_Color; } //tolua_export + + typedef std::list< cGroup* > GroupList; + const GroupList & GetInherits() const { return m_Inherits; } +private: + std::string m_Name; + std::string m_Color; + + PermissionMap m_Permissions; + CommandMap m_Commands; + GroupList m_Inherits; +};//tolua_export \ No newline at end of file diff --git a/source/GroupManager.cpp b/source/GroupManager.cpp new file mode 100644 index 000000000..982b67b45 --- /dev/null +++ b/source/GroupManager.cpp @@ -0,0 +1,108 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "GroupManager.h" +#include "Group.h" +#include "../iniFile/iniFile.h" +#include "ChatColor.h" +#include "Root.h" + + + + + +typedef std::map< std::string, cGroup* > GroupMap; +struct cGroupManager::sGroupManagerState +{ + GroupMap Groups; +}; + +cGroupManager* cGroupManager::GetGroupManager() +{ + LOGWARN("WARNING: Using deprecated function cGroupManager::GetGroupManager() use cRoot::Get()->GetGroupManager() instead!"); + return cRoot::Get()->GetGroupManager(); +} + +cGroupManager::~cGroupManager() +{ + for( GroupMap::iterator itr = m_pState->Groups.begin(); itr != m_pState->Groups.end(); ++itr ) + { + delete itr->second; + } + m_pState->Groups.clear(); + + delete m_pState; +} + +cGroupManager::cGroupManager() + : m_pState( new sGroupManagerState ) +{ + LOG("-- Loading Groups --"); + cIniFile IniFile("groups.ini"); + if( IniFile.ReadFile() ) + { + unsigned int NumKeys = IniFile.GetNumKeys(); + for( unsigned int i = 0; i < NumKeys; i++ ) + { + std::string KeyName = IniFile.GetKeyName( i ); + cGroup* Group = GetGroup( KeyName.c_str() ); + + LOG("Loading group: %s", KeyName.c_str() ); + + Group->SetName( KeyName ); + char Color = IniFile.GetValue( KeyName, "Color", "-" )[0]; + if( Color != '-' ) + Group->SetColor( cChatColor::MakeColor(Color) ); + else + Group->SetColor( cChatColor::White ); + + std::string Commands = IniFile.GetValue( KeyName, "Commands", "" ); + if( Commands.size() > 0 ) + { + AStringVector Split = StringSplit( Commands, "," ); + for( unsigned int i = 0; i < Split.size(); i++) + { + Group->AddCommand( Split[i] ); + //LOG("%s", Split[i].c_str() ); + } + } + + std::string Permissions = IniFile.GetValue( KeyName, "Permissions", "" ); + if( Permissions.size() > 0 ) + { + AStringVector Split = StringSplit( Permissions, "," ); + for( unsigned int i = 0; i < Split.size(); i++) + { + Group->AddPermission( Split[i] ); + LOGINFO("Permission: %s", Split[i].c_str() ); + } + } + + std::string Groups = IniFile.GetValue( KeyName, "Inherits", "" ); + if( Groups.size() > 0 ) + { + AStringVector Split = StringSplit( Groups, "," ); + for( unsigned int i = 0; i < Split.size(); i++) + { + Group->InheritFrom( GetGroup( Split[i].c_str() ) ); + } + } + } + } + LOG("-- Done Loading Groups --"); +} + +cGroup* cGroupManager::GetGroup( const char* a_Name ) +{ + GroupMap::iterator itr = m_pState->Groups.find( a_Name ); + if( itr != m_pState->Groups.end() ) + { + return itr->second; + } + + cGroup* Group = new cGroup(); + m_pState->Groups[a_Name] = Group; + + return Group; + +} \ No newline at end of file diff --git a/source/GroupManager.h b/source/GroupManager.h new file mode 100644 index 000000000..214d6991d --- /dev/null +++ b/source/GroupManager.h @@ -0,0 +1,17 @@ +#pragma once + +class cGroup; +class cGroupManager +{ +public: + static cGroupManager * GetGroupManager(); //tolua_export + + cGroup* GetGroup( const char* a_Name ); +private: + friend class cRoot; + cGroupManager(); + ~cGroupManager(); + + struct sGroupManagerState; + sGroupManagerState* m_pState; +}; \ No newline at end of file diff --git a/source/HeartBeat.cpp b/source/HeartBeat.cpp new file mode 100644 index 000000000..45acd0e1f --- /dev/null +++ b/source/HeartBeat.cpp @@ -0,0 +1,143 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "HeartBeat.h" +#include "MCLogger.h" +#include "md5/md5.h" + +#include "Root.h" +#include "Server.h" + + + + + +cHeartBeat::cHeartBeat() +{ + m_State = 0; + Authenticate(); +} + + + + + +cHeartBeat::~cHeartBeat() +{ +} + + + + + +void cHeartBeat::ReceivedData( char a_Data[256], int a_Size ) +{ + if( a_Size < 0 ) // Disconnected + return; + + char MySalt[] = "1234567890"; + + if( a_Size == 0 ) + { + Authenticate(); + return; + } + + bool bLoop = false; + do + { + switch (m_State) + { + case 1: + { + m_ServerID = std::string( a_Data, a_Size ); + LOGINFO("Got server ID %s", m_ServerID.c_str() ); + std::string Hash = md5( m_ServerID + std::string( MySalt ) ); + CloseSocket(); + if( Connect( "mc-server.org", 80 ) ) + { + SendMessage( (std::string("GET http://master.mc-server.org/?hash=") + Hash + std::string("&server=") + m_ServerID + "\n").c_str() ); + m_State = 2; + } + } + break; + case 2: + { + std::string ReturnedString = std::string( a_Data, a_Size ); + if( ReturnedString.compare("VALIDATED") == 0 ) + { + LOGINFO("Successfully validated server on master server list"); + } + else + { + LOGINFO("Could not validate server! Will try again later."); + cSleep::MilliSleep( 10*1000 ); + Authenticate(); + return; + } + m_State = 3; + } // Don't break, but fall through and update server info + case 3: + { + cSleep::MilliSleep( 10*1000 ); + SendUpdate(); + m_State = 4; + } + break; + case 4: + { + if( a_Data[0] == '0' ) + { + LOGINFO("Successfully updated server info!"); + cSleep::MilliSleep( 10*1000 ); + SendUpdate(); + } + else + { + LOGINFO("Failed to update server info, reauthenticating"); + Authenticate(); + } + } + default: + break; + }; + } while( bLoop ); +} + + + + + +void cHeartBeat::SendUpdate() +{ + CloseSocket(); + if( Connect( "mc-server.org", 80 ) ) + { + int Port = cRoot::Get()->GetServer()->GetPort(); + AString Msg; + AString sPort; + Printf(sPort, "%i", Port); + AString sChecksum = md5( m_ServerID + sPort ); + Printf(Msg, "GET http://master.mc-server.org/?update=%s&checksum=%s&port=%d\n", m_ServerID.c_str(), sChecksum.c_str(), Port); + SendMessage(Msg.c_str()); + } +} + + + + + +void cHeartBeat::Authenticate() +{ + CloseSocket(); + if (Connect( "mc-server.org", 80)) + { + m_State = 1; + int RetVal = SendMessage( "GET http://master.mc-server.org/\r\n\r\n"); + LOGINFO("Returned %i", RetVal); + } +} + + + + diff --git a/source/HeartBeat.h b/source/HeartBeat.h new file mode 100644 index 000000000..79465507c --- /dev/null +++ b/source/HeartBeat.h @@ -0,0 +1,28 @@ + +#pragma once + +#include "OSSupport/TCPLink.h" + + + + + +class cHeartBeat : public cTCPLink +{ +public: + cHeartBeat(); + ~cHeartBeat(); +private: + virtual void ReceivedData( char a_Data[256], int a_Size ); + + void Authenticate(); + int m_State; + + void SendUpdate(); + + std::string m_ServerID; +}; + + + + diff --git a/source/Inventory.cpp b/source/Inventory.cpp new file mode 100644 index 000000000..1ad349fb5 --- /dev/null +++ b/source/Inventory.cpp @@ -0,0 +1,427 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Inventory.h" +#include "Player.h" +#include "ClientHandle.h" +#include "UI/Window.h" +#include "Item.h" +#include "Root.h" + +#include + +#include "items/ItemHandler.h" + + + + + +cInventory::~cInventory() +{ + /* + // TODO + cWindow wnd = GetWindow(); + if (wnd != NULL) + { + wnd->Close(*m_Owner); + } + CloseWindow(); + */ +} + + + + + +cInventory::cInventory(cPlayer & a_Owner) : + m_Owner(a_Owner) +{ + for (unsigned int i = 0; i < c_NumSlots; i++) + { + m_Slots[i].Empty(); + } + + m_CraftSlots = m_Slots + c_CraftOffset; + m_ArmorSlots = m_Slots + c_ArmorOffset; + m_MainSlots = m_Slots + c_MainOffset; + m_HotSlots = m_Slots + c_HotOffset; + + SetEquippedSlot(0); +} + + + + + +bool cInventory::AddItem( cItem & a_Item ) +{ + cItem BackupSlots[c_NumSlots]; + memcpy( BackupSlots, m_Slots, c_NumSlots * sizeof( cItem ) ); + + bool ChangedSlots[c_NumSlots]; + memset( ChangedSlots, false, c_NumSlots * sizeof( bool ) ); + + if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_HotOffset, c_HotSlots, ChangedSlots, 0 ); + if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_MainOffset, c_MainSlots, ChangedSlots, 0 ); + if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_HotOffset, c_HotSlots, ChangedSlots, 2 ); + if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_MainOffset, c_MainSlots, ChangedSlots, 2 ); + + if( a_Item.m_ItemCount > 0 ) // Could not add all items + { + // retore backup + memcpy( m_Slots, BackupSlots, c_NumSlots * sizeof( cItem ) ); + return false; + } + + for(unsigned int i = 0; i < c_NumSlots; i++) + { + if( ChangedSlots[i] ) + { + LOG("Item was added to %i ID:%i Count:%i", i, m_Slots[i].m_ItemID, m_Slots[i].m_ItemCount ); + SendSlot(i); + } + } + + return (a_Item.m_ItemCount == 0); +} + + + + + +// TODO: Right now if you dont have enough items, the items you did have are removed, and the function returns false anyway +bool cInventory::RemoveItem( cItem & a_Item ) +{ + // First check equipped slot + if ((m_EquippedSlot >= 0) && (m_EquippedSlot < 9)) + { + if (m_HotSlots[m_EquippedSlot].m_ItemID == a_Item.m_ItemID) + { + cItem & Item = m_HotSlots[m_EquippedSlot]; + if(Item.m_ItemCount > a_Item.m_ItemCount) + { + Item.m_ItemCount -= a_Item.m_ItemCount; + SendSlot( m_EquippedSlot + c_HotOffset ); + return true; + } + else if(Item.m_ItemCount > 0 ) + { + a_Item.m_ItemCount -= Item.m_ItemCount; + Item.Empty(); + SendSlot( m_EquippedSlot + c_HotOffset ); + } + } + } + + // Then check other slotz + if (a_Item.m_ItemCount > 0) + { + for(int i = 0; i < 36; i++) + { + cItem & Item = m_MainSlots[i]; + if( Item.m_ItemID == a_Item.m_ItemID ) + { + if(Item.m_ItemCount > a_Item.m_ItemCount) + { + Item.m_ItemCount -= a_Item.m_ItemCount; + SendSlot( i + c_MainOffset ); + return true; + } + else if(Item.m_ItemCount > 0 ) + { + a_Item.m_ItemCount -= Item.m_ItemCount; + Item.Empty(); + SendSlot( i + c_MainOffset ); + } + } + } + } + + return (a_Item.m_ItemCount == 0); +} + + + + + +void cInventory::Clear() +{ + for(unsigned int i = 0; i < c_NumSlots; i++) + m_Slots[i].Empty(); +} + + + + + +cItem * cInventory::GetSlotsForType( int a_Type ) +{ + switch( a_Type ) + { + case -1: + return m_MainSlots; + case -2: + return m_CraftSlots; + case -3: + return m_ArmorSlots; + } + return 0; +} + + + + + +/* +int cInventory::GetSlotCountForType( int a_Type ) +{ + switch (a_Type) + { + case -1: + return 36; + case -2: + case -3: + return 4; + } + return 0; +} +*/ + + + + + +cItem* cInventory::GetSlot( int a_SlotNum ) +{ + if( a_SlotNum < 0 || a_SlotNum >= (short)c_NumSlots ) return 0; + return &m_Slots[a_SlotNum]; +} + + + + + +cItem* cInventory::GetFromHotBar( int a_SlotNum ) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= 9)) + { + return NULL; + } + return &m_HotSlots[a_SlotNum]; +} + + + + + +void cInventory::SetEquippedSlot(int a_SlotNum) +{ + if ((a_SlotNum < 0) || (a_SlotNum >= 9)) + { + m_EquippedSlot = 0; + } + else + { + m_EquippedSlot = (short)a_SlotNum; + } + m_EquippedItem = GetFromHotBar(m_EquippedSlot); +} + + + + + +cItem & cInventory::GetEquippedItem(void) +{ + cItem* Item = GetFromHotBar( m_EquippedSlot ); + if( Item ) + { + *m_EquippedItem = *Item; + return *Item; + } + else + { + m_EquippedItem->Empty(); + } + return *m_EquippedItem; +} + + + + + +const cItem & cInventory::GetEquippedItem(void) const +{ + return *m_EquippedItem; +} + + + + + +void cInventory::SendWholeInventory(cClientHandle & a_Client) +{ + a_Client.SendWholeInventory(*this); +} + + + + + +void cInventory::SendSlot(int a_SlotNum) +{ + cItem * Item = GetSlot(a_SlotNum); + if (Item != NULL) + { + if (Item->IsEmpty()) + { + // Sanitize items that are not completely empty (ie. count == 0, but type != empty) + Item->Empty(); + } + m_Owner.GetClientHandle()->SendInventorySlot(0, a_SlotNum, *Item); + } +} + + + + + +int cInventory::HowManyCanFit(short a_ItemType, short a_ItemDamage, int a_BeginSlot, int a_EndSlot) +{ + int res = 0; + for (int i = a_BeginSlot; i <= a_EndSlot; i++) + { + if ( + m_Slots[i].IsEmpty() || + ((m_Slots[i].m_ItemID == a_ItemType) && (m_Slots[i].m_ItemHealth == a_ItemDamage)) + ) + { + int MaxCount = ItemHandler(a_ItemType)->GetMaxStackSize(); + ASSERT(m_Slots[i].m_ItemCount <= MaxCount); + res += MaxCount - m_Slots[i].m_ItemCount; + } + } // for i - m_Slots[] + return res; +} + + + + + +int cInventory::MoveItem(short a_ItemType, short a_ItemDamage, int a_Count, int a_BeginSlot, int a_EndSlot) +{ + int res = 0; + for (int i = a_BeginSlot; i <= a_EndSlot; i++) + { + if ( + m_Slots[i].IsEmpty() || + ((m_Slots[i].m_ItemID == a_ItemType) && (m_Slots[i].m_ItemHealth == a_ItemDamage)) + ) + { + int MaxCount = ItemHandler(a_ItemType)->GetMaxStackSize(); + ASSERT(m_Slots[i].m_ItemCount <= MaxCount); + int NumToMove = std::min(a_Count, MaxCount - m_Slots[i].m_ItemCount); + m_Slots[i].m_ItemCount += NumToMove; + m_Slots[i].m_ItemHealth = a_ItemDamage; + m_Slots[i].m_ItemID = a_ItemType; + SendSlot(i); + res += NumToMove; + a_Count -= NumToMove; + if (a_Count <= 0) + { + // No more items to distribute + return res; + } + } + } // for i - m_Slots[] + // No more space to distribute to + return res; +} + + + + + +bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode /* = 0 */ ) +{ + // Fill already present stacks + if( a_Mode < 2 ) + { + for(int i = 0; i < a_Size; i++) + { + if( m_Slots[i + a_Offset].m_ItemID == a_Item.m_ItemID && m_Slots[i + a_Offset].m_ItemCount < 64 && m_Slots[i + a_Offset].m_ItemHealth == a_Item.m_ItemHealth ) + { + int NumFree = 64 - m_Slots[i + a_Offset].m_ItemCount; + if( NumFree >= a_Item.m_ItemCount ) + { + + //printf("1. Adding %i items ( free: %i )\n", a_Item.m_ItemCount, NumFree ); + m_Slots[i + a_Offset].m_ItemCount += a_Item.m_ItemCount; + a_Item.m_ItemCount = 0; + a_bChangedSlots[i + a_Offset] = true; + break; + } + else + { + //printf("2. Adding %i items\n", NumFree ); + m_Slots[i + a_Offset].m_ItemCount += (char)NumFree; + a_Item.m_ItemCount -= (char)NumFree; + a_bChangedSlots[i + a_Offset] = true; + } + } + } + } + + if( a_Mode > 0 ) + { + // If we got more left, find first empty slot + for(int i = 0; i < a_Size && a_Item.m_ItemCount > 0; i++) + { + if( m_Slots[i + a_Offset].m_ItemID == -1 ) + { + m_Slots[i + a_Offset] = a_Item; + a_Item.m_ItemCount = 0; + a_bChangedSlots[i + a_Offset] = true; + } + } + } + + return true; +} + + + + + +void cInventory::SaveToJson(Json::Value & a_Value) +{ + for(unsigned int i = 0; i < c_NumSlots; i++) + { + Json::Value JSON_Item; + m_Slots[i].GetJson( JSON_Item ); + a_Value.append( JSON_Item ); + } +} + + + + + +bool cInventory::LoadFromJson(Json::Value & a_Value) +{ + int SlotIdx = 0; + + // TODO: Limit the number of slots written to the actual number of slots, + // otherwise an invalid json will crash the server! + + for( Json::Value::iterator itr = a_Value.begin(); itr != a_Value.end(); ++itr ) + { + m_Slots[SlotIdx].FromJson( *itr ); + SlotIdx++; + } + return true; +} + + + + diff --git a/source/Inventory.h b/source/Inventory.h new file mode 100644 index 000000000..1dd22deec --- /dev/null +++ b/source/Inventory.h @@ -0,0 +1,88 @@ + +#pragma once + +#include "Item.h" + + + + + +namespace Json +{ + class Value; +}; + +class cClientHandle; +class cPlayer; + + + + + +class cInventory //tolua_export +{ //tolua_export +public: + cInventory(cPlayer & a_Owner); + ~cInventory(); + + void Clear(); //tolua_export + + cItem* GetSlotsForType( int a_Type ); + int GetSlotCountForType( int a_Type ); + + bool AddItem( cItem & a_Item ); //tolua_export + bool RemoveItem( cItem & a_Item ); //tolua_export + + void SaveToJson(Json::Value & a_Value); + bool LoadFromJson(Json::Value & a_Value); + + void SendWholeInventory(cClientHandle & a_Client); + + cItem * GetSlot(int a_SlotNum ); //tolua_export + cItem * GetSlots(void) { return m_Slots; } + const cItem * GetSlots(void) const { return m_Slots; } + cItem * GetFromHotBar(int a_HotBarSlotNum); //tolua_export + + cItem & GetEquippedItem(void); //tolua_export + const cItem & GetEquippedItem(void) const; + void SetEquippedSlot(int a_SlotNum); //tolua_export + short GetEquippedSlot(void) { return m_EquippedSlot; } //tolua_export + + void SendSlot( int a_SlotNum ); //tolua_export + + /// Returns how many items of the specified type would fit into the slot range specified + int HowManyCanFit(short a_ItemType, short a_ItemDamage, int a_BeginSlot, int a_EndSlot); + + /// Moves items, fitting them into the slot range specified, up to a_Count items. Returns the number of items moved + int MoveItem(short a_ItemType, short a_ItemDamage, int a_Count, int a_BeginSlot, int a_EndSlot); + + static const unsigned int c_NumSlots = 45; + static const unsigned int c_MainSlots = 27; + static const unsigned int c_HotSlots = 9; + static const unsigned int c_CraftSlots = 4; + static const unsigned int c_ArmorSlots = 4; + + static const unsigned int c_CraftOffset = 0; + static const unsigned int c_ArmorOffset = 5; + static const unsigned int c_MainOffset = 9; + static const unsigned int c_HotOffset = 36; + +protected: + bool AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode = 0 ); + + cItem m_Slots[c_NumSlots]; + + cItem * m_MainSlots; + cItem * m_CraftSlots; + cItem * m_ArmorSlots; + cItem * m_HotSlots; + + cItem * m_EquippedItem; + short m_EquippedSlot; + + cPlayer & m_Owner; +}; //tolua_export + + + + diff --git a/source/Item.cpp b/source/Item.cpp new file mode 100644 index 000000000..7d8a18f98 --- /dev/null +++ b/source/Item.cpp @@ -0,0 +1,58 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Item.h" +#include + + + + + +void cItem::GetJson( Json::Value & a_OutValue ) const +{ + a_OutValue["ID"] = m_ItemID; + if( m_ItemID > 0 ) + { + a_OutValue["Count"] = m_ItemCount; + a_OutValue["Health"] = m_ItemHealth; + } +} + +void cItem::FromJson( const Json::Value & a_Value ) +{ + m_ItemID = (ENUM_ITEM_ID)a_Value.get("ID", -1 ).asInt(); + if( m_ItemID > 0 ) + { + m_ItemCount = (char)a_Value.get("Count", -1 ).asInt(); + m_ItemHealth = (short)a_Value.get("Health", -1 ).asInt(); + } +} + + + + + +bool cItem::IsEnchantable(short item) +{ + if ((item >= 256) && (item <= 259)) + return true; + if ((item >= 267) && (item <= 279)) + return true; + if ((item >= 283) && (item <= 286)) + return true; + if ((item >= 290) && (item <= 294)) + return true; + if ((item >= 298) && (item <= 317)) + return true; + if ((item >= 290) && (item <= 294)) + return true; + + if ((item == 346) || (item == 359) || (item == 261)) + return true; + + return false; +} + + + + diff --git a/source/Item.h b/source/Item.h new file mode 100644 index 000000000..aa4b76bbc --- /dev/null +++ b/source/Item.h @@ -0,0 +1,146 @@ +#pragma once + +#include "Defines.h" +#include "BlockID.h" + +namespace Json +{ + class Value; +} + + + + + +// tolua_begin +class cItem +{ +public: + cItem(short a_ItemType = E_ITEM_EMPTY, char a_ItemCount = 0, short a_ItemHealth = 0) + : m_ItemType (a_ItemType) + , m_ItemCount (a_ItemCount) + , m_ItemHealth(a_ItemHealth) + { + if (!IsValidItem( m_ItemID ) ) m_ItemID = E_ITEM_EMPTY; + } + + void Empty() + { + m_ItemID = E_ITEM_EMPTY; + m_ItemCount = 0; + m_ItemHealth = 0; + } + + void Clear(void) + { + m_ItemID = E_ITEM_EMPTY; + m_ItemCount = 0; + m_ItemHealth = 0; + } + + bool IsEmpty(void) const + { + return (m_ItemID <= 0 || m_ItemCount <= 0); + } + + // tolua_end + OBSOLETE + // tolua_begin + bool Equals(const cItem & a_Item) const // obsolete, use IsEqual() instead + { + return IsEqual(a_Item); + } + + bool IsEqual(const cItem & a_Item) const + { + return (IsSameType(a_Item) && (m_ItemHealth == a_Item.m_ItemHealth)); + } + + bool IsSameType(const cItem & a_Item) const + { + return (m_ItemID == a_Item.m_ItemID) || (IsEmpty() && a_Item.IsEmpty()); + } + + // TODO Sorry for writing the functions in the header. But somehow it doesnīt worked when I put them into the cpp File :s + + inline int GetMaxDuration(void) const + { + switch(m_ItemID) + { + case 256: return 251; + case 257: return 251; + case 258: return 251; + case 259: return 65; //Lighter / Flint and Steel + case 267: return 251; + case 268: return 60; + case 269: return 60; + case 270: return 60; + case 271: return 60; + case 272: return 132; + case 273: return 132; + case 274: return 132; + case 275: return 132; + case 276: return 1563; + case 277: return 1563; + case 278: return 1563; + case 279: return 1563; + case 283: return 32; + case 284: return 32; + case 285: return 32; + case 286: return 32; + case 290: return 60; + case 291: return 132; + case 292: return 251; + case 293: return 1563; + case 294: return 32; + case 359: return 251; + default: return 0; + } + } + + // Damages a weapon / tool. Returns true when destroyed + inline bool DamageItem() + { + if (HasDuration()) + { + m_ItemHealth++; + if(m_ItemHealth >= GetMaxDuration()) + return true; + } + return false; + } + + inline bool HasDuration() { return GetMaxDuration() > 0; } + + // tolua_end + void GetJson( Json::Value & a_OutValue ) const; + void FromJson( const Json::Value & a_Value ); + // tolua_begin + + static bool IsEnchantable(short a_ItemType); + + // tolua_end + union + { + // tolua_begin + short m_ItemID; // OBSOLETE, use m_ItemType instead + short m_ItemType; + // tolua_end + } ; + char m_ItemCount; // tolua_export + union + { + // tolua_begin + short m_ItemHealth; // OBSOLETE, use m_ItemDamage instead + short m_ItemDamage; + // tolua_end + } ; + // tolua_begin +}; +// tolua_end + +typedef std::vector cItems; + + + + diff --git a/source/Ladder.h b/source/Ladder.h new file mode 100644 index 000000000..b3dc0fb96 --- /dev/null +++ b/source/Ladder.h @@ -0,0 +1,43 @@ +#pragma once + +class cLadder //tolua_export +{ //tolua_export +public: + + static char DirectionToMetaData( char a_Direction ) //tolua_export + { //tolua_export + switch( a_Direction ) + { + case 0x2: + return 0x2; + case 0x3: + return 0x3; + case 0x4: + return 0x4; + case 0x5: + return 0x5; + default: + break; + }; + return 0x2; + } //tolua_export + + static char MetaDataToDirection( char a_MetaData ) //tolua_export + { //tolua_export + switch( a_MetaData ) + { + case 0x2: + return 0x2; + case 0x3: + return 0x3; + case 0x4: + return 0x4; + case 0x5: + return 0x5; + default: + break; + }; + return 0x2; + } //tolua_export + +}; //tolua_export \ No newline at end of file diff --git a/source/LavaSimulator.cpp b/source/LavaSimulator.cpp new file mode 100644 index 000000000..15cf5591c --- /dev/null +++ b/source/LavaSimulator.cpp @@ -0,0 +1,20 @@ +#include "Globals.h" +#include "LavaSimulator.h" +#include "Defines.h" +#include "World.h" + + +cLavaSimulator::cLavaSimulator(cWorld *a_World) + : cFluidSimulator(a_World) +{ + m_FluidBlock = E_BLOCK_LAVA; + m_StationaryFluidBlock = E_BLOCK_STATIONARY_LAVA; + m_MaxHeight = 6; + m_FlowReduction = 2; +} + + +bool cLavaSimulator::IsAllowedBlock(char a_BlockID) +{ + return IsBlockLava(a_BlockID); +} \ No newline at end of file diff --git a/source/LavaSimulator.h b/source/LavaSimulator.h new file mode 100644 index 000000000..3a9159f1c --- /dev/null +++ b/source/LavaSimulator.h @@ -0,0 +1,12 @@ +#pragma once +#include "FluidSimulator.h" + +class cLavaSimulator : public cFluidSimulator +{ +public: + cLavaSimulator( cWorld* a_World ); + + virtual bool IsAllowedBlock( char a_BlockID ); + + +}; \ No newline at end of file diff --git a/source/LightingThread.cpp b/source/LightingThread.cpp index 67f3b4116..071c1893a 100644 --- a/source/LightingThread.cpp +++ b/source/LightingThread.cpp @@ -5,8 +5,8 @@ #include "Globals.h" #include "LightingThread.h" -#include "cChunkMap.h" -#include "cWorld.h" +#include "ChunkMap.h" +#include "World.h" diff --git a/source/Log.cpp b/source/Log.cpp new file mode 100644 index 000000000..42c58eb27 --- /dev/null +++ b/source/Log.cpp @@ -0,0 +1,168 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Log.h" + +#include +#include +#include "OSSupport/MakeDir.h" +#include "OSSupport/IsThread.h" + +#if defined(ANDROID_NDK) + #include + #include "ToJava.h" +#endif + + + + +cLog* cLog::s_Log = NULL; + +cLog::cLog(const AString & a_FileName ) + : m_File(NULL) +{ + s_Log = this; + + // create logs directory + cMakeDir::MakeDir("logs"); + + OpenLog( (FILE_IO_PREFIX + std::string("logs/") + a_FileName).c_str() ); +} + + + + + +cLog::~cLog() +{ + CloseLog(); + s_Log = NULL; +} + + + + + +cLog* cLog::GetInstance() +{ + if(s_Log) + return s_Log; + + new cLog("log.txt"); + return s_Log; +} + + + + + +void cLog::CloseLog() +{ + if( m_File ) + fclose (m_File); + m_File = 0; +} + + + + + +void cLog::OpenLog( const char* a_FileName ) +{ + if(m_File) fclose (m_File); + #ifdef _WIN32 + fopen_s( &m_File, a_FileName, "a+" ); + #else + m_File = fopen(a_FileName, "a+" ); + #endif +} + + + + + +void cLog::ClearLog() +{ + #ifdef _WIN32 + if( fopen_s( &m_File, "log.txt", "w" ) == 0) + fclose (m_File); + #else + m_File = fopen("log.txt", "w" ); + if( m_File ) + fclose (m_File); + #endif + m_File = 0; +} + + + + + +void cLog::Log(const char * a_Format, va_list argList) +{ + AString Message; + AppendVPrintf(Message, a_Format, argList); + + time_t rawtime; + time ( &rawtime ); + + struct tm* timeinfo; +#ifdef _WIN32 + struct tm timeinforeal; + timeinfo = &timeinforeal; + localtime_s(timeinfo, &rawtime ); +#else + timeinfo = localtime( &rawtime ); +#endif + + AString Line; + #ifdef _DEBUG + Printf(Line, "[%04x|%02d:%02d:%02d] %s\n", cIsThread::GetCurrentID(), timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, Message.c_str()); + #else + Printf(Line, "[%02d:%02d:%02d] %s\n", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, Message.c_str()); + #endif + if (m_File) + { + fputs(Line.c_str(), m_File); + fflush(m_File); + } + + // Print to console: +#if defined(ANDROID_NDK) + //__android_log_vprint(ANDROID_LOG_ERROR,"MCServer", a_Format, argList); + __android_log_print(ANDROID_LOG_ERROR, "MCServer", "%s", Line.c_str() ); + //CallJavaFunction_Void_String(g_JavaThread, "AddToLog", Line ); +#else + printf("%s", Line.c_str()); +#endif + + #if defined (_WIN32) && defined(_DEBUG) + // In a Windows Debug build, output the log to debug console as well: + OutputDebugString(Line.c_str()); + #endif // _WIN32 +} + + + + + +void cLog::Log(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + Log( a_Format, argList ); + va_end(argList); +} + + + + + +void cLog::SimpleLog(const char* a_String) +{ + Log("%s", a_String ); +} + + + + diff --git a/source/Log.h b/source/Log.h new file mode 100644 index 000000000..b405b52a4 --- /dev/null +++ b/source/Log.h @@ -0,0 +1,30 @@ + +#pragma once + + + + + +class cLog +{ // tolua_export +private: + FILE * m_File; + static cLog * s_Log; + +public: + cLog(const AString & a_FileName); + ~cLog(); + void Log(const char* a_Format, va_list argList ); + void Log(const char* a_Format, ...); + //tolua_begin + void SimpleLog(const char* a_String); + void OpenLog( const char* a_FileName ); + void CloseLog(); + void ClearLog(); + static cLog* GetInstance(); +}; +//tolua_end + + + + diff --git a/source/LuaChunk.cpp b/source/LuaChunk.cpp new file mode 100644 index 000000000..8e3148cbe --- /dev/null +++ b/source/LuaChunk.cpp @@ -0,0 +1,4 @@ +#include "Globals.h" + +#include "LuaChunk.h" + diff --git a/source/LuaChunk.h b/source/LuaChunk.h new file mode 100644 index 000000000..dc6f2a0f2 --- /dev/null +++ b/source/LuaChunk.h @@ -0,0 +1,139 @@ +#pragma once + +#include "ChunkDef.h" + +class cLuaChunk //tolua_export +{ //tolua_export +public: + cLuaChunk( cChunkDef::BlockTypes & a_BlockTypes + , cChunkDef::BlockNibbles & a_BlockNibbles + , cChunkDef::HeightMap & a_HeightMap + , cChunkDef::BiomeMap & a_BiomeMap + ) + : m_BiomeMap( a_BiomeMap ) + , m_BlockTypes( a_BlockTypes ) + , m_BlockMeta( a_BlockNibbles ) + , m_HeightMap( a_HeightMap ) + , m_bUseDefaultBiomes( false ) + , m_bUseDefaultComposition( false ) + , m_bUseDefaultStructures( false ) + , m_bUseDefaultFinish( false ) + { + memset( m_BlockTypes, 0, sizeof( cChunkDef::BlockTypes ) ); + memset( m_BlockMeta, 0, sizeof( cChunkDef::BlockNibbles ) ); + memset( m_BiomeMap, 0, sizeof( cChunkDef::BiomeMap ) ); + memset( m_HeightMap, 0, sizeof( cChunkDef::HeightMap ) ); + } + ~cLuaChunk() + {} + + //tolua_begin + + // Block functions + void FillBlocks( char a_BlockID, unsigned char a_BlockMeta ) + { + const NIBBLETYPE CompressedMeta = a_BlockMeta | a_BlockMeta << 4; + memset( m_BlockTypes, a_BlockID, sizeof( cChunkDef::BlockTypes ) ); + memset( m_BlockMeta, CompressedMeta, sizeof( cChunkDef::BlockNibbles ) ); + } + + void SetBlock( int a_X, int a_Y, int a_Z, char a_BlockID, unsigned char a_BlockMeta ) + { + cChunkDef::SetBlock( m_BlockTypes, a_X, a_Y, a_Z, a_BlockID ); + cChunkDef::SetNibble( m_BlockMeta, a_X, a_Y, a_Z, a_BlockMeta ); + } + + char GetBlock( int a_X, int a_Y, int a_Z ) + { + return cChunkDef::GetBlock( m_BlockTypes, a_X, a_Y, a_Z ); + } + + char GetBlockMeta( int a_X, int a_Y, int a_Z ) + { + return cChunkDef::GetNibble( m_BlockMeta, a_X, a_Y, a_Z ); + } + + + + + + // Biome functinos + void SetBiome( int a_X, int a_Z, int a_BiomeID ) + { + cChunkDef::SetBiome( m_BiomeMap, a_X, a_Z, (EMCSBiome)a_BiomeID ); + } + + int GetBiome( int a_X, int a_Z ) + { + return cChunkDef::GetBiome( m_BiomeMap, a_X, a_Z ); + } + + + + + + // Height functions + void SetHeight( int a_X, int a_Z, int a_Height ) + { + cChunkDef::SetHeight( m_HeightMap, a_X, a_Z, a_Height ); + } + + int GetHeight( int a_X, int a_Z ) + { + return cChunkDef::GetHeight( m_HeightMap, a_X, a_Z ); + } + + + + + + // Functions to explicitly tell the server to use default behavior for certain parts of generating terrain + void SetUseDefaultBiomes( bool a_bUseDefaultBiomes ) + { + m_bUseDefaultBiomes = a_bUseDefaultBiomes; + } + bool IsUsingDefaultBiomes() + { + return m_bUseDefaultBiomes; + } + + void SetUseDefaultComposition( bool a_bUseDefaultComposition ) + { + m_bUseDefaultComposition = a_bUseDefaultComposition; + } + bool IsUsingDefaultComposition() + { + return m_bUseDefaultComposition; + } + + void SetUseDefaultStructures( bool a_bUseDefaultStructures ) + { + m_bUseDefaultStructures = a_bUseDefaultStructures; + } + bool IsUsingDefaultStructures() + { + return m_bUseDefaultStructures; + } + + void SetUseDefaultFinish( bool a_bUseDefaultFinish ) + { + m_bUseDefaultFinish = a_bUseDefaultFinish; + } + bool IsUsingDefaultFinish() + { + return m_bUseDefaultFinish; + } + + //tolua_end + +private: + bool m_bUseDefaultBiomes; + bool m_bUseDefaultComposition; + bool m_bUseDefaultStructures; + bool m_bUseDefaultFinish; + + cChunkDef::BiomeMap & m_BiomeMap; + cChunkDef::BlockTypes & m_BlockTypes; + cChunkDef::BlockNibbles & m_BlockMeta; + cChunkDef::HeightMap & m_HeightMap; +}; //tolua_export \ No newline at end of file diff --git a/source/LuaCommandBinder.cpp b/source/LuaCommandBinder.cpp new file mode 100644 index 000000000..c25b422a7 --- /dev/null +++ b/source/LuaCommandBinder.cpp @@ -0,0 +1,122 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "LuaCommandBinder.h" +#include "Player.h" +#include "Plugin.h" +#include "Plugin_Lua.h" + +#include "tolua++.h" + + + + + +extern bool report_errors(lua_State* lua, int status); + +cLuaCommandBinder::cLuaCommandBinder() +{ +} + +cLuaCommandBinder::~cLuaCommandBinder() +{ +} + +void cLuaCommandBinder::ClearBindings() +{ + m_BoundCommands.clear(); +} + +void cLuaCommandBinder::RemoveBindingsForPlugin( cPlugin* a_Plugin ) +{ + for( CommandMap::iterator itr = m_BoundCommands.begin(); itr != m_BoundCommands.end(); ) + { + if( itr->second.Plugin == a_Plugin ) + { + LOGINFO("Unbinding %s ", itr->first.c_str( ) ); + luaL_unref( itr->second.LuaState, LUA_REGISTRYINDEX, itr->second.Reference ); // unreference + CommandMap::iterator eraseme = itr; + ++itr; + m_BoundCommands.erase( eraseme ); + continue; + } + ++itr; + } +} + +bool cLuaCommandBinder::BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, lua_State * a_LuaState, int a_FunctionReference ) +{ + if( !a_Plugin->CanBindCommands() ) + { + LOGERROR("ERROR: Trying to bind command \"%s\" to a plugin that is not initialized.", a_Command.c_str() ); + return false; + } + if( m_BoundCommands.find( a_Command ) != m_BoundCommands.end() ) + { + LOGERROR("ERROR: Trying to bind command \"%s\" that has already been bound.", a_Command.c_str() ); + return false; + } + LOGINFO("Binding %s (%s)", a_Command.c_str(), a_Permission.c_str() ); + m_BoundCommands[ a_Command ] = BoundFunction( a_Plugin, a_LuaState, a_FunctionReference, a_Permission ); + return true; +} + +bool cLuaCommandBinder::HandleCommand( const std::string & a_Command, cPlayer* a_Player ) +{ + AStringVector Split = StringSplit(a_Command, " "); + if (Split.size() == 0) + { + return false; + } + + CommandMap::iterator FoundCommand = m_BoundCommands.find( Split[0] ); + if( FoundCommand != m_BoundCommands.end() ) + { + const BoundFunction & func = FoundCommand->second; + if( func.Permission.size() > 0 ) + { + if( !a_Player->HasPermission( func.Permission.c_str() ) ) + { + return false; + } + } + + // For enabling 'self' in the function, it's kind of a hack I'm not sure this is the way to go + lua_pushvalue(func.LuaState, LUA_GLOBALSINDEX); + lua_pushstring(func.LuaState, "self"); + tolua_pushusertype( func.LuaState, func.Plugin, "cPlugin" ); + lua_rawset(func.LuaState, -3); + lua_pop(func.LuaState, 1); + + LOGINFO("1. Stack size: %i", lua_gettop(func.LuaState) ); + lua_rawgeti( func.LuaState, LUA_REGISTRYINDEX, func.Reference); // same as lua_getref() + + // Push the split + LOGINFO("2. Stack size: %i", lua_gettop(func.LuaState) ); + lua_createtable(func.LuaState, Split.size(), 0); + int newTable = lua_gettop(func.LuaState); + int index = 1; + std::vector::const_iterator iter = Split.begin(); + while(iter != Split.end()) { + tolua_pushstring( func.LuaState, (*iter).c_str() ); + lua_rawseti(func.LuaState, newTable, index); + ++iter; + ++index; + } + LOGINFO("3. Stack size: %i", lua_gettop(func.LuaState) ); + // Push player + tolua_pushusertype( func.LuaState, a_Player, "cPlayer" ); + LOGINFO("Calling bound function! :D"); + int s = lua_pcall(func.LuaState, 2, 1, 0); + if( report_errors( func.LuaState, s ) ) + { + LOGINFO("error. Stack size: %i", lua_gettop(func.LuaState) ); + return false; + } + bool RetVal = (tolua_toboolean(func.LuaState, -1, 0) > 0); + lua_pop(func.LuaState, 1); // Pop return value + LOGINFO("ok. Stack size: %i", lua_gettop(func.LuaState) ); + return RetVal; + } + return false; +} diff --git a/source/LuaCommandBinder.h b/source/LuaCommandBinder.h new file mode 100644 index 000000000..768d097f8 --- /dev/null +++ b/source/LuaCommandBinder.h @@ -0,0 +1,42 @@ + +#pragma once + +struct lua_State; +class cPlugin; +class cPlayer; + + + + + +class cLuaCommandBinder +{ +public: + cLuaCommandBinder(); + ~cLuaCommandBinder(); + + bool HandleCommand( const std::string & a_Command, cPlayer* a_Player ); + + bool BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, lua_State * a_LuaState, int a_FunctionReference ); + + void ClearBindings(); + void RemoveBindingsForPlugin( cPlugin* a_Plugin ); +private: + struct BoundFunction + { + BoundFunction() : Plugin( 0 ), LuaState( 0 ), Reference( 0 ) {} + BoundFunction( cPlugin* a_Plugin, lua_State * a_LuaState, int a_Reference, const std::string & a_Permission ) : Plugin( a_Plugin ), LuaState( a_LuaState ), Reference( a_Reference ), Permission( a_Permission ) {} + cPlugin* Plugin; + lua_State* LuaState; + int Reference; + std::string Permission; + }; + + typedef std::map< std::string, BoundFunction > CommandMap; + CommandMap m_BoundCommands; +}; + + + + + diff --git a/source/LuaFunctions.h b/source/LuaFunctions.h index 66a505980..0ad420881 100644 --- a/source/LuaFunctions.h +++ b/source/LuaFunctions.h @@ -1,6 +1,6 @@ #pragma once -#include "cMCLogger.h" +#include "MCLogger.h" #include // tolua_begin diff --git a/source/LuaItems.h b/source/LuaItems.h index 37bd713c4..f6f06c903 100644 --- a/source/LuaItems.h +++ b/source/LuaItems.h @@ -9,7 +9,7 @@ #pragma once -#include "cItem.h" +#include "Item.h" diff --git a/source/MCLogger.cpp b/source/MCLogger.cpp new file mode 100644 index 000000000..daf43f796 --- /dev/null +++ b/source/MCLogger.cpp @@ -0,0 +1,191 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include +#include "Log.h" + + + + + +cMCLogger* cMCLogger::s_MCLogger = 0; + +cMCLogger* cMCLogger::GetInstance() +{ + return s_MCLogger; +} + + + + + +cMCLogger::cMCLogger() +{ + AString FileName; + Printf(FileName, "LOG_%d.txt", (int)time(0) ); + m_Log = new cLog(FileName); + m_Log->Log("--- Started Log ---"); + + s_MCLogger = this; +} + + + + + +cMCLogger::cMCLogger( char* a_File ) +{ + m_Log = new cLog( a_File ); +} + + + + + +cMCLogger::~cMCLogger() +{ + m_Log->Log("--- Stopped Log ---"); + delete m_Log; + if (this == s_MCLogger) + s_MCLogger = NULL; +} + + + + + +void cMCLogger::LogSimple(const char* a_Text, int a_LogType /* = 0 */ ) +{ + switch( a_LogType ) + { + case 0: + LOG("%s", a_Text); + break; + case 1: + LOGINFO("%s", a_Text); + break; + case 2: + LOGWARN("%s", a_Text); + break; + case 3: + LOGERROR("%s", a_Text); + break; + default: + LOG("(#%d#: %s", a_LogType, a_Text); + break; + } +} + + + + + +void cMCLogger::Log(const char* a_Format, va_list a_ArgList) +{ + cCSLock Lock(m_CriticalSection); + SetColor( 0x7 ); // 0x7 is default grey color + m_Log->Log( a_Format, a_ArgList ); + SetColor(0x07); // revert color back +} + + + + + +void cMCLogger::Info(const char* a_Format, va_list a_ArgList) +{ + cCSLock Lock(m_CriticalSection); +// for( int i = 0; i < 16; i++) +// { +// for( int j = 0; j < 16; j++ ) +// { +// SetConsoleTextAttribute( hConsole, i | (j<<4) ); +// printf("0x%x", (i|j<<4)); +// } +// printf("\n"); +// } + + SetColor( 0xe ); // 0xe is yellow + m_Log->Log( a_Format, a_ArgList ); + SetColor(0x07); // revert color back +} + + + + + +void cMCLogger::Warn(const char* a_Format, va_list a_ArgList) +{ + cCSLock Lock(m_CriticalSection); + SetColor( 0xc ); // 0xc is red + m_Log->Log( a_Format, a_ArgList ); + SetColor(0x07); // revert color back +} + + + + + +void cMCLogger::Error(const char* a_Format, va_list a_ArgList) +{ + cCSLock Lock(m_CriticalSection); + SetColor( 0xc0 ); // 0xc0 is red bg and black text + m_Log->Log( a_Format, a_ArgList ); + SetColor(0x07); // revert color back +} + + + + + +void cMCLogger::SetColor( unsigned char a_Color ) +{ +#ifdef _WIN32 + HANDLE hConsole = GetStdHandle( STD_OUTPUT_HANDLE ); + SetConsoleTextAttribute( hConsole, a_Color ); +#else + (void)a_Color; +#endif +} + + + + + +////////////////////////////////////////////////////////////////////////// +// Global functions +void LOG(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + cMCLogger::GetInstance()->Log( a_Format, argList ); + va_end(argList); +} + +void LOGINFO(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + cMCLogger::GetInstance()->Info( a_Format, argList ); + va_end(argList); +} + +void LOGWARN(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + cMCLogger::GetInstance()->Warn( a_Format, argList ); + va_end(argList); +} + +void LOGERROR(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + cMCLogger::GetInstance()->Error( a_Format, argList ); + va_end(argList); +} + + + + diff --git a/source/MCLogger.h b/source/MCLogger.h new file mode 100644 index 000000000..04ba732bf --- /dev/null +++ b/source/MCLogger.h @@ -0,0 +1,60 @@ + +#pragma once + + + + +class cLog; + + + + + +class cMCLogger //tolua_export +{ //tolua_export +public: //tolua_export + cMCLogger(); + cMCLogger( char* a_File ); //tolua_export + ~cMCLogger(); //tolua_export + + void Log(const char* a_Format, va_list a_ArgList); + void Info(const char* a_Format, va_list a_ArgList); + void Warn(const char* a_Format, va_list a_ArgList); + void Error(const char* a_Format, va_list a_ArgList); + + void LogSimple(const char* a_Text, int a_LogType = 0 ); //tolua_export + + static cMCLogger* GetInstance(); +private: + void SetColor( unsigned char a_Color ); + + cCriticalSection m_CriticalSection; + cLog* m_Log; + static cMCLogger* s_MCLogger; +}; //tolua_export + +extern void LOG(const char* a_Format, ...); +extern void LOGINFO(const char* a_Format, ...); +extern void LOGWARN(const char* a_Format, ...); +extern void LOGERROR(const char* a_Format, ...); + + + + + +// In debug builds, translate LOGD to LOG, otherwise leave it out altogether: +#ifdef _DEBUG + #define LOGD LOG +#else + #define LOGD(...) +#endif // _DEBUG + + + + + +#define LOGWARNING LOGWARN + + + + diff --git a/source/ManualBindings.cpp b/source/ManualBindings.cpp index 6e07b0aff..34cf73520 100644 --- a/source/ManualBindings.cpp +++ b/source/ManualBindings.cpp @@ -4,16 +4,16 @@ #include "ManualBindings.h" #include "tolua++.h" -#include "cRoot.h" -#include "cWorld.h" -#include "cPlugin.h" -#include "cPlugin_NewLua.h" -#include "cPluginManager.h" -#include "cLuaCommandBinder.h" -#include "cPlayer.h" -#include "cWebAdmin.h" -#include "cStringMap.h" -#include "cClientHandle.h" +#include "Root.h" +#include "World.h" +#include "Plugin.h" +#include "Plugin_NewLua.h" +#include "PluginManager.h" +#include "LuaCommandBinder.h" +#include "Player.h" +#include "WebAdmin.h" +#include "StringMap.h" +#include "ClientHandle.h" #include "md5/md5.h" diff --git a/source/Mobs/AggressiveMonster.cpp b/source/Mobs/AggressiveMonster.cpp index 2a5370968..d178ce8f8 100644 --- a/source/Mobs/AggressiveMonster.cpp +++ b/source/Mobs/AggressiveMonster.cpp @@ -4,7 +4,7 @@ #include "AggressiveMonster.h" #include "../Vector3f.h" -#include "../cPlayer.h" +#include "../Player.h" #include "../MersenneTwister.h" diff --git a/source/Mobs/Monster.cpp b/source/Mobs/Monster.cpp index 9142bd6a8..1a93f8d75 100644 --- a/source/Mobs/Monster.cpp +++ b/source/Mobs/Monster.cpp @@ -2,20 +2,20 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Monster.h" -#include "../cRoot.h" -#include "../cServer.h" -#include "../cClientHandle.h" -#include "../cWorld.h" -#include "../cPlayer.h" +#include "../Root.h" +#include "../Server.h" +#include "../ClientHandle.h" +#include "../World.h" +#include "../Player.h" #include "../Defines.h" -#include "../cMonsterConfig.h" +#include "../MonsterConfig.h" #include "../MersenneTwister.h" #include "../Vector3f.h" #include "../Vector3i.h" #include "../Vector3d.h" -#include "../cTracer.h" +#include "../Tracer.h" #include "../../iniFile/iniFile.h" /* diff --git a/source/Mobs/Monster.h b/source/Mobs/Monster.h index 0958d182c..f0da23977 100644 --- a/source/Mobs/Monster.h +++ b/source/Mobs/Monster.h @@ -1,11 +1,11 @@ #pragma once -#include "../cPawn.h" +#include "../Pawn.h" #include "../Defines.h" -#include "../cWorld.h" +#include "../World.h" #include "../BlockID.h" -#include "../cItem.h" +#include "../Item.h" diff --git a/source/Mobs/PassiveAggressiveMonster.cpp b/source/Mobs/PassiveAggressiveMonster.cpp index abf37ee59..9d9e156ee 100644 --- a/source/Mobs/PassiveAggressiveMonster.cpp +++ b/source/Mobs/PassiveAggressiveMonster.cpp @@ -3,7 +3,7 @@ #include "PassiveAggressiveMonster.h" -#include "../cPlayer.h" +#include "../Player.h" diff --git a/source/MonsterConfig.cpp b/source/MonsterConfig.cpp new file mode 100644 index 000000000..f4e1d2c70 --- /dev/null +++ b/source/MonsterConfig.cpp @@ -0,0 +1,114 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "MonsterConfig.h" +#include "Mobs/Monster.h" +#include "../iniFile/iniFile.h" +//#include + + + + + +struct cMonsterConfig::sAttributesStruct +{ + AString m_name; + float m_SightDistance; + float m_AttackDamage; + float m_AttackRange; + float m_AttackRate; + int m_MaxHealth; +}; + + + + + +struct cMonsterConfig::sMonsterConfigState +{ + AString MonsterTypes; + std::list< sAttributesStruct > AttributesList; +}; + + + + + +cMonsterConfig::cMonsterConfig(void) + : m_pState( new sMonsterConfigState ) +{ + Initialize(); +} + + + + + +cMonsterConfig::~cMonsterConfig() { + delete m_pState; +} + + + + + +void cMonsterConfig::Initialize() { + + sAttributesStruct Attributes; + cIniFile SettingsIniFile("settings.ini"); + cIniFile MonstersIniFile("monsters.ini"); + + if (!SettingsIniFile.ReadFile() || !MonstersIniFile.ReadFile()) + { + LOGWARNING("cMonsterConfig: Must have both settings.ini and monsters.ini to configure attributes\n\tusing default attributes \n"); + return; + } + + m_pState->MonsterTypes = SettingsIniFile.GetValue("Monsters","Types",""); + + if ( m_pState->MonsterTypes.empty() ) + { + LOGWARNING("cMonsterConfig: No Monster types listed in config file, using default attributes \n"); + return; + } + + AStringVector SplitList = StringSplit(m_pState->MonsterTypes,","); + for (unsigned int i = 0; i < SplitList.size(); ++i) + { + if (!SplitList[i].empty()) + { + Attributes.m_name = SplitList[i].c_str(); + Attributes.m_AttackDamage = (float)MonstersIniFile.GetValueF(SplitList[i].c_str(), "AttackDamage", 0); + Attributes.m_AttackRange = (float)MonstersIniFile.GetValueF(SplitList[i].c_str(), "AttackRange", 0); + Attributes.m_SightDistance = (float)MonstersIniFile.GetValueF(SplitList[i].c_str(), "SightDistance", 0); + Attributes.m_AttackRate = (float)MonstersIniFile.GetValueF(SplitList[i].c_str(), "AttackRate", 0); + Attributes.m_MaxHealth = MonstersIniFile.GetValueI(SplitList[i].c_str(), "MaxHealth", 0); + m_pState->AttributesList.push_front(Attributes); + } + } // for i - SplitList[] +} + + + + + +void cMonsterConfig::AssignAttributes(cMonster *m, const char* n) +{ + std::list::const_iterator itr; + for (itr = m_pState->AttributesList.begin(); itr != m_pState->AttributesList.end(); ++itr) + { + if(itr->m_name.compare(n) == 0) + { + m->SetAttackDamage (itr->m_AttackDamage); + m->SetAttackRange (itr->m_AttackRange); + m->SetSightDistance(itr->m_SightDistance); + m->SetAttackRate ((int)itr->m_AttackRate); + m->SetMaxHealth ((short)itr->m_MaxHealth); + } + } // for itr - m_pState->AttributesList[] +} + + + + + diff --git a/source/MonsterConfig.h b/source/MonsterConfig.h new file mode 100644 index 000000000..d1a514b17 --- /dev/null +++ b/source/MonsterConfig.h @@ -0,0 +1,17 @@ +#pragma once + +class cMonster; +class cMonsterConfig +{ +public: + cMonsterConfig(void); + ~cMonsterConfig(); + + void AssignAttributes(cMonster *m, const char* n); + +private: + struct sAttributesStruct; + struct sMonsterConfigState; + sMonsterConfigState* m_pState; + void Initialize(); +}; \ No newline at end of file diff --git a/source/Noise.cpp b/source/Noise.cpp new file mode 100644 index 000000000..fab46d652 --- /dev/null +++ b/source/Noise.cpp @@ -0,0 +1,377 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Noise.h" +#include + +#if NOISE_USE_SSE +#include //_mm_mul_epi32 +#endif + +#define FAST_FLOOR( x ) ( (x) < 0 ? ((int)x)-1 : ((int)x) ) + + + + + +cNoise::cNoise( unsigned int a_Seed ) + : m_Seed( a_Seed ) +{ +} + + +cNoise::~cNoise() +{ +} + +#if NOISE_USE_SSE +/**************** + * SSE Random value generator + **/ +__m128 cNoise::SSE_IntNoise2D( int a_X1, int a_Y1, int a_X2, int a_Y2, int a_X3, int a_Y3, int a_X4, int a_Y4 ) const +{ + const __m128i X4 = _mm_set_epi32(a_X4, a_X3, a_X2, a_X1); + const __m128i Y4 = _mm_set_epi32(a_Y4, a_Y3, a_Y2, a_Y1); + + const __m128 One4 = _mm_set_ps1( 1.f ); + const __m128i YScale4 = _mm_set1_epi32( 57 ); + + const __m128i i15731 = _mm_set1_epi32( 15731 ); + const __m128i i789221 = _mm_set1_epi32( 789221 ); + const __m128i i1376312589 = _mm_set1_epi32(1376312589); + const __m128i MaskValue4 = _mm_set1_epi32(0x7fffffff); + const __m128 f1073741824 = _mm_set_ps1( 1073741824.0f ); + + const __m128i Seed4 = _mm_mullo_epi32( _mm_mullo_epi32( _mm_set1_epi32( m_Seed ), YScale4 ), YScale4 ); + + const __m128i ScaledY4 = _mm_mullo_epi32( Y4, YScale4 ); + const __m128i n4 = _mm_add_epi32( _mm_add_epi32( X4, ScaledY4 ), Seed4 ); + const __m128i nn4 = _mm_slli_epi32( n4, 13 ); + const __m128i nnn4 = _mm_xor_si128( nn4, n4 ); + + const __m128i StepA4 = _mm_mullo_epi32( nnn4, nnn4 ); + const __m128i StepAA4 = _mm_add_epi32( _mm_mullo_epi32( StepA4, i15731 ), i789221 ); + const __m128i StepB4 = _mm_add_epi32( _mm_mullo_epi32( nnn4, StepAA4 ), i1376312589 ); + const __m128i StepC4 = _mm_and_si128( StepB4, MaskValue4 ); + const __m128 StepD4 = _mm_div_ps( _mm_cvtepi32_ps( StepC4 ), f1073741824 ); + const __m128 Result4 = _mm_sub_ps( One4, StepD4 ); + + return Result4; +} +#endif + + +/*************** + * Interpolated (and 1 smoothed) noise in 1-dimension + **/ +float cNoise::LinearNoise1D( float a_X ) const +{ + int BaseX = FAST_FLOOR( a_X ); + float FracX = (a_X) - BaseX; + return LinearInterpolate( IntNoise( BaseX ), IntNoise( BaseX+1 ), FracX); +} + +float cNoise::CosineNoise1D( float a_X ) const +{ + int BaseX = FAST_FLOOR( a_X ); + float FracX = (a_X) - BaseX; + return CosineInterpolate( IntNoise( BaseX ), IntNoise( BaseX+1 ), FracX); +} + +float cNoise::CubicNoise1D( float a_X ) const +{ + int BaseX = FAST_FLOOR( a_X ); + float FracX = (a_X) - BaseX; + return CubicInterpolate( IntNoise( BaseX-1 ), IntNoise( BaseX ), IntNoise( BaseX+1 ), IntNoise( BaseX+2 ), FracX); +} + +float cNoise::SmoothNoise1D( int a_X ) const +{ + return IntNoise(a_X)/2 + IntNoise(a_X-1)/4 + IntNoise(a_X+1)/4; +} + +/****************** + * Interpolated (and 1 smoothed) noise in 2-dimensions + **/ +float cNoise::LinearNoise2D( float a_X, float a_Y ) const +{ + const int BaseX = FAST_FLOOR( a_X ); + const int BaseY = FAST_FLOOR( a_Y ); + + const float tl = IntNoise2D( BaseX, BaseY ); + const float tr = IntNoise2D( BaseX+1, BaseY ); + const float bl = IntNoise2D( BaseX, BaseY+1 ); + const float br = IntNoise2D( BaseX+1, BaseY+1 ); + + const float FracX = (a_X) - BaseX; + const float interp1 = LinearInterpolate( tl, tr, FracX ); + const float interp2 = LinearInterpolate( bl, br, FracX ); + + const float FracY = (a_Y) - BaseY; + return LinearInterpolate( interp1, interp2, FracY ); +} + +float cNoise::CosineNoise2D( float a_X, float a_Y ) const +{ + const int BaseX = FAST_FLOOR( a_X ); + const int BaseY = FAST_FLOOR( a_Y ); + + const float tl = IntNoise2D( BaseX, BaseY ); + const float tr = IntNoise2D( BaseX+1, BaseY ); + const float bl = IntNoise2D( BaseX, BaseY+1 ); + const float br = IntNoise2D( BaseX+1, BaseY+1 ); + + const float FracX = (a_X) - BaseX; + const float interp1 = CosineInterpolate( tl, tr, FracX ); + const float interp2 = CosineInterpolate( bl, br, FracX ); + + const float FracY = (a_Y) - BaseY; + return CosineInterpolate( interp1, interp2, FracY ); +} + + + + + +float cNoise::CubicNoise2D( float a_X, float a_Y ) const +{ + const int BaseX = FAST_FLOOR( a_X ); + const int BaseY = FAST_FLOOR( a_Y ); + + const float points[4][4] = + { + IntNoise2D( BaseX-1, BaseY-1 ), IntNoise2D( BaseX, BaseY-1 ), IntNoise2D( BaseX+1, BaseY-1 ), IntNoise2D( BaseX+2, BaseY-1 ), + IntNoise2D( BaseX-1, BaseY ), IntNoise2D( BaseX, BaseY ), IntNoise2D( BaseX+1, BaseY ), IntNoise2D( BaseX+2, BaseY ), + IntNoise2D( BaseX-1, BaseY+1 ), IntNoise2D( BaseX, BaseY+1 ), IntNoise2D( BaseX+1, BaseY+1 ), IntNoise2D( BaseX+2, BaseY+1 ), + IntNoise2D( BaseX-1, BaseY+2 ), IntNoise2D( BaseX, BaseY+2 ), IntNoise2D( BaseX+1, BaseY+2 ), IntNoise2D( BaseX+2, BaseY+2 ), + }; + + const float FracX = (a_X) - BaseX; + const float interp1 = CubicInterpolate( points[0][0], points[0][1], points[0][2], points[0][3], FracX ); + const float interp2 = CubicInterpolate( points[1][0], points[1][1], points[1][2], points[1][3], FracX ); + const float interp3 = CubicInterpolate( points[2][0], points[2][1], points[2][2], points[2][3], FracX ); + const float interp4 = CubicInterpolate( points[3][0], points[3][1], points[3][2], points[3][3], FracX ); + + + const float FracY = (a_Y) - BaseY; + return CubicInterpolate( interp1, interp2, interp3, interp4, FracY ); +} + + + + + +#if NOISE_USE_SSE +float cNoise::SSE_CubicNoise2D( float a_X, float a_Y ) const +{ + const int BaseX = FAST_FLOOR( a_X ); + const int BaseY = FAST_FLOOR( a_Y ); + + __m128 points4[4] = { + SSE_IntNoise2D( BaseX-1, BaseY-1, BaseX-1, BaseY, BaseX-1, BaseY+1, BaseX-1, BaseY+2 ), + SSE_IntNoise2D( BaseX, BaseY-1, BaseX, BaseY, BaseX, BaseY+1, BaseX, BaseY+2 ), + SSE_IntNoise2D( BaseX+1, BaseY-1, BaseX+1, BaseY, BaseX+1, BaseY+1, BaseX+1, BaseY+2 ), + SSE_IntNoise2D( BaseX+2, BaseY-1, BaseX+2, BaseY, BaseX+2, BaseY+1, BaseX+2, BaseY+2 ), + }; + + const float FracX = (a_X) - BaseX; + union { __m128 p4; float p[4]; } + AllInterp = { CubicInterpolate4( points4[0], points4[1], points4[2], points4[3], FracX ) }; + + const float FracY = (a_Y) - BaseY; + return CubicInterpolate( AllInterp.p[0], AllInterp.p[1], AllInterp.p[2], AllInterp.p[3], FracY ); +} +#endif + + + + + +/****************** + * Interpolated (and 1 smoothed) noise in 3-dimensions + **/ +float cNoise::CosineNoise3D( float a_X, float a_Y, float a_Z ) const +{ + const int BaseX = FAST_FLOOR( a_X ); + const int BaseY = FAST_FLOOR( a_Y ); + const int BaseZ = FAST_FLOOR( a_Z ); + + const float ftl = IntNoise3D( BaseX, BaseY, BaseZ ); + const float ftr = IntNoise3D( BaseX+1, BaseY, BaseZ ); + const float fbl = IntNoise3D( BaseX, BaseY+1, BaseZ ); + const float fbr = IntNoise3D( BaseX+1, BaseY+1, BaseZ ); + + const float btl = IntNoise3D( BaseX, BaseY, BaseZ+1 ); + const float btr = IntNoise3D( BaseX+1, BaseY, BaseZ+1 ); + const float bbl = IntNoise3D( BaseX, BaseY+1, BaseZ+1 ); + const float bbr = IntNoise3D( BaseX+1, BaseY+1, BaseZ+1 ); + + const float FracX = (a_X) - BaseX; + const float finterp1 = CosineInterpolate( ftl, ftr, FracX ); + const float finterp2 = CosineInterpolate( fbl, fbr, FracX ); + const float binterp1 = CosineInterpolate( btl, btr, FracX ); + const float binterp2 = CosineInterpolate( bbl, bbr, FracX ); + + const float FracY = (a_Y) - BaseY; + const float interp1 = CosineInterpolate( finterp1, finterp2, FracY ); + const float interp2 = CosineInterpolate( binterp1, binterp2, FracY ); + + const float FracZ = (a_Z) - BaseZ; + return CosineInterpolate( interp1, interp2, FracZ ); +} + + + + + +float cNoise::CubicNoise3D( float a_X, float a_Y, float a_Z ) const +{ + const int BaseX = FAST_FLOOR( a_X ); + const int BaseY = FAST_FLOOR( a_Y ); + const int BaseZ = FAST_FLOOR( a_Z ); + + const float points1[4][4] = { + IntNoise3D( BaseX-1, BaseY-1, BaseZ-1 ), IntNoise3D( BaseX, BaseY-1, BaseZ-1 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ-1 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ-1 ), + IntNoise3D( BaseX-1, BaseY, BaseZ-1 ), IntNoise3D( BaseX, BaseY, BaseZ-1 ), IntNoise3D( BaseX+1, BaseY, BaseZ-1 ), IntNoise3D( BaseX+2, BaseY, BaseZ-1 ), + IntNoise3D( BaseX-1, BaseY+1, BaseZ-1 ), IntNoise3D( BaseX, BaseY+1, BaseZ-1 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ-1 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ-1 ), + IntNoise3D( BaseX-1, BaseY+2, BaseZ-1 ), IntNoise3D( BaseX, BaseY+2, BaseZ-1 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ-1 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ-1 ), + }; + + const float FracX = (a_X) - BaseX; + const float x1interp1 = CubicInterpolate( points1[0][0], points1[0][1], points1[0][2], points1[0][3], FracX ); + const float x1interp2 = CubicInterpolate( points1[1][0], points1[1][1], points1[1][2], points1[1][3], FracX ); + const float x1interp3 = CubicInterpolate( points1[2][0], points1[2][1], points1[2][2], points1[2][3], FracX ); + const float x1interp4 = CubicInterpolate( points1[3][0], points1[3][1], points1[3][2], points1[3][3], FracX ); + + const float points2[4][4] = { + IntNoise3D( BaseX-1, BaseY-1, BaseZ ), IntNoise3D( BaseX, BaseY-1, BaseZ ), IntNoise3D( BaseX+1, BaseY-1, BaseZ ), IntNoise3D( BaseX+2, BaseY-1, BaseZ ), + IntNoise3D( BaseX-1, BaseY, BaseZ ), IntNoise3D( BaseX, BaseY, BaseZ ), IntNoise3D( BaseX+1, BaseY, BaseZ ), IntNoise3D( BaseX+2, BaseY, BaseZ ), + IntNoise3D( BaseX-1, BaseY+1, BaseZ ), IntNoise3D( BaseX, BaseY+1, BaseZ ), IntNoise3D( BaseX+1, BaseY+1, BaseZ ), IntNoise3D( BaseX+2, BaseY+1, BaseZ ), + IntNoise3D( BaseX-1, BaseY+2, BaseZ ), IntNoise3D( BaseX, BaseY+2, BaseZ ), IntNoise3D( BaseX+1, BaseY+2, BaseZ ), IntNoise3D( BaseX+2, BaseY+2, BaseZ ), + }; + + const float x2interp1 = CubicInterpolate( points2[0][0], points2[0][1], points2[0][2], points2[0][3], FracX ); + const float x2interp2 = CubicInterpolate( points2[1][0], points2[1][1], points2[1][2], points2[1][3], FracX ); + const float x2interp3 = CubicInterpolate( points2[2][0], points2[2][1], points2[2][2], points2[2][3], FracX ); + const float x2interp4 = CubicInterpolate( points2[3][0], points2[3][1], points2[3][2], points2[3][3], FracX ); + + const float points3[4][4] = { + IntNoise3D( BaseX-1, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ+1 ), + IntNoise3D( BaseX-1, BaseY, BaseZ+1 ), IntNoise3D( BaseX, BaseY, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY, BaseZ+1 ), + IntNoise3D( BaseX-1, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ+1 ), + IntNoise3D( BaseX-1, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ+1 ), + }; + + const float x3interp1 = CubicInterpolate( points3[0][0], points3[0][1], points3[0][2], points3[0][3], FracX ); + const float x3interp2 = CubicInterpolate( points3[1][0], points3[1][1], points3[1][2], points3[1][3], FracX ); + const float x3interp3 = CubicInterpolate( points3[2][0], points3[2][1], points3[2][2], points3[2][3], FracX ); + const float x3interp4 = CubicInterpolate( points3[3][0], points3[3][1], points3[3][2], points3[3][3], FracX ); + + const float points4[4][4] = { + IntNoise3D( BaseX-1, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ+2 ), + IntNoise3D( BaseX-1, BaseY, BaseZ+2 ), IntNoise3D( BaseX, BaseY, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY, BaseZ+2 ), + IntNoise3D( BaseX-1, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ+2 ), + IntNoise3D( BaseX-1, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ+2 ), + }; + + const float x4interp1 = CubicInterpolate( points4[0][0], points4[0][1], points4[0][2], points4[0][3], FracX ); + const float x4interp2 = CubicInterpolate( points4[1][0], points4[1][1], points4[1][2], points4[1][3], FracX ); + const float x4interp3 = CubicInterpolate( points4[2][0], points4[2][1], points4[2][2], points4[2][3], FracX ); + const float x4interp4 = CubicInterpolate( points4[3][0], points4[3][1], points4[3][2], points4[3][3], FracX ); + + const float FracY = (a_Y) - BaseY; + const float yinterp1 = CubicInterpolate( x1interp1, x1interp2, x1interp3, x1interp4, FracY ); + const float yinterp2 = CubicInterpolate( x2interp1, x2interp2, x2interp3, x2interp4, FracY ); + const float yinterp3 = CubicInterpolate( x3interp1, x3interp2, x3interp3, x3interp4, FracY ); + const float yinterp4 = CubicInterpolate( x4interp1, x4interp2, x4interp3, x4interp4, FracY ); + + const float FracZ = (a_Z) - BaseZ; + return CubicInterpolate( yinterp1, yinterp2, yinterp3, yinterp4, FracZ ); +} + + + + + +/****************** + * Private + **/ + +#if NOISE_USE_SSE +__m128 cNoise::CubicInterpolate4( const __m128 & a_A, const __m128 & a_B, const __m128 & a_C, const __m128 & a_D, float a_Pct ) const +{ + const __m128 P = _mm_sub_ps( _mm_sub_ps( a_D, a_C ), _mm_sub_ps( a_A, a_B ) ); + const __m128 Q = _mm_sub_ps( _mm_sub_ps( a_A, a_B ), P ); + const __m128 R = _mm_sub_ps( a_C, a_A ); + + const __m128 Pct = _mm_set_ps1( a_Pct ); + const __m128 Pct2 = _mm_mul_ps( Pct, Pct ); + const __m128 Pct3 = _mm_mul_ps( Pct2, Pct ); + + return _mm_add_ps( _mm_add_ps( _mm_add_ps( _mm_mul_ps(P, Pct3), _mm_mul_ps( Q, Pct2 ) ), _mm_mul_ps( R, Pct ) ), a_B ); +} +#endif + + + + + +void IntArrayLinearInterpolate2D( + int * a_Array, + int a_SizeX, int a_SizeY, // Dimensions of the array + int a_AnchorStepX, int a_AnchorStepY // Distances between the anchor points in each direction +) +{ + // First interpolate columns where the anchor points are: + int LastYCell = a_SizeY - a_AnchorStepY; + for (int y = 0; y < LastYCell; y += a_AnchorStepY) + { + int Idx = a_SizeX * y; + for (int x = 0; x < a_SizeX; x += a_AnchorStepX) + { + int StartValue = a_Array[Idx]; + int EndValue = a_Array[Idx + a_SizeX * a_AnchorStepY]; + int Diff = EndValue - StartValue; + for (int CellY = 1; CellY < a_AnchorStepY; CellY++) + { + a_Array[Idx + a_SizeX * CellY] = StartValue + CellY * Diff / a_AnchorStepY; + } // for CellY + Idx += a_AnchorStepX; + } // for x + } // for y + + // Now interpolate in rows, each row has values in the anchor columns + int LastXCell = a_SizeX - a_AnchorStepX; + for (int y = 0; y < a_SizeY; y++) + { + int Idx = a_SizeX * y; + for (int x = 0; x < LastXCell; x += a_AnchorStepX) + { + int StartValue = a_Array[Idx]; + int EndValue = a_Array[Idx + a_AnchorStepX]; + int Diff = EndValue - StartValue; + for (int CellX = 1; CellX < a_AnchorStepX; CellX++) + { + a_Array[Idx + CellX] = StartValue + CellX * Diff / a_AnchorStepX; + } // for CellY + Idx += a_AnchorStepX; + } + } +} + + + + + + + + + + +#if NOISE_USE_INLINE + #include "Noise.inc" +#endif + + + + diff --git a/source/Noise.h b/source/Noise.h new file mode 100644 index 000000000..68428d33e --- /dev/null +++ b/source/Noise.h @@ -0,0 +1,94 @@ +#pragma once + +// Some settings +#define NOISE_USE_INLINE 1 +#define NOISE_USE_SSE 0 + +// Do not touch +#if NOISE_USE_INLINE + #ifdef _MSC_VER + #define __NOISE_INLINE__ __forceinline + #else + #define __NOISE_INLINE__ inline + #endif // _MSC_VER +#else + #define __NOISE_INLINE__ +#endif + +#if NOISE_USE_SSE +# include +#endif + + + + + +class cNoise +{ +public: + cNoise( unsigned int a_Seed ); + ~cNoise(); + +#if NOISE_USE_SSE + __m128 SSE_IntNoise2D( int a_X1, int a_Y1, int a_X2, int a_Y2, int a_X3, int a_Y3, int a_X4, int a_Y4 ) const; +#endif + + __NOISE_INLINE__ float IntNoise( int a_X ) const; + __NOISE_INLINE__ float IntNoise2D( int a_X, int a_Y ) const; + __NOISE_INLINE__ float IntNoise3D( int a_X, int a_Y, int a_Z ) const; + + // Note: These functions have a mod8-irregular chance - each of the mod8 remainders has different chance of occurrence. Divide by 8 to rectify. + __NOISE_INLINE__ int IntNoise1DInt( int a_X ) const; + __NOISE_INLINE__ int IntNoise2DInt( int a_X, int a_Y ) const; + __NOISE_INLINE__ int IntNoise3DInt( int a_X, int a_Y, int a_Z ) const; + + float LinearNoise1D( float a_X ) const; + float CosineNoise1D( float a_X ) const; + float CubicNoise1D( float a_X ) const; + float SmoothNoise1D( int a_X ) const; + + float LinearNoise2D( float a_X, float a_Y ) const; + float CosineNoise2D( float a_X, float a_Y ) const; + float CubicNoise2D( float a_X, float a_Y ) const; + float SSE_CubicNoise2D( float a_X, float a_Y ) const; + + float CosineNoise3D( float a_X, float a_Y, float a_Z ) const; + float CubicNoise3D( float a_X, float a_Y, float a_Z ) const; + + void SetSeed( unsigned int a_Seed ) { m_Seed = a_Seed; } + + __NOISE_INLINE__ static float CubicInterpolate( float a_A, float a_B, float a_C, float a_D, float a_Pct ); + __NOISE_INLINE__ static float CosineInterpolate( float a_A, float a_B, float a_Pct ); + __NOISE_INLINE__ static float LinearInterpolate( float a_A, float a_B, float a_Pct ); + +private: + +#if NOISE_USE_SSE + __m128 CubicInterpolate4( const __m128 & a_A, const __m128 & a_B, const __m128 & a_C, const __m128 & a_D, float a_Pct ) const; +#endif + + unsigned int m_Seed; +}; + + + + + +/// Linearly interpolates values in the array between the anchor points +extern void IntArrayLinearInterpolate2D( + int * a_Array, + int a_SizeX, int a_SizeY, // Dimensions of the array + int a_AnchorStepX, int a_AnchorStepY // Distances between the anchor points in each direction +); + + + + + +#if NOISE_USE_INLINE + #include "Noise.inc" +#endif + + + + diff --git a/source/Noise.inc b/source/Noise.inc new file mode 100644 index 000000000..60fd2f394 --- /dev/null +++ b/source/Noise.inc @@ -0,0 +1,124 @@ + +#ifndef __C_NOISE_INC__ +#define __C_NOISE_INC__ + +#include + + + + + +/**************** + * Random value generator + **/ + +float cNoise::IntNoise( int a_X ) const +{ + int x = ((a_X*m_Seed)<<13) ^ a_X; + return ( 1.0f - ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f); + // returns a float number in the range of [-1, 1] +} + + + + + +float cNoise::IntNoise2D( int a_X, int a_Y ) const +{ + int n = a_X + a_Y * 57 + m_Seed*57*57; + n = (n<<13) ^ n; + return ( 1.0f - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f); + // returns a float number in the range of [-1, 1] +} + + + + + +float cNoise::IntNoise3D( int a_X, int a_Y, int a_Z ) const +{ + int n = a_X + a_Y * 57 + a_Z * 57*57 + m_Seed*57*57*57; + n = (n<<13) ^ n; + return ( 1.0f - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f); + // returns a float number in the range of [-1, 1] +} + + + + + +int cNoise::IntNoise1DInt( int a_X ) const +{ + int x = ((a_X*m_Seed)<<13) ^ a_X; + return ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff); +} + + + + + +int cNoise::IntNoise2DInt( int a_X, int a_Y ) const +{ + int n = a_X + a_Y * 57 + m_Seed*57*57; + n = (n<<13) ^ n; + return ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); +} + + + + + +int cNoise::IntNoise3DInt( int a_X, int a_Y, int a_Z ) const +{ + int n = a_X + a_Y * 57 + a_Z * 57*57 + m_Seed*57*57*57; + n = (n<<13) ^ n; + return ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); +} + + + + + +/**************** + * Interpolation functions + **/ + +float cNoise::CubicInterpolate( float a_A, float a_B, float a_C, float a_D, float a_Pct ) +{ + float P = (a_D - a_C) - (a_A - a_B); + float Q = (a_A - a_B) - P; + float R = a_C - a_A; + float S = a_B; + + return ((P * a_Pct + Q) * a_Pct + R) * a_Pct + S; +} + + + + + +float cNoise::CosineInterpolate( float a_A, float a_B, float a_Pct ) +{ + const float ft = a_Pct * 3.1415927f; + const float f = (1.f - cosf(ft)) * 0.5f; + return a_A*(1-f) + a_B*f; +} + + + + + +float cNoise::LinearInterpolate( float a_A, float a_B, float a_Pct ) +{ + return a_A*(1.f-a_Pct) + a_B*a_Pct; +} + + + + + +#endif + + + + diff --git a/source/NoteEntity.cpp b/source/NoteEntity.cpp new file mode 100644 index 000000000..6dc32ddd5 --- /dev/null +++ b/source/NoteEntity.cpp @@ -0,0 +1,159 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "NoteEntity.h" +#include "World.h" +#include + + +cNoteEntity::cNoteEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) + : cBlockEntity(E_BLOCK_NOTE_BLOCK, a_BlockX, a_BlockY, a_BlockZ, a_World) + , m_Pitch( 0 ) +{ +} + + + + + +cNoteEntity::~cNoteEntity() +{ +} + + + + + +void cNoteEntity::UsedBy( cPlayer * a_Player ) +{ + IncrementPitch(); + MakeSound(); +} + + + + + +void cNoteEntity::MakeSound( void ) +{ + char instrument; + AString sampleName; + + switch (m_World->GetBlock(m_PosX, m_PosY - 1, m_PosZ)) + { + case E_BLOCK_PLANKS: + case E_BLOCK_LOG: + case E_BLOCK_NOTE_BLOCK: + { + // TODO: add other wood-based blocks if needed + instrument = E_INST_DOUBLE_BASS; + sampleName = "note.db"; + break; + } + + case E_BLOCK_SAND: + case E_BLOCK_GRAVEL: + case E_BLOCK_SOULSAND: + { + instrument = E_INST_SNARE_DRUM; + sampleName = "note.snare"; + break; + } + + case E_BLOCK_GLASS: + case E_BLOCK_GLASS_PANE: + case E_BLOCK_GLOWSTONE: + { + instrument = E_INST_CLICKS; + sampleName = "note.hat"; + break; + } + + case E_BLOCK_STONE: + case E_BLOCK_STONE_BRICKS: + case E_BLOCK_COBBLESTONE: + case E_BLOCK_OBSIDIAN: + case E_BLOCK_NETHERRACK: + case E_BLOCK_BRICK: + case E_BLOCK_NETHER_BRICK: + { + // TODO: add other stone-based blocks if needed + instrument = E_INST_BASS_DRUM; + sampleName = "note.bassattack"; + break; + } + + default: + { + instrument = E_INST_HARP_PIANO; + sampleName = "note.harp"; + break; + } + } + + m_World->BroadcastBlockAction(m_PosX, m_PosY, m_PosZ, instrument, m_Pitch, E_BLOCK_NOTE_BLOCK); + + // TODO: instead of calculating the power function over and over, make a precalculated table - there's only 24 pitches after all + float calcPitch = pow(2.0f, ((float)m_Pitch - 12.0f) / 12.0f); + m_World->BroadcastSoundEffect(sampleName, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 3.0f, calcPitch); +} + + + + + +char cNoteEntity::GetPitch( void ) +{ + return m_Pitch; +} + + + + + +void cNoteEntity::SetPitch( char a_Pitch ) +{ + m_Pitch = a_Pitch % 25; +} + + + + + +void cNoteEntity::IncrementPitch( void ) +{ + SetPitch( m_Pitch + 1 ); +} + + + + + +bool cNoteEntity::LoadFromJson( const Json::Value & a_Value ) +{ + + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + m_Pitch = (char)a_Value.get("p", 0).asInt(); + + return true; +} + + + + + +void cNoteEntity::SaveToJson( Json::Value & a_Value ) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + a_Value["p"] = m_Pitch; +} + + + + diff --git a/source/NoteEntity.h b/source/NoteEntity.h new file mode 100644 index 000000000..b78437f6f --- /dev/null +++ b/source/NoteEntity.h @@ -0,0 +1,52 @@ + +#pragma once + +#include "BlockEntity.h" + + +namespace Json +{ + class Value; +} + + + + + +enum ENUM_NOTE_INSTRUMENTS +{ + E_INST_HARP_PIANO = 0, + E_INST_DOUBLE_BASS = 1, + E_INST_SNARE_DRUM = 2, + E_INST_CLICKS = 3, + E_INST_BASS_DRUM = 4 +}; + + + + + +class cNoteEntity : + public cBlockEntity +{ +public: + cNoteEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); + virtual ~cNoteEntity(); + + bool LoadFromJson( const Json::Value& a_Value ); + virtual void SaveToJson( Json::Value& a_Value ) override; + + char GetPitch( void ); + void SetPitch( char a_Pitch ); + void IncrementPitch( void ); + void MakeSound( void ); + virtual void UsedBy( cPlayer * a_Player ) override; + virtual void SendTo(cClientHandle & a_Client) override { }; + +private: + unsigned char m_Pitch; +}; + + + + diff --git a/source/Pawn.cpp b/source/Pawn.cpp new file mode 100644 index 000000000..ae8133420 --- /dev/null +++ b/source/Pawn.cpp @@ -0,0 +1,230 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Pawn.h" +#include "Root.h" +#include "Server.h" +#include "World.h" +#include "Player.h" +#include "PluginManager.h" +#include "Vector3d.h" +#include "BlockID.h" +#include "Defines.h" + + + + + +CLASS_DEFINITION( cPawn, cEntity ) + + + + + +cPawn::cPawn() + : cEntity( 0, 0, 0 ) + , m_LastPosX( 0.0 ) + , m_LastPosY( 0.0 ) + , m_LastPosZ( 0.0 ) + , m_TimeLastTeleportPacket( 0.f ) + , m_bBurnable(true) + , m_MetaData(NORMAL) +{ + SetMaxHealth(20); +} + + + + + +cPawn::~cPawn() +{ + +} + + + + + +void cPawn::Heal( int a_Health ) +{ + (void)a_Health; +} + + + + + +void cPawn::TakeDamage(int a_Damage, cEntity * a_Instigator) +{ + TakeDamageInfo TDI; + TDI.Damage = a_Damage; + TDI.Instigator = a_Instigator; + cRoot::Get()->GetPluginManager()->CallHook( cPluginManager::E_PLUGIN_TAKE_DAMAGE, 2, this, &TDI); + + if (TDI.Damage == 0) + { + return; + } + if (m_Health <= 0) + { + // Can't take damage if already dead + return; + } + + m_Health -= (short)TDI.Damage; + if (m_Health < 0) + { + m_Health = 0; + } + + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_HURT); + + if (m_Health <= 0) + { + KilledBy(TDI.Instigator); + } +} + + + + + +void cPawn::KilledBy(cEntity * a_Killer) +{ + m_Health = 0; + + if( cRoot::Get()->GetPluginManager()->CallHook( cPluginManager::E_PLUGIN_KILLED, 2, this, a_Killer ) ) + { + return; // Give plugins a chance to 'unkill' the pawn. + } + + m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_DEAD); +} + + + + + +void cPawn::TeleportToEntity(cEntity * a_Entity) +{ + TeleportTo(a_Entity->GetPosX(), a_Entity->GetPosY(), a_Entity->GetPosZ()); +} + + + + + +void cPawn::TeleportTo( const double & a_PosX, const double & a_PosY, const double & a_PosZ ) +{ + SetPosition( a_PosX, a_PosY, a_PosZ ); + + GetWorld()->BroadcastTeleportEntity(*this); +} + + + + + +void cPawn::Tick(float a_Dt) +{ + CheckMetaDataBurn(); // Check to see if pawn should burn based on block they are on + + if (GetMetaData() == BURNING) + { + InStateBurning(a_Dt); + } +} + + + + + + +void cPawn::SetMetaData(MetaData a_MetaData) +{ + //Broadcast new status to clients in the chunk + m_MetaData = a_MetaData; + m_World->BroadcastMetadata(*this); +} + + + + + +//----Change Entity MetaData +void cPawn::CheckMetaDataBurn() +{ + char Block = GetWorld()->GetBlock((int) m_Pos.x, (int) m_Pos.y, (int) m_Pos.z); + char BlockAbove = GetWorld()->GetBlock((int) m_Pos.x, (int) m_Pos.y + 1, (int) m_Pos.z); + char BlockBelow = GetWorld()->GetBlock((int) m_Pos.x, (int) m_Pos.y - 1, (int) m_Pos.z); + + if ( + (GetMetaData() == BURNING) && + (IsBlockWater(Block) || IsBlockWater(BlockAbove) || IsBlockWater(BlockBelow)) + ) + { + SetMetaData(NORMAL); + } + else if ( + m_bBurnable && + (GetMetaData() != BURNING) && + ( + IsBlockLava(Block) || (Block == E_BLOCK_FIRE) || + IsBlockLava(BlockAbove) || (BlockAbove == E_BLOCK_FIRE) || + IsBlockLava(BlockBelow) || (BlockBelow == E_BLOCK_FIRE) + ) + ) + { + SetMetaData(BURNING); + } +} + + + + + +//What to do if On fire +void cPawn::InStateBurning(float a_Dt) +{ + m_FireDamageInterval += a_Dt; + char Block = GetWorld()->GetBlock( (int)m_Pos.x, (int)m_Pos.y, (int)m_Pos.z ); + char BlockAbove = GetWorld()->GetBlock( (int)m_Pos.x, (int)m_Pos.y + 1, (int)m_Pos.z ); + if (m_FireDamageInterval > 800) + { + + m_FireDamageInterval = 0; + TakeDamage(1, this); + + m_BurnPeriod++; + if (IsBlockLava(Block) || Block == E_BLOCK_FIRE + || IsBlockLava(BlockAbove) || BlockAbove == E_BLOCK_FIRE) + { + m_BurnPeriod = 0; + TakeDamage(6, this); + } + else + { + TakeDamage(1, this); + } + + if (m_BurnPeriod > 7) + { + SetMetaData(NORMAL); + m_BurnPeriod = 0; + } + } +} + + + + + +void cPawn::SetMaxHealth(short a_MaxHealth) +{ + this->m_MaxHealth = a_MaxHealth; + + //Reset health + m_Health = a_MaxHealth; +} + diff --git a/source/Pawn.h b/source/Pawn.h new file mode 100644 index 000000000..9cc106cc1 --- /dev/null +++ b/source/Pawn.h @@ -0,0 +1,66 @@ + +#pragma once + +#include "Entity.h" + + + + + +struct TakeDamageInfo //tolua_export +{ //tolua_export + int Damage; //tolua_export + cEntity* Instigator; //tolua_export +}; //tolua_export + + + + + +class cPawn : public cEntity //tolua_export +{ //tolua_export +public: + CLASS_PROTOTYPE() + + cPawn(); + virtual ~cPawn(); + + virtual void TeleportToEntity( cEntity* a_Entity ); //tolua_export + virtual void TeleportTo( const double & a_PosX, const double & a_PosY, const double & a_PosZ ); //tolua_export + + virtual void Tick(float a_Dt) override; + + void Heal( int a_Health ); //tolua_export + virtual void TakeDamage( int a_Damage, cEntity* a_Instigator ); //tolua_export + virtual void KilledBy( cEntity* a_Killer ); //tolua_export + int GetHealth() { return m_Health; } //tolua_export + + enum MetaData {NORMAL, BURNING, CROUCHED, RIDING, SPRINTING, EATING, BLOCKING}; + + virtual void SetMetaData(MetaData a_MetaData); + virtual MetaData GetMetaData(void) const { return m_MetaData; } + + virtual void InStateBurning(float a_Dt); + + virtual void CheckMetaDataBurn(); + + virtual void SetMaxHealth(short a_MaxHealth); + virtual short GetMaxHealth() { return m_MaxHealth; } + +protected: + + short m_Health; + short m_MaxHealth; + + + bool m_bBurnable; + + MetaData m_MetaData; + + double m_LastPosX, m_LastPosY, m_LastPosZ; + float m_TimeLastTeleportPacket; +}; //tolua_export + + + + diff --git a/source/Pickup.cpp b/source/Pickup.cpp new file mode 100644 index 000000000..8a32a328b --- /dev/null +++ b/source/Pickup.cpp @@ -0,0 +1,275 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#ifndef _WIN32 +#include +#endif + +#include "Pickup.h" +#include "ClientHandle.h" +#include "Inventory.h" +#include "World.h" +#include "WaterSimulator.h" +#include "Server.h" +#include "Player.h" +#include "PluginManager.h" +#include "Item.h" +#include "Root.h" +#include "Tracer.h" + +#include "Vector3d.h" +#include "Vector3f.h" + + + + + +CLASS_DEFINITION( cPickup, cEntity ) + + + + + +cPickup::cPickup(int a_X, int a_Y, int a_Z, const cItem & a_Item, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */) + : cEntity( ((double)(a_X))/32, ((double)(a_Y))/32, ((double)(a_Z))/32 ) + , m_Speed( a_SpeedX, a_SpeedY, a_SpeedZ ) + , m_bOnGround( false ) + , m_bReplicated( false ) + , m_Timer( 0.f ) + , m_Item( new cItem( a_Item ) ) + , m_bCollected( false ) +{ + // LOGD("New pickup: ID(%i) Amount(%i) Health(%i)", m_Item.m_ItemID, m_Item.m_ItemCount, m_Item.m_ItemHealth ); + + m_EntityType = eEntityType_Pickup; +} + + + + + +cPickup::~cPickup() +{ + delete m_Item; +} + + + + + +void cPickup::Initialize(cWorld * a_World) +{ + super::Initialize(a_World); + a_World->BroadcastSpawn(*this); +} + + + + + +void cPickup::SpawnOn(cClientHandle & a_Client) +{ + a_Client.SendPickupSpawn(*this); +} + + + + + +void cPickup::Tick(float a_Dt) +{ + m_Timer += a_Dt; + a_Dt = a_Dt / 1000.f; + if(m_bCollected) + { + if(m_Timer > 500.f) // 0.5 second + { + Destroy(); + return; + } + } + + if( m_Timer > 1000*60*5 ) // 5 minutes + { + Destroy(); + return; + } + + if( m_Pos.y < 0 ) // Out of this world! + { + Destroy(); + return; + } + + if (!m_bCollected) + { + HandlePhysics(a_Dt); + } + + if (!m_bReplicated || m_bDirtyPosition) + { + MoveToCorrectChunk(); + m_bReplicated = true; + m_bDirtyPosition = false; + GetWorld()->BroadcastTeleportEntity(*this); + } +} + + + + + +void cPickup::HandlePhysics(float a_Dt) +{ + m_ResultingSpeed.Set(0.f, 0.f, 0.f); + cWorld * World = GetWorld(); + + if( m_bOnGround ) // check if it's still on the ground + { + int BlockX = (m_Pos.x)<0 ? (int)m_Pos.x-1 : (int)m_Pos.x; + int BlockZ = (m_Pos.z)<0 ? (int)m_Pos.z-1 : (int)m_Pos.z; + char BlockBelow = World->GetBlock( BlockX, (int)m_Pos.y -1, BlockZ ); + //Not only air, falls through water ;) + if( BlockBelow == E_BLOCK_AIR || IsBlockWater(BlockBelow)) + { + m_bOnGround = false; + } + char Block = World->GetBlock( BlockX, (int)m_Pos.y - (int)m_bOnGround, BlockZ ); + char BlockIn = World->GetBlock( BlockX, (int)m_Pos.y, BlockZ ); + + if( IsBlockLava(Block) || Block == E_BLOCK_FIRE + || IsBlockLava(BlockIn) || BlockIn == E_BLOCK_FIRE) + { + m_bCollected = true; + m_Timer = 0; + return; + } + + if( BlockIn != E_BLOCK_AIR && !IsBlockWater(BlockIn) ) // If in ground itself, push it out + { + m_bOnGround = true; + m_Pos.y += 0.2; + m_bReplicated = false; + } + m_Speed.x *= 0.7f/(1+a_Dt); + if( fabs(m_Speed.x) < 0.05 ) m_Speed.x = 0; + m_Speed.z *= 0.7f/(1+a_Dt); + if( fabs(m_Speed.z) < 0.05 ) m_Speed.z = 0; + } + + + //get flowing direction + Direction WaterDir = World->GetWaterSimulator()->GetFlowingDirection((int) m_Pos.x - 1, (int) m_Pos.y, (int) m_Pos.z - 1); + + + m_WaterSpeed *= 0.9f; //Keep old speed but lower it + + switch(WaterDir) + { + case X_PLUS: + m_WaterSpeed.x = 1.f; + m_bOnGround = false; + break; + case X_MINUS: + m_WaterSpeed.x = -1.f; + m_bOnGround = false; + break; + case Z_PLUS: + m_WaterSpeed.z = 1.f; + m_bOnGround = false; + break; + case Z_MINUS: + m_WaterSpeed.z = -1.f; + m_bOnGround = false; + break; + + default: + break; + } + + m_ResultingSpeed += m_WaterSpeed; + + + if( !m_bOnGround ) + { + + float Gravity = -9.81f*a_Dt; + m_Speed.y += Gravity; + + // Set to hit position + m_ResultingSpeed += m_Speed; + + cTracer Tracer( GetWorld() ); + int Ret = Tracer.Trace( m_Pos, m_Speed, 2 ); + if( Ret ) // Oh noez! we hit something + { + + + if( (Tracer.RealHit - Vector3f(m_Pos)).SqrLength() <= ( m_ResultingSpeed * a_Dt ).SqrLength() ) + { + m_bReplicated = false; // It's only interesting to replicate when we actually hit something... + if( Ret == 1 ) + { + + if( Tracer.HitNormal.x != 0.f ) m_Speed.x = 0.f; + if( Tracer.HitNormal.y != 0.f ) m_Speed.y = 0.f; + if( Tracer.HitNormal.z != 0.f ) m_Speed.z = 0.f; + + if( Tracer.HitNormal.y > 0 ) // means on ground + { + m_bOnGround = true; + } + } + m_Pos = Tracer.RealHit; + m_Pos += Tracer.HitNormal * 0.2f; + + } + else + m_Pos += m_ResultingSpeed*a_Dt; + } + else + { // We didn't hit anything, so move =] + m_Pos += m_ResultingSpeed * a_Dt; + } + } + //Usable for debugging + //SetPosition(m_Pos.x, m_Pos.y, m_Pos.z); +} + + + + + +bool cPickup::CollectedBy( cPlayer* a_Dest ) +{ + if (m_bCollected) + { + return false; // It's already collected! + } + + // 800 is to long + if (m_Timer < 500.f) + { + return false; // Not old enough + } + + if (cRoot::Get()->GetPluginManager()->CallHookCollectPickup(a_Dest, *this)) + { + return false; + } + + if (a_Dest->GetInventory().AddItem(*m_Item)) + { + m_World->BroadcastCollectPickup(*this, *a_Dest); + + m_bCollected = true; + m_Timer = 0; + return true; + } + + return false; +} + + + + diff --git a/source/Pickup.h b/source/Pickup.h new file mode 100644 index 000000000..56a34c6a6 --- /dev/null +++ b/source/Pickup.h @@ -0,0 +1,63 @@ + +#pragma once + +#include "Entity.h" + + + + +class cPlayer; +class cItem; + + + + + +// tolua_begin +class cPickup : + public cEntity +{ + // tolua_end + typedef cEntity super; + // tolua_begin + +public: + // tolua_end + CLASS_PROTOTYPE(); + + cPickup(int a_X, int a_Y, int a_Z, const cItem & a_Item, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); //tolua_export + ~cPickup(); //tolua_export + + virtual void Initialize(cWorld * a_World) override; + + cItem * GetItem(void) {return m_Item; } //tolua_export + const cItem * GetItem(void) const {return m_Item; } + + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + + virtual bool CollectedBy( cPlayer* a_Dest ); //tolua_export + + void Tick(float a_Dt); + void HandlePhysics(float a_Dt); + + const Vector3f & GetSpeed(void) const {return m_Speed; } + +private: + + Vector3f m_Speed; + Vector3f m_ResultingSpeed; //Can be used to modify the resulting speed for the current tick ;) + + Vector3f m_WaterSpeed; + bool m_bOnGround; + bool m_bReplicated; + + float m_Timer; + + cItem* m_Item; + + bool m_bCollected; +}; //tolua_export + + + + diff --git a/source/Piston.cpp b/source/Piston.cpp new file mode 100644 index 000000000..a4c014729 --- /dev/null +++ b/source/Piston.cpp @@ -0,0 +1,152 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Piston.h" +#include "Redstone.h" +#include "ChunkDef.h" +#include "Pickup.h" +#include "Item.h" +#include "Root.h" +#include "ClientHandle.h" +#include "World.h" +#include "BlockID.h" +#include "Server.h" +#include "blocks/BlockHandler.h" + +extern bool g_BlockPistonBreakable[]; + +#define AddDir( x, y, z, dir, amount ) switch(dir) { case 0: (y)-=(amount); break; case 1: (y)+=(amount); break;\ + case 2: (z)-=(amount); break; case 3: (z)+=(amount); break;\ + case 4: (x)-=(amount); break; case 5: (x)+=(amount); break; } + + + + + +cPiston::cPiston( cWorld* a_World ) + :m_World ( a_World ) +{ + +} + +unsigned short cPiston::FirstPassthroughBlock( int pistonX, int pistonY, int pistonZ, char pistonmeta ) { + unsigned short ret; + pistonmeta &= 7; + if(pistonmeta >= 6) { // just in case, it shouldn't happen but if it would, it'd case inf loop + printf("cPiston::FirstPassthroughBlock - piston has invalid meta data!\n"); + return 9001; + } + char currBlock; + for( ret = 0; ret < 24; ret++ ) { // push up to 24 blocks + AddDir( pistonX, pistonY, pistonZ, pistonmeta, 1 ) + currBlock = m_World->GetBlock( pistonX, pistonY, pistonZ ); + if(currBlock == E_BLOCK_BEDROCK || currBlock == E_BLOCK_OBSIDIAN || currBlock == E_BLOCK_PISTON_EXTENSION ) {return 9001;} + if(g_BlockPistonBreakable[currBlock]) {return ret;} + } + return 9001; +} + + + + + +void cPiston::ExtendPiston( int pistx, int pisty, int pistz ) +{ + char pistonBlock = m_World->GetBlock( pistx, pisty, pistz ); + char pistonMeta = m_World->GetBlockMeta( pistx, pisty, pistz ); + char isSticky = (char)(pistonBlock == E_BLOCK_STICKY_PISTON) * 8; + bool recalc = false; + if ( (pistonMeta & 0x8) == 0x0 ) // only extend if piston is not already extended + { + unsigned short dist = FirstPassthroughBlock(pistx, pisty, pistz, pistonMeta); + if (dist > 9000) return; // too many blocks + + AddDir( pistx, pisty, pistz, pistonMeta & 7, dist+1 ) + BLOCKTYPE currBlock = m_World->GetBlock (pistx, pisty, pistz); + NIBBLETYPE currMeta = m_World->GetBlockMeta(pistx, pisty, pistz); + if (currBlock != E_BLOCK_AIR) + { + cBlockHandler * Handler = BlockHandler(currBlock); + if(Handler->DropOnUnsuitable()) + { + Handler->DropBlock(m_World, pistx, pisty, pistz); + } + recalc = true; + } + int oldx = pistx, oldy = pisty, oldz = pistz; + char currBlockMeta; + for (int i = dist + 1; i>0; i--) + { + AddDir(pistx, pisty, pistz, pistonMeta & 7, -1) + currBlock = m_World->GetBlock(pistx, pisty, pistz); + currBlockMeta = m_World->GetBlockMeta(pistx, pisty, pistz); + m_World->SetBlock( oldx, oldy, oldz, currBlock, currBlockMeta); + oldx = pistx; + oldy = pisty; + oldz = pistz; + } + m_World->BroadcastBlockAction(pistx, pisty, pistz, 0, pistonMeta, E_BLOCK_PISTON); + m_World->FastSetBlock( pistx, pisty, pistz, pistonBlock, pistonMeta | 0x8 ); + + int extx = pistx; + int exty = pisty; + int extz = pistz; + + AddDir(extx, exty, extz, pistonMeta & 7, 1) + + m_World->SetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, isSticky + pistonMeta & 7); + + if (recalc) + { + cRedstone Redstone(m_World); + Redstone.ChangeRedstone(extx, exty, extz, false); // recalculate redstone around current device + Redstone.ChangeRedstone(pistx, pisty, pistz, false); // recalculate redstone around current device + } + } +} + + + + + +void cPiston::RetractPiston( int pistx, int pisty, int pistz ) +{ + char pistonBlock = m_World->GetBlock( pistx, pisty, pistz ); + char pistonMeta = m_World->GetBlockMeta( pistx, pisty, pistz ); + if (pistonMeta <= 6) // only retract if piston is not already retracted + { + return; + } + m_World->BroadcastBlockAction(pistx, pisty, pistz, 1, pistonMeta & ~(8), E_BLOCK_PISTON); + m_World->FastSetBlock(pistx, pisty, pistz, pistonBlock, pistonMeta & ~(8)); + + AddDir(pistx, pisty, pistz, pistonMeta & 7, 1) + if (m_World->GetBlock(pistx, pisty, pistz) == E_BLOCK_PISTON_EXTENSION) + { + if (pistonBlock == E_BLOCK_STICKY_PISTON) + { + int tempx = pistx, tempy = pisty, tempz = pistz; + AddDir( tempx, tempy, tempz, pistonMeta & 7, 1 ) + char tempblock = m_World->GetBlock( tempx, tempy, tempz ); + if ( + (tempblock == E_BLOCK_OBSIDIAN) || + (tempblock == E_BLOCK_BEDROCK) || + (tempblock == E_BLOCK_PISTON_EXTENSION) + ) + { + // These cannot be moved by the sticky piston, bail out + return; + } + m_World->SetBlock( pistx, pisty, pistz, tempblock, m_World->GetBlockMeta( tempx, tempy, tempz ) ); + m_World->SetBlock( tempx, tempy, tempz, E_BLOCK_AIR, 0 ); + } + else + { + m_World->SetBlock( pistx, pisty, pistz, E_BLOCK_AIR, 0 ); + } + } +} + + + + diff --git a/source/Piston.h b/source/Piston.h new file mode 100644 index 000000000..d52b92b3d --- /dev/null +++ b/source/Piston.h @@ -0,0 +1,60 @@ + +#pragma once + + + + + +// fwd: "cWorld.h" +class cWorld; + + + + + +class cPiston +{ +public: + + cPiston( cWorld* a_World ); + + static char RotationPitchToMetaData( float a_Rotation, float a_Pitch ) + { + LOGD("pre:a_Rotation %f \n",a_Rotation); + LOGD("a_Pitch %f \n",a_Pitch); + + if (a_Pitch >= 50.f ){ + return 0x1; + } else if ( a_Pitch <= -50.f ) { + return 0x0; + } else { + + a_Rotation += 90 + 45; // So its not aligned with axis + std::printf("a_Rotation %f \n",a_Rotation); + + if( a_Rotation > 360.f ) a_Rotation -= 360.f; + if( a_Rotation >= 0.f && a_Rotation < 90.f ) + { LOG("1111\n");return 0x4;} + else if( a_Rotation >= 180 && a_Rotation < 270 ) + { LOG("2222\n");return 0x5;} + else if( a_Rotation >= 90 && a_Rotation < 180 ) + { LOG("3333\n");return 0x2;} + else + { LOG("4444\n");return 0x3;} + } + } + + void ExtendPiston( int, int, int ); + void RetractPiston( int, int, int ); + + cWorld* m_World; + +private: + void ChainMove( int, int, int, char, unsigned short * ); + unsigned short FirstPassthroughBlock( int, int, int, char ); + +}; + + + + diff --git a/source/Player.cpp b/source/Player.cpp new file mode 100644 index 000000000..b20287750 --- /dev/null +++ b/source/Player.cpp @@ -0,0 +1,1019 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Player.h" +#include "Server.h" +#include "ClientHandle.h" +#include "UI/Window.h" +#include "UI/WindowOwner.h" +#include "World.h" +#include "Pickup.h" +#include "PluginManager.h" +#include "BlockEntity.h" +#include "GroupManager.h" +#include "Group.h" +#include "ChatColor.h" +#include "Item.h" +#include "Tracer.h" +#include "Root.h" +#include "OSSupport/MakeDir.h" +#include "OSSupport/Timer.h" +#include "MersenneTwister.h" + +#include "Vector3d.h" +#include "Vector3f.h" + +#include "../iniFile/iniFile.h" +#include + +#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x)) + + + + + +CLASS_DEFINITION( cPlayer, cPawn ); + + + + + +cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) + : m_GameMode(eGameMode_NotSet) + , m_IP("") + , m_LastBlockActionTime( 0 ) + , m_LastBlockActionCnt( 0 ) + , m_bVisible( true ) + , m_LastGroundHeight( 0 ) + , m_bTouchGround( false ) + , m_Stance( 0.0 ) + , m_Inventory(*this) + , m_CurrentWindow(NULL) + , m_InventoryWindow(NULL) + , m_TimeLastPickupCheck( 0.f ) + , m_Color('-') + , m_ClientHandle( a_Client ) + , m_FoodExhaustionLevel(0.f) + , m_FoodTickTimer(0) +{ + LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d", + a_PlayerName.c_str(), a_Client->GetIPString().c_str(), + this, GetUniqueID() + ); + m_EntityType = eEntityType_Player; + + m_InventoryWindow = new cInventoryWindow(*this); + m_CurrentWindow = m_InventoryWindow; + m_InventoryWindow->OpenedByPlayer(*this); + + SetMaxHealth(20); + m_MaxFoodLevel = 20; + m_MaxFoodSaturationLevel = 20.f; + + m_FoodLevel = m_MaxFoodLevel; + m_FoodSaturationLevel = 5.f; + + cTimer t1; + m_LastPlayerListTime = t1.GetNowTime(); + + m_TimeLastTeleportPacket = cWorld::GetTime(); + m_TimeLastPickupCheck = cWorld::GetTime(); + + m_PlayerName = a_PlayerName; + m_bDirtyPosition = true; // So chunks are streamed to player at spawn + + if (!LoadFromDisk()) + { + m_Inventory.Clear(); + m_Pos.x = cRoot::Get()->GetDefaultWorld()->GetSpawnX(); + m_Pos.y = cRoot::Get()->GetDefaultWorld()->GetSpawnY(); + m_Pos.z = cRoot::Get()->GetDefaultWorld()->GetSpawnZ(); + + LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}", + a_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z + ); + } + m_LastGroundHeight = (float)(m_Pos.y); + m_Stance = m_Pos.y + 1.62; +} + + + + + +cPlayer::~cPlayer(void) +{ + LOG("Deleting cPlayer \"%s\" at %p, ID %d", m_PlayerName.c_str(), this, GetUniqueID()); + + SaveToDisk(); + + m_World->RemovePlayer( this ); + + m_ClientHandle = NULL; + + delete m_InventoryWindow; + + LOG("Player %p deleted", this); +} + + + + + +void cPlayer::Initialize( cWorld* a_World ) +{ + cPawn::Initialize( a_World ); + GetWorld()->AddPlayer( this ); +} + + + + + +void cPlayer::Destroyed() +{ + CloseWindow(-1); + m_ClientHandle = NULL; +} + + + + + +void cPlayer::SpawnOn(cClientHandle & a_Client) +{ + /* + LOGD("cPlayer::SpawnOn(%s) for \"%s\" at pos {%.2f, %.2f, %.2f}", + a_Client.GetUsername().c_str(), m_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z + ); + */ + + if (m_bVisible) + { + a_Client.SendPlayerSpawn(*this); + } +} + + + + + +void cPlayer::Tick(float a_Dt) +{ + if (!m_ClientHandle->IsPlaying()) + { + // We're not yet in the game, ignore everything + return; + } + + cPawn::Tick(a_Dt); + + if (m_bDirtyOrientation && !m_bDirtyPosition) + { + m_World->BroadcastEntLook(*this, m_ClientHandle); + m_World->BroadcastEntHeadLook(*this, m_ClientHandle); + m_bDirtyOrientation = false; + } + else if (m_bDirtyPosition) + { + cRoot::Get()->GetPluginManager()->CallHook( cPluginManager::E_PLUGIN_PLAYER_MOVE, 1, this ); + + float DiffX = (float)(GetPosX() - m_LastPosX ); + float DiffY = (float)(GetPosY() - m_LastPosY ); + float DiffZ = (float)(GetPosZ() - m_LastPosZ ); + float SqrDist = DiffX * DiffX + DiffY * DiffY + DiffZ * DiffZ; + if ( + (SqrDist > 4 * 4) || // 4 blocks is max Relative Move + (cWorld::GetTime() - m_TimeLastTeleportPacket > 2 ) // Send an absolute position every 2 seconds + ) + { + // LOG("Teleported %f", sqrtf(SqrDist) ); + m_World->BroadcastTeleportEntity(*this, m_ClientHandle); + m_TimeLastTeleportPacket = cWorld::GetTime(); + } + else + { + // Relative move sucks balls! It's always wrong wtf! + if (m_bDirtyOrientation) + { + m_World->BroadcastEntRelMoveLook(*this, (char)(DiffX * 32), (char)(DiffY * 32), (char)(DiffZ * 32), m_ClientHandle); + m_bDirtyOrientation = false; + } + else + { + m_World->BroadcastEntRelMove(*this, (char)(DiffX * 32), (char)(DiffY * 32), (char)(DiffZ * 32), m_ClientHandle); + } + } + m_LastPosX = GetPosX(); + m_LastPosY = GetPosY(); + m_LastPosZ = GetPosZ(); + m_bDirtyPosition = false; + m_ClientHandle->StreamChunks(); + } + + if (m_Health > 0) // make sure player is alive + { + m_World->CollectPickupsByPlayer(this); + + //Handle Health: + m_FoodTickTimer++; + if(m_FoodTickTimer >= 80) + { + m_FoodTickTimer = 0; + + if(m_FoodLevel >= 17) + { + Heal(1); + }else if(m_FoodLevel == 0) + { + TakeDamage(1, NULL); + } + } + + // TODO: Increase Exhaustion level http://www.minecraftwiki.net/wiki/Hunger#Exhaustion_level_increase + if (m_FoodExhaustionLevel >= 4.f) + { + m_FoodExhaustionLevel -= 4.f; + + if (m_FoodSaturationLevel >= 1.f) + { + m_FoodSaturationLevel--; + } + else + { + m_FoodLevel = MAX(m_FoodLevel -1, 0); + } + + SendHealth(); + } + } + + cTimer t1; + // Send Player List (Once per m_LastPlayerListTime/1000 ms) + if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime()) + { + m_World->SendPlayerList(this); + m_LastPlayerListTime = t1.GetNowTime(); + } +} + + + + + +void cPlayer::SetTouchGround(bool a_bTouchGround) +{ + m_bTouchGround = a_bTouchGround; + + if (!m_bTouchGround) + { + cWorld* World = GetWorld(); + char BlockID = World->GetBlock( float2int(m_Pos.x), float2int(m_Pos.y), float2int(m_Pos.z) ); + if( BlockID != E_BLOCK_AIR ) + { + // LOGD("TouchGround set to true by server"); + m_bTouchGround = true; + } + if( BlockID == E_BLOCK_WATER || BlockID == E_BLOCK_STATIONARY_WATER || BlockID == E_BLOCK_LADDER || BlockID == E_BLOCK_TORCH ) + { + // LOGD("Water / Ladder / Torch"); + m_LastGroundHeight = (float)m_Pos.y; + } + } + + if (m_bTouchGround) + { + float Dist = (float)(m_LastGroundHeight - m_Pos.y); + int Damage = (int)(Dist - 4.f); + if (Damage > 0) + { + TakeDamage(Damage, 0); + } + + m_LastGroundHeight = (float)m_Pos.y; + } +} + + + + + +void cPlayer::Heal( int a_Health ) +{ + if( m_Health < GetMaxHealth() ) + { + m_Health = (short) MIN(a_Health + m_Health, GetMaxHealth()); + + + SendHealth(); + } +} + + + + + +bool cPlayer::Feed(short a_Food, float a_Saturation) +{ + if (m_FoodLevel >= GetMaxFoodLevel()) + { + return false; + } + + m_FoodLevel = MIN(a_Food + m_FoodLevel, GetMaxFoodLevel()); + m_FoodSaturationLevel = MIN(m_FoodSaturationLevel + a_Saturation, GetMaxFoodSaturationLevel()); + + SendHealth(); + return true; +} + + + + + +void cPlayer::SendHealth() +{ + if (m_ClientHandle != NULL) + { + m_ClientHandle->SendHealth(); + } +} + + + + + +void cPlayer::TakeDamage( int a_Damage, cEntity* a_Instigator ) +{ + if (m_GameMode != eGameMode_Creative) + { + cPawn::TakeDamage( a_Damage, a_Instigator ); + + AddFoodExhaustion(0.3f); + + SendHealth(); + } +} + + + + + +void cPlayer::KilledBy(cEntity * a_Killer) +{ + cPawn::KilledBy(a_Killer); + + if (m_Health > 0) + { + return; // not dead yet =] + } + + m_bVisible = false; // So new clients don't see the player + + // Puke out all the items + cItem * Items = m_Inventory.GetSlots(); + cItems Pickups; + for (unsigned int i = 1; i < m_Inventory.c_NumSlots; ++i) + { + if( !Items[i].IsEmpty() ) + { + Pickups.push_back(Items[i]); + } + Items[i].Empty(); + } + m_World->SpawnItemPickups(Pickups, m_Pos.x, m_Pos.y, m_Pos.z, 10); + SaveToDisk(); // Save it, yeah the world is a tough place ! +} + + + + + +void cPlayer::Respawn() +{ + m_Health = GetMaxHealth(); + + m_ClientHandle->SendRespawn(); + + // Set non Burning + SetMetaData(NORMAL); + + TeleportTo(GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ()); + + SetVisible(true); +} + + + + + +double cPlayer::GetEyeHeight() +{ + return m_Stance; +} + +Vector3d cPlayer::GetEyePosition() +{ + return Vector3d( m_Pos.x, m_Stance, m_Pos.z ); +} + + + + + +void cPlayer::OpenWindow( cWindow* a_Window ) +{ + CloseWindow(m_CurrentWindow ? (char)m_CurrentWindow->GetWindowType() : 0); + a_Window->OpenedByPlayer(*this); + m_CurrentWindow = a_Window; +} + + + + + +void cPlayer::CloseWindow(char a_WindowType) +{ + if (m_CurrentWindow == m_InventoryWindow) + { + // The inventory window must not be closed and must not be even sent a close packet + return; + } + + if (m_CurrentWindow != NULL) + { + // TODO: This code should be in cChestWindow instead + if ((a_WindowType == 1) && (m_CurrentWindow->GetWindowType() == cWindow::Chest)) + { + int x, y, z; + m_CurrentWindow->GetOwner()->GetBlockPos(x, y, z); + m_World->BroadcastBlockAction(x, y, z, 1, 0, E_BLOCK_CHEST); + } + + m_CurrentWindow->ClosedByPlayer(*this); + } + m_CurrentWindow = m_InventoryWindow; +} + + + + + +void cPlayer::SetLastBlockActionTime() +{ + if (m_World != NULL) + { + m_LastBlockActionTime = m_World->GetTime(); + } +} + + + + + +void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt ) +{ + m_LastBlockActionCnt = a_LastBlockActionCnt; +} + + + + + +void cPlayer::SetGameMode(eGameMode a_GameMode) +{ + if ((a_GameMode >= 2) || (a_GameMode < 0)) + { + LOGWARNING("%s: Setting invalid gamemode: %d", GetName().c_str(), a_GameMode); + return; + } + + if (m_GameMode == a_GameMode) + { + // Gamemode already set + return; + } + + m_GameMode = a_GameMode; + m_ClientHandle->SendGameMode(a_GameMode); +} + + + + + +void cPlayer::LoginSetGameMode( eGameMode a_GameMode ) +{ + m_GameMode = a_GameMode; +} + + + + + +void cPlayer::SetIP(const AString & a_IP) +{ + m_IP = a_IP; +} + + + + + +void cPlayer::SendMessage(const AString & a_Message) +{ + m_ClientHandle->SendChat(a_Message); +} + + + + + +void cPlayer::TeleportTo(const double & a_PosX, const double & a_PosY, const double & a_PosZ) +{ + SetPosition( a_PosX, a_PosY, a_PosZ ); + + m_World->BroadcastTeleportEntity(*this, GetClientHandle()); + m_ClientHandle->SendPlayerMoveLook(); +} + + + + + +void cPlayer::MoveTo( const Vector3d & a_NewPos ) +{ + // TODO: should do some checks to see if player is not moving through terrain + // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too + + SetPosition( a_NewPos ); + SetStance(a_NewPos.y + 1.62); +} + + + + + +void cPlayer::SetVisible(bool a_bVisible) +{ + if (a_bVisible && !m_bVisible) // Make visible + { + m_bVisible = true; + m_World->BroadcastSpawn(*this); + } + if (!a_bVisible && m_bVisible) + { + m_bVisible = false; + m_World->BroadcastDestroyEntity(*this, m_ClientHandle); // Destroy on all clients + } +} + + + + + +void cPlayer::AddToGroup( const char* a_GroupName ) +{ + cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName ); + m_Groups.push_back( Group ); + LOGD("Added %s to group %s", m_PlayerName.c_str(), a_GroupName ); + ResolveGroups(); + ResolvePermissions(); +} + + + + + +bool cPlayer::CanUseCommand( const char* a_Command ) +{ + for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr ) + { + if( (*itr)->HasCommand( a_Command ) ) return true; + } + return false; +} + + + + + +bool cPlayer::HasPermission( const char* a_Permission ) +{ + AStringVector Split = StringSplit( a_Permission, "." ); + PermissionMap Possibilities = m_ResolvedPermissions; + // Now search the namespaces + while( Possibilities.begin() != Possibilities.end() ) + { + PermissionMap::iterator itr = Possibilities.begin(); + if( itr->second ) + { + AStringVector OtherSplit = StringSplit( itr->first, "." ); + if( OtherSplit.size() <= Split.size() ) + { + unsigned int i; + for( i = 0; i < OtherSplit.size(); ++i ) + { + if( OtherSplit[i].compare( Split[i] ) != 0 ) + { + if( OtherSplit[i].compare("*") == 0 ) return true; // WildCard man!! WildCard! + break; + } + } + if( i == Split.size() ) return true; + } + } + Possibilities.erase( itr ); + } + + // Nothing that matched :( + return false; +} + + + + + +bool cPlayer::IsInGroup( const char* a_Group ) +{ + for( GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr ) + { + if( strcmp( a_Group, (*itr)->GetName().c_str() ) == 0 ) + return true; + } + return false; +} + + + + + +void cPlayer::ResolvePermissions() +{ + m_ResolvedPermissions.clear(); // Start with an empty map yo~ + + // Copy all player specific permissions into the resolved permissions map + for( PermissionMap::iterator itr = m_Permissions.begin(); itr != m_Permissions.end(); ++itr ) + { + m_ResolvedPermissions[ itr->first ] = itr->second; + } + + for( GroupList::iterator GroupItr = m_ResolvedGroups.begin(); GroupItr != m_ResolvedGroups.end(); ++GroupItr ) + { + const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions(); + for( cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr ) + { + m_ResolvedPermissions[ itr->first ] = itr->second; + } + } +} + + + + + +void cPlayer::ResolveGroups() +{ + // Clear resolved groups first + m_ResolvedGroups.clear(); + + // Get a complete resolved list of all groups the player is in + std::map< cGroup*, bool > AllGroups; // Use a map, because it's faster than iterating through a list to find duplicates + GroupList ToIterate; + for( GroupList::iterator GroupItr = m_Groups.begin(); GroupItr != m_Groups.end(); ++GroupItr ) + { + ToIterate.push_back( *GroupItr ); + } + while( ToIterate.begin() != ToIterate.end() ) + { + cGroup* CurrentGroup = *ToIterate.begin(); + if( AllGroups.find( CurrentGroup ) != AllGroups.end() ) + { + LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!", + m_PlayerName.c_str(), CurrentGroup->GetName().c_str() + ); + } + else + { + AllGroups[ CurrentGroup ] = true; + m_ResolvedGroups.push_back( CurrentGroup ); // Add group to resolved list + const cGroup::GroupList & Inherits = CurrentGroup->GetInherits(); + for( cGroup::GroupList::const_iterator itr = Inherits.begin(); itr != Inherits.end(); ++itr ) + { + if( AllGroups.find( *itr ) != AllGroups.end() ) + { + LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", m_PlayerName.c_str(), (*itr)->GetName().c_str() ); + continue; + } + ToIterate.push_back( *itr ); + } + } + ToIterate.erase( ToIterate.begin() ); + } +} + + + + + +AString cPlayer::GetColor(void) const +{ + if ( m_Color != '-' ) + { + return cChatColor::MakeColor( m_Color ); + } + + if ( m_Groups.size() < 1 ) + { + return cChatColor::White; + } + + return (*m_Groups.begin())->GetColor(); +} + + + + + +void cPlayer::TossItem( + bool a_bDraggingItem, + char a_Amount /* = 1 */, + short a_CreateType /* = 0 */, + short a_CreateHealth /* = 0 */ +) +{ + cItems Drops; + if (a_CreateType) + { + // Just create item without touching the inventory (used in creative mode) + Drops.push_back(cItem(a_CreateType, a_Amount, a_CreateHealth)); + } + else + { + // Drop an item from the inventory: + if (a_bDraggingItem) + { + cItem & Item = GetDraggingItem(); + if (!Item.IsEmpty()) + { + Drops.push_back(Item); + if (Item.m_ItemCount > a_Amount) + { + Item.m_ItemCount -= (char)a_Amount; + } + else + { + Item.Empty(); + } + } + } + else + { + // Else drop equipped item + cItem DroppedItem = GetInventory().GetEquippedItem(); + if (!DroppedItem.IsEmpty()) + { + DroppedItem.m_ItemCount = 1; + if (GetInventory().RemoveItem(DroppedItem)) + { + DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again + Drops.push_back(DroppedItem); + } + } + } + } + float vX = 0, vY = 0, vZ = 0; + EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY); + vY = -vY * 2 + 1.f; + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 2, vY * 2, vZ * 2); +} + + + + + +bool cPlayer::MoveToWorld( const char* a_WorldName ) +{ + cWorld * World = cRoot::Get()->GetWorld( a_WorldName ); + if ( World ) + { + /* Remove all links to the old world */ + m_World->RemovePlayer( this ); + m_ClientHandle->RemoveFromAllChunks(); + m_World->RemoveEntityFromChunk(this, m_ChunkX, m_ChunkY, m_ChunkZ); + + /* Add player to all the necessary parts of the new world */ + SetWorld( World ); + GetWorld()->AddPlayer( this ); + MoveToCorrectChunk(true); + GetClientHandle()->StreamChunks(); + + return true; + } + + return false; +} + + + + + +void cPlayer::LoadPermissionsFromDisk() +{ + m_Groups.clear(); + m_Permissions.clear(); + + cIniFile IniFile("users.ini"); + if( IniFile.ReadFile() ) + { + std::string Groups = IniFile.GetValue(m_PlayerName, "Groups", ""); + if( Groups.size() > 0 ) + { + AStringVector Split = StringSplit( Groups, "," ); + for( unsigned int i = 0; i < Split.size(); i++ ) + { + AddToGroup( Split[i].c_str() ); + } + } + else + { + AddToGroup("Default"); + } + + m_Color = IniFile.GetValue(m_PlayerName, "Color", "-")[0]; + } + else + { + LOGWARN("WARNING: Failed to read ini file users.ini"); + AddToGroup("Default"); + } + ResolvePermissions(); +} + + + + +bool cPlayer::LoadFromDisk() +{ + LoadPermissionsFromDisk(); + + // Log player permissions, cause it's what the cool kids do + LOGINFO("Player %s has permissions:", m_PlayerName.c_str() ); + for( PermissionMap::iterator itr = m_ResolvedPermissions.begin(); itr != m_ResolvedPermissions.end(); ++itr ) + { + if( itr->second ) LOGINFO("%s", itr->first.c_str() ); + } + + AString SourceFile; + Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); + + cFile f; + if (!f.Open(SourceFile, cFile::fmRead)) + { + return false; + } + + AString buffer; + if (f.ReadRestOfFile(buffer) != f.GetSize()) + { + LOGERROR("ERROR READING FROM FILE \"%s\"", SourceFile.c_str()); + return false; + } + f.Close(); + + Json::Value root; + Json::Reader reader; + if (!reader.parse(buffer, root, false)) + { + LOGERROR("ERROR WHILE PARSING JSON FROM FILE %s", SourceFile.c_str()); + } + + Json::Value & JSON_PlayerPosition = root["position"]; + if (JSON_PlayerPosition.size() == 3) + { + m_Pos.x = JSON_PlayerPosition[(unsigned int)0].asDouble(); + m_Pos.y = JSON_PlayerPosition[(unsigned int)1].asDouble(); + m_Pos.z = JSON_PlayerPosition[(unsigned int)2].asDouble(); + } + + Json::Value & JSON_PlayerRotation = root["rotation"]; + if (JSON_PlayerRotation.size() == 3) + { + m_Rot.x = (float)JSON_PlayerRotation[(unsigned int)0].asDouble(); + m_Rot.y = (float)JSON_PlayerRotation[(unsigned int)1].asDouble(); + m_Rot.z = (float)JSON_PlayerRotation[(unsigned int)2].asDouble(); + } + + m_Health = (short)root.get("health", 0 ).asInt(); + m_FoodLevel = (short)root.get("food", m_MaxFoodLevel ).asInt(); + m_FoodSaturationLevel = (float)root.get("foodSaturation", m_MaxFoodSaturationLevel ).asDouble(); + + m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt(); + + m_Inventory.LoadFromJson(root["inventory"]); + + m_LoadedWorldName = root.get("world", "world").asString(); + + LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"", + m_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z, m_LoadedWorldName.c_str() + ); + + return true; +} + + + + + +bool cPlayer::SaveToDisk() +{ + cMakeDir::MakeDir("players"); + + // create the JSON data + Json::Value JSON_PlayerPosition; + JSON_PlayerPosition.append( Json::Value( m_Pos.x ) ); + JSON_PlayerPosition.append( Json::Value( m_Pos.y ) ); + JSON_PlayerPosition.append( Json::Value( m_Pos.z ) ); + + Json::Value JSON_PlayerRotation; + JSON_PlayerRotation.append( Json::Value( m_Rot.x ) ); + JSON_PlayerRotation.append( Json::Value( m_Rot.y ) ); + JSON_PlayerRotation.append( Json::Value( m_Rot.z ) ); + + Json::Value JSON_Inventory; + m_Inventory.SaveToJson( JSON_Inventory ); + + Json::Value root; + root["position"] = JSON_PlayerPosition; + root["rotation"] = JSON_PlayerRotation; + root["inventory"] = JSON_Inventory; + root["health"] = m_Health; + root["food"] = m_FoodLevel; + root["foodSaturation"] = m_FoodSaturationLevel; + root["world"] = GetWorld()->GetName(); + + if (m_GameMode == GetWorld()->GetGameMode()) + { + root["gamemode"] = (int) eGameMode_NotSet; + } + else + { + root["gamemode"] = (int) m_GameMode; + } + + Json::StyledWriter writer; + std::string JsonData = writer.write( root ); + + AString SourceFile; + Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); + + cFile f; + if (!f.Open(SourceFile, cFile::fmWrite)) + { + LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", m_PlayerName.c_str(), SourceFile.c_str()); + return false; + } + if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size()) + { + LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str()); + return false; + } + return true; +} + + + + + +cPlayer::StringList cPlayer::GetResolvedPermissions() +{ + StringList Permissions; + + const PermissionMap& ResolvedPermissions = m_ResolvedPermissions; + for( PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr ) + { + if( itr->second ) Permissions.push_back( itr->first ); + } + + return Permissions; +} + + + + + +void cPlayer::UseEquippedItem() +{ + if(GetGameMode() != 1) //No damage in creative + { + if (GetInventory().GetEquippedItem().DamageItem()) + { + LOG("Player %s Broke ID: %i", GetClientHandle()->GetUsername().c_str(), GetInventory().GetEquippedItem().m_ItemID); + GetInventory().RemoveItem( GetInventory().GetEquippedItem()); + } + } +} + + + + diff --git a/source/Player.h b/source/Player.h new file mode 100644 index 000000000..d9cfddece --- /dev/null +++ b/source/Player.h @@ -0,0 +1,171 @@ + +#pragma once + +#include "Pawn.h" +#include "Inventory.h" +#include "Defines.h" + + + + + +class cGroup; +class cWindow; +class cClientHandle; + + + + + +class cPlayer : public cPawn //tolua_export +{ //tolua_export +public: + typedef cPawn super; + + CLASS_PROTOTYPE() + + cPlayer(cClientHandle * a_Client, const AString & a_PlayerName); + virtual ~cPlayer(); + + virtual void Initialize( cWorld* a_World ); //tolua_export + + virtual void SpawnOn(cClientHandle & a_Client) override; + + virtual void Tick(float a_Dt) override; + + void SetTouchGround( bool a_bTouchGround ); + inline void SetStance( const double a_Stance ) { m_Stance = a_Stance; } + double GetEyeHeight(); //tolua_export + Vector3d GetEyePosition(); //tolua_export + OBSOLETE + inline bool GetFlying() { return m_bTouchGround; } //tolua_export + inline bool IsOnGround(void) const {return m_bTouchGround; } // tolua_export + inline const double GetStance(void) const { return m_Pos.y + 1.62; } //tolua_export // TODO: Proper stance when crouching etc. + inline cInventory & GetInventory(void) { return m_Inventory; } //tolua_export + inline const cInventory & GetInventory(void) const { return m_Inventory; } + + inline const cItem & GetEquippedItem(void) const {return GetInventory().GetEquippedItem(); } + + virtual void TeleportTo( const double & a_PosX, const double & a_PosY, const double & a_PosZ ); //tolua_export + + eGameMode GetGameMode(void) const { return m_GameMode; } //tolua_export + std::string GetIP() { return m_IP; } //tolua_export + float GetLastBlockActionTime() { return m_LastBlockActionTime; } //tolua_export + int GetLastBlockActionCnt() { return m_LastBlockActionCnt; } //tolua_export + void SetLastBlockActionCnt( int ); //tolua_export + void SetLastBlockActionTime(); //tolua_export + void SetGameMode( eGameMode a_GameMode ); //tolua_export + void LoginSetGameMode( eGameMode a_GameMode ); + void SetIP(const AString & a_IP); + + // Tries to move to a new position, with collision checks and stuff + virtual void MoveTo( const Vector3d & a_NewPos ); //tolua_export + + cWindow* GetWindow() { return m_CurrentWindow; } + void OpenWindow( cWindow* a_Window ); + void CloseWindow(char a_WindowType); + + cClientHandle * GetClientHandle(void) const { return m_ClientHandle; } //tolua_export + + void SendMessage(const AString & a_Message); //tolua_export + + const AString & GetName(void) const { return m_PlayerName; } //tolua_export + void SetName(const AString & a_Name) { m_PlayerName = a_Name; } //tolua_export + + typedef std::list< cGroup* > GroupList; + typedef std::list< std::string > StringList; + void AddToGroup( const char* a_GroupName ); //tolua_export + bool CanUseCommand( const char* a_Command ); //tolua_export + bool HasPermission( const char* a_Permission ); //tolua_export + const GroupList & GetGroups() { return m_Groups; } // >> EXPORTED IN MANUALBINDINGS << + StringList GetResolvedPermissions(); // >> EXPORTED IN MANUALBINDINGS << + bool IsInGroup( const char* a_Group ); //tolua_export + + AString GetColor(void) const; //tolua_export + + void TossItem(bool a_bDraggingItem, char a_Amount = 1, short a_CreateType = 0, short a_CreateHealth = 0); //tolua_export + + void Heal( int a_Health ); //tolua_export + + /// Returns true if any food has been consumed, false if player "full" + bool Feed(short a_Food, float a_Saturation); + + short GetMaxFoodLevel() { return m_MaxFoodLevel; } + short GetFoodLevel() { return m_FoodLevel; } + + float GetMaxFoodSaturationLevel() { return m_MaxFoodSaturationLevel; } + float GetFoodSaturationLevel() { return m_FoodSaturationLevel; } + + void AddFoodExhaustion(float a_Exhaustion) { m_FoodExhaustionLevel += a_Exhaustion; } + + void TakeDamage( int a_Damage, cEntity* a_Instigator ); //tolua_export + void KilledBy( cEntity* a_Killer ); //tolua_export + void Respawn(); //tolua_export + + void SetVisible( bool a_bVisible ); //tolua_export + bool IsVisible() { return m_bVisible; } //tolua_export + + bool MoveToWorld( const char* a_WorldName ); //tolua_export + + bool SaveToDisk(); + bool LoadFromDisk(); + void LoadPermissionsFromDisk(); //tolua_export + + const AString & GetLoadedWorldName() { return m_LoadedWorldName; } + + void UseEquippedItem(void); + + void SendHealth(); + + // In UI windows, the item that the player is dragging: + bool IsDraggingItem(void) const { return !m_DraggingItem.IsEmpty(); } + cItem & GetDraggingItem(void) {return m_DraggingItem; } + +protected: + virtual void Destroyed(); + + typedef std::map< std::string, bool > PermissionMap; + PermissionMap m_ResolvedPermissions; + PermissionMap m_Permissions; + + GroupList m_ResolvedGroups; + GroupList m_Groups; + + std::string m_PlayerName; + std::string m_LoadedWorldName; + + bool m_bVisible; + + short m_FoodLevel; + short m_MaxFoodLevel; + float m_FoodSaturationLevel; + float m_MaxFoodSaturationLevel; + float m_FoodExhaustionLevel; + char m_FoodTickTimer; + + float m_LastGroundHeight; + bool m_bTouchGround; + double m_Stance; + cInventory m_Inventory; + cWindow * m_CurrentWindow; + cWindow * m_InventoryWindow; + + float m_TimeLastPickupCheck; + + void ResolvePermissions(); + + void ResolveGroups(); + char m_Color; + + float m_LastBlockActionTime; + int m_LastBlockActionCnt; + eGameMode m_GameMode; + std::string m_IP; + + cItem m_DraggingItem; + + long long m_LastPlayerListTime; + static const unsigned short PLAYER_LIST_TIME_MS = 1000; // 1000 = once per second + + cClientHandle* m_ClientHandle; +}; //tolua_export diff --git a/source/Plugin.cpp b/source/Plugin.cpp new file mode 100644 index 000000000..563e6237d --- /dev/null +++ b/source/Plugin.cpp @@ -0,0 +1,308 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Plugin.h" + + + + + +cPlugin::cPlugin() + : m_Version( 0 ) + , m_Language( E_CPP ) + , m_bCanBindCommands( false ) +{ +} + +cPlugin::~cPlugin() +{ +} + +// bool cPlugin::Initialize() +// { +// LOG("cPlugin::Initialize()"); +// return false; +// } + + + + + +void cPlugin::Tick(float a_Dt) +{ + UNUSED(a_Dt); +} + + + + + +bool cPlugin::OnBlockDig(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) +{ + UNUSED(a_Player); + UNUSED(a_BlockX); + UNUSED(a_BlockY); + UNUSED(a_BlockZ); + UNUSED(a_BlockFace); + UNUSED(a_Status); + UNUSED(a_OldBlock); + UNUSED(a_OldMeta); + return false; +} + + + + + +bool cPlugin::OnBlockPlace(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) +{ + UNUSED(a_Player); + UNUSED(a_BlockX); + UNUSED(a_BlockY); + UNUSED(a_BlockZ); + UNUSED(a_BlockFace); + UNUSED(a_HeldItem); + return false; +} + + + + + +bool cPlugin::OnBlockToPickup(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups) +{ + UNUSED(a_BlockType); + UNUSED(a_BlockMeta); + UNUSED(a_Player); + UNUSED(a_EquippedItem); + UNUSED(a_Pickups); + return false; +} + + + + + +bool cPlugin::OnChat(cPlayer * a_Player, const AString & a_Message) +{ + UNUSED(a_Player); + UNUSED(a_Message); + return false; +} + + + + + +void cPlugin::OnChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ) +{ + UNUSED(a_World); + UNUSED(a_ChunkX); + UNUSED(a_ChunkZ); +} + + + + + +bool cPlugin::OnChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cLuaChunk * a_pLuaChunk) +{ + UNUSED(a_World); + UNUSED(a_ChunkX); + UNUSED(a_ChunkZ); + UNUSED(a_pLuaChunk); + return false; +} + + + + + +bool cPlugin::OnCollectPickup(cPlayer * a_Player, cPickup * a_Pickup) +{ + UNUSED(a_Player); + UNUSED(a_Pickup); + return false; +} + + + + + +bool cPlugin::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + UNUSED(a_Player); + UNUSED(a_Grid); + UNUSED(a_Recipe); + return false; +} + + + + + +bool cPlugin::OnDisconnect(cPlayer * a_Player, const AString & a_Reason) +{ + UNUSED(a_Reason); + UNUSED(a_Player); + return false; +} + + + + + +bool cPlugin::OnKilled(cPawn * a_Killed, cEntity * a_Killer) +{ + UNUSED(a_Killed); + UNUSED(a_Killer); + return false; +} + + + + + +bool cPlugin::OnLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) +{ + UNUSED(a_Client); + UNUSED(a_ProtocolVersion); + UNUSED(a_Username); + return false; +} + + + + + +bool cPlugin::OnPlayerJoin(cPlayer * a_Player) +{ + UNUSED(a_Player); + return false; +} + + + + + +void cPlugin::OnPlayerMove(cPlayer * a_Player) +{ + UNUSED(a_Player); +} + + + + + +void cPlugin::OnPlayerSpawn(cPlayer * a_Player) +{ + UNUSED(a_Player); +} + + + + + +bool cPlugin::OnPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + UNUSED(a_Player); + UNUSED(a_Grid); + UNUSED(a_Recipe); + return false; +} + + + + + +bool cPlugin::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + UNUSED(a_Player); + UNUSED(a_Grid); + UNUSED(a_Recipe); + return false; +} + + + + + +void cPlugin::OnTakeDamage(cPawn * a_Pawn, TakeDamageInfo * a_TakeDamageInfo) +{ + UNUSED(a_Pawn); + UNUSED(a_TakeDamageInfo); +} + + + + + +bool cPlugin::OnUpdatedSign(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) +{ + UNUSED(a_World); + UNUSED(a_BlockX); + UNUSED(a_BlockY); + UNUSED(a_BlockZ); + UNUSED(a_Line1); + UNUSED(a_Line2); + UNUSED(a_Line3); + UNUSED(a_Line4); + UNUSED(a_Player); + return false; +} + + + + + +bool cPlugin::OnUpdatingSign(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) +{ + UNUSED(a_World); + UNUSED(a_BlockX); + UNUSED(a_BlockY); + UNUSED(a_BlockZ); + UNUSED(a_Line1); + UNUSED(a_Line2); + UNUSED(a_Line3); + UNUSED(a_Line4); + UNUSED(a_Player); + return false; +} + + + + + +bool cPlugin::OnWeatherChanged(cWorld * a_World) +{ + UNUSED(a_World); + return false; +} + + + + + +bool cPlugin::OnHandshake(cClientHandle * a_Client, const AString & a_Username) +{ + UNUSED(a_Client); + UNUSED(a_Username); + return false; +} + + + + + +void cPlugin::AddCommand(const AString & a_Command, const AString & a_Description, const AString & a_Permission) +{ + CommandStruct Command; + Command.Command = a_Command; + Command.Description = a_Description; + Command.Permission = a_Permission; + m_Commands.push_back( Command ); +} + + + + diff --git a/source/Plugin.h b/source/Plugin.h new file mode 100644 index 000000000..d88a26d4e --- /dev/null +++ b/source/Plugin.h @@ -0,0 +1,111 @@ + +#pragma once + +#include "Item.h" + +class cClientHandle; +class cPlayer; +class cPickup; +class cItem; +class cEntity; +class cPawn; +class cWorld; +class cLuaChunk; +struct TakeDamageInfo; + +// fwd: cPlayer.h +class cPlayer; + +// fwd: CraftingRecipes.h +class cCraftingGrid; +class cCraftingRecipe; + + + + + +// tolua_begin +class cPlugin +{ +public: + cPlugin(); + virtual ~cPlugin(); + + virtual void OnDisable() {} + virtual bool Initialize() = 0; + + // Called each tick + virtual void Tick(float a_Dt); + + /** + * On all these functions, return true if you want to override default behavior + * You can also return false, so default behavior is used. + **/ + virtual bool OnBlockDig (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta); + virtual bool OnBlockPlace (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem); + virtual bool OnBlockToPickup (BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups); + virtual bool OnChat (cPlayer * a_Player, const AString & a_Message); + virtual void OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ); + virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cLuaChunk * a_pLuaChunk); + virtual bool OnCollectPickup (cPlayer * a_Player, cPickup * a_Pickup); + virtual bool OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); + virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason); + virtual bool OnKilled (cPawn * a_Killed, cEntity* a_Killer ); + virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username); + virtual bool OnPlayerJoin (cPlayer* a_Player ); + virtual void OnPlayerMove (cPlayer* a_Player ); + virtual void OnPlayerSpawn (cPlayer* a_Player ); + virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); + virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); + virtual void OnTakeDamage (cPawn * a_Pawn, TakeDamageInfo * a_TakeDamageInfo ); + virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player); + virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player); + virtual bool OnWeatherChanged (cWorld * a_World); + virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username); + + // Accessors + const AString & GetName() const { return m_Name; } + virtual void SetName( const AString & a_Name ) { m_Name = a_Name; } + + int GetVersion() const { return m_Version; } + void SetVersion( int a_Version ) { m_Version = a_Version; } + + struct CommandStruct + { + std::string Command; + std::string Description; + std::string Permission; + }; + + void AddCommand(const AString & a_Command, const AString & a_Description, const AString & a_Permission); + // tolua_end + + typedef bool (FuncCommandHandler)( std::string & a_Command, std::vector< std::string > & a_Split ); + void BindCommand( FuncCommandHandler* a_Function, std::string & a_Command ); // >> EXPORTED IN MANUALBINDINGS << + const std::vector< CommandStruct > & GetCommands() const { return m_Commands; } // >> EXPORTED IN MANUALBINDINGS << + + + /* This should not be exposed to scripting languages */ + enum PluginLanguage + { + E_CPP, + E_LUA, + E_SQUIRREL, + }; + PluginLanguage GetLanguage() { return m_Language; } + void SetLanguage( PluginLanguage a_Language ) { m_Language = a_Language; } + + bool CanBindCommands() { return m_bCanBindCommands; } +private: + friend class cPluginManager; + bool m_bCanBindCommands; // Only changed by cPluginManager + + PluginLanguage m_Language; + std::vector< CommandStruct > m_Commands; + std::string m_Name; + int m_Version; +}; //tolua_export + + + + diff --git a/source/PluginManager.cpp b/source/PluginManager.cpp new file mode 100644 index 000000000..a76fc0442 --- /dev/null +++ b/source/PluginManager.cpp @@ -0,0 +1,883 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "PluginManager.h" +#include "Plugin.h" +#include "Plugin_Lua.h" +#include "Plugin_NewLua.h" +#include "WebAdmin.h" +#include "Item.h" +#include "Root.h" +#include "LuaCommandBinder.h" + +#ifdef USE_SQUIRREL + #include "Plugin_Squirrel.h" + #include "SquirrelCommandBinder.h" +#endif + +#include "../iniFile/iniFile.h" +#include "tolua++.h" +#include "Player.h" + +#ifdef USE_SQUIRREL + #include "squirrelbindings/SquirrelBindings.h" + #include "squirrelbindings/SquirrelFunctions.h" + #pragma warning(disable:4100;disable:4127;disable:4510;disable:4610;disable:4244;disable:4512) // Getting A LOT of these warnings from SqPlus + + #pragma warning(default:4100;default:4127;default:4510;default:4610;default:4244;default:4512) +#endif + + + + + +cPluginManager* cPluginManager::GetPluginManager() +{ + LOGWARN("WARNING: Using deprecated function cPluginManager::GetPluginManager() use cRoot::Get()->GetPluginManager() instead!"); + return cRoot::Get()->GetPluginManager(); +} + + + + + +cPluginManager::cPluginManager() + : m_LuaCommandBinder( new cLuaCommandBinder() ) +#ifdef USE_SQUIRREL + , m_SquirrelCommandBinder( new cSquirrelCommandBinder() ) +#endif + , m_bReloadPlugins(false) +{ +} + + + + + +cPluginManager::~cPluginManager() +{ + UnloadPluginsNow(); + + delete m_LuaCommandBinder; +#ifdef USE_SQUIRREL + delete m_SquirrelCommandBinder; +#endif +} + + + + + +void cPluginManager::ReloadPlugins() +{ + m_bReloadPlugins = true; +} + + + + + +void cPluginManager::ReloadPluginsNow() +{ + LOG("Loading plugins"); + m_bReloadPlugins = false; + UnloadPluginsNow(); + + #ifdef USE_SQUIRREL + CloseSquirrelVM(); + OpenSquirrelVM(); + #endif // USE_SQUIRREL + + cIniFile IniFile("settings.ini"); + if (!IniFile.ReadFile() ) + { + LOGWARNING("cPluginManager: Can't find settings.ini, so can't load any plugins."); + } + + unsigned int KeyNum = IniFile.FindKey("Plugins"); + unsigned int NumPlugins = IniFile.GetNumValues( KeyNum ); + if( NumPlugins > 0 ) + { + for(unsigned int i = 0; i < NumPlugins; i++) + { + AString ValueName = IniFile.GetValueName(KeyNum, i ); + if( ValueName.compare("Plugin") == 0 ) // It's a Lua plugin + { + AString PluginFile = IniFile.GetValue(KeyNum, i ); + if( !PluginFile.empty() ) + { + // allow for comma separated plugin list + // degrades and works fine for the plugin + // per line + AStringVector split = StringSplit( PluginFile, "," ); + for (unsigned int j = 0; j < split.size(); j++) + { + cPlugin_Lua* Plugin = new cPlugin_Lua( (split[j] + AString(".lua") ).c_str() ); + if( !AddLuaPlugin( Plugin ) ) + { + delete Plugin; + } + } + } + } + else if( ValueName.compare("NewPlugin") == 0 ) // New plugin style + { + AString PluginFile = IniFile.GetValue(KeyNum, i ); + if( !PluginFile.empty() ) + { + cPlugin_NewLua* Plugin = new cPlugin_NewLua( PluginFile.c_str() ); + if( !AddPlugin( Plugin ) ) + { + delete Plugin; + } + } + } + + #ifdef USE_SQUIRREL + else if( ValueName.compare("Squirrel") == 0 ) // Squirrel plugin + { + AString PluginFile = IniFile.GetValue(KeyNum, i ); + if( !PluginFile.empty() ) + { + LOGINFO("Loading Squirrel plugin: %s", PluginFile.c_str() ); + + cPlugin_Squirrel *Plugin = new cPlugin_Squirrel(PluginFile.c_str()); + + if( !AddPlugin( Plugin ) ) + { + delete Plugin; + } + } + } + #endif // USE_SQUIRREL + } + } + + if( GetNumPlugins() == 0 ) + { + LOG("No plugins loaded"); + } + else + { + LOG("Loaded %i plugin(s)", GetNumPlugins() ); + } +} + + + + + +void cPluginManager::Tick(float a_Dt) +{ + if( m_bReloadPlugins ) + { + ReloadPluginsNow(); + } + + HookMap::iterator Plugins = m_Hooks.find( E_PLUGIN_TICK ); + if( Plugins != m_Hooks.end() ) + { + for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr ) + { + (*itr)->Tick( a_Dt ); + } + } +} + + + + + +bool cPluginManager::CallHook(PluginHook a_Hook, unsigned int a_NumArgs, ...) +{ + HookMap::iterator Plugins = m_Hooks.find( a_Hook ); + + if (Plugins == m_Hooks.end()) + { + return false; + } + + switch( a_Hook ) + { + case HOOK_PLAYER_JOIN: + { + if( a_NumArgs != 1 ) break; + va_list argptr; + va_start( argptr, a_NumArgs); + cPlayer* Player = va_arg(argptr, cPlayer* ); + va_end (argptr); + for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr ) + { + if( (*itr)->OnPlayerJoin( Player ) ) + return true; + } + break; + } + + case HOOK_PLAYER_MOVE: + { + if( a_NumArgs != 1 ) break; + va_list argptr; + va_start( argptr, a_NumArgs); + cPlayer* Player = va_arg(argptr, cPlayer* ); + va_end (argptr); + for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr ) + { + (*itr)->OnPlayerMove( Player ); + } + break; + } + + case HOOK_TAKE_DAMAGE: + { + if( a_NumArgs != 2 ) break; + va_list argptr; + va_start( argptr, a_NumArgs); + cPawn* Pawn = va_arg(argptr, cPawn* ); + TakeDamageInfo* TDI = va_arg(argptr, TakeDamageInfo* ); + va_end (argptr); + for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr ) + { + (*itr)->OnTakeDamage( Pawn, TDI ); + } + break; + } + + case HOOK_KILLED: + { + if( a_NumArgs != 2 ) break; + va_list argptr; + va_start( argptr, a_NumArgs); + cPawn* Killed = va_arg(argptr, cPawn* ); + cEntity* Killer = va_arg(argptr, cEntity* ); + va_end (argptr); + for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr ) + { + if( (*itr)->OnKilled( Killed, Killer ) ) + return true; + } + break; + } + + case HOOK_CHUNK_GENERATED: + { + if (a_NumArgs != 3) + { + break; + } + va_list argptr; + va_start( argptr, a_NumArgs); + cWorld * World = va_arg(argptr, cWorld *); + int ChunkX = va_arg(argptr, int); + int ChunkZ = va_arg(argptr, int); + va_end (argptr); + for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr ) + { + (*itr)->OnChunkGenerated(World, ChunkX, ChunkZ); + } + break; + } + + case HOOK_PLAYER_SPAWN: + { + if (a_NumArgs != 1) + { + break; + } + va_list argptr; + va_start( argptr, a_NumArgs); + cPlayer * Player = va_arg(argptr, cPlayer *); + va_end (argptr); + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + (*itr)->OnPlayerSpawn(Player); + } + break; + } + + default: + { + LOGWARNING("cPluginManager: Calling Unknown hook: %i", a_Hook ); + ASSERT(!"Calling an unknown hook"); + break; + } + } // switch (a_Hook) + return false; +} + + + + + +bool cPluginManager::CallHookLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_LOGIN); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnLogin(a_Client, a_ProtocolVersion, a_Username)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookBlockDig(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_BLOCK_DIG); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnBlockDig(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status, a_OldBlock, a_OldMeta)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookBlockPlace(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_BLOCK_PLACE); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnBlockPlace(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_HeldItem)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookChat(cPlayer * a_Player, const AString & a_Message) +{ + #ifdef USE_SQUIRREL + if (m_SquirrelCommandBinder->HandleCommand(a_Message, a_Player)) + { + return true; + } + #endif // USE_SQUIRREL + + if (m_LuaCommandBinder->HandleCommand(a_Message, a_Player)) + { + return true; + } + + //Check if it was a standard command (starts with a slash) + if (a_Message[0] == '/') + { + a_Player->SendMessage("Unknown Command"); + LOGINFO("Player \"%s\" issued an unknown command: \"%s\"", a_Player->GetName().c_str(), a_Message.c_str()); + return true; // Cancel sending + } + + HookMap::iterator Plugins = m_Hooks.find(HOOK_CHAT); + if (Plugins == m_Hooks.end()) + { + return false; + } + + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnChat(a_Player, a_Message)) + { + return true; + } + } + + return false; +} + + + + + +bool cPluginManager::CallHookChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cLuaChunk * a_LuaChunk) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_GENERATING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnChunkGenerating(a_World, a_ChunkX, a_ChunkZ, a_LuaChunk)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookCollectPickup(cPlayer * a_Player, cPickup & a_Pickup) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_COLLECT_PICKUP); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnCollectPickup(a_Player, &a_Pickup)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_PRE_CRAFTING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPreCrafting(a_Player, a_Grid, a_Recipe)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_CRAFTING_NO_RECIPE); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnCraftingNoRecipe(a_Player, a_Grid, a_Recipe)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookDisconnect(cPlayer * a_Player, const AString & a_Reason) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_DISCONNECT); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnDisconnect(a_Player, a_Reason)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_POST_CRAFTING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPostCrafting(a_Player, a_Grid, a_Recipe)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookBlockToPickup( + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups +) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_POST_CRAFTING); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnBlockToPickup(a_BlockType, a_BlockMeta, a_Player, a_EquippedItem, a_Pickups)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookWeatherChanged(cWorld * a_World) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_WEATHER_CHANGED); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnWeatherChanged(a_World)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookUpdatingSign(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_UPDATING_SIGN); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnUpdatingSign(a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookUpdatedSign(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_UPDATED_SIGN); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnUpdatedSign(a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player)) + { + return true; + } + } + return false; +} + + + + + +bool cPluginManager::CallHookHandshake(cClientHandle * a_ClientHandle, const AString & a_Username) +{ + HookMap::iterator Plugins = m_Hooks.find(HOOK_HANDSHAKE); + if (Plugins == m_Hooks.end()) + { + return false; + } + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnHandshake(a_ClientHandle, a_Username)) + { + return true; + } + } + return false; +} + + + + + +cPlugin* cPluginManager::GetPlugin( const AString & a_Plugin ) const +{ + for( PluginList::const_iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr ) + { + if ((*itr)->GetName().compare(a_Plugin) == 0) + { + return *itr; + } + } + return 0; +} + + + + + +const cPluginManager::PluginList & cPluginManager::GetAllPlugins() const +{ + return m_Plugins; +} + + + + + +void cPluginManager::UnloadPluginsNow() +{ + m_Hooks.clear(); + + while( m_LuaPlugins.size() > 0 ) + { + cPlugin_Lua* LuaPlugin = *m_LuaPlugins.begin(); + if( LuaPlugin ) + { + delete LuaPlugin; + } + m_LuaPlugins.remove( LuaPlugin ); + } + + while( m_Plugins.size() > 0 ) + { + RemovePlugin( *m_Plugins.begin(), true ); + } + + //SquirrelVM::Shutdown(); // This breaks shit +} + + + + + +void cPluginManager::RemoveHooks( cPlugin* a_Plugin ) +{ + m_Hooks[ E_PLUGIN_TICK].remove ( a_Plugin ); + m_Hooks[ E_PLUGIN_CHAT].remove ( a_Plugin ); + m_Hooks[ E_PLUGIN_COLLECT_ITEM].remove ( a_Plugin ); + m_Hooks[ E_PLUGIN_BLOCK_DIG].remove ( a_Plugin ); + m_Hooks[ E_PLUGIN_BLOCK_PLACE].remove ( a_Plugin ); + m_Hooks[ E_PLUGIN_DISCONNECT].remove ( a_Plugin ); + m_Hooks[ E_PLUGIN_HANDSHAKE].remove ( a_Plugin ); + m_Hooks[ E_PLUGIN_LOGIN].remove ( a_Plugin ); + m_Hooks[ E_PLUGIN_PLAYER_SPAWN].remove ( a_Plugin ); + m_Hooks[ E_PLUGIN_PLAYER_JOIN].remove ( a_Plugin ); + m_Hooks[ E_PLUGIN_PLAYER_MOVE].remove ( a_Plugin ); + m_Hooks[ E_PLUGIN_TAKE_DAMAGE].remove ( a_Plugin ); + m_Hooks[ E_PLUGIN_KILLED].remove ( a_Plugin ); + m_Hooks[ E_PLUGIN_CHUNK_GENERATED ].remove ( a_Plugin ); + m_Hooks[ E_PLUGIN_CHUNK_GENERATING ].remove( a_Plugin ); + m_Hooks[ E_PLUGIN_BLOCK_TO_DROPS ].remove ( a_Plugin ); +} + + + + + +void cPluginManager::RemovePlugin( cPlugin* a_Plugin, bool a_bDelete /* = false */ ) +{ + if( a_bDelete ) + { + m_LuaCommandBinder->RemoveBindingsForPlugin( a_Plugin ); +#ifdef USE_SQUIRREL + m_SquirrelCommandBinder->RemoveBindingsForPlugin( a_Plugin ); +#endif + m_Plugins.remove( a_Plugin ); + RemoveHooks( a_Plugin ); + a_Plugin->OnDisable(); + + delete a_Plugin; + } + else + { + for( LuaPluginList::iterator itr = m_LuaPlugins.begin(); itr != m_LuaPlugins.end(); ++itr ) + { + (*itr)->RemovePlugin( a_Plugin ); + } + } +} + + + + + +bool cPluginManager::AddPlugin( cPlugin* a_Plugin ) +{ + a_Plugin->m_bCanBindCommands = true; + if( a_Plugin->Initialize() ) + { + m_Plugins.remove( a_Plugin ); + m_Plugins.push_back( a_Plugin ); + return true; + } + + a_Plugin->m_bCanBindCommands = false; + RemoveHooks( a_Plugin ); // Undo any damage the Initialize() might have done + return false; +} + + + + + +bool cPluginManager::AddPlugin( lua_State* a_LuaState, cPlugin* a_Plugin ) +{ + a_Plugin->SetLanguage( cPlugin::E_LUA ); + cPlugin_Lua* LuaPlugin = GetLuaPlugin( a_LuaState ); + if( LuaPlugin == NULL ) + { + lua_Debug ar; + lua_getstack(a_LuaState, 1, &ar); + lua_getinfo(a_LuaState, "nSl", &ar); + LOGERROR("ERROR: Trying to add an 'old style' plugin from within a 'new style' plugin.\nIn file: %s at line: %i", ar.source, ar.currentline); + } + a_Plugin->m_bCanBindCommands = true; + if( LuaPlugin && a_Plugin->Initialize() ) + { + m_Plugins.remove( a_Plugin ); + m_Plugins.push_back( a_Plugin ); + LuaPlugin->AddPlugin( a_Plugin ); + return true; + } + + a_Plugin->m_bCanBindCommands = false; + return false; +} + + + + + +bool cPluginManager::AddLuaPlugin( cPlugin_Lua* a_Plugin ) +{ + m_LuaPlugins.push_back( a_Plugin ); // It HAS to be in here before calling Initialize, so it can be found by AddPlugin() + if(a_Plugin->Initialize() ) + { + return true; + } + LOG(">>>>>>> Could not initialize a plugin! "); + m_LuaPlugins.remove( a_Plugin ); + return false; +} + + + + + +void cPluginManager::RemoveLuaPlugin( std::string a_FileName ) +{ + for( LuaPluginList::iterator itr = m_LuaPlugins.begin(); itr != m_LuaPlugins.end(); ++itr ) + { + if( (*itr)->GetFileName() == a_FileName ) + { + cPlugin_Lua* Plugin = *itr; + m_LuaPlugins.remove( Plugin ); + delete Plugin; + return; + } + } +} + + + + + +cPlugin_Lua* cPluginManager::GetLuaPlugin( lua_State* a_State ) +{ + for( LuaPluginList::iterator itr = m_LuaPlugins.begin(); itr != m_LuaPlugins.end(); ++itr ) + { + if( (*itr)->GetLuaState() == a_State ) + { + return *itr; + } + } + return NULL; +} + + + + + +void cPluginManager::AddHook( cPlugin* a_Plugin, PluginHook a_Hook ) +{ + if( !a_Plugin ) + { + LOGWARN("Called cPluginManager::AddHook while a_Plugin is NULL"); + return; + } + PluginList & Plugins = m_Hooks[ a_Hook ]; + Plugins.remove( a_Plugin ); + Plugins.push_back( a_Plugin ); +} + + + + + +unsigned int cPluginManager::GetNumPlugins() const +{ + return m_Plugins.size(); +} + + + + + +bool cPluginManager::HasPlugin( cPlugin* a_Plugin ) const +{ + for( PluginList::const_iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr ) + { + if( *itr == a_Plugin ) + return true; + } + return false; +} \ No newline at end of file diff --git a/source/PluginManager.h b/source/PluginManager.h new file mode 100644 index 000000000..a62aa795a --- /dev/null +++ b/source/PluginManager.h @@ -0,0 +1,149 @@ + +#pragma once + +#include "Item.h" + +struct lua_State; +class cLuaCommandBinder; +class cSquirrelCommandBinder; +class cPlugin; +class cPlugin_Lua; + +// fwd: cWorld.h +class cWorld; + +// fwd: cLuaChunk.h +class cLuaChunk; + +// fwd: cPlayer.h +class cPlayer; + +// fwd: CraftingRecipes.h +class cCraftingGrid; +class cCraftingRecipe; + +// fwd: cPickup.h +class cPickup; + + + + + +class cPluginManager //tolua_export +{ //tolua_export +public: //tolua_export + + // Called each tick + virtual void Tick(float a_Dt); + + // tolua_begin + enum PluginHook + { + HOOK_TICK, + HOOK_CHAT, + HOOK_COLLECT_PICKUP, + HOOK_COLLECT_ITEM = HOOK_COLLECT_PICKUP, // OBSOLETE, use HOOK_COLLECT_PICKUP instead + HOOK_BLOCK_DIG, + HOOK_BLOCK_PLACE, + HOOK_DISCONNECT, + HOOK_HANDSHAKE, + HOOK_LOGIN, + HOOK_PLAYER_SPAWN, + HOOK_PLAYER_JOIN, + HOOK_PLAYER_MOVE, + HOOK_TAKE_DAMAGE, + HOOK_KILLED, + HOOK_CHUNK_GENERATED, + HOOK_CHUNK_GENERATING, + HOOK_BLOCK_TO_DROPS, + HOOK_PRE_CRAFTING, /// cPlayer, cCraftingGrid, cCraftingRecipe + HOOK_CRAFTING_NO_RECIPE, /// cPlayer, cCraftingGrid, cCraftingRecipe + HOOK_POST_CRAFTING, /// cPlayer, cCraftingGrid, cCraftingRecipe + HOOK_BLOCK_TO_PICKUP, /// BlockType, BlockMeta, cPlayer, cItem, cItems + HOOK_WEATHER_CHANGED, /// cWorld + HOOK_UPDATING_SIGN, /// cWorld, int, int, int, string, string, string, string + HOOK_UPDATED_SIGN, /// cWorld, int, int, int, string, string, string, string + + // E_PLUGIN_ names are obsolete, but are kept for compatibility reasons + E_PLUGIN_TICK = HOOK_TICK, + E_PLUGIN_CHAT = HOOK_CHAT, + E_PLUGIN_COLLECT_ITEM = HOOK_COLLECT_ITEM, + E_PLUGIN_BLOCK_DIG = HOOK_BLOCK_DIG, + E_PLUGIN_BLOCK_PLACE = HOOK_BLOCK_PLACE, + E_PLUGIN_DISCONNECT = HOOK_DISCONNECT, + E_PLUGIN_HANDSHAKE = HOOK_HANDSHAKE, + E_PLUGIN_LOGIN = HOOK_LOGIN, + E_PLUGIN_PLAYER_SPAWN = HOOK_PLAYER_SPAWN, + E_PLUGIN_PLAYER_JOIN = HOOK_PLAYER_JOIN, + E_PLUGIN_PLAYER_MOVE = HOOK_PLAYER_MOVE, + E_PLUGIN_TAKE_DAMAGE = HOOK_TAKE_DAMAGE, + E_PLUGIN_KILLED = HOOK_KILLED, + E_PLUGIN_CHUNK_GENERATED = HOOK_CHUNK_GENERATED, + E_PLUGIN_CHUNK_GENERATING = HOOK_CHUNK_GENERATING, + E_PLUGIN_BLOCK_TO_DROPS = HOOK_BLOCK_TO_DROPS, + }; + // tolua_end + + static cPluginManager * GetPluginManager(); //tolua_export + + typedef std::list< cPlugin * > PluginList; + cPlugin * GetPlugin( const AString & a_Plugin ) const; //tolua_export + const PluginList & GetAllPlugins() const; // >> EXPORTED IN MANUALBINDINGS << + + void ReloadPlugins(); //tolua_export + bool AddPlugin( cPlugin* a_Plugin ); + bool AddPlugin( lua_State* a_LuaState, cPlugin* a_Plugin ); //tolua_export + bool AddLuaPlugin( cPlugin_Lua* a_Plugin ); + void AddHook( cPlugin* a_Plugin, PluginHook a_Hook ); //tolua_export + + unsigned int GetNumPlugins() const; //tolua_export + + // If the hook returns true, no further hook is called and the functions return false + bool CallHook( PluginHook a_Hook, unsigned int a_NumArgs, ... ); + + bool CallHookBlockDig (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE OldBlock, NIBBLETYPE OldMeta); + bool CallHookBlockPlace (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem); + bool CallHookBlockToPickup (BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups); + bool CallHookChat (cPlayer * a_Player, const AString & a_Message); + bool CallHookChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cLuaChunk * a_Chunk); + bool CallHookCollectPickup (cPlayer * a_Player, cPickup & a_Pickup); + bool CallHookCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); + bool CallHookDisconnect (cPlayer * a_Player, const AString & a_Reason); + bool CallHookLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username); + bool CallHookPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); + bool CallHookPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); + bool CallHookUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player); + bool CallHookUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player); + bool CallHookWeatherChanged (cWorld * a_World); + bool CallHookHandshake (cClientHandle * a_ClientHandle, const AString & a_Username); + + void RemoveHooks( cPlugin* a_Plugin ); + void RemovePlugin( cPlugin* a_Plugin, bool a_bDelete = false ); //tolua_export + void RemoveLuaPlugin( std::string a_FileName ); //tolua_export + cPlugin_Lua* GetLuaPlugin( lua_State* a_State ); //tolua_export + + cLuaCommandBinder* GetLuaCommandBinder() const { return m_LuaCommandBinder; } + + cSquirrelCommandBinder* GetSquirrelCommandBinder() { return m_SquirrelCommandBinder; } + + bool HasPlugin( cPlugin* a_Plugin ) const; +private: + friend class cRoot; + cPluginManager(); + ~cPluginManager(); + + typedef std::list< cPlugin_Lua* > LuaPluginList; + typedef std::map< cPluginManager::PluginHook, cPluginManager::PluginList > HookMap; + + LuaPluginList m_LuaPlugins; + PluginList m_Plugins; + HookMap m_Hooks; + + void ReloadPluginsNow(); + void UnloadPluginsNow(); + + cLuaCommandBinder* m_LuaCommandBinder; + cSquirrelCommandBinder* m_SquirrelCommandBinder; + + bool m_bReloadPlugins; +}; //tolua_export diff --git a/source/Plugin_Lua.cpp b/source/Plugin_Lua.cpp new file mode 100644 index 000000000..14258dd0d --- /dev/null +++ b/source/Plugin_Lua.cpp @@ -0,0 +1,98 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#define LUA_USE_POSIX +#include "Plugin_Lua.h" +#include "PluginManager.h" +#include "Root.h" + +extern "C" +{ + #include "lualib.h" +} + +#include "tolua++.h" +#include "Bindings.h" +#include "ManualBindings.h" + +bool report_errors(lua_State* lua, int status) +{ + if ( status!=0 ) + { + std::string s = lua_tostring(lua, -1); + LOGERROR("-- %s", s.c_str() ); + lua_pop(lua, 1); + return true; + } + return false; +} + +cPlugin_Lua::~cPlugin_Lua() +{ + UnloadPlugins(); + if( m_LuaState ) + { + lua_close( m_LuaState ); + m_LuaState = 0; + } +} + +cPlugin_Lua::cPlugin_Lua(const char* a_Plugin) +: m_LuaState( 0 ) +{ + m_FileName.assign( a_Plugin ); +} + +bool cPlugin_Lua::Initialize() +{ + if( !m_LuaState ) + { + m_LuaState = lua_open(); + luaL_openlibs( m_LuaState ); + tolua_AllToLua_open(m_LuaState); + ManualBindings::Bind( m_LuaState ); + } + + int s = luaL_loadfile(m_LuaState, (std::string("Plugins/") + m_FileName ).c_str() ); + if( report_errors( m_LuaState, s ) ) + { + LOGERROR("Can't load plugin %s", m_FileName.c_str() ); + lua_close( m_LuaState ); + m_LuaState = 0; + return false; + } + + s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0); + if( report_errors( m_LuaState, s ) ) + { + LOGERROR("Error in plugin %s", m_FileName.c_str() ); + lua_close( m_LuaState ); + m_LuaState = 0; + return false; + } + return true; +} + +void cPlugin_Lua::AddPlugin( cPlugin* a_Plugin ) +{ + m_Plugins.push_back( a_Plugin ); +} + +void cPlugin_Lua::RemovePlugin( cPlugin* a_Plugin ) +{ + m_Plugins.remove( a_Plugin ); + cRoot::Get()->GetPluginManager()->RemovePlugin( a_Plugin, true ); +} + +void cPlugin_Lua::UnloadPlugins() +{ + while( m_Plugins.begin() != m_Plugins.end() ) + { + RemovePlugin( *m_Plugins.begin() ); + } +} + +lua_State* cPlugin_Lua::GetLuaState() +{ + return m_LuaState; +} \ No newline at end of file diff --git a/source/Plugin_Lua.h b/source/Plugin_Lua.h new file mode 100644 index 000000000..73787de0c --- /dev/null +++ b/source/Plugin_Lua.h @@ -0,0 +1,38 @@ + +#pragma once + +class cPickup; +class cPlayer; +class cPlugin; + + + + + +class cPlugin_Lua //tolua_export +{ //tolua_export +public: + cPlugin_Lua(const char* a_Plugin); + ~cPlugin_Lua(); + + virtual bool Initialize(); + + std::string GetFileName() { return m_FileName; } //tolua_export + typedef struct lua_State lua_State; + lua_State* GetLuaState(); + + void AddPlugin( cPlugin* a_Plugin ); + void RemovePlugin( cPlugin* a_Plugin ); +private: + void UnloadPlugins(); + + std::string m_FileName; + lua_State* m_LuaState; + + typedef std::list< cPlugin* > PluginList; + PluginList m_Plugins; +}; //tolua_export + + + + diff --git a/source/Plugin_NewLua.cpp b/source/Plugin_NewLua.cpp new file mode 100644 index 000000000..061441735 --- /dev/null +++ b/source/Plugin_NewLua.cpp @@ -0,0 +1,794 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#define LUA_USE_POSIX +#include "Plugin_NewLua.h" +#include "MCLogger.h" +#include "LuaItems.h" + +extern "C" +{ +#include "lualib.h" +} + +#include "tolua++.h" +#include "Bindings.h" +#include "ManualBindings.h" + +#ifdef _WIN32 +// #include "wdirent.h" +#else +#include +#endif + + + + + +extern bool report_errors(lua_State* lua, int status); + + + + + +cPlugin_NewLua::cPlugin_NewLua( const char* a_PluginName ) + : m_LuaState( 0 ) + , cWebPlugin() +{ + m_Directory = a_PluginName; +} + + + + + +cPlugin_NewLua::~cPlugin_NewLua() +{ + cCSLock Lock( m_CriticalSection ); + + if( m_LuaState ) + { + lua_close( m_LuaState ); + m_LuaState = 0; + } +} + + + + + +bool cPlugin_NewLua::Initialize() +{ + cCSLock Lock( m_CriticalSection ); + if( !m_LuaState ) + { + m_LuaState = lua_open(); + luaL_openlibs( m_LuaState ); + tolua_AllToLua_open(m_LuaState); + ManualBindings::Bind( m_LuaState ); + } + + std::string PluginPath = FILE_IO_PREFIX + GetLocalDirectory() + "/"; + + // Load all files for this plugin, and execute them + AStringList Files = GetDirectoryContents(PluginPath.c_str()); + for (AStringList::const_iterator itr = Files.begin(); itr != Files.end(); ++itr) + { + if (itr->rfind(".lua") == AString::npos) + { + continue; + } + AString Path = PluginPath + *itr; + int s = luaL_loadfile(m_LuaState, Path.c_str() ); + if( report_errors( m_LuaState, s ) ) + { + LOGERROR("Can't load plugin %s because of an error in file %s", m_Directory.c_str(), Path.c_str() ); + lua_close( m_LuaState ); + m_LuaState = 0; + return false; + } + + s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0); + if( report_errors( m_LuaState, s ) ) + { + LOGERROR("Error in plugin %s in file %s", m_Directory.c_str(), Path.c_str() ); + lua_close( m_LuaState ); + m_LuaState = 0; + return false; + } + } // for itr - Files[] + + // Call intialize function + if( !PushFunction("Initialize") ) + { + lua_close( m_LuaState ); + m_LuaState = 0; + return false; + } + + tolua_pushusertype(m_LuaState, this, "cPlugin_NewLua"); + + if( !CallFunction(1, 1, "Initialize") ) + { + lua_close( m_LuaState ); + m_LuaState = 0; + return false; + } + + if( !lua_isboolean( m_LuaState, -1 ) ) + { + LOGWARN("Error in plugin %s Initialize() must return a boolean value!", m_Directory.c_str() ); + lua_close( m_LuaState ); + m_LuaState = 0; + return false; + } + + bool bSuccess = (tolua_toboolean( m_LuaState, -1, 0) > 0); + return bSuccess; +} + + + + + +AString cPlugin_NewLua::GetLocalDirectory(void) const +{ + return std::string("Plugins/") + m_Directory; +} + + + + + +void cPlugin_NewLua::OnDisable() +{ + cCSLock Lock( m_CriticalSection ); + if( !PushFunction("OnDisable", false) ) // false = don't log error if not found + return; + + CallFunction(0, 0, "OnDisable"); +} + + + + + +void cPlugin_NewLua::Tick(float a_Dt) +{ + cCSLock Lock( m_CriticalSection ); + if( !PushFunction("Tick") ) + return; + + tolua_pushnumber( m_LuaState, a_Dt ); + + CallFunction(1, 0, "Tick"); +} + + + + + +bool cPlugin_NewLua::OnCollectPickup(cPlayer * a_Player, cPickup * a_Pickup) +{ + cCSLock Lock(m_CriticalSection); + if (!PushFunction("OnCollectPickup")) + { + return false; + } + + tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); + tolua_pushusertype(m_LuaState, a_Pickup, "cPickup"); + + if (!CallFunction(2, 1, "OnCollectPickup")) + { + return false; + } + + return (tolua_toboolean(m_LuaState, -1, 0) > 0); +} + + + + + +bool cPlugin_NewLua::OnDisconnect(cPlayer * a_Player, const AString & a_Reason) +{ + cCSLock Lock(m_CriticalSection); + if (!PushFunction("OnDisconnect")) + { + return false; + } + + tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); + tolua_pushstring (m_LuaState, a_Reason.c_str()); + + if (!CallFunction(2, 1, "OnDisconnect")) + { + return false; + } + + return (tolua_toboolean( m_LuaState, -1, 0) > 0); +} + + + + + +bool cPlugin_NewLua::OnBlockPlace(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) +{ + cCSLock Lock(m_CriticalSection); + if (!PushFunction("OnBlockPlace")) + { + return false; + } + + tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); + tolua_pushnumber (m_LuaState, a_BlockX); + tolua_pushnumber (m_LuaState, a_BlockY); + tolua_pushnumber (m_LuaState, a_BlockZ); + tolua_pushnumber (m_LuaState, a_BlockFace); + tolua_pushusertype(m_LuaState, (void *)&a_HeldItem, "cItem"); + + if (!CallFunction(6, 1, "OnBlockPlace")) + { + return false; + } + + return (tolua_toboolean( m_LuaState, -1, 0) > 0); +} + + + + + +bool cPlugin_NewLua::OnBlockDig(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) +{ + cCSLock Lock(m_CriticalSection); + if (!PushFunction("OnBlockDig")) + { + return false; + } + + tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); + tolua_pushnumber (m_LuaState, a_BlockX); + tolua_pushnumber (m_LuaState, a_BlockY); + tolua_pushnumber (m_LuaState, a_BlockZ); + tolua_pushnumber (m_LuaState, a_BlockFace); + tolua_pushnumber (m_LuaState, a_Status); + tolua_pushnumber (m_LuaState, a_OldBlock); + tolua_pushnumber (m_LuaState, a_OldMeta); + + if (!CallFunction(8, 1, "OnBlockDig")) + { + return false; + } + + return (tolua_toboolean( m_LuaState, -1, 0) > 0); +} + + + + + +bool cPlugin_NewLua::OnChat(cPlayer * a_Player, const AString & a_Message) +{ + cCSLock Lock(m_CriticalSection); + if (!PushFunction("OnChat")) + { + return false; + } + + tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); + tolua_pushstring (m_LuaState, a_Message.c_str()); + + if (!CallFunction(2, 1, "OnChat")) + { + return false; + } + + return (tolua_toboolean( m_LuaState, -1, 0) > 0); +} + + + + + +bool cPlugin_NewLua::OnLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) +{ + cCSLock Lock( m_CriticalSection ); + if( !PushFunction("OnLogin") ) + return false; + + tolua_pushusertype (m_LuaState, a_Client, "cClientHandle"); + tolua_pushnumber (m_LuaState, a_ProtocolVersion); + tolua_pushcppstring(m_LuaState, a_Username); + + if (!CallFunction(3, 1, "OnLogin")) + return false; + + bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); + return bRetVal; +} + + + + + +void cPlugin_NewLua::OnPlayerSpawn( cPlayer* a_Player ) +{ + cCSLock Lock( m_CriticalSection ); + if( !PushFunction("OnPlayerSpawn") ) + return; + + tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); + + CallFunction(1, 0, "OnPlayerSpawn"); +} + + + + + +bool cPlugin_NewLua::OnPlayerJoin( cPlayer* a_Player ) +{ + cCSLock Lock( m_CriticalSection ); + if( !PushFunction("OnPlayerJoin") ) + return false; + + tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); + + if( !CallFunction(1, 1, "OnPlayerJoin") ) + return false; + + bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); + return bRetVal; +} + + + + + +void cPlugin_NewLua::OnPlayerMove( cPlayer* a_Player ) +{ + cCSLock Lock( m_CriticalSection ); + if( !PushFunction("OnPlayerMove") ) + return; + + tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); + + CallFunction(1, 0, "OnPlayerMove"); +} + + + + + +void cPlugin_NewLua::OnTakeDamage( cPawn* a_Pawn, TakeDamageInfo* a_TakeDamageInfo ) +{ + cCSLock Lock( m_CriticalSection ); + if( !PushFunction("OnTakeDamage") ) + return; + + tolua_pushusertype(m_LuaState, a_Pawn, "cPawn"); + tolua_pushusertype(m_LuaState, a_TakeDamageInfo, "TakeDamageInfo"); + + CallFunction(2, 0, "OnTakeDamage"); +} + + + + + +bool cPlugin_NewLua::OnKilled( cPawn* a_Killed, cEntity* a_Killer ) +{ + cCSLock Lock( m_CriticalSection ); + if( !PushFunction("OnKilled") ) + return false; + + tolua_pushusertype(m_LuaState, a_Killed, "cPawn"); + tolua_pushusertype(m_LuaState, a_Killer, "cEntity"); + + if( !CallFunction(2, 1, "OnKilled") ) + return false; + + bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); + return bRetVal; +} + + + + + +void cPlugin_NewLua::OnChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CriticalSection); + if (!PushFunction("OnChunkGenerated")) + { + return; + } + + tolua_pushusertype(m_LuaState, a_World, "cWorld"); + tolua_pushnumber (m_LuaState, a_ChunkX); + tolua_pushnumber (m_LuaState, a_ChunkZ); + + CallFunction(3, 0, "OnChunkGenerated"); +} + + + + + +bool cPlugin_NewLua::OnChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cLuaChunk * a_pLuaChunk) +{ + cCSLock Lock(m_CriticalSection); + if (!PushFunction("OnChunkGenerating")) + return false; + + tolua_pushusertype(m_LuaState, a_World, "cWorld"); + tolua_pushnumber (m_LuaState, a_ChunkX); + tolua_pushnumber (m_LuaState, a_ChunkZ); + tolua_pushusertype(m_LuaState, a_pLuaChunk, "cLuaChunk"); + + if( !CallFunction(3, 1, "OnChunkGenerating") ) + return false; + + bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); + return bRetVal; +} + + + + + +bool cPlugin_NewLua::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + cCSLock Lock(m_CriticalSection); + if (!PushFunction("OnPreCrafting")) + return false; + + tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer"); + tolua_pushusertype(m_LuaState, (void *)a_Grid, "cCraftingGrid"); + tolua_pushusertype(m_LuaState, (void *)a_Recipe, "cCraftingRecipe"); + + if (!CallFunction(3, 1, "OnPreCrafting")) + { + return false; + } + + bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); + return bRetVal; +} + + + + + +bool cPlugin_NewLua::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + cCSLock Lock(m_CriticalSection); + if (!PushFunction("OnCraftingNoRecipe")) + return false; + + tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer"); + tolua_pushusertype(m_LuaState, (void *)a_Grid, "cCraftingGrid"); + tolua_pushusertype(m_LuaState, (void *)a_Recipe, "cCraftingRecipe"); + + if (!CallFunction(3, 1, "OnCraftingNoRecipe")) + { + return false; + } + + bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); + return bRetVal; +} + + + + + +bool cPlugin_NewLua::OnPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + cCSLock Lock(m_CriticalSection); + if (!PushFunction("OnPostCrafting")) + return false; + + tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer"); + tolua_pushusertype(m_LuaState, (void *)a_Grid, "cCraftingGrid"); + tolua_pushusertype(m_LuaState, (void *)a_Recipe, "cCraftingRecipe"); + + if (!CallFunction(3, 1, "OnPostCrafting")) + { + return false; + } + + bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); + return bRetVal; +} + + + + + +bool cPlugin_NewLua::OnBlockToPickup( + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups +) +{ + cLuaItems Pickups(a_Pickups); + cCSLock Lock(m_CriticalSection); + if (!PushFunction("OnBlockToPickup")) + return false; + + tolua_pushnumber (m_LuaState, a_BlockType); + tolua_pushnumber (m_LuaState, a_BlockMeta); + tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer"); + tolua_pushusertype(m_LuaState, (void *)&a_EquippedItem, "cItem"); + tolua_pushusertype(m_LuaState, (void *)&Pickups, "cLuaItems"); + + if (!CallFunction(5, 1, "OnBlockToPickup")) + { + return false; + } + + bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); + return bRetVal; +} + + + + + +bool cPlugin_NewLua::OnWeatherChanged(cWorld * a_World) +{ + cCSLock Lock(m_CriticalSection); + if (!PushFunction("OnWeatherChanged")) + { + return false; + } + + tolua_pushusertype(m_LuaState, (void *)a_World, "cWorld"); + + if (!CallFunction(1, 1, "OnWeatherChanged")) + { + return false; + } + + bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); + return bRetVal; +} + + + + + +bool cPlugin_NewLua::OnUpdatingSign( + cWorld * a_World, + int a_BlockX, int a_BlockY, int a_BlockZ, + AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, + cPlayer * a_Player +) +{ + cCSLock Lock(m_CriticalSection); + if (!PushFunction("OnUpdatingSign")) + { + return false; + } + + tolua_pushusertype(m_LuaState, (void *)a_World, "cWorld"); + tolua_pushnumber (m_LuaState, a_BlockX); + tolua_pushnumber (m_LuaState, a_BlockY); + tolua_pushnumber (m_LuaState, a_BlockZ); + tolua_pushstring (m_LuaState, a_Line1.c_str()); + tolua_pushstring (m_LuaState, a_Line2.c_str()); + tolua_pushstring (m_LuaState, a_Line3.c_str()); + tolua_pushstring (m_LuaState, a_Line4.c_str()); + tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer"); + + if (!CallFunction(9, 5, "OnUpdatingSign")) + { + return false; + } + + bool bRetVal = (tolua_toboolean( m_LuaState, -5, 0) > 0); + if (lua_isstring(m_LuaState, -4)) + { + a_Line1 = tolua_tostring(m_LuaState, -4, ""); + } + if (lua_isstring(m_LuaState, -3)) + { + a_Line2 = tolua_tostring(m_LuaState, -3, ""); + } + if (lua_isstring(m_LuaState, -2)) + { + a_Line3 = tolua_tostring(m_LuaState, -2, ""); + } + if (lua_isstring(m_LuaState, -1)) + { + a_Line4 = tolua_tostring(m_LuaState, -1, ""); + } + return bRetVal; +} + + + + + +bool cPlugin_NewLua::OnUpdatedSign( + cWorld * a_World, + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, + cPlayer * a_Player +) +{ + cCSLock Lock(m_CriticalSection); + if (!PushFunction("OnUpdatedSign")) + { + return false; + } + + tolua_pushusertype(m_LuaState, (void *)a_World, "cWorld"); + tolua_pushnumber (m_LuaState, a_BlockX); + tolua_pushnumber (m_LuaState, a_BlockY); + tolua_pushnumber (m_LuaState, a_BlockZ); + tolua_pushstring (m_LuaState, a_Line1.c_str()); + tolua_pushstring (m_LuaState, a_Line2.c_str()); + tolua_pushstring (m_LuaState, a_Line3.c_str()); + tolua_pushstring (m_LuaState, a_Line4.c_str()); + tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer"); + + if (!CallFunction(9, 1, "OnUpdatedSign")) + { + return false; + } + + bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); + return bRetVal; +} + + + + + +bool cPlugin_NewLua::OnHandshake(cClientHandle * a_Client, const AString & a_Username) +{ + cCSLock Lock(m_CriticalSection); + if (!PushFunction("OnHandshake")) + { + return false; + } + + tolua_pushusertype(m_LuaState, a_Client, "cClientHandle"); + tolua_pushstring (m_LuaState, a_Username.c_str()); + + if (!CallFunction(2, 1, "OnHandshake")) + { + return false; + } + + bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); + return bRetVal; +} + + + + + +cPlugin_NewLua * cPlugin_NewLua::CreateWebPlugin(lua_State * a_LuaState) +{ + LOGWARN("WARNING: Using deprecated function CreateWebPlugin()! A Lua plugin is a WebPlugin by itself now. (plugin \"%s\" in folder \"%s\")", + cPlugin::GetName().c_str(), m_Directory.c_str() + ); + return this; +} + + + + + +AString cPlugin_NewLua::HandleWebRequest( HTTPRequest * a_Request ) +{ + cCSLock Lock( m_CriticalSection ); + std::string RetVal = ""; + + std::pair< std::string, std::string > TabName = GetTabNameForRequest(a_Request); + std::string SafeTabName = TabName.second; + if( SafeTabName.empty() ) + return ""; + + sWebPluginTab* Tab = 0; + for( TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr ) + { + if( (*itr)->SafeTitle.compare( SafeTabName ) == 0 ) // This is the one! Rawr + { + Tab = *itr; + break; + } + } + + if( Tab ) + { + //LOGINFO("1. Stack size: %i", lua_gettop(m_LuaState) ); + lua_rawgeti( m_LuaState, LUA_REGISTRYINDEX, Tab->UserData); // same as lua_getref() + + //LOGINFO("2. Stack size: %i", lua_gettop(m_LuaState) ); + // Push HTTPRequest + tolua_pushusertype( m_LuaState, a_Request, "HTTPRequest" ); + //LOGINFO("Calling bound function! :D"); + int s = lua_pcall( m_LuaState, 1, 1, 0); + + if ( s != 0 ) + { + std::string err = lua_tostring(m_LuaState, -1); + LOGERROR("-- %s", err.c_str() ); + lua_pop(m_LuaState, 1); + LOGINFO("error. Stack size: %i", lua_gettop(m_LuaState) ); + return err; // Show the error message in the web page, looks cool + } + + if( !lua_isstring( m_LuaState, -1 ) ) + { + LOGWARN("WARNING: WebPlugin tab '%s' did not return a string!", Tab->Title.c_str() ); + lua_pop(m_LuaState, 1); // Pop return value + return std::string("WARNING: WebPlugin tab '") + Tab->Title + std::string("' did not return a string!"); + } + + RetVal += tolua_tostring(m_LuaState, -1, 0); + lua_pop(m_LuaState, 1); // Pop return value + //LOGINFO("ok. Stack size: %i", lua_gettop(m_LuaState) ); + } + + return RetVal; +} + + + + + +bool cPlugin_NewLua::AddWebTab( const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference ) +{ + cCSLock Lock( m_CriticalSection ); + if( a_LuaState != m_LuaState ) + { + LOGERROR("Only allowed to add a tab to a WebPlugin of your own Plugin!"); + return false; + } + sWebPluginTab* Tab = new sWebPluginTab(); + Tab->Title = a_Title; + Tab->SafeTitle = SafeString( a_Title ); + + Tab->UserData = a_FunctionReference; + + GetTabs().push_back( Tab ); + return true; +} + + + + + +// Helper functions +bool cPlugin_NewLua::PushFunction( const char* a_FunctionName, bool a_bLogError /* = true */ ) +{ + lua_getglobal(m_LuaState, a_FunctionName); + if(!lua_isfunction(m_LuaState,-1)) + { + if( a_bLogError ) + { + LOGWARN("Error in plugin %s: Could not find function %s()", m_Directory.c_str(), a_FunctionName ); + } + lua_pop(m_LuaState,1); + return false; + } + return true; +} + +bool cPlugin_NewLua::CallFunction( int a_NumArgs, int a_NumResults, const char* a_FunctionName ) +{ + int s = lua_pcall(m_LuaState, a_NumArgs, a_NumResults, 0); + if( report_errors( m_LuaState, s ) ) + { + LOGWARN("Error in plugin %s calling function %s()", m_Directory.c_str(), a_FunctionName ); + return false; + } + return true; +} \ No newline at end of file diff --git a/source/Plugin_NewLua.h b/source/Plugin_NewLua.h new file mode 100644 index 000000000..38e66e329 --- /dev/null +++ b/source/Plugin_NewLua.h @@ -0,0 +1,76 @@ + +#pragma once + +#include "Plugin.h" +#include "WebPlugin.h" + + + + + +typedef struct lua_State lua_State; + + + + + +class cPlugin_NewLua : public cPlugin, public cWebPlugin //tolua_export +{ //tolua_export +public: //tolua_export + cPlugin_NewLua( const char* a_PluginName ); + ~cPlugin_NewLua(); + + virtual void OnDisable(); //tolua_export + virtual bool Initialize(); //tolua_export + + virtual void Tick(float a_Dt); //tolua_export + + virtual bool OnBlockDig (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) override; + virtual bool OnBlockPlace (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) override; + virtual bool OnBlockToPickup (BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups); + virtual bool OnChat (cPlayer * a_Player, const AString & a_Message) override; + virtual void OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override; + virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cLuaChunk * a_pLuaChunk ) override; + virtual bool OnCollectPickup (cPlayer * a_Player, cPickup * a_Pickup) override; + virtual bool OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; + virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override; + virtual bool OnKilled (cPawn * a_Killed, cEntity* a_Killer ) override; + virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) override; + virtual bool OnPlayerJoin (cPlayer * a_Player ) override; + virtual void OnPlayerMove (cPlayer * a_Player ) override; + virtual void OnPlayerSpawn (cPlayer * a_Player ) override; + virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; + virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; + virtual void OnTakeDamage (cPawn * a_Pawn, TakeDamageInfo * a_TakeDamageInfo ) override; + virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) override; + virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) override; + virtual bool OnWeatherChanged (cWorld * a_World) override; + virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username) override; + + const AString & GetDirectory(void) const {return m_Directory; } + AString GetLocalDirectory(void) const; //tolua_export + + virtual void SetName( const AString & a_Name ) override { cPlugin::SetName(a_Name); } + + // cWebPlugin override + virtual const AString & GetName(void) const {return cPlugin::GetName(); } + + // cWebPlugin and WebAdmin stuff + virtual AString HandleWebRequest( HTTPRequest * a_Request ) override; + bool AddWebTab( const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference ); // >> EXPORTED IN MANUALBINDINGS << + + lua_State* GetLuaState() { return m_LuaState; } + + OBSOLETE cPlugin_NewLua * CreateWebPlugin(lua_State * a_LuaState); //tolua_export + + cCriticalSection & GetCriticalSection() { return m_CriticalSection; } + +private: + bool PushFunction( const char* a_FunctionName, bool a_bLogError = true ); + bool CallFunction( int a_NumArgs, int a_NumResults, const char* a_FunctionName ); // a_FunctionName is only used for error messages, nothing else + + cCriticalSection m_CriticalSection; + + std::string m_Directory; + lua_State * m_LuaState; +};//tolua_export \ No newline at end of file diff --git a/source/Plugin_Squirrel.cpp b/source/Plugin_Squirrel.cpp new file mode 100644 index 000000000..930c0cf11 --- /dev/null +++ b/source/Plugin_Squirrel.cpp @@ -0,0 +1,387 @@ +#include "Globals.h" +#include "Plugin_Squirrel.h" +#include "squirrelbindings/SquirrelFunctions.h" +#include "squirrelbindings/SquirrelBindings.h" +#include "squirrelbindings/SquirrelBaseClass.h" + + +cPlugin_Squirrel::cPlugin_Squirrel( const char* a_PluginName ) +{ + SetLanguage( cPlugin::E_SQUIRREL ); + m_PluginName = a_PluginName; +} + +cPlugin_Squirrel::~cPlugin_Squirrel() +{ + delete m_Plugin; +} + +bool cPlugin_Squirrel::Initialize() +{ + cCSLock Lock(m_CriticalSection); + + std::string PluginPath = std::string("Plugins/") + m_PluginName + ".nut"; + + Sqrat::Script script; + script.CompileFile(PluginPath); + if(script.IsNull()) + { + LOGERROR("Unable to run script \"%s\"", m_PluginName); + } + + try { + script.Run(); + + Sqrat::Function construct(Sqrat::RootTable(), m_PluginName); + + if(construct.IsNull()) + { + LOGERROR("Constructor for Plugin \"%s\" not found.", m_PluginName); + return false; + } + + Sqrat::Object obj = construct.Evaluate(); + + ((cSquirrelBaseClass *) obj.GetInstanceUP())->setInstance(this); + + m_Plugin = new SquirrelObject(obj); + + + Sqrat::Object PluginName = obj.GetSlot("name"); + if(!PluginName.IsNull()) + { + this->SetName(PluginName.Cast()); + } + + Sqrat::Function init = m_Plugin->GetFunction("Initialize"); + if(init.IsNull()) + { + LOGERROR("Can not initialize plugin \"%s\"", m_PluginName); + return false; + } + + return init.Evaluate(); + + } catch(Sqrat::Exception &e) + { + LOGERROR("Initialisation of \"%s\" failed: %s", m_PluginName, e.Message().c_str()); + return false; + } +} + + + + + +void cPlugin_Squirrel::OnDisable() +{ + cCSLock Lock(m_CriticalSection); + + if(!m_Plugin->HasFunction("OnDisable")) return; + + m_Plugin->GetFunction("OnDisable").Execute(); +} + + + + + +void cPlugin_Squirrel::Tick(float a_Dt) +{ + cCSLock Lock( m_CriticalSection ); + + if(!m_Plugin->HasFunction("OnTick")) return; + + m_Plugin->GetFunction("OnTick").Execute(a_Dt); +} + + + + + +bool cPlugin_Squirrel::OnCollectPickup(cPlayer * a_Player, cPickup * a_Pickup) +{ + cCSLock Lock(m_CriticalSection); + + if (!m_Plugin->HasFunction("OnCollectPickup")) + { + return false; + } + + return m_Plugin->GetFunction("OnCollectPickup").Evaluate(a_Player, a_Pickup); +} + + + + + +bool cPlugin_Squirrel::OnDisconnect(cPlayer* a_Player, const AString & a_Reason) +{ + cCSLock Lock( m_CriticalSection ); + + if (!m_Plugin->HasFunction("OnDisconnect")) return false; + + return m_Plugin->GetFunction("OnDisconnect").Evaluate(a_Player, a_Reason); +} + + + + + +bool cPlugin_Squirrel::OnBlockPlace(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) +{ + cCSLock Lock(m_CriticalSection); + + if (!m_Plugin->HasFunction("OnBlockPlace")) return false; + + return m_Plugin->GetFunction("OnBlockPlace").Evaluate(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_HeldItem); +} + + + + + +bool cPlugin_Squirrel::OnBlockDig(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) +{ + cCSLock Lock(m_CriticalSection); + + if (!m_Plugin->HasFunction("OnBlockDig")) return false; + + return m_Plugin->GetFunction("OnBlockDig").Evaluate(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status, a_OldBlock, a_OldMeta); +} + + + + + +bool cPlugin_Squirrel::OnChat(cPlayer * a_Player, const AString & a_Message) +{ + cCSLock Lock(m_CriticalSection); + + if (!m_Plugin->HasFunction("OnChat")) return false; + + return m_Plugin->GetFunction("OnChat").Evaluate(a_Player, a_Message); + +} + + + + + +bool cPlugin_Squirrel::OnLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) +{ + cCSLock Lock( m_CriticalSection ); + + if (!m_Plugin->HasFunction("OnLogin")) + { + return false; + } + + return m_Plugin->GetFunction("OnLogin").Evaluate(a_Client, a_ProtocolVersion, a_Username); +} + + + + + +void cPlugin_Squirrel::OnPlayerSpawn( cPlayer* a_Player ) +{ + cCSLock Lock( m_CriticalSection ); + + if(!m_Plugin->HasFunction("OnPlayerSpawn")) return; + + return m_Plugin->GetFunction("OnPlayerSpawn").Execute(a_Player); + +} + + + + + +bool cPlugin_Squirrel::OnPlayerJoin( cPlayer* a_Player ) +{ + cCSLock Lock( m_CriticalSection ); + + if(!m_Plugin->HasFunction("OnPlayerJoin")) return false; + + return m_Plugin->GetFunction("OnPlayerJoin").Evaluate(a_Player); +} + + + + + +void cPlugin_Squirrel::OnPlayerMove( cPlayer* a_Player ) +{ + cCSLock Lock( m_CriticalSection ); + + if(!m_Plugin->HasFunction("OnPlayerMove")) return; + + return m_Plugin->GetFunction("OnPlayerMove").Execute(a_Player); + +} + + + + + +void cPlugin_Squirrel::OnTakeDamage( cPawn* a_Pawn, TakeDamageInfo* a_TakeDamageInfo ) +{ + cCSLock Lock( m_CriticalSection ); + + if(!m_Plugin->HasFunction("OnTakeDamage")) return; + + return m_Plugin->GetFunction("OnTakeDamage")(a_Pawn, a_TakeDamageInfo); +} + + + + + +bool cPlugin_Squirrel::OnKilled( cPawn* a_Killed, cEntity* a_Killer ) +{ + cCSLock Lock( m_CriticalSection ); + if(!m_Plugin->HasFunction("OnKilled")) return false; + return m_Plugin->GetFunction("OnKilled").Evaluate(a_Killed, a_Killer); +} + + + + + +void cPlugin_Squirrel::OnChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CriticalSection); + if(!m_Plugin->HasFunction("OnChunkGenerated")) return; + return m_Plugin->GetFunction("OnChunkGenerated")(a_World, a_ChunkX, a_ChunkZ); +} + + + + + +bool cPlugin_Squirrel::OnChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cLuaChunk * a_pLuaChunk) +{ + cCSLock Lock(m_CriticalSection); + if(!m_Plugin->HasFunction("OnChunkGenerating")) return false; + return m_Plugin->GetFunction("OnChunkGenerating").Evaluate(a_World, a_ChunkX, a_ChunkZ, a_pLuaChunk); +} + + + + + +bool cPlugin_Squirrel::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + cCSLock Lock(m_CriticalSection); + if(!m_Plugin->HasFunction("OnPreCrafting")) return false; + return m_Plugin->GetFunction("OnPreCrafting").Evaluate((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe); + +} + + + + + +bool cPlugin_Squirrel::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + cCSLock Lock(m_CriticalSection); + if(!m_Plugin->HasFunction("OnCraftingNoRecipe")) return false; + return m_Plugin->GetFunction("OnCraftingNoRecipe").Evaluate((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe); +} + + + + + +bool cPlugin_Squirrel::OnPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) +{ + cCSLock Lock(m_CriticalSection); + + if(!m_Plugin->HasFunction("OnPostCrafting")) return false; + return m_Plugin->GetFunction("OnPostCrafting").Evaluate((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe); +} + + + + + +bool cPlugin_Squirrel::OnBlockToPickup( + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, + const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups +) +{ + cCSLock Lock(m_CriticalSection); + + if(!m_Plugin->HasFunction("OnBlockToPickup")) return false; + return m_Plugin->GetFunction("OnBlockToPickup").Evaluate(a_BlockType, a_BlockMeta, (cPlayer *) a_Player, a_EquippedItem, a_Pickups); + +} + + + + + +bool cPlugin_Squirrel::OnWeatherChanged(cWorld * a_World) +{ + cCSLock Lock(m_CriticalSection); + + if(!m_Plugin->HasFunction("OnWeatherChanged")) return false; + return m_Plugin->GetFunction("OnWeatherChanged").Evaluate(a_World); +} + + + + + +bool cPlugin_Squirrel::OnUpdatingSign( + cWorld * a_World, + int a_BlockX, int a_BlockY, int a_BlockZ, + AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, + cPlayer * a_Player +) +{ + cCSLock Lock(m_CriticalSection); + + if(!m_Plugin->HasFunction("OnUpdatingSign")) return false; + return m_Plugin->GetFunction("OnUpdatingSign") + .Evaluate( + a_World, + a_BlockX, + a_BlockY, + a_BlockZ, + a_Line1, + a_Line2, + a_Line3, + a_Line4, + a_Player + ); +} + + + + + +bool cPlugin_Squirrel::OnUpdatedSign( + cWorld * a_World, + int a_BlockX, int a_BlockY, int a_BlockZ, + const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, + cPlayer * a_Player +) +{ + cCSLock Lock(m_CriticalSection); + + if(!m_Plugin->HasFunction("OnUpdatedSign")) return false; + return m_Plugin->GetFunction("OnUpdatedSign") + .Evaluate( + a_World, + a_BlockX, + a_BlockY, + a_BlockZ, + a_Line1, + a_Line2, + a_Line3, + a_Line4, + a_Player + ); +} diff --git a/source/Plugin_Squirrel.h b/source/Plugin_Squirrel.h new file mode 100644 index 000000000..9d27261d0 --- /dev/null +++ b/source/Plugin_Squirrel.h @@ -0,0 +1,53 @@ + +#pragma once + +#include "Plugin.h" +#include +#include "squirrelbindings/SquirrelObject.h" + + + + + +class cPlugin_Squirrel : + public cPlugin +{ +public: + cPlugin_Squirrel(const char* a_PluginName); + ~cPlugin_Squirrel(); + + void OnDisable(); + bool Initialize(); + + void Tick(float a_Dt); + + virtual bool OnBlockDig (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) override; + virtual bool OnBlockPlace (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) override; + virtual bool OnBlockToPickup (BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups); + virtual bool OnChat (cPlayer * a_Player, const AString & a_Message) override; + virtual void OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override; + virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cLuaChunk * a_pLuaChunk ) override; + virtual bool OnCollectPickup (cPlayer * a_Player, cPickup * a_Pickup) override; + virtual bool OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; + virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override; + virtual bool OnKilled (cPawn* a_Killed, cEntity* a_Killer ) override; + virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) override; + virtual bool OnPlayerJoin (cPlayer* a_Player ) override; + virtual void OnPlayerMove (cPlayer* a_Player ) override; + virtual void OnPlayerSpawn (cPlayer* a_Player ) override; + virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; + virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; + virtual void OnTakeDamage (cPawn* a_Pawn, TakeDamageInfo* a_TakeDamageInfo ) override; + virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) override; + virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) override; + virtual bool OnWeatherChanged (cWorld * a_World) override; + +protected: + const char * m_PluginName; + cCriticalSection m_CriticalSection; + SquirrelObject *m_Plugin; +}; + + + + diff --git a/source/Protocol/Protocol125.cpp b/source/Protocol/Protocol125.cpp index 67c8f53b3..2ae82fb43 100644 --- a/source/Protocol/Protocol125.cpp +++ b/source/Protocol/Protocol125.cpp @@ -13,16 +13,16 @@ Documentation: #include "Protocol125.h" -#include "../cClientHandle.h" +#include "../ClientHandle.h" #include "ChunkDataSerializer.h" -#include "../cEntity.h" +#include "../Entity.h" #include "../Mobs/Monster.h" -#include "../cPickup.h" -#include "../cPlayer.h" -#include "../cChatColor.h" +#include "../Pickup.h" +#include "../Player.h" +#include "../ChatColor.h" #include "../UI/Window.h" -#include "../cRoot.h" -#include "../cServer.h" +#include "../Root.h" +#include "../Server.h" diff --git a/source/Protocol/Protocol132.cpp b/source/Protocol/Protocol132.cpp index 9e6af0729..32b55e280 100644 --- a/source/Protocol/Protocol132.cpp +++ b/source/Protocol/Protocol132.cpp @@ -5,13 +5,13 @@ #include "Globals.h" #include "Protocol132.h" -#include "../cRoot.h" -#include "../cServer.h" -#include "../cClientHandle.h" +#include "../Root.h" +#include "../Server.h" +#include "../ClientHandle.h" #include "../../CryptoPP/randpool.h" -#include "../cItem.h" +#include "../Item.h" #include "ChunkDataSerializer.h" -#include "../cPlayer.h" +#include "../Player.h" #include "../Mobs/Monster.h" #include "../UI/Window.h" diff --git a/source/Protocol/ProtocolRecognizer.cpp b/source/Protocol/ProtocolRecognizer.cpp index 18bf3b0b3..095d63f8f 100644 --- a/source/Protocol/ProtocolRecognizer.cpp +++ b/source/Protocol/ProtocolRecognizer.cpp @@ -9,10 +9,10 @@ #include "ProtocolRecognizer.h" #include "Protocol125.h" #include "Protocol132.h" -#include "../cClientHandle.h" -#include "../cRoot.h" -#include "../cWorld.h" -#include "../cChatColor.h" +#include "../ClientHandle.h" +#include "../Root.h" +#include "../World.h" +#include "../ChatColor.h" diff --git a/source/Redstone.cpp b/source/Redstone.cpp new file mode 100644 index 000000000..01a343223 --- /dev/null +++ b/source/Redstone.cpp @@ -0,0 +1,374 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Redstone.h" +#include "Piston.h" +#include "Root.h" +#include "World.h" +#include "BlockID.h" +#include + + + + + +bool cRedstone::s_UseRedstone = false; + + + + + +cRedstone::cRedstone( cWorld* a_World ) + :m_World ( a_World ) + ,m_Metadata ( 0 ) + ,m_Direction ( 0 ) +{ + +} + + + + + +void cRedstone::ChangeRedstone( int fillx, int filly, int fillz, bool added ) +{ + s_UseRedstone = false; + if( !s_UseRedstone ) return; + + char before; + //int tempX; + //int tempY; + //int tempZ; + //int state; + //m_Direction + // 0 = x+ + // 1 = x- + // 2 = z+ + // 3 = z- + // 4 = y+ + // 5 = v- + + + if (added) { + m_Metadata = 15; + } else { + m_Metadata = 0; + } + before = m_Metadata; + + //printf("etb1\n"); + CalculateRedstone( fillx, filly, fillz ); //calculate all item centers. + //printf("etb2\n"); + + int Block = (int)m_World->GetBlock( fillx, filly, fillz ); + + switch (Block)//these blocks won't trigger the normal calculaton because they are affected by the redstone around them. So we check each possible channel around them instead. + { + case E_BLOCK_REDSTONE_TORCH_ON: + case E_BLOCK_REDSTONE_TORCH_OFF: + case E_BLOCK_AIR: + case E_BLOCK_PISTON_EXTENSION: + case E_BLOCK_PISTON: + case E_BLOCK_STICKY_PISTON: + { + m_Metadata = 0; + m_Direction = 0; + CalculateRedstone( fillx+1, filly, fillz ); + m_Metadata = 0; + m_Direction = 1; + CalculateRedstone( fillx-1, filly, fillz ); + m_Metadata = 0; + m_Direction = 2; + CalculateRedstone( fillx, filly, fillz+1 ); + m_Metadata = 0; + m_Direction = 3; + CalculateRedstone( fillx, filly, fillz-1 ); + m_Metadata = 0; + CalculateRedstone( fillx, filly-1, fillz ); + break; + } + case E_BLOCK_REDSTONE_WIRE: //special case for redstone wire. + { + m_Direction = 0; + CalculateRedstone( fillx+1, filly, fillz ); + m_Direction = 1; + CalculateRedstone( fillx-1, filly, fillz ); + m_Direction = 2; + CalculateRedstone( fillx, filly, fillz+1 ); + m_Direction = 3; + CalculateRedstone( fillx, filly, fillz-1 ); + m_Metadata = 0; + CalculateRedstone( fillx, filly-1, fillz ); + + m_Direction = 4; + CalculateRedstone( fillx+1, filly+1, fillz ); + CalculateRedstone( fillx-1, filly+1, fillz ); + CalculateRedstone( fillx, filly+1, fillz+1 ); + CalculateRedstone( fillx, filly+1, fillz-1 ); + + m_Direction = 5; + CalculateRedstone( fillx+1, filly-1, fillz ); + CalculateRedstone( fillx-1, filly-1, fillz ); + CalculateRedstone( fillx, filly-1, fillz+1 ); + CalculateRedstone( fillx, filly-1, fillz-1 ); + break; + } + } + + //printf("done here\n"); +} + + + + + +void cRedstone::CalculateRedstone( int fillx, int filly, int fillz) +{ + + if ( ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_BLOCK_STICKY_PISTON ) || ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_BLOCK_PISTON ) ) { + char pistonMeta = m_World->GetBlockMeta( fillx, filly, fillz ); + if (m_Metadata > 0) { + if (pistonMeta < 6) { // only extend if piston is not already extended + cPiston Piston(m_World); + Piston.ExtendPiston(fillx, filly, fillz); + } + } else { + if (pistonMeta > 6) { // only retract if piston is not already retracted + cPiston Piston(m_World); + Piston.RetractPiston(fillx, filly, fillz); + } + } + + } else if ( ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_ITEM_LEVER ) || ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_ITEM_STONE_BUTTON ) ) { + + if ( (int)m_World->GetBlockMeta( fillx, filly, fillz) > 6 ) { //button powered + m_Metadata = 15; //change meta to 15 only if redstone power device in on possition is found. + if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) != m_Metadata ) ) { + CalculateRedstone(fillx-1,filly,fillz); + } + if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) != m_Metadata ) ) { + CalculateRedstone(fillx+1,filly,fillz); + } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) != m_Metadata ) ) { + CalculateRedstone(fillx,filly,fillz-1); + } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) != m_Metadata ) ) { + CalculateRedstone(fillx,filly,fillz+1); + } + } else { + if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) != m_Metadata ) ) { + CalculateRedstone(fillx-1,filly,fillz); + } + if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) != m_Metadata ) ) { + CalculateRedstone(fillx+1,filly,fillz); + } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) != m_Metadata ) ) { + CalculateRedstone(fillx,filly,fillz-1); + } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) != m_Metadata ) ) { + CalculateRedstone(fillx,filly,fillz+1); + } + } + + } else if ( ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_BLOCK_STONE_PRESSURE_PLATE ) || ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_BLOCK_STONE_BUTTON ) ) { + + if ( (int)m_World->GetBlockMeta( fillx, filly, fillz) == 1 ) { //plate powered + m_Metadata = 15; //change meta to 15 only if redstone power device in on possition is found. + if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) != m_Metadata ) ) { + CalculateRedstone(fillx-1,filly,fillz); + } + if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) != m_Metadata ) ) { + CalculateRedstone(fillx+1,filly,fillz); + } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) != m_Metadata ) ) { + CalculateRedstone(fillx,filly,fillz-1); + } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) != m_Metadata ) ) { + CalculateRedstone(fillx,filly,fillz+1); + } + + } else { + + if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) != m_Metadata ) ) { + CalculateRedstone(fillx-1,filly,fillz); + } + if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) != m_Metadata ) ) { + CalculateRedstone(fillx+1,filly,fillz); + } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) != m_Metadata ) ) { + CalculateRedstone(fillx,filly,fillz-1); + } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) != m_Metadata ) ) { + CalculateRedstone(fillx,filly,fillz+1); + } + } + + } else if ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_BLOCK_REDSTONE_TORCH_ON ) { //If torch is on + //printf("found torch on\n"); + m_Metadata = 15; //change meta to 15 only if redstone torch in on possition is found. + if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) != m_Metadata ) ) { + CalculateRedstone(fillx-1,filly,fillz); + } + if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) != m_Metadata ) ) { + CalculateRedstone(fillx+1,filly,fillz); + } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) != m_Metadata ) ) { + CalculateRedstone(fillx,filly,fillz-1); + } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) != m_Metadata ) ) { + CalculateRedstone(fillx,filly,fillz+1); + } + + } else if ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_BLOCK_REDSTONE_TORCH_OFF ) { //if the torch is off + //printf("found torch off\n"); + // no need to change meta here. + if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) != m_Metadata ) ) { + CalculateRedstone(fillx-1,filly,fillz); + } + if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) != m_Metadata ) ) { + CalculateRedstone(fillx+1,filly,fillz); + } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) != m_Metadata ) ) { + CalculateRedstone(fillx,filly,fillz-1); + } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) != m_Metadata ) ) { + CalculateRedstone(fillx,filly,fillz+1); + } + + } else if ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_BLOCK_REDSTONE_WIRE ) { //simple fill algorithm for redstone wire. + + if ( (int)m_World->GetBlockMeta( fillx, filly, fillz) != m_Metadata ) { + m_World->FastSetBlock( fillx, filly, fillz, (char)E_BLOCK_REDSTONE_WIRE, m_Metadata ); + m_Direction = 0; + CalculateRedstone( fillx+1, filly, fillz ); + m_Direction = 1; + CalculateRedstone( fillx-1, filly, fillz ); + m_Direction = 2; + CalculateRedstone( fillx, filly, fillz+1 ); + m_Direction = 3; + CalculateRedstone( fillx, filly, fillz-1 ); + CalculateRedstone( fillx, filly-1, fillz ); //check one block below //similar to same plane + + m_Direction = 4; + CalculateRedstone( fillx+1, filly+1, fillz ); + CalculateRedstone( fillx-1, filly+1, fillz ); + CalculateRedstone( fillx, filly+1, fillz+1 ); + CalculateRedstone( fillx, filly+1, fillz-1 ); + + m_Direction = 5; + CalculateRedstone( fillx+1, filly-1, fillz ); + CalculateRedstone( fillx-1, filly-1, fillz ); + CalculateRedstone( fillx, filly-1, fillz+1 ); + CalculateRedstone( fillx, filly-1, fillz-1 ); + } + + } else { //default, check item for torch attached to it. If meta > 0 then turn that torch off, otherwise turn it on. This change needs to be passed to the next world tick. + //check for torch to east with meta 1 //turn off + //check for torch to west with meta 2 //turn off + //check for torch to south with meta 3 //turn off + //check for torch to north with meta 4 //turn off + //check for torch above with meta 5 //turn off + if ( (int)m_World->GetBlock( fillx, filly, fillz ) != E_BLOCK_AIR ) { + if (m_Direction < 4) { //redstone wire can only power blocks on the same plane or directly below + if ( (int)m_Metadata > 0 ) { //wire powered + + //printf("bird: %i dog: %i \n",(int)m_World->GetBlock( fillx, filly+1, fillz ),(int)m_World->GetBlockMeta( fillx, filly+1, fillz)); + if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_TORCH_ON) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) == 1 ) ) { //east + m_World->m_RSList.push_back(fillx+1); + m_World->m_RSList.push_back(filly); + m_World->m_RSList.push_back(fillz); + m_World->m_RSList.push_back(00000); + } + if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_TORCH_ON) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) == 2 ) ) { //west + m_World->m_RSList.push_back(fillx-1); + m_World->m_RSList.push_back(filly); + m_World->m_RSList.push_back(fillz); + m_World->m_RSList.push_back(00000); + } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_TORCH_ON) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) == 3 ) ) { //south + m_World->m_RSList.push_back(fillx); + m_World->m_RSList.push_back(filly); + m_World->m_RSList.push_back(fillz+1); + m_World->m_RSList.push_back(00000); + } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_TORCH_ON) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) == 4 ) ) { //north + m_World->m_RSList.push_back(fillx); + m_World->m_RSList.push_back(filly); + m_World->m_RSList.push_back(fillz-1); + m_World->m_RSList.push_back(00000); + } + if ( ( (int)m_World->GetBlock( fillx, filly+1, fillz ) == E_BLOCK_REDSTONE_TORCH_ON) && ( (int)m_World->GetBlockMeta( fillx, filly+1, fillz) == 5 ) ) { //top + m_World->m_RSList.push_back(fillx); + m_World->m_RSList.push_back(filly+1); + m_World->m_RSList.push_back(fillz); + m_World->m_RSList.push_back(00000); + } + + } else { //wire not powered + + bool BlockPowered = IsBlockPowered( fillx, filly, fillz ); //chck this block for other wire turned on or torch turned on below: + if (BlockPowered == false) { //if block is not bowered by something else then I need to check for off torches and turn them on. + + //printf("echo: %i cruiser: %i \n",(int)m_World->GetBlock( fillx, filly+1, fillz ),(int)m_World->GetBlockMeta( fillx, filly+1, fillz)); + if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_TORCH_OFF) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) == 1 ) ) { //east + m_World->m_RSList.push_back(fillx+1); + m_World->m_RSList.push_back(filly); + m_World->m_RSList.push_back(fillz); + m_World->m_RSList.push_back(11111); + } + if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_TORCH_OFF) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) == 2 ) ) { //west + m_World->m_RSList.push_back(fillx-1); + m_World->m_RSList.push_back(filly); + m_World->m_RSList.push_back(fillz); + m_World->m_RSList.push_back(11111); + } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_TORCH_OFF) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) == 3 ) ) { //south + m_World->m_RSList.push_back(fillx); + m_World->m_RSList.push_back(filly); + m_World->m_RSList.push_back(fillz+1); + m_World->m_RSList.push_back(11111);; + } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_TORCH_OFF) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) == 4 ) ) { //north + m_World->m_RSList.push_back(fillx); + m_World->m_RSList.push_back(filly); + m_World->m_RSList.push_back(fillz-1); + m_World->m_RSList.push_back(11111); + } + if ( ( (int)m_World->GetBlock( fillx, filly+1, fillz ) == E_BLOCK_REDSTONE_TORCH_OFF) && ( (int)m_World->GetBlockMeta( fillx, filly+1, fillz) == 5 ) ) { //top + m_World->m_RSList.push_back(fillx); + m_World->m_RSList.push_back(filly+1); + m_World->m_RSList.push_back(fillz); + m_World->m_RSList.push_back(11111); + } + + } + + } + + } + + } + + } + +} + + + + + +bool cRedstone::IsBlockPowered( int fillx, int filly, int fillz ) +{ + + if ( (int)m_World->GetBlock( fillx, filly-1, fillz ) == E_BLOCK_REDSTONE_TORCH_ON) { return true; } + if ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_TORCH_ON) { return true; } + if ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_TORCH_ON) { return true; } + if ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_TORCH_ON) { return true; } + if ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_TORCH_ON) { return true; } + if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) > 0 ) ) { return true; } + if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) > 0 ) ) { return true; } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) > 0 ) ) { return true; } + if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) > 0 ) ) { return true; } + if ( ( (int)m_World->GetBlock( fillx, filly+1, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly+1, fillz) > 0 ) ) { return true; } + return false; +} \ No newline at end of file diff --git a/source/Redstone.h b/source/Redstone.h new file mode 100644 index 000000000..a5377e8a7 --- /dev/null +++ b/source/Redstone.h @@ -0,0 +1,122 @@ +#pragma once + +#include "Vector3i.h" + +class cWorld; +class cRedstone +{ +public: + + cRedstone( cWorld* a_World ); + + static char RepeaterRotationToMetaData( float a_Rotation ) + { + a_Rotation += 90 + 45; // So its not aligned with axis + if( a_Rotation > 360.f ) a_Rotation -= 360.f; + if( a_Rotation >= 0.f && a_Rotation < 90.f ) + return 0x1; + else if( a_Rotation >= 180 && a_Rotation < 270 ) + return 0x3; + else if( a_Rotation >= 90 && a_Rotation < 180 ) + return 0x2; + else + return 0x0; + } + + static bool IsRepeaterPointingTo( const Vector3i & a_RepeaterPos, char a_MetaData, const Vector3i & a_BlockPos ) + { + switch( a_MetaData & 0x3 ) + { + case 0x0: + if( (a_RepeaterPos - a_BlockPos).Equals( Vector3i( 0, 0, 1 ) ) ) + { + return true; + } + break; + case 0x1: + if( (a_RepeaterPos - a_BlockPos).Equals( Vector3i(-1, 0, 0 ) ) ) + { + return true; + } + break; + case 0x2: + if( (a_RepeaterPos - a_BlockPos).Equals( Vector3i( 0, 0,-1 ) ) ) + { + return true; + } + break; + case 0x3: + if( (a_RepeaterPos - a_BlockPos).Equals( Vector3i( 1, 0, 0 ) ) ) + { + return true; + } + break; + default: + break; + } + return false; + } + + static bool IsRepeaterPointingAway( const Vector3i & a_RepeaterPos, char a_MetaData, const Vector3i & a_BlockPos ) + { + switch( a_MetaData & 0x3 ) + { + case 0x0: + if( (a_RepeaterPos - a_BlockPos).Equals( Vector3i( 0, 0,-1 ) ) ) + { + return true; + } + break; + case 0x1: + if( (a_RepeaterPos - a_BlockPos).Equals( Vector3i( 1, 0, 0 ) ) ) + { + return true; + } + break; + case 0x2: + if( (a_RepeaterPos - a_BlockPos).Equals( Vector3i( 0, 0, 1 ) ) ) + { + return true; + } + break; + case 0x3: + if( (a_RepeaterPos - a_BlockPos).Equals( Vector3i(-1, 0, 0 ) ) ) + { + return true; + } + break; + default: + break; + } + return false; + } + + static Vector3i GetRepeaterDirection( char a_MetaData ) + { + switch( a_MetaData & 0x3 ) + { + case 0x0: + return Vector3i( 0, 0,-1 ); + case 0x1: + return Vector3i( 1, 0, 0 ); + case 0x2: + return Vector3i( 0, 0, 1 ); + case 0x3: + return Vector3i(-1, 0, 0 ); + default: + break; + } + return Vector3i(); + } + + void CalculateRedstone( int, int, int ); + void ChangeRedstone( int, int, int, bool ); + bool IsBlockPowered( int, int, int ); + + cWorld* m_World; + + char m_Metadata; + char m_Direction; + + static bool s_UseRedstone; +}; diff --git a/source/RedstoneSimulator.cpp b/source/RedstoneSimulator.cpp new file mode 100644 index 000000000..3d5fd51f0 --- /dev/null +++ b/source/RedstoneSimulator.cpp @@ -0,0 +1,701 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "RedstoneSimulator.h" +#include "Piston.h" +#include "World.h" +#include "BlockID.h" +#include "Torch.h" + +#include "Redstone.h" + + + +cRedstoneSimulator::cRedstoneSimulator( cWorld* a_World ) + : super(a_World) +{ +} + + + + + +cRedstoneSimulator::~cRedstoneSimulator() +{ +} + + + + + +void cRedstoneSimulator::WakeUp( int a_X, int a_Y, int a_Z ) +{ + if( cRedstone::s_UseRedstone ) return; // Using the other/broken simulator + + cCSLock Lock( m_CS ); + m_Blocks.push_back( Vector3i( a_X, a_Y, a_Z ) ); +} + + + + + +void cRedstoneSimulator::Simulate( float a_Dt ) +{ + if( cRedstone::s_UseRedstone ) return; // Using the other/broken simulator + + // Toggle torches on/off + while( !m_RefreshTorchesAround.empty() ) + { + Vector3i pos = m_RefreshTorchesAround.front(); + m_RefreshTorchesAround.pop_front(); + + RefreshTorchesAround( pos ); + } + + // Set repeaters to correct values, and decrement ticks + for( RepeaterList::iterator itr = m_SetRepeaters.begin(); itr != m_SetRepeaters.end(); ) + { + (*itr).Ticks--; + if( (*itr).Ticks <= 0 ) + { + char Block = m_World->GetBlock( (*itr).Position ); + if( (*itr).bPowerOn == true && Block == E_BLOCK_REDSTONE_REPEATER_OFF ) + { + m_World->FastSetBlock( (*itr).Position.x, (*itr).Position.y, (*itr).Position.z, E_BLOCK_REDSTONE_REPEATER_ON, m_World->GetBlockMeta( (*itr).Position ) ); + m_Blocks.push_back( (*itr).Position ); + } + else if( (*itr).bPowerOn == false && Block == E_BLOCK_REDSTONE_REPEATER_ON ) + { + m_World->FastSetBlock( (*itr).Position.x, (*itr).Position.y, (*itr).Position.z, E_BLOCK_REDSTONE_REPEATER_OFF, m_World->GetBlockMeta( (*itr).Position ) ); + m_Blocks.push_back( (*itr).Position ); + } + + if( (*itr).bPowerOffNextTime ) + { + (*itr).bPowerOn = false; + (*itr).bPowerOffNextTime = false; + (*itr).Ticks = 10; // TODO: Look up actual ticks from block metadata + ++itr; + } + else + { + itr = m_SetRepeaters.erase( itr ); + } + } + else + { + ++itr; + } + } + + // Handle changed blocks + { + cCSLock Lock( m_CS ); + std::swap( m_Blocks, m_BlocksBuffer ); + } + for( BlockList::iterator itr = m_BlocksBuffer.begin(); itr != m_BlocksBuffer.end(); ++itr ) + { + HandleChange( *itr ); + } + m_BlocksBuffer.clear(); +} + + + + + +void cRedstoneSimulator::RefreshTorchesAround( const Vector3i & a_BlockPos ) +{ + static Vector3i Surroundings [] = { + Vector3i(-1, 0, 0), + Vector3i( 1, 0, 0), + Vector3i( 0, 0,-1), + Vector3i( 0, 0, 1), + Vector3i( 0, 1, 0), // Also toggle torch on top + }; + char TargetBlockID = E_BLOCK_REDSTONE_TORCH_ON; + char TargetRepeaterID = E_BLOCK_REDSTONE_REPEATER_OFF; + if( IsPowered( a_BlockPos, true ) ) + { + TargetBlockID = E_BLOCK_REDSTONE_TORCH_OFF; + TargetRepeaterID = E_BLOCK_REDSTONE_REPEATER_ON; + //if( m_World->GetBlock( a_BlockPos ) == E_BLOCK_DIRT ) + //{ + // m_World->FastSetBlock( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_STONE, 0 ); + //} + } + else + { + //if( m_World->GetBlock( a_BlockPos ) == E_BLOCK_STONE ) + //{ + // m_World->FastSetBlock( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_DIRT, 0 ); + //} + } + + for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) + { + Vector3i TorchPos = a_BlockPos + Surroundings[i]; + const char Block = m_World->GetBlock( TorchPos ); + switch( Block ) + { + case E_BLOCK_REDSTONE_TORCH_ON: + case E_BLOCK_REDSTONE_TORCH_OFF: + if( Block != TargetBlockID ) + { + char TorchMeta = m_World->GetBlockMeta( TorchPos ); + if( cTorch::IsAttachedTo( TorchPos, TorchMeta, a_BlockPos ) ) + { + m_World->FastSetBlock( TorchPos.x, TorchPos.y, TorchPos.z, TargetBlockID, m_World->GetBlockMeta( TorchPos ) ); + m_Blocks.push_back( TorchPos ); + } + } + break; + case E_BLOCK_REDSTONE_REPEATER_ON: + case E_BLOCK_REDSTONE_REPEATER_OFF: + if( Block != TargetRepeaterID && cRedstone::IsRepeaterPointingAway( TorchPos, m_World->GetBlockMeta( TorchPos ), a_BlockPos ) ) + { + SetRepeater( TorchPos, 10, TargetRepeaterID == E_BLOCK_REDSTONE_REPEATER_ON ); + } + break; + default: + break; + }; + } +} + + + + + +void cRedstoneSimulator::HandleChange( const Vector3i & a_BlockPos ) +{ + std::deque< Vector3i > SpreadStack; + + Vector3i Surroundings[] = { + Vector3i( 1, 0, 0 ), + Vector3i( 1, 1, 0 ), + Vector3i( 1,-1, 0 ), + Vector3i(-1, 0, 0 ), + Vector3i(-1, 1, 0 ), + Vector3i(-1,-1, 0 ), + Vector3i( 0, 0, 1 ), + Vector3i( 0, 1, 1 ), + Vector3i( 0,-1, 1 ), + Vector3i( 0, 0,-1 ), + Vector3i( 0, 1,-1 ), + Vector3i( 0,-1,-1 ), + Vector3i( 0,-1, 0 ), + }; + + char Block = m_World->GetBlock( a_BlockPos ); + + // First check whether torch should be on or off + if( Block == E_BLOCK_REDSTONE_TORCH_ON || Block == E_BLOCK_REDSTONE_TORCH_OFF ) + { + static Vector3i Surroundings [] = { + Vector3i(-1, 0, 0), + Vector3i( 1, 0, 0), + Vector3i( 0, 0,-1), + Vector3i( 0, 0, 1), + Vector3i( 0,-1, 0), + }; + for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) + { + Vector3i pos = a_BlockPos + Surroundings[i]; + char OtherBlock = m_World->GetBlock( pos ); + if( (OtherBlock != E_BLOCK_AIR) && (OtherBlock != E_BLOCK_REDSTONE_TORCH_ON) && (OtherBlock != E_BLOCK_REDSTONE_TORCH_OFF) ) + { + RefreshTorchesAround( pos ); + } + } + Block = m_World->GetBlock( a_BlockPos ); // Make sure we got the updated block + } + else if( Block == E_BLOCK_REDSTONE_REPEATER_ON || Block == E_BLOCK_REDSTONE_REPEATER_OFF ) // Check if repeater is powered by a 'powered block' (not wires/torch) + { + Vector3i Direction = cRedstone::GetRepeaterDirection( m_World->GetBlockMeta( a_BlockPos ) ); + Vector3i pos = a_BlockPos - Direction; // NOTE: It's minus Direction + char OtherBlock = m_World->GetBlock( pos ); + if( (OtherBlock != E_BLOCK_AIR) && (OtherBlock != E_BLOCK_REDSTONE_TORCH_ON) && (OtherBlock != E_BLOCK_REDSTONE_TORCH_OFF) && (OtherBlock != E_BLOCK_REDSTONE_WIRE) ) + { + RefreshTorchesAround( pos ); + } + else + { + SetRepeater( a_BlockPos, 10, IsPowered( a_BlockPos, false ) ); + } + Block = m_World->GetBlock( a_BlockPos ); + } + + BlockList Sources; + // If torch is still on, use it as a source + if( Block == E_BLOCK_REDSTONE_TORCH_ON ) + { + Sources.push_back( a_BlockPos ); + } + else if( Block == E_BLOCK_REDSTONE_REPEATER_ON ) + { + static Vector3i Surroundings [] = { // It only spreads right in front, and one block up + Vector3i( 0, 0, 0), + Vector3i( 0, 1, 0), + }; + Vector3i Direction = cRedstone::GetRepeaterDirection( m_World->GetBlockMeta( a_BlockPos ) ); + for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) + { + Vector3i pos = a_BlockPos + Direction + Surroundings[i]; + if( PowerBlock( pos, a_BlockPos, 0xf ) ) + { + SpreadStack.push_back( pos ); + } + } + } + + // Power all blocks legally connected to the sources + if( Block != E_BLOCK_REDSTONE_REPEATER_ON ) + { + BlockList NewSources = RemoveCurrent( a_BlockPos ); + Sources.insert( Sources.end(), NewSources.begin(), NewSources.end() ); + while( !Sources.empty() ) + { + Vector3i SourcePos = Sources.back(); + Sources.pop_back(); + + char Block = m_World->GetBlock( SourcePos ); + switch( Block ) + { + case E_BLOCK_REDSTONE_TORCH_OFF: + case E_BLOCK_REDSTONE_TORCH_ON: + { + static Vector3i Surroundings [] = { + Vector3i(-1, 0, 0), + Vector3i( 1, 0, 0), + Vector3i( 0, 0,-1), + Vector3i( 0, 0, 1), + }; + for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) + { + Vector3i OtherPos = SourcePos + Surroundings[i]; + if( PowerBlock( OtherPos, a_BlockPos, 0xf ) ) + { + SpreadStack.push_back( OtherPos ); // Changed, so add to stack + } + } + } + break; + case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_REDSTONE_REPEATER_ON: + { + static Vector3i Surroundings [] = { + Vector3i( 0, 0, 0), + Vector3i( 0, 1, 0), + }; + Vector3i Direction = cRedstone::GetRepeaterDirection( m_World->GetBlockMeta( SourcePos ) ); + for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) + { + Vector3i pos = SourcePos + Direction + Surroundings[i]; + if( PowerBlock( pos, a_BlockPos, 0xf ) ) + { + SpreadStack.push_back( pos ); + } + } + } + break; + }; + + + } + } + + + // Do a floodfill + while( !SpreadStack.empty() ) + { + Vector3i pos = SpreadStack.back(); + SpreadStack.pop_back(); + + char Meta = m_World->GetBlockMeta( pos ); + + for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) + { + Vector3i OtherPos = pos + Surroundings[i]; + if( PowerBlock( OtherPos, pos, Meta-1 ) ) + { + SpreadStack.push_back( OtherPos ); // Changed, so add to stack + } + } + } + + // Only after a redstone area has been completely simulated the redstone entities can react + while( !m_RefreshPistons.empty() ) + { + Vector3i pos = m_RefreshPistons.back(); + m_RefreshPistons.pop_back(); + + char Block = m_World->GetBlock( pos ); + switch( Block ) + { + case E_BLOCK_PISTON: + case E_BLOCK_STICKY_PISTON: + if( IsPowered( pos ) ) + { + cPiston Piston( m_World ); + Piston.ExtendPiston( pos.x, pos.y, pos.z ); + } + else + { + cPiston Piston( m_World ); + Piston.RetractPiston( pos.x, pos.y, pos.z ); + } + break; + default: + break; + }; + } +} + + + + + +bool cRedstoneSimulator::PowerBlock( const Vector3i & a_BlockPos, const Vector3i & a_FromBlock, char a_Power ) +{ + char Block = m_World->GetBlock( a_BlockPos ); + switch( Block ) + { + case E_BLOCK_REDSTONE_WIRE: + { + if( m_World->GetBlockMeta( a_BlockPos ) < a_Power ) + { + m_World->SetBlockMeta( a_BlockPos, a_Power ); + return true; + } + } + break; + case E_BLOCK_PISTON: + case E_BLOCK_STICKY_PISTON: + { + m_RefreshPistons.push_back( a_BlockPos ); + } + break; + case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_REDSTONE_REPEATER_ON: + { + if( cRedstone::IsRepeaterPointingAway( a_BlockPos, m_World->GetBlockMeta( a_BlockPos ), a_FromBlock ) ) + { + SetRepeater( a_BlockPos, 10, true ); + } + } + break; + default: + { + if( Block != E_BLOCK_AIR && Block != E_BLOCK_REDSTONE_TORCH_ON && Block != E_BLOCK_REDSTONE_TORCH_OFF ) + { + if( IsPowered( a_BlockPos, true ) ) + { + m_RefreshTorchesAround.push_back( a_BlockPos ); + } + } + } + break; + }; + + return false; +} + + + + + +int cRedstoneSimulator::UnPowerBlock( const Vector3i & a_BlockPos, const Vector3i & a_FromBlock ) +{ + char Block = m_World->GetBlock( a_BlockPos ); + switch( Block ) + { + case E_BLOCK_REDSTONE_WIRE: + { + if( m_World->GetBlockMeta( a_BlockPos ) > 0 ) + { + m_World->SetBlockMeta( a_BlockPos, 0 ); + return 1; + } + } + break; + case E_BLOCK_PISTON: + case E_BLOCK_STICKY_PISTON: + { + m_RefreshPistons.push_back( a_BlockPos ); + } + break; + case E_BLOCK_REDSTONE_TORCH_ON: + { + return 2; + } + break; + case E_BLOCK_REDSTONE_REPEATER_ON: + { + if( cRedstone::IsRepeaterPointingTo( a_BlockPos, m_World->GetBlockMeta( a_BlockPos ), a_FromBlock ) + || cRedstone::IsRepeaterPointingTo( a_BlockPos, m_World->GetBlockMeta( a_BlockPos ), a_FromBlock - Vector3i(0, 1, 0) ) ) // Check if repeater is next/below wire + { + return 2; + } + else if( cRedstone::IsRepeaterPointingAway( a_BlockPos, m_World->GetBlockMeta( a_BlockPos ), a_FromBlock ) ) + { + SetRepeater( a_BlockPos, 10, false ); + } + } + case E_BLOCK_REDSTONE_REPEATER_OFF: + if( cRedstone::IsRepeaterPointingAway( a_BlockPos, m_World->GetBlockMeta( a_BlockPos ), a_FromBlock ) ) + { + SetRepeater( a_BlockPos, 10, false ); + } + break; + default: + if( Block != E_BLOCK_AIR && Block != E_BLOCK_REDSTONE_TORCH_ON && Block != E_BLOCK_REDSTONE_TORCH_OFF ) + { + if( !IsPowered( a_BlockPos, true ) ) + { + m_RefreshTorchesAround.push_back( a_BlockPos ); + } + } + break; + }; + + return 0; +} + + + + + +// Removes current from all powered redstone wires until it reaches an energy source. +// Also returns all energy sources it encountered +cRedstoneSimulator::BlockList cRedstoneSimulator::RemoveCurrent( const Vector3i & a_BlockPos ) +{ + + + std::deque< Vector3i > SpreadStack; + std::deque< Vector3i > FoundSources; + + Vector3i Surroundings[] = { + Vector3i( 1, 0, 0 ), + Vector3i( 1, 1, 0 ), + Vector3i( 1,-1, 0 ), + Vector3i(-1, 0, 0 ), + Vector3i(-1, 1, 0 ), + Vector3i(-1,-1, 0 ), + Vector3i( 0, 0, 1 ), + Vector3i( 0, 1, 1 ), + Vector3i( 0,-1, 1 ), + Vector3i( 0, 0,-1 ), + Vector3i( 0, 1,-1 ), + Vector3i( 0,-1,-1 ), + Vector3i( 0,-1, 0 ), + }; + + char Block = m_World->GetBlock( a_BlockPos ); + if( Block == E_BLOCK_REDSTONE_REPEATER_ON || Block == E_BLOCK_REDSTONE_REPEATER_OFF ) + { + static Vector3i Surroundings [] = { // Repeaters only spread right in front and 1 block up + Vector3i( 0, 0, 0), + Vector3i( 0, 1, 0), + }; + Vector3i Direction = cRedstone::GetRepeaterDirection( m_World->GetBlockMeta( a_BlockPos ) ); + for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) + { + Vector3i pos = a_BlockPos + Direction + Surroundings[i]; + int RetVal = UnPowerBlock( pos, a_BlockPos ); + if( RetVal == 1 ) + { + SpreadStack.push_back( pos ); // Changed, so add to stack + } + else if( RetVal == 2 ) + { + FoundSources.push_back( pos ); + } + } + } + else if( Block == E_BLOCK_REDSTONE_TORCH_OFF || Block == E_BLOCK_REDSTONE_TORCH_ON ) + { + static Vector3i Surroundings [] = { // Torches only spread on the same level + Vector3i(-1, 0, 0), + Vector3i( 1, 0, 0), + Vector3i( 0, 0,-1), + Vector3i( 0, 0, 1), + }; + + for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) + { + Vector3i pos = Vector3i( a_BlockPos ) + Surroundings[i]; + int RetVal = UnPowerBlock( pos, a_BlockPos ); + if( RetVal == 1 ) + { + SpreadStack.push_back( pos ); // Changed, so add to stack + } + else if( RetVal == 2 ) + { + FoundSources.push_back( pos ); + } + } + } + else + { + SpreadStack.push_back( a_BlockPos ); + } + + + while( !SpreadStack.empty() ) + { + Vector3i pos = SpreadStack.back(); + SpreadStack.pop_back(); + + for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) + { + Vector3i OtherPos = pos + Surroundings[i]; + int RetVal = UnPowerBlock( OtherPos, pos ); + if( RetVal == 1 ) + { + SpreadStack.push_back( OtherPos ); // Changed, so add to stack + } + else if( RetVal == 2 ) + { + FoundSources.push_back( OtherPos ); + } + } + } + + return FoundSources; +} + + + + + +bool cRedstoneSimulator::IsPowering( const Vector3i & a_PowerPos, const Vector3i & a_BlockPos, eRedstoneDirection a_WireDirection, bool a_bOnlyByWire ) +{ + char PowerBlock = m_World->GetBlock( a_PowerPos ); + if( !a_bOnlyByWire && PowerBlock == E_BLOCK_REDSTONE_TORCH_ON ) return true; + if( PowerBlock == E_BLOCK_REDSTONE_REPEATER_ON ) // A repeater pointing towards block is regarded as wire + { + if( cRedstone::IsRepeaterPointingTo( a_PowerPos, m_World->GetBlockMeta( a_PowerPos ), a_BlockPos ) ) + { + return true; + } + } + if( PowerBlock == E_BLOCK_REDSTONE_WIRE ) + { + if( m_World->GetBlockMeta( a_PowerPos ) > 0 ) + { + if( GetDirection( a_PowerPos ) == a_WireDirection ) + { + return true; + } + } + } + + return false; +} + + + + + +bool cRedstoneSimulator::IsPowered( const Vector3i & a_BlockPos, bool a_bOnlyByWire /* = false */ ) +{ + char Block = m_World->GetBlock( a_BlockPos ); + if( Block == E_BLOCK_REDSTONE_REPEATER_OFF || Block == E_BLOCK_REDSTONE_REPEATER_ON ) + { + Vector3i Behind = a_BlockPos - cRedstone::GetRepeaterDirection( m_World->GetBlockMeta( a_BlockPos ) ); + char BehindBlock = m_World->GetBlock( Behind ); + if( BehindBlock == E_BLOCK_REDSTONE_TORCH_ON ) + { + return true; + } + if( BehindBlock == E_BLOCK_REDSTONE_WIRE ) + { + if( m_World->GetBlockMeta( Behind ) > 0 ) + { + return true; + } + } + if( BehindBlock == E_BLOCK_REDSTONE_REPEATER_ON ) + { + if( cRedstone::IsRepeaterPointingTo( Behind, m_World->GetBlockMeta( Behind ), a_BlockPos ) ) + { + return true; + } + } + return false; + } + + if( IsPowering( Vector3i( a_BlockPos.x-1, a_BlockPos.y, a_BlockPos.z ), a_BlockPos, REDSTONE_X_POS, a_bOnlyByWire ) ) + return true; + if( IsPowering( Vector3i( a_BlockPos.x+1, a_BlockPos.y, a_BlockPos.z ), a_BlockPos, REDSTONE_X_NEG, a_bOnlyByWire ) ) + return true; + if( IsPowering( Vector3i( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z-1 ), a_BlockPos, REDSTONE_Z_POS, a_bOnlyByWire ) ) + return true; + if( IsPowering( Vector3i( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z+1 ), a_BlockPos, REDSTONE_Z_NEG, a_bOnlyByWire ) ) + return true; + + // Only wires can power the bottom block + char PosY = m_World->GetBlock( a_BlockPos.x, a_BlockPos.y+1, a_BlockPos.z ); + if( PosY == E_BLOCK_REDSTONE_WIRE ) + { + if( m_World->GetBlockMeta( a_BlockPos.x, a_BlockPos.y+1, a_BlockPos.z ) > 0 ) + { + return true; + } + } + + return false; +} + + + + +// Believe me, it works!! TODO: Add repeaters and low/high wires +cRedstoneSimulator::eRedstoneDirection cRedstoneSimulator::GetDirection( int a_X, int a_Y, int a_Z ) +{ + int Dir = REDSTONE_NONE; + + char NegX = m_World->GetBlock( a_X-1, a_Y, a_Z ); + if( NegX == E_BLOCK_REDSTONE_WIRE || NegX == E_BLOCK_REDSTONE_TORCH_ON ) + { + Dir |= (REDSTONE_X_POS); + } + char PosX = m_World->GetBlock( a_X+1, a_Y, a_Z ); + if( PosX == E_BLOCK_REDSTONE_WIRE || PosX == E_BLOCK_REDSTONE_TORCH_ON ) + { + Dir |= (REDSTONE_X_NEG); + } + char NegZ = m_World->GetBlock( a_X, a_Y, a_Z-1 ); + if( NegZ == E_BLOCK_REDSTONE_WIRE || NegZ == E_BLOCK_REDSTONE_TORCH_ON ) + { + if( (Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG ) ) //corner + { + Dir ^= REDSTONE_X_POS; + Dir |= REDSTONE_X_NEG; + } + if( (Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS ) ) //corner + { + Dir ^= REDSTONE_X_NEG; + Dir |= REDSTONE_X_POS; + } + Dir |= REDSTONE_Z_POS; + } + char PosZ = m_World->GetBlock( a_X, a_Y, a_Z+1 ); + if( PosZ == E_BLOCK_REDSTONE_WIRE || PosZ == E_BLOCK_REDSTONE_TORCH_ON ) + { + if( (Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG ) ) //corner + { + Dir ^= REDSTONE_X_POS; + Dir |= REDSTONE_X_NEG; + } + if( (Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS ) ) //corner + { + Dir ^= REDSTONE_X_NEG; + Dir |= REDSTONE_X_POS; + } + Dir |= REDSTONE_Z_NEG; + } + + return (eRedstoneDirection)Dir; +} \ No newline at end of file diff --git a/source/RedstoneSimulator.h b/source/RedstoneSimulator.h new file mode 100644 index 000000000..3a845bf6b --- /dev/null +++ b/source/RedstoneSimulator.h @@ -0,0 +1,90 @@ +#pragma once + +#include "Simulator.h" +#include "Vector3i.h" + +class cRedstoneSimulator : public cSimulator +{ + typedef cSimulator super; +public: + cRedstoneSimulator( cWorld* a_World ); + ~cRedstoneSimulator(); + + virtual void Simulate( float a_Dt ); + virtual bool IsAllowedBlock( char a_BlockID ) { return true; } + + virtual void WakeUp( int a_X, int a_Y, int a_Z ); + + enum eRedstoneDirection + { + REDSTONE_NONE = 0, + REDSTONE_X_POS = 0x1, + REDSTONE_X_NEG = 0x2, + REDSTONE_Z_POS = 0x4, + REDSTONE_Z_NEG = 0x8, + }; + eRedstoneDirection GetDirection( int a_X, int a_Y, int a_Z ); + eRedstoneDirection GetDirection( const Vector3i & a_Pos ) { return GetDirection( a_Pos.x, a_Pos.y, a_Pos.z ); } +private: + struct sRepeaterChange + { + Vector3i Position; + int Ticks; + bool bPowerOn; + bool bPowerOffNextTime; + }; + + typedef std::deque BlockList; + + typedef std::deque< sRepeaterChange > RepeaterList; + RepeaterList m_SetRepeaters; + void SetRepeater( const Vector3i & a_Position, int a_Ticks, bool a_bPowerOn ) + { + for( RepeaterList::iterator itr = m_SetRepeaters.begin(); itr != m_SetRepeaters.end(); ++itr ) + { + sRepeaterChange & Change = *itr; + if( Change.Position.Equals( a_Position ) ) + { + if( Change.bPowerOn && a_bPowerOn == false ) + { + Change.bPowerOffNextTime = true; + } + if( a_bPowerOn == true ) + { + Change.bPowerOffNextTime = false; + } + Change.bPowerOn |= a_bPowerOn; + return; + } + } + + sRepeaterChange RC; + RC.Position = a_Position; + RC.Ticks = a_Ticks; + RC.bPowerOn = a_bPowerOn; + RC.bPowerOffNextTime = false; + m_SetRepeaters.push_back( RC ); + } + + virtual void AddBlock(int a_X, int a_Y, int a_Z) {} + + void HandleChange( const Vector3i & a_BlockPos ); + BlockList RemoveCurrent( const Vector3i & a_BlockPos ); + + bool PowerBlock( const Vector3i & a_BlockPos, const Vector3i & a_FromBlock, char a_Power ); + int UnPowerBlock( const Vector3i & a_BlockPos, const Vector3i & a_FromBlock ); + + bool IsPowered( const Vector3i & a_BlockPos, bool a_bOnlyByWire = false ); + bool IsPowering( const Vector3i & a_PowerPos, const Vector3i & a_BlockPos, eRedstoneDirection a_WireDirection, bool a_bOnlyByWire ); + + BlockList m_Blocks; + BlockList m_BlocksBuffer; + + BlockList m_RefreshPistons; + + BlockList m_RefreshTorchesAround; + + void RefreshTorchesAround( const Vector3i & a_BlockPos ); + + cCriticalSection m_CS; +}; \ No newline at end of file diff --git a/source/ReferenceManager.cpp b/source/ReferenceManager.cpp new file mode 100644 index 000000000..259e3a1a0 --- /dev/null +++ b/source/ReferenceManager.cpp @@ -0,0 +1,43 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ReferenceManager.h" +#include "Entity.h" + + + + + +cReferenceManager::cReferenceManager( ENUM_REFERENCE_MANAGER_TYPE a_Type ) + : m_Type( a_Type ) +{ +} + +cReferenceManager::~cReferenceManager() +{ + if( m_Type == RFMNGR_REFERENCERS ) + { + for( std::list< cEntity** >::iterator itr = m_References.begin(); itr != m_References.end(); ++itr ) + { + *(*itr) = 0; // Set referenced pointer to 0 + } + } + else + { + for( std::list< cEntity** >::iterator itr = m_References.begin(); itr != m_References.end(); ++itr ) + { + cEntity* Ptr = (*(*itr)); + if( Ptr ) Ptr->Dereference( *(*itr) ); + } + } +} + +void cReferenceManager::AddReference( cEntity*& a_EntityPtr ) +{ + m_References.push_back( &a_EntityPtr ); +} + +void cReferenceManager::Dereference( cEntity*& a_EntityPtr ) +{ + m_References.remove( &a_EntityPtr ); +} \ No newline at end of file diff --git a/source/ReferenceManager.h b/source/ReferenceManager.h new file mode 100644 index 000000000..bcd451f72 --- /dev/null +++ b/source/ReferenceManager.h @@ -0,0 +1,34 @@ + +#pragma once + + + + + +class cEntity; + + + + + +class cReferenceManager +{ +public: + enum ENUM_REFERENCE_MANAGER_TYPE + { + RFMNGR_REFERENCERS, + RFMNGR_REFERENCES, + }; + cReferenceManager( ENUM_REFERENCE_MANAGER_TYPE a_Type ); + ~cReferenceManager(); + + void AddReference( cEntity*& a_EntityPtr ); + void Dereference( cEntity*& a_EntityPtr ); +private: + ENUM_REFERENCE_MANAGER_TYPE m_Type; + std::list< cEntity** > m_References; +}; + + + + diff --git a/source/Root.cpp b/source/Root.cpp new file mode 100644 index 000000000..2a6afc0e7 --- /dev/null +++ b/source/Root.cpp @@ -0,0 +1,524 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Root.h" +#include "Server.h" +#include "World.h" +#include "WebAdmin.h" +#include "FurnaceRecipe.h" +#include "GroupManager.h" +#include "CraftingRecipes.h" +#include "PluginManager.h" +#include "MonsterConfig.h" +#include "Redstone.h" +#include "Player.h" +#include "blocks/BlockHandler.h" +#include "items/ItemHandler.h" +#include "Chunk.h" + +#ifdef USE_SQUIRREL + #include "squirrelbindings/SquirrelFunctions.h" + #include "squirrelbindings/SquirrelBindings.h" +#endif + +#include "../iniFile/iniFile.h" + +#include + + + + + +cRoot* cRoot::s_Root = 0; + + + + + +cRoot::cRoot() + : m_Server( NULL ) + , m_MonsterConfig( NULL ) + , m_GroupManager( NULL ) + , m_CraftingRecipes(NULL) + , m_FurnaceRecipe( NULL ) + , m_WebAdmin( NULL ) + , m_PluginManager( NULL ) + , m_Log( NULL ) + , m_bStop( false ) + , m_bRestart( false ) + , m_InputThread( NULL ) + , m_pDefaultWorld( NULL ) +{ + s_Root = this; +} + + + + + +cRoot::~cRoot() +{ + s_Root = 0; +} + + + + + +void cRoot::InputThread(void * a_Params) +{ + cRoot & self = *(cRoot*)a_Params; + + while (!(self.m_bStop || self.m_bRestart)) + { + std::string Command; + std::getline(std::cin, Command); + self.ServerCommand(Command); + } +} + + + + + +void cRoot::Start() +{ + delete m_Log; + m_Log = new cMCLogger(); + + m_bStop = false; + while (!m_bStop) + { + m_bRestart = false; + + LoadGlobalSettings(); + + LOG("Creating new server instance..."); + m_Server = new cServer(); + + LOG("Starting server..."); + cIniFile IniFile("settings.ini"); + IniFile.ReadFile(); + int Port = IniFile.GetValueSetI("Server", "Port", 25565 ); + if(!m_Server->InitServer( Port )) + { + LOG("Failed to start server, shutting down."); + return; + } + IniFile.WriteFile(); + + cIniFile WebIniFile("webadmin.ini"); + if( WebIniFile.ReadFile() ) + { + if( WebIniFile.GetValueB("WebAdmin", "Enabled", false ) == true ) + { + LOG("Creating WebAdmin..."); + m_WebAdmin = new cWebAdmin(8080); + } + } + + LOG("Loading settings..."); + m_GroupManager = new cGroupManager(); + m_CraftingRecipes = new cCraftingRecipes; + m_FurnaceRecipe = new cFurnaceRecipe(); + + LOG("Loading worlds..."); + LoadWorlds(); + + LOG("Loading plugin manager..."); + m_PluginManager = new cPluginManager(); // This should be last + m_PluginManager->ReloadPluginsNow(); + + LOG("Loading MonsterConfig..."); + m_MonsterConfig = new cMonsterConfig; + + // This sets stuff in motion + LOG("Starting Authenticator..."); + m_Authenticator.Start(); + + LOG("Starting worlds..."); + StartWorlds(); + + LOG("Starting server..."); + m_Server->StartListenThread(); + //cHeartBeat* HeartBeat = new cHeartBeat(); + +#if !defined(ANDROID_NDK) + LOG("Starting InputThread..."); + m_InputThread = new cThread( InputThread, this, "cRoot::InputThread" ); + m_InputThread->Start( false ); //we should NOT wait? Otherwise we canīt stop the server from other threads than the input thread +#endif + + LOG("Initialization done, server running now."); + while( !m_bStop && !m_bRestart ) // These are modified by external threads + { + cSleep::MilliSleep( 1000 ); + } + +#if !defined(ANDROID_NDK) + delete m_InputThread; m_InputThread = 0; +#endif + + // Deallocate stuffs + LOG("Shutting down server..."); + m_Server->Shutdown(); // This waits for threads to stop and d/c clients + LOG("Stopping world threads..."); + StopWorlds(); + LOG("Stopping authenticator..."); + m_Authenticator.Stop(); + LOG("Stopping plugin manager..."); + delete m_PluginManager; m_PluginManager = 0; // This should be first + + + #ifdef USE_SQUIRREL + CloseSquirrelVM(); + #endif + LOG("Freeing MonsterConfig..."); + delete m_MonsterConfig; m_MonsterConfig = 0; + LOG("Stopping WebAdmin..."); + delete m_WebAdmin; m_WebAdmin = 0; + LOG("Unloading recipes..."); + delete m_FurnaceRecipe; m_FurnaceRecipe = NULL; + delete m_CraftingRecipes; m_CraftingRecipes = NULL; + LOG("Forgetting groups..."); + delete m_GroupManager; m_GroupManager = 0; + LOG("Unloading worlds..."); + UnloadWorlds(); + + cItemHandler::Deinit(); + cBlockHandler::Deinit(); + + LOG("Destroying server..."); + //delete HeartBeat; HeartBeat = 0; + delete m_Server; m_Server = 0; + LOG("Shutdown done."); + } + + delete m_Log; m_Log = 0; +} + + + + + +void cRoot::LoadGlobalSettings() +{ + cIniFile IniFile("settings.ini"); + if( IniFile.ReadFile() ) + { + cRedstone::s_UseRedstone = IniFile.GetValueB("Redstone", "SimulateRedstone", true ); + } +} + + + + + +void cRoot::LoadWorlds(void) +{ + cIniFile IniFile("settings.ini"); IniFile.ReadFile(); + + // First get the default world + AString DefaultWorldName = IniFile.GetValueSet("Worlds", "DefaultWorld", "world"); + m_pDefaultWorld = new cWorld( DefaultWorldName.c_str() ); + m_WorldsByName[ DefaultWorldName ] = m_pDefaultWorld; + + // Then load the other worlds + unsigned int KeyNum = IniFile.FindKey("Worlds"); + unsigned int NumWorlds = IniFile.GetNumValues( KeyNum ); + if (NumWorlds <= 0) + { + return; + } + + for (unsigned int i = 0; i < NumWorlds; i++) + { + AString ValueName = IniFile.GetValueName(KeyNum, i ); + if (ValueName.compare("World") != 0) + { + continue; + } + AString WorldName = IniFile.GetValue(KeyNum, i ); + if (WorldName.empty()) + { + continue; + } + cWorld* NewWorld = new cWorld( WorldName.c_str() ); + m_WorldsByName[ WorldName ] = NewWorld; + } // for i - Worlds +} + + + + + +void cRoot::StartWorlds(void) +{ + for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) + { + itr->second->InitializeSpawn(); + } +} + + + + + +void cRoot::StopWorlds(void) +{ + for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) + { + itr->second->StopThreads(); + } +} + + + + + +void cRoot::UnloadWorlds(void) +{ + m_pDefaultWorld = NULL; + for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) + { + delete itr->second; + } + m_WorldsByName.clear(); +} + + + + + +cWorld* cRoot::GetDefaultWorld() +{ + return m_pDefaultWorld; +} + + + + + +cWorld* cRoot::GetWorld( const AString & a_WorldName ) +{ + WorldMap::iterator itr = m_WorldsByName.find( a_WorldName ); + if( itr != m_WorldsByName.end() ) + return itr->second; + return 0; +} + + + + + +bool cRoot::ForEachWorld(cWorldListCallback & a_Callback) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(), itr2 = itr; itr != m_WorldsByName.end(); itr = itr2) + { + ++itr2; + if (a_Callback.Item(itr->second)) + { + return false; + } + } + return true; +} + + + + + +void cRoot::TickWorlds( float a_Dt ) +{ + for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) + { + itr->second->Tick( a_Dt ); + } +} + + + + + +void cRoot::ServerCommand(const AString & a_Cmd) +{ + LOG("Server console command: \"%s\"", a_Cmd.c_str()); + m_Server->ServerCommand(a_Cmd); + if (a_Cmd == "stop") + { + m_bStop = true; + } + else if (a_Cmd == "restart") + { + m_bRestart = true; + } +} + + + + + +void cRoot::KickUser(int a_ClientID, const AString & a_Reason) +{ + m_Server->KickUser(a_ClientID, a_Reason); +} + + + + + +void cRoot::AuthenticateUser(int a_ClientID) +{ + m_Server->AuthenticateUser(a_ClientID); +} + + + + + +int cRoot::GetTotalChunkCount(void) +{ + int res = 0; + for ( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) + { + res += itr->second->GetNumChunks(); + } + return res; +} + + + + + +void cRoot::SaveAllChunks(void) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) + { + itr->second->SaveAllChunks(); + } +} + + + + + +bool cRoot::ForEachPlayer(cPlayerListCallback & a_Callback) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(), itr2 = itr; itr != m_WorldsByName.end(); itr = itr2) + { + ++itr2; + if (!itr->second->ForEachPlayer(a_Callback)) + { + return false; + } + } + return true; +} + + + + + +bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback) +{ + class cCallback : public cPlayerListCallback + { + unsigned int BestRating; + unsigned int NameLength; + const AString PlayerName; + + cPlayerListCallback & m_Callback; + virtual bool Item (cPlayer * a_pPlayer) + { + unsigned int Rating = RateCompareString (PlayerName, a_pPlayer->GetName()); + if (Rating > 0 && Rating >= BestRating) + { + BestMatch = a_pPlayer; + if( Rating > BestRating ) NumMatches = 0; + BestRating = Rating; + ++NumMatches; + } + if (Rating == NameLength) // Perfect match + { + return false; + } + return true; + } + + public: + cCallback (const AString & a_PlayerName, cPlayerListCallback & a_Callback) + : m_Callback( a_Callback ) + , BestMatch( NULL ) + , BestRating( 0 ) + , NumMatches( 0 ) + , NameLength( a_PlayerName.length() ) + , PlayerName( a_PlayerName ) + {} + + cPlayer * BestMatch; + unsigned int NumMatches; + } Callback (a_PlayerName, a_Callback); + ForEachPlayer( Callback ); + + if (Callback.NumMatches == 1) + { + return a_Callback.Item (Callback.BestMatch); + } + return false; +} + + + + + +void cRoot::LogChunkStats(void) +{ + int SumNumValid = 0; + int SumNumDirty = 0; + int SumNumInLighting = 0; + int SumNumInGenerator = 0; + int SumMem = 0; + for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr) + { + cWorld * World = itr->second; + int NumInGenerator = World->GetGeneratorQueueLength(); + int NumInSaveQueue = World->GetStorageSaveQueueLength(); + int NumInLoadQueue = World->GetStorageLoadQueueLength(); + int NumValid = 0; + int NumDirty = 0; + int NumInLighting = 0; + World->GetChunkStats(NumValid, NumDirty, NumInLighting); + LOG("World %s:", World->GetName().c_str()); + LOG(" Num loaded chunks: %d", NumValid); + LOG(" Num dirty chunks: %d", NumDirty); + LOG(" Num chunks in lighting queue: %d", NumInLighting); + LOG(" Num chunks in generator queue: %d", NumInGenerator); + LOG(" Num chunks in storage load queue: %d", NumInLoadQueue); + LOG(" Num chunks in storage save queue: %d", NumInSaveQueue); + int Mem = NumValid * sizeof(cChunk); + LOG(" Memory used by chunks: %d KiB (%d MiB)", (Mem + 1023) / 1024, (Mem + 1024 * 1024 - 1) / (1024 * 1024)); + LOG(" Per-chunk memory size breakdown:"); + LOG(" block types: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockTypes), (sizeof(cChunkDef::BlockTypes) + 1023) / 1024); + LOG(" block metadata: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockNibbles), (sizeof(cChunkDef::BlockNibbles) + 1023) / 1024); + LOG(" block lighting: %6d bytes (%3d KiB)", 2 * sizeof(cChunkDef::BlockNibbles), (2 * sizeof(cChunkDef::BlockNibbles) + 1023) / 1024); + LOG(" heightmap: %6d bytes (%3d KiB)", sizeof(cChunkDef::HeightMap), (sizeof(cChunkDef::HeightMap) + 1023) / 1024); + LOG(" biomemap: %6d bytes (%3d KiB)", sizeof(cChunkDef::BiomeMap), (sizeof(cChunkDef::BiomeMap) + 1023) / 1024); + int Rest = sizeof(cChunk) - sizeof(cChunkDef::BlockTypes) - 3 * sizeof(cChunkDef::BlockNibbles) - sizeof(cChunkDef::HeightMap) - sizeof(cChunkDef::BiomeMap); + LOG(" other: %6d bytes (%3d KiB)", Rest, (Rest + 1023) / 1024); + SumNumValid += NumValid; + SumNumDirty += NumDirty; + SumNumInLighting += NumInLighting; + SumNumInGenerator += NumInGenerator; + SumMem += Mem; + } + LOG("Totals:"); + LOG(" Num loaded chunks: %d", SumNumValid); + LOG(" Num dirty chunks: %d", SumNumDirty); + LOG(" Num chunks in lighting queue: %d", SumNumInLighting); + LOG(" Num chunks in generator queue: %d", SumNumInGenerator); + LOG(" Memory used by chunks: %d KiB (%d MiB)", (SumMem + 1023) / 1024, (SumMem + 1024 * 1024 - 1) / (1024 * 1024)); +} + + + + diff --git a/source/Root.h b/source/Root.h new file mode 100644 index 000000000..5a2238332 --- /dev/null +++ b/source/Root.h @@ -0,0 +1,120 @@ + +#pragma once + + + + +#include "Authenticator.h" + + + + + +class cThread; +class cMonsterConfig; +class cGroupManager; +class cCraftingRecipes; +class cFurnaceRecipe; +class cWebAdmin; +class cPluginManager; +class cServer; +class cWorld; +class cPlayer; +typedef cItemCallback cPlayerListCallback; +typedef cItemCallback cWorldListCallback; + + + + + +class cRoot //tolua_export +{ //tolua_export +public: + static cRoot* Get() { return s_Root; } //tolua_export + + cRoot(void); + ~cRoot(); + + void Start(void); + + cServer * GetServer(void) { return m_Server; } //tolua_export + cWorld * GetDefaultWorld(void); //tolua_export + cWorld * GetWorld(const AString & a_WorldName); //tolua_export + + /// Calls the callback for each world; returns true if the callback didn't abort (return true) + bool ForEachWorld(cWorldListCallback & a_Callback); // >> Exported in ManualBindings << + + /// Logs chunkstats for each world and totals + void LogChunkStats(void); + + cMonsterConfig * GetMonsterConfig() { return m_MonsterConfig; } + + cGroupManager * GetGroupManager (void) { return m_GroupManager; } // tolua_export + cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export + cFurnaceRecipe * GetFurnaceRecipe (void) { return m_FurnaceRecipe; } // tolua_export + cWebAdmin * GetWebAdmin (void) { return m_WebAdmin; } // tolua_export + cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export + cAuthenticator & GetAuthenticator (void) { return m_Authenticator; } + + void ServerCommand(const AString & a_Cmd); //tolua_export + + void KickUser(int a_ClientID, const AString & a_Reason); // Kicks the user, no matter in what world they are. Used from cAuthenticator + void AuthenticateUser(int a_ClientID); // Called by cAuthenticator to auth the specified user + + void TickWorlds( float a_Dt ); + + /// Returns the number of chunks loaded + int GetTotalChunkCount(void); // tolua_export + + /// Saves all chunks in all worlds + void SaveAllChunks(void); + + /// Calls the callback for each player in all worlds + bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + + /// Finds a player from a partial or complete player name and calls the callback - case-insensitive + bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + +private: + void LoadGlobalSettings(); + + /// Loads the worlds from settings.ini, creates the worldmap + void LoadWorlds(void); + + /// Starts each world's life + void StartWorlds(void); + + /// Stops each world's threads, so that it's safe to unload them + void StopWorlds(void); + + /// Unloads all worlds from memory + void UnloadWorlds(void); + + cServer * m_Server; + cMonsterConfig * m_MonsterConfig; + + cGroupManager * m_GroupManager; + cCraftingRecipes * m_CraftingRecipes; + cFurnaceRecipe * m_FurnaceRecipe; + cWebAdmin * m_WebAdmin; + cPluginManager * m_PluginManager; + cAuthenticator m_Authenticator; + + cMCLogger * m_Log; + + bool m_bStop; + bool m_bRestart; + + typedef std::map< AString, cWorld* > WorldMap; + cWorld* m_pDefaultWorld; + WorldMap m_WorldsByName; + + cThread* m_InputThread; + static void InputThread(void* a_Params); + + static cRoot* s_Root; +}; //tolua_export + + + + diff --git a/source/SandSimulator.cpp b/source/SandSimulator.cpp new file mode 100644 index 000000000..1dd9d3d88 --- /dev/null +++ b/source/SandSimulator.cpp @@ -0,0 +1,91 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "SandSimulator.h" +#include "World.h" +#include "Vector3i.h" +#include "BlockID.h" +#include "Defines.h" + + + + + +cSandSimulator::cSandSimulator( cWorld* a_World ) + : cSimulator(a_World) + , m_Blocks(new BlockList) + , m_Buffer(new BlockList) +{ + +} + +cSandSimulator::~cSandSimulator() +{ + delete m_Buffer; + delete m_Blocks; +} + +void cSandSimulator::Simulate( float a_Dt ) +{ + m_Buffer->clear(); + std::swap( m_Blocks, m_Buffer ); + + for( BlockList::iterator itr = m_Buffer->begin(); itr != m_Buffer->end(); ++itr ) + { + Vector3i Pos = *itr; + char BlockID = m_World->GetBlock(Pos.x, Pos.y, Pos.z); + if(!IsAllowedBlock(BlockID)) + continue; + + char BottomBlock = m_World->GetBlock( Pos.x, Pos.y - 1, Pos.z ); + + if( IsPassable(BottomBlock) ) + { + m_World->SetBlock( Pos.x, Pos.y, Pos.z, E_BLOCK_AIR, 0 ); + m_World->SetBlock( Pos.x, Pos.y - 1, Pos.z, BlockID, 0 ); + } + } + +} + + +bool cSandSimulator::IsAllowedBlock( char a_BlockID ) +{ + return a_BlockID == E_BLOCK_SAND + || a_BlockID == E_BLOCK_GRAVEL; +} + +void cSandSimulator::AddBlock(int a_X, int a_Y, int a_Z) +{ + if(!IsAllowedBlock(m_World->GetBlock(a_X, a_Y, a_Z))) //This should save very much time because it doesnīt have to iterate through all blocks + return; + + Vector3i Block(a_X, a_Y, a_Z); + + //check for duplicates + for( BlockList::iterator itr = m_Blocks->begin(); itr != m_Blocks->end(); ++itr ) + { + Vector3i Pos = *itr; + if( Pos.x == a_X && Pos.y == a_Y && Pos.z == a_Z ) + return; + } + + m_Blocks->push_back(Block); + +} + +bool cSandSimulator::IsPassable( char a_BlockID ) +{ + return a_BlockID == E_BLOCK_AIR + || IsBlockWater(a_BlockID) + || IsBlockLava(a_BlockID) + || a_BlockID == E_BLOCK_FIRE; + +} + +void cSandSimulator::WakeUp( int a_X, int a_Y, int a_Z ) +{ + //Nothing else needs to be simulated :D (Bugs not included :s) + AddBlock( a_X, a_Y+1, a_Z ); + AddBlock( a_X, a_Y, a_Z ); +} diff --git a/source/SandSimulator.h b/source/SandSimulator.h new file mode 100644 index 000000000..6dbac1974 --- /dev/null +++ b/source/SandSimulator.h @@ -0,0 +1,40 @@ + +#pragma once + +#include "Simulator.h" +#include "BlockEntity.h" +#include "Vector3i.h" + + + + + +class cWorld; + + + + + +class cSandSimulator : public cSimulator +{ +public: + cSandSimulator( cWorld* a_World ); + ~cSandSimulator(); + + virtual void Simulate( float a_Dt ); + virtual void WakeUp( int a_X, int a_Y, int a_Z ); + + virtual bool IsAllowedBlock( char a_BlockID ); + virtual bool IsPassable( char a_BlockID ); + +protected: + virtual void AddBlock(int a_X, int a_Y, int a_Z); + + typedef std::list BlockList; + BlockList * m_Blocks; + BlockList * m_Buffer; +}; + + + + diff --git a/source/Server.cpp b/source/Server.cpp new file mode 100644 index 000000000..c77892076 --- /dev/null +++ b/source/Server.cpp @@ -0,0 +1,714 @@ + +// ReDucTor is an awesome guy who helped me a lot + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Server.h" +#include "ClientHandle.h" +#include "OSSupport/Timer.h" +#include "Mobs/Monster.h" +#include "OSSupport/Socket.h" +#include "Root.h" +#include "World.h" +#include "ChunkDef.h" +#include "PluginManager.h" +#include "GroupManager.h" +#include "ChatColor.h" +#include "Player.h" +#include "Inventory.h" +#include "Item.h" +#include "FurnaceRecipe.h" +#include "Tracer.h" +#include "WebAdmin.h" +#include "Protocol/ProtocolRecognizer.h" + +#include "MersenneTwister.h" + +#include "../iniFile/iniFile.h" +#include "Vector3f.h" + +#include +#include +#include + + + + + +extern "C" { + #include "zlib.h" +} + + + +bool g_bWaterPhysics = false; + +typedef std::list< cClientHandle* > ClientList; + + + + + +struct cServer::sServerState +{ + sServerState() + : pListenThread( 0 ) + , pTickThread( 0 ) + , bStopListenThread( false ) + , bStopTickThread( false ) + {} + cSocket SListenClient; // socket listening for client calls + + cThread* pListenThread; bool bStopListenThread; + cThread* pTickThread; bool bStopTickThread; + + cEvent RestartEvent; + std::string ServerID; +}; + + + + + +cServer * cServer::GetServer() +{ + LOGWARN("WARNING: Using deprecated function cServer::GetServer() use cRoot::Get()->GetServer() instead!"); + return cRoot::Get()->GetServer(); +} + + + + + +void cServer::ServerListenThread( void *a_Args ) +{ + LOG("ServerListenThread"); + cServer* self = (cServer*)a_Args; + sServerState* m_pState = self->m_pState; + while( !m_pState->bStopListenThread ) + { + self->StartListenClient(); + } +} + + + + + +void cServer::ClientDestroying(const cClientHandle * a_Client) +{ + m_SocketThreads.StopReading(a_Client); +} + + + + + +void cServer::NotifyClientWrite(const cClientHandle * a_Client) +{ + m_NotifyWriteThread.NotifyClientWrite(a_Client); +} + + + + + +void cServer::WriteToClient(const cSocket * a_Socket, const AString & a_Data) +{ + m_SocketThreads.Write(a_Socket, a_Data); +} + + + + + +void cServer::QueueClientClose(const cSocket * a_Socket) +{ + m_SocketThreads.QueueClose(a_Socket); +} + + + + + +void cServer::RemoveClient(const cSocket * a_Socket) +{ + m_SocketThreads.RemoveClient(a_Socket); +} + + + + + +bool cServer::InitServer( int a_Port ) +{ + if( m_bIsConnected ) + { + LOGERROR("ERROR: Trying to initialize server while server is already running!"); + return false; + } + + printf("/============================\\\n"); + printf("| Custom Minecraft Server |\n"); + printf("| Created by Kevin Bansberg |\n"); + printf("| A.K.A. FakeTruth |\n"); + printf("| Monsters by Alex Sonek |\n"); + printf("| A.K.A. Duralex |\n"); + printf("| Stuff by Mattes D |\n"); + printf("| A.K.A. _Xoft(o) |\n"); + printf("\\============================/\n"); + printf("More info: WWW.MC-SERVER.ORG\n"); + printf(" WWW.AE-C.NET\n"); + printf(" WWW.RBTHINKTANK.COM\n"); + printf("email: faketruth@gmail.com\n\n"); + + LOG("Starting up server."); + LOGINFO("Compatible clients: %s, protocol versions %s", MCS_CLIENT_VERSIONS, MCS_PROTOCOL_VERSIONS); + + if( cSocket::WSAStartup() != 0 ) // Only does anything on Windows, but whatever + { + LOGERROR("WSAStartup() != 0"); + return false; + } + + m_pState->SListenClient = cSocket::CreateSocket(); + + if( !m_pState->SListenClient.IsValid() ) + { + LOGERROR("m_SListenClient==INVALID_SOCKET (%s)", cSocket::GetErrorString( cSocket::GetLastError() ).c_str() ); + return false; + } + + if( m_pState->SListenClient.SetReuseAddress() == -1 ) + { + LOGERROR("setsockopt == -1"); + return false; + } + + cSocket::SockAddr_In local; + local.Family = cSocket::ADDRESS_FAMILY_INTERNET; + local.Address = cSocket::INTERNET_ADDRESS_ANY; + local.Port = (unsigned short)a_Port; // 25565 + + if( m_pState->SListenClient.Bind( local ) != 0 ) + { + LOGERROR("bind fail (%s)", cSocket::GetErrorString( cSocket::GetLastError() ).c_str() ); + return false; + } + + if( m_pState->SListenClient.Listen( 10 ) != 0) + { + LOGERROR("listen fail (%s)", cSocket::GetErrorString( cSocket::GetLastError() ).c_str() ); + return false; + } + + m_iServerPort = a_Port; + LOG("Port %i has been bound, server is open for connections", m_iServerPort); + m_bIsConnected = true; + + cIniFile IniFile("settings.ini"); + if (IniFile.ReadFile()) + { + g_bWaterPhysics = IniFile.GetValueB("Physics", "Water", false ); + + m_pState->ServerID = "-"; + if (IniFile.GetValueB("Authentication", "Authenticate")) + { + MTRand mtrand1; + unsigned int r1 = (mtrand1.randInt()%1147483647) + 1000000000; + unsigned int r2 = (mtrand1.randInt()%1147483647) + 1000000000; + std::ostringstream sid; + sid << std::hex << r1; + sid << std::hex << r2; + std::string ServerID = sid.str(); + ServerID.resize(16, '0'); + m_pState->ServerID = ServerID; + } + + m_ClientViewDistance = IniFile.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE); + if (m_ClientViewDistance < cClientHandle::MIN_VIEW_DISTANCE) + { + m_ClientViewDistance = cClientHandle::MIN_VIEW_DISTANCE; + LOGINFO("Setting default viewdistance to the minimum of %d", m_ClientViewDistance); + } + if (m_ClientViewDistance > cClientHandle::MAX_VIEW_DISTANCE) + { + m_ClientViewDistance = cClientHandle::MAX_VIEW_DISTANCE; + LOGINFO("Setting default viewdistance to the maximum of %d", m_ClientViewDistance); + } + IniFile.WriteFile(); + } + + m_NotifyWriteThread.Start(this); + + PrepareKeys(); + + return true; +} + + + + + +cServer::cServer() + : m_pState( new sServerState ) + , m_Millisecondsf( 0 ) + , m_Milliseconds( 0 ) + , m_bIsConnected( false ) + , m_iServerPort( 0 ) + , m_bRestarting( false ) +{ +} + + + + + +cServer::~cServer() +{ + // TODO: Shut down the server gracefully + if ( m_pState->SListenClient ) + { + m_pState->SListenClient.CloseSocket(); + } + m_pState->SListenClient = 0; + + m_pState->bStopListenThread = true; + delete m_pState->pListenThread; m_pState->pListenThread = NULL; + m_pState->bStopTickThread = true; + delete m_pState->pTickThread; m_pState->pTickThread = NULL; + + delete m_pState; +} + + + + + +void cServer::PrepareKeys(void) +{ + // TODO: Save and load key for persistence across sessions + // But generating the key takes only a moment, do we even need that? + + LOG("Generating protocol encryption keypair..."); + + time_t CurTime = time(NULL); + CryptoPP::RandomPool rng; + rng.Put((const byte *)&CurTime, sizeof(CurTime)); + m_PrivateKey.GenerateRandomWithKeySize(rng, 1024); + CryptoPP::RSA::PublicKey pk(m_PrivateKey); + m_PublicKey = pk; +} + + + + + +void cServer::BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSClients); + for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) + { + if ((*itr == a_Exclude) || !(*itr)->IsLoggedIn()) + { + continue; + } + (*itr)->SendChat(a_Message); + } +} + + + + + +void cServer::StartListenClient() +{ + cSocket SClient = m_pState->SListenClient.Accept(); + + if (!SClient.IsValid()) + { + return; + } + + const AString & ClientIP = SClient.GetIPString(); + if (ClientIP.empty()) + { + LOGWARN("cServer: A client connected, but didn't present its IP, disconnecting."); + SClient.CloseSocket(); + return; + } + + LOG("Client \"%s\" connected!", ClientIP.c_str()); + + cClientHandle *NewHandle = new cClientHandle(SClient, m_ClientViewDistance); + if (!m_SocketThreads.AddClient(&(NewHandle->GetSocket()), NewHandle)) + { + // For some reason SocketThreads have rejected the handle, clean it up + LOGERROR("Client \"%s\" cannot be handled, server probably unstable", SClient.GetIPString().c_str()); + SClient.CloseSocket(); + delete NewHandle; + return; + } + + cCSLock Lock(m_CSClients); + m_Clients.push_back( NewHandle ); +} + + + + + +bool cServer::Tick(float a_Dt) +{ + //LOG("1. Tick %0.2f", a_Dt); + if( a_Dt > 100.f ) a_Dt = 100.f; // Don't go over 1/10 second + + m_Millisecondsf += a_Dt; + if( m_Millisecondsf > 1.f ) + { + m_Milliseconds += (int)m_Millisecondsf; + m_Millisecondsf = m_Millisecondsf - (int)m_Millisecondsf; + } + + cRoot::Get()->TickWorlds( a_Dt ); // TODO - Maybe give all worlds their own thread? + + cClientHandleList RemoveClients; + { + cCSLock Lock(m_CSClients); + for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();) + { + if ((*itr)->IsDestroyed()) + { + RemoveClients.push_back(*itr); // Remove the client later, when CS is not held, to avoid deadlock ( http://forum.mc-server.org/showthread.php?tid=374 ) + itr = m_Clients.erase(itr); + continue; + } + (*itr)->Tick(a_Dt); + ++itr; + } // for itr - m_Clients[] + } + for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr) + { + delete *itr; + } // for itr - RemoveClients[] + + cRoot::Get()->GetPluginManager()->Tick( a_Dt ); + + if( !m_bRestarting ) + { + return true; + } + else + { + m_bRestarting = false; + m_pState->RestartEvent.Set(); + return false; + } +} + + + + + +void ServerTickThread( void * a_Param ) +{ + LOG("ServerTickThread"); + cServer *CServerObj = (cServer*)a_Param; + + cTimer Timer; + + long long msPerTick = 50; // TODO - Put this in server config file + long long LastTime = Timer.GetNowTime(); + + bool bKeepGoing = true; + while( bKeepGoing ) + { + long long NowTime = Timer.GetNowTime(); + float DeltaTime = (float)(NowTime-LastTime); + bKeepGoing = CServerObj->Tick( DeltaTime ); + long long TickTime = Timer.GetNowTime() - NowTime; + + if( TickTime < msPerTick ) // Stretch tick time until it's at least msPerTick + { + cSleep::MilliSleep( (unsigned int)( msPerTick - TickTime ) ); + } + + LastTime = NowTime; + } + + LOG("TICK THREAD STOPPED"); +} + + + + + +void cServer::StartListenThread() +{ + m_pState->pListenThread = new cThread( ServerListenThread, this, "cServer::ServerListenThread" ); + m_pState->pTickThread = new cThread( ServerTickThread, this, "cServer::ServerTickThread" ); + m_pState->pListenThread->Start( true ); + m_pState->pTickThread->Start( true ); +} + + + + + +bool cServer::Command(cClientHandle & a_Client, const AString & a_Cmd) +{ + return cRoot::Get()->GetPluginManager()->CallHookChat(a_Client.GetPlayer(), a_Cmd); +} + + + + + +void cServer::ServerCommand(const AString & a_Cmd) +{ + AStringVector split = StringSplit(a_Cmd, " "); + if (split.empty()) + { + return; + } + + if (split[0].compare( "help" ) == 0) + { + printf("================== ALL COMMANDS ===================\n"); + printf("help - Shows this message\n"); + printf("save-all - Saves all loaded chunks to disk\n"); + printf("list - Lists all players currently in server\n"); + printf("unload - Unloads all unused chunks\n"); + printf("numchunks - Shows number of chunks currently loaded\n"); + printf("chunkstats - Shows chunks statistics\n"); + printf("say - Sends a chat message to all players\n"); + printf("restart - Kicks all clients, and saves everything\n"); + printf(" and clears memory\n"); + printf("stop - Saves everything and closes server\n"); + printf("===================================================\n"); + return; + } + if ((split[0].compare("stop") == 0) || (split[0].compare("restart") == 0)) + { + return; + } + if (split[0].compare("save-all") == 0) + { + cRoot::Get()->SaveAllChunks(); + return; + } + if (split[0].compare("unload") == 0) + { + LOG("Num loaded chunks before: %i", cRoot::Get()->GetTotalChunkCount() ); + cRoot::Get()->GetDefaultWorld()->UnloadUnusedChunks(); // TODO: Iterate through ALL worlds + LOG("Num loaded chunks after: %i", cRoot::Get()->GetTotalChunkCount() ); + return; + } + if (split[0].compare("list") == 0) + { + class cPlayerLogger : public cPlayerListCallback + { + virtual bool Item(cPlayer * a_Player) override + { + LOG("\t%s @ %s", a_Player->GetName().c_str(), a_Player->GetClientHandle()->GetIPString().c_str()); + return false; + } + } Logger; + cRoot::Get()->ForEachPlayer(Logger); + return; + } + if (split[0].compare("numchunks") == 0) + { + LOG("Num loaded chunks: %i", cRoot::Get()->GetTotalChunkCount() ); + return; + } + if (split[0].compare("chunkstats") == 0) + { + cRoot::Get()->LogChunkStats(); + return; + } + + if (split[0].compare("monsters") == 0) + { + // TODO: cWorld::ListMonsters(); + return; + } + + if (split.size() > 1) + { + if (split[0].compare("say") == 0) + { + AString ToSay = a_Cmd.substr(a_Cmd.find_first_of("say") + 4); + AString Message = cChatColor::Purple + "[SERVER] " + ToSay; + LOG("[SERVER]: %s", ToSay.c_str()); + BroadcastChat(Message); + return; + } + } + printf("Unknown command, type 'help' for all commands.\n"); + // LOG("You didn't enter anything? (%s)", a_Cmd.c_str() ); +} + + + + + +void cServer::SendMessage(const AString & a_Message, cPlayer * a_Player /* = NULL */, bool a_bExclude /* = false */ ) +{ + if ((a_Player != NULL) && !a_bExclude) + { + cClientHandle * Client = a_Player->GetClientHandle(); + if (Client != NULL) + { + Client->SendChat(a_Message); + } + return; + } + + BroadcastChat(a_Message, (a_Player != NULL) ? a_Player->GetClientHandle() : NULL); +} + + + + + +void cServer::Shutdown() +{ + m_bRestarting = true; + m_pState->RestartEvent.Wait(); + + cRoot::Get()->SaveAllChunks(); + + cCSLock Lock(m_CSClients); + for( ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr ) + { + (*itr)->Destroy(); + delete *itr; + } + m_Clients.clear(); +} + + + + + +const AString & cServer::GetServerID(void) const +{ + return m_pState->ServerID; +} + + + + + +void cServer::KickUser(int a_ClientID, const AString & a_Reason) +{ + cCSLock Lock(m_CSClients); + for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) + { + if ((*itr)->GetUniqueID() == a_ClientID) + { + (*itr)->Kick(a_Reason); + } + } // for itr - m_Clients[] +} + + + + + +void cServer::AuthenticateUser(int a_ClientID) +{ + cCSLock Lock(m_CSClients); + for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) + { + if ((*itr)->GetUniqueID() == a_ClientID) + { + (*itr)->Authenticate(); + } + } // for itr - m_Clients[] +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cServer::cClientPacketThread: + +cServer::cNotifyWriteThread::cNotifyWriteThread(void) : + super("ClientPacketThread"), + m_Server(NULL) +{ +} + + + + + +cServer::cNotifyWriteThread::~cNotifyWriteThread() +{ + m_ShouldTerminate = true; + m_Event.Set(); + Wait(); +} + + + + + +bool cServer::cNotifyWriteThread::Start(cServer * a_Server) +{ + m_Server = a_Server; + return super::Start(); +} + + + + + +void cServer::cNotifyWriteThread::Execute(void) +{ + cClientHandleList Clients; + while (!m_ShouldTerminate) + { + cCSLock Lock(m_CS); + while (m_Clients.size() == 0) + { + cCSUnlock Unlock(Lock); + m_Event.Wait(); + if (m_ShouldTerminate) + { + return; + } + } + + // Copy the clients to notify and unlock the CS: + Clients.splice(Clients.begin(), m_Clients); + Lock.Unlock(); + + for (cClientHandleList::iterator itr = Clients.begin(); itr != Clients.end(); ++itr) + { + m_Server->m_SocketThreads.NotifyWrite(*itr); + } // for itr - Clients[] + Clients.clear(); + } // while (!mShouldTerminate) +} + + + + + +void cServer::cNotifyWriteThread::NotifyClientWrite(const cClientHandle * a_Client) +{ + { + cCSLock Lock(m_CS); + m_Clients.remove(const_cast(a_Client)); // Put it there only once + m_Clients.push_back(const_cast(a_Client)); + } + m_Event.Set(); +} + + + + diff --git a/source/Server.h b/source/Server.h new file mode 100644 index 000000000..b3599aa1e --- /dev/null +++ b/source/Server.h @@ -0,0 +1,141 @@ + +// cServer.h + +// Interfaces to the cServer object representing the network server + + + + + +#pragma once +#ifndef CSERVER_H_INCLUDED +#define CSERVER_H_INCLUDED + +#include "OSSupport/SocketThreads.h" +#include "CryptoPP/rsa.h" +#include "CryptoPP/randpool.h" + + + + + +class cPlayer; +class cClientHandle; + +typedef std::list cClientHandleList; + + + + + +class cServer //tolua_export +{ //tolua_export +public: //tolua_export + static cServer * GetServer(); //tolua_export + + bool InitServer( int a_Port = 25565 ); + + int GetPort() { return m_iServerPort; } + bool IsConnected(){return m_bIsConnected;} // returns connection status + void StartListenClient(); // Listen to client + + void BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude = NULL); + + bool Tick(float a_Dt); + + void StartListenThread(); + + bool Command(cClientHandle & a_Client, const AString & a_Cmd); + void ServerCommand(const AString & a_Cmd); //tolua_export + void Shutdown(); + + void SendMessage(const AString & a_Message, cPlayer * a_Player = NULL, bool a_bExclude = false ); // tolua_export + + void KickUser(int a_ClientID, const AString & a_Reason); + void AuthenticateUser(int a_ClientID); // Called by cAuthenticator to auth the specified user + + static void ServerListenThread( void* a_Args ); + + const AString & GetServerID(void) const; + + void ClientDestroying(const cClientHandle * a_Client); // Called by cClientHandle::Destroy(); stop m_SocketThreads from calling back into a_Client + + void NotifyClientWrite(const cClientHandle * a_Client); // Notifies m_SocketThreads that client has something to be written + + void WriteToClient(const cSocket * a_Socket, const AString & a_Data); // Queues outgoing data for the socket through m_SocketThreads + + void QueueClientClose(const cSocket * a_Socket); // Queues the socket to close when all its outgoing data is sent + + void RemoveClient(const cSocket * a_Socket); // Removes the socket from m_SocketThreads + + CryptoPP::RSA::PrivateKey & GetPrivateKey(void) { return m_PrivateKey; } + CryptoPP::RSA::PublicKey & GetPublicKey (void) { return m_PublicKey; } + +private: + + friend class cRoot; // so cRoot can create and destroy cServer + + /// When NotifyClientWrite() is called, it is queued for this thread to process (to avoid deadlocks between cSocketThreads, cClientHandle and cChunkMap) + class cNotifyWriteThread : + public cIsThread + { + typedef cIsThread super; + + cEvent m_Event; // Set when m_Clients gets appended + cServer * m_Server; + + cCriticalSection m_CS; + cClientHandleList m_Clients; + + virtual void Execute(void); + + public: + + cNotifyWriteThread(void); + ~cNotifyWriteThread(); + + bool Start(cServer * a_Server); + + void NotifyClientWrite(const cClientHandle * a_Client); + } ; + + struct sServerState; + sServerState* m_pState; + + cNotifyWriteThread m_NotifyWriteThread; + + cCriticalSection m_CSClients; // Locks client list + cClientHandleList m_Clients; // Clients that are connected to the server + + cSocketThreads m_SocketThreads; + + int m_ClientViewDistance; // The default view distance for clients; settable in Settings.ini + + // Time since server was started + float m_Millisecondsf; + unsigned int m_Milliseconds; + + bool m_bIsConnected; // true - connected false - not connected + int m_iServerPort; + + bool m_bRestarting; + + CryptoPP::RSA::PrivateKey m_PrivateKey; + CryptoPP::RSA::PublicKey m_PublicKey; + + cServer(); + ~cServer(); + + /// Loads, or generates, if missing, RSA keys for protocol encryption + void PrepareKeys(void); +}; //tolua_export + + + + + +#endif // CSERVER_H_INCLUDED + + + + diff --git a/source/Sign.h b/source/Sign.h new file mode 100644 index 000000000..9daa8234c --- /dev/null +++ b/source/Sign.h @@ -0,0 +1,32 @@ +#pragma once + +class cSign //tolua_export +{ //tolua_export +public: + static char RotationToMetaData( float a_Rotation ) //tolua_export + { //tolua_export + a_Rotation += 180 + (180/16); // So its not aligned with axis + if( a_Rotation > 360.f ) a_Rotation -= 360.f; + + a_Rotation = (a_Rotation/360) * 16; + + return ((char)a_Rotation) % 16; + } //tolua_export + static char DirectionToMetaData( char a_Direction ) //tolua_export + { //tolua_export + switch( a_Direction ) + { + case 0x2: + return 0x2; + case 0x3: + return 0x3; + case 0x4: + return 0x4; + case 0x5: + return 0x5; + default: + break; + }; + return 0x2; + } +}; //tolua_export \ No newline at end of file diff --git a/source/SignEntity.cpp b/source/SignEntity.cpp new file mode 100644 index 000000000..f2ac4b0b1 --- /dev/null +++ b/source/SignEntity.cpp @@ -0,0 +1,130 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "SignEntity.h" + +#include "Player.h" +#include "ClientHandle.h" +#include "World.h" +#include "Root.h" + +#include + + + + + +cSignEntity::cSignEntity(ENUM_BLOCK_ID a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World) + : cBlockEntity(a_BlockType, a_X, a_Y, a_Z, a_World) +{ +} + + + + + +cSignEntity::~cSignEntity() +{ +} + + + + + +// It don't do anything when 'used' +void cSignEntity::UsedBy( cPlayer * a_Player ) +{ + (void)a_Player; +} + + + + + +void cSignEntity::SetLines( const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 ) +{ + m_Line[0] = a_Line1; + m_Line[1] = a_Line2; + m_Line[2] = a_Line3; + m_Line[3] = a_Line4; +} + + + + + +void cSignEntity::SetLine( int a_Index, const AString & a_Line ) +{ + if( a_Index < 4 && a_Index > -1 ) + { + m_Line[a_Index] = a_Line; + } +} + + + + + +AString cSignEntity::GetLine( int a_Index ) const +{ + if( a_Index < 4 && a_Index > -1 ) + { + return m_Line[a_Index]; + } + return ""; +} + + + + + +void cSignEntity::SendTo(cClientHandle & a_Client) +{ + a_Client.SendUpdateSign(m_PosX, m_PosY, m_PosZ, m_Line[0], m_Line[1], m_Line[2], m_Line[3]); +} + + + + + +#define READ(File, Var) \ + if (File.Read(&Var, sizeof(Var)) != sizeof(Var)) \ + { \ + LOGERROR("ERROR READING cSignEntity %s FROM FILE (line %d)", #Var, __LINE__); \ + return false; \ + } + + + + + + +bool cSignEntity::LoadFromJson( const Json::Value & a_Value ) +{ + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + m_Line[0] = a_Value.get("Line1", "").asString(); + m_Line[1] = a_Value.get("Line2", "").asString(); + m_Line[2] = a_Value.get("Line3", "").asString(); + m_Line[3] = a_Value.get("Line4", "").asString(); + + return true; +} + +void cSignEntity::SaveToJson( Json::Value & a_Value ) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + a_Value["Line1"] = m_Line[0]; + a_Value["Line2"] = m_Line[1]; + a_Value["Line3"] = m_Line[2]; + a_Value["Line4"] = m_Line[3]; +} + + + + diff --git a/source/SignEntity.h b/source/SignEntity.h new file mode 100644 index 000000000..6455afbe7 --- /dev/null +++ b/source/SignEntity.h @@ -0,0 +1,41 @@ + +#pragma once + +#include "BlockEntity.h" + + + + + +namespace Json +{ + class Value; +} + + +class cSignEntity : + public cBlockEntity +{ +public: + cSignEntity(ENUM_BLOCK_ID a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World); + virtual ~cSignEntity(); + + bool LoadFromJson( const Json::Value& a_Value ); + virtual void SaveToJson(Json::Value& a_Value ) override; + + void SetLines( const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 ); + void SetLine( int a_Index, const AString & a_Line ); + + AString GetLine( int a_Index ) const; + + virtual void UsedBy( cPlayer * a_Player ) override; + virtual void SendTo(cClientHandle & a_Client) override; + +private: + + AString m_Line[4]; +}; + + + + diff --git a/source/Simulator.cpp b/source/Simulator.cpp new file mode 100644 index 000000000..e4608e150 --- /dev/null +++ b/source/Simulator.cpp @@ -0,0 +1,33 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Simulator.h" +#include "World.h" +#include "Vector3i.h" +#include "BlockID.h" +#include "Defines.h" + + + + + +cSimulator::cSimulator( cWorld* a_World ) + : m_World(a_World) +{ + +} + +cSimulator::~cSimulator() +{ +} + +void cSimulator::WakeUp( int a_X, int a_Y, int a_Z ) +{ + AddBlock( a_X, a_Y, a_Z ); + AddBlock( a_X-1, a_Y, a_Z ); + AddBlock( a_X+1, a_Y, a_Z ); + AddBlock( a_X, a_Y-1, a_Z ); + AddBlock( a_X, a_Y+1, a_Z ); + AddBlock( a_X, a_Y, a_Z-1 ); + AddBlock( a_X, a_Y, a_Z+1 ); +} diff --git a/source/Simulator.h b/source/Simulator.h new file mode 100644 index 000000000..158e74b99 --- /dev/null +++ b/source/Simulator.h @@ -0,0 +1,20 @@ +#pragma once + +class Vector3i; +class cWorld; +class cSimulator +{ +public: + cSimulator( cWorld* a_World ); + ~cSimulator(); + + virtual void Simulate( float a_Dt ) = 0; + virtual void WakeUp( int a_X, int a_Y, int a_Z ); + + virtual bool IsAllowedBlock( char a_BlockID ) = 0; + +protected: + virtual void AddBlock(int a_X, int a_Y, int a_Z) = 0; + + cWorld * m_World; +}; \ No newline at end of file diff --git a/source/SimulatorManager.cpp b/source/SimulatorManager.cpp new file mode 100644 index 000000000..0aef1ec6c --- /dev/null +++ b/source/SimulatorManager.cpp @@ -0,0 +1,67 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "SimulatorManager.h" + + + + + +cSimulatorManager::cSimulatorManager() +{ + +} + + + + + +cSimulatorManager::~cSimulatorManager() +{ + for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr ) + { + delete *itr; + } // for itr - m_Simulators[] +} + + + + + +void cSimulatorManager::Simulate( float a_Dt ) +{ + m_Ticks++; + for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr ) + { + if(m_Ticks % (*itr)->second == 0) + (*itr)->first->Simulate(a_Dt); + } +} + + + + + +void cSimulatorManager::WakeUp(int a_X, int a_Y, int a_Z) +{ + for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr ) + { + (*itr)->first->WakeUp(a_X, a_Y, a_Z); + } +} + + + + + +void cSimulatorManager::RegisterSimulator(cSimulator *a_Simulator, short a_Rate) +{ + //TODO needs some checking + std::pair *Pair = new std::pair(a_Simulator, a_Rate); + + m_Simulators.push_back(Pair); +} + + + + diff --git a/source/SimulatorManager.h b/source/SimulatorManager.h new file mode 100644 index 000000000..e90acffab --- /dev/null +++ b/source/SimulatorManager.h @@ -0,0 +1,39 @@ + +// cSimulatorManager.h + + + + +#pragma once + + + + +#include "Simulator.h" + + + + + +class cSimulatorManager +{ +public: + cSimulatorManager(); + ~cSimulatorManager(); + + void Simulate( float a_Dt ); + void WakeUp(int a_X, int a_Y, int a_Z); + + void RegisterSimulator(cSimulator * a_Simulator, short a_Rate); // Takes ownership of the simulator object! + +protected: + + typedef std::vector *> cSimulators; + + cSimulators m_Simulators; + long long m_Ticks; +}; + + + + diff --git a/source/SquirrelCommandBinder.cpp b/source/SquirrelCommandBinder.cpp new file mode 100644 index 000000000..50eae9333 --- /dev/null +++ b/source/SquirrelCommandBinder.cpp @@ -0,0 +1,98 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "SquirrelCommandBinder.h" +#include "Player.h" +#include "Plugin.h" +#include "Plugin_Squirrel.h" +#include "squirrelbindings/SquirrelArray.h" + + +cSquirrelCommandBinder::cSquirrelCommandBinder() +{ +} + +cSquirrelCommandBinder::~cSquirrelCommandBinder() +{ +} + +void cSquirrelCommandBinder::ClearBindings() +{ + m_BoundCommands.clear(); +} + +void cSquirrelCommandBinder::RemoveBindingsForPlugin( cPlugin* a_Plugin ) +{ + for( CommandMap::iterator itr = m_BoundCommands.begin(); itr != m_BoundCommands.end(); ) + { + if( itr->second.Plugin == a_Plugin ) + { + LOGINFO("Unbinding %s ", itr->first.c_str( ) ); + CommandMap::iterator eraseme = itr; + ++itr; + m_BoundCommands.erase( eraseme ); + continue; + } + ++itr; + } +} + +bool cSquirrelCommandBinder::BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, Sqrat::Function a_Callback ) +{ + if( !a_Plugin->CanBindCommands() ) + { + LOGERROR("ERROR: Trying to bind command \"%s\" to a plugin that is not initialized.", a_Command.c_str() ); + return false; + } + if( m_BoundCommands.find( a_Command ) != m_BoundCommands.end() ) + { + LOGERROR("ERROR: Trying to bind command \"%s\" that has already been bound.", a_Command.c_str() ); + return false; + } + LOGINFO("Binding %s (%s)", a_Command.c_str(), a_Permission.c_str() ); + + BoundFunction Callback; + Callback.Callback = a_Callback; + Callback.Plugin = a_Plugin; + Callback.Permission = a_Permission; + + m_BoundCommands[ a_Command ] = Callback; + return true; +} + +bool cSquirrelCommandBinder::HandleCommand( const std::string & a_Command, cPlayer* a_Player ) +{ + AStringVector Split = StringSplit(a_Command, " "); + if (Split.size() == 0) + { + return false; + } + + CommandMap::iterator FoundCommand = m_BoundCommands.find( Split[0] ); + if( FoundCommand != m_BoundCommands.end() ) + { + const BoundFunction & func = FoundCommand->second; + if( func.Permission.size() > 0 ) + { + if( !a_Player->HasPermission( func.Permission.c_str() ) ) + { + return false; + } + } + + + // Push the split + SquirrelStringArray SplitData; + + std::vector::const_iterator iter = Split.begin(); + while(iter != Split.end()) { + SplitData.Add(*iter); + ++iter; + } + + // Push player + Sqrat::Function callback = func.Callback; + return callback.Evaluate(&SplitData, a_Player); + } + return false; +} diff --git a/source/SquirrelCommandBinder.h b/source/SquirrelCommandBinder.h new file mode 100644 index 000000000..50e026d06 --- /dev/null +++ b/source/SquirrelCommandBinder.h @@ -0,0 +1,35 @@ +#pragma once +#include +#include + +class cPlugin; +class cPlayer; + +class cSquirrelCommandBinder +{ +public: + cSquirrelCommandBinder(); + ~cSquirrelCommandBinder(); + + bool HandleCommand( const std::string & a_Command, cPlayer* a_Player ); + + bool BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, Sqrat::Function a_Callback); + + void ClearBindings(); + void RemoveBindingsForPlugin( cPlugin* a_Plugin ); +private: + struct BoundFunction + { + Sqrat::Function Callback; + cPlugin *Plugin; + std::string Permission; + }; + + typedef std::map< std::string, BoundFunction > CommandMap; + CommandMap m_BoundCommands; +}; + + + + + diff --git a/source/Stairs.h b/source/Stairs.h new file mode 100644 index 000000000..e17f4abc1 --- /dev/null +++ b/source/Stairs.h @@ -0,0 +1,28 @@ +#pragma once + +class cStairs //tolua_export +{ //tolua_export +public: + static char RotationToMetaData( float a_Rotation, int a_Direction ) //tolua_export + { //tolua_export + a_Rotation += 90 + 45; // So its not aligned with axis + char result = 0x0; + if( a_Direction == 0) + { + + result = 0x4; + } + + if( a_Rotation > 360.f ) a_Rotation -= 360.f; + if( a_Rotation >= 0.f && a_Rotation < 90.f ) + return result; + else if( a_Rotation >= 180 && a_Rotation < 270 ) + result += 0x1; + else if( a_Rotation >= 90 && a_Rotation < 180 ) + result += 0x2; + else + result += 0x3; + + return result; + } //tolua_export +}; //tolua_export diff --git a/source/StringMap.cpp b/source/StringMap.cpp new file mode 100644 index 000000000..8ba8891e0 --- /dev/null +++ b/source/StringMap.cpp @@ -0,0 +1,23 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "StringMap.h" + + + + + +unsigned int cStringMap::size() const +{ + return m_StringMap.size(); +} + +void cStringMap::clear() +{ + m_StringMap.clear(); +} + +std::string & cStringMap::get( const std::string & index ) +{ + return m_StringMap[index]; +} \ No newline at end of file diff --git a/source/StringMap.h b/source/StringMap.h new file mode 100644 index 000000000..1cf20048d --- /dev/null +++ b/source/StringMap.h @@ -0,0 +1,29 @@ + +// A std::map interface for Lua + +#pragma once + +#include "tolua++.h" + + + + + +class cStringMap // tolua_export +{ // tolua_export +public: // tolua_export + cStringMap(std::map< std::string, std::string > a_StringMap) : m_StringMap( a_StringMap ) {} + void clear(); // tolua_export + + unsigned int size() const; // tolua_export + + std::string & get( const std::string & index ); //tolua_export + + std::map< std::string, std::string >& GetStringMap() { return m_StringMap; } +private: + std::map< std::string, std::string > m_StringMap; +}; // tolua_export + + + + diff --git a/source/Torch.h b/source/Torch.h new file mode 100644 index 000000000..fa301c3d4 --- /dev/null +++ b/source/Torch.h @@ -0,0 +1,77 @@ + +#pragma once +#include "Vector3i.h" +#include "Defines.h" + + + + + +class cTorch //tolua_export +{ //tolua_export +public: + + static char DirectionToMetaData( char a_Direction ) //tolua_export + { //tolua_export + switch (a_Direction) + { + case BLOCK_FACE_BOTTOM: ASSERT(!"Shouldn't be getting this direction"); return 0; + case BLOCK_FACE_TOP: return E_META_TORCH_FLOOR; + case BLOCK_FACE_EAST: return E_META_TORCH_EAST; + case BLOCK_FACE_WEST: return E_META_TORCH_WEST; + case BLOCK_FACE_NORTH: return E_META_TORCH_NORTH; + case BLOCK_FACE_SOUTH: return E_META_TORCH_SOUTH; + default: + { + ASSERT(!"Unhandled torch direction!"); + break; + } + }; + return 0x0; + } //tolua_export + + + static char MetaDataToDirection(char a_MetaData) //tolua_export + { //tolua_export + switch (a_MetaData) + { + case 0: return BLOCK_FACE_TOP; // by default, the torches stand on the ground + case E_META_TORCH_FLOOR: return BLOCK_FACE_TOP; + case E_META_TORCH_EAST: return BLOCK_FACE_EAST; + case E_META_TORCH_WEST: return BLOCK_FACE_WEST; + case E_META_TORCH_NORTH: return BLOCK_FACE_NORTH; + case E_META_TORCH_SOUTH: return BLOCK_FACE_SOUTH; + default: + { + ASSERT(!"Unhandled torch metadata"); + break; + } + } + return 0; + } //tolua_export + + + static bool IsAttachedTo(const Vector3i & a_TorchPos, char a_TorchMeta, const Vector3i & a_BlockPos) + { + switch (a_TorchMeta) + { + case 0x0: + case E_META_TORCH_FLOOR: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 1, 0))); + case E_META_TORCH_EAST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, -1))); + case E_META_TORCH_WEST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, 1))); + case E_META_TORCH_NORTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(-1, 0, 0))); + case E_META_TORCH_SOUTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(1, 0, 0))); + default: + { + ASSERT(!"Unhandled torch meta!"); + break; + } + } + return false; + } + +} ; //tolua_export + + + + diff --git a/source/Tracer.cpp b/source/Tracer.cpp new file mode 100644 index 000000000..fb1ab6c3b --- /dev/null +++ b/source/Tracer.cpp @@ -0,0 +1,350 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Tracer.h" +#include "World.h" + +#include "Vector3f.h" +#include "Vector3i.h" +#include "Vector3d.h" + +#include "BlockID.h" +#include "Entity.h" + +#include "Defines.h" + +#ifndef _WIN32 + #include // abs() +#endif + +cTracer::cTracer(cWorld* a_World) + : m_World( a_World ) +{ + m_NormalTable[0].Set(-1, 0, 0); + m_NormalTable[1].Set( 0, 0,-1); + m_NormalTable[2].Set( 1, 0, 0); + m_NormalTable[3].Set( 0, 0, 1); + m_NormalTable[4].Set( 0, 1, 0); + m_NormalTable[5].Set( 0,-1, 0); +} + +cTracer::~cTracer() +{ +} + +float cTracer::SigNum( float a_Num ) +{ + if (a_Num < 0.f) return -1.f; + if (a_Num > 0.f) return 1.f; + return 0.f; +} + +void cTracer::SetValues( const Vector3f & a_Start, const Vector3f & a_Direction ) +{ + // calculate the direction of the ray (linear algebra) + dir = a_Direction; + + // decide which direction to start walking in + step.x = (int) SigNum(dir.x); + step.y = (int) SigNum(dir.y); + step.z = (int) SigNum(dir.z); + + // normalize the direction vector + if( dir.SqrLength() > 0.f ) dir.Normalize(); + + // how far we must move in the ray direction before + // we encounter a new voxel in x-direction + // same but y-direction + if( dir.x != 0.f ) tDelta.x = 1/fabs(dir.x); + else tDelta.x = 0; + if( dir.y != 0.f ) tDelta.y = 1/fabs(dir.y); + else tDelta.y = 0; + if( dir.z != 0.f ) tDelta.z = 1/fabs(dir.z); + else tDelta.z = 0; + + // start voxel coordinates + // use your + // transformer + // function here + pos.x = (int)floorf(a_Start.x); + pos.y = (int)floorf(a_Start.y); + pos.z = (int)floorf(a_Start.z); + + // calculate distance to first intersection in the voxel we start from + if(dir.x < 0) + { + tMax.x = ((float)pos.x - a_Start.x) / dir.x; + } + else + { + tMax.x = (((float)pos.x + 1) - a_Start.x) / dir.x; + } + + if(dir.y < 0) + { + tMax.y = ((float)pos.y - a_Start.y) / dir.y; + } + else + { + tMax.y = (((float)pos.y + 1) - a_Start.y) / dir.y; + } + + if(dir.z < 0) + { + tMax.z = ((float)pos.z - a_Start.z) / dir.z; + } + else + { + tMax.z = (((float)pos.z + 1) - a_Start.z) / dir.z; + } +} + +int cTracer::Trace( const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance) +{ + SetValues( a_Start, a_Direction ); + + const Vector3f End = a_Start + (dir * (float)a_Distance); + + // end voxel coordinates + end1.x = (int)floorf(End.x); + end1.y = (int)floorf(End.y); + end1.z = (int)floorf(End.z); + + // check if first is occupied + if( pos.Equals( end1 ) ) + { + LOG("WARNING: cTracer: Start and end in same block"); + return 0; + } + + bool reachedX = false, reachedY = false, reachedZ = false; + + int Iterations = 0; + while ( Iterations < a_Distance ) + { + Iterations++; + if(tMax.x < tMax.y && tMax.x < tMax.z) + { + tMax.x += tDelta.x; + pos.x += step.x; + } + else if(tMax.y < tMax.z) + { + tMax.y += tDelta.y; + pos.y += step.y; + } + else + { + tMax.z += tDelta.z; + pos.z += step.z; + } + + if(step.x > 0.0f) + { + if(pos.x >= end1.x) + { + reachedX = true; + } + } + else if(pos.x <= end1.x) + { + reachedX = true; + } + + if(step.y > 0.0f) + { + if(pos.y >= end1.y) + { + reachedY = true; + } + } + else if(pos.y <= end1.y) + { + reachedY = true; + } + + if(step.z > 0.0f) + { + if(pos.z >= end1.z) + { + reachedZ = true; + } + } + else if(pos.z <= end1.z) + { + reachedZ = true; + } + + if (reachedX && reachedY && reachedZ) + { + return false; + } + + char BlockID = m_World->GetBlock( pos.x, pos.y, pos.z ); + //No collision with water ;) + if ( BlockID != E_BLOCK_AIR || IsBlockWater(BlockID)) + { + BlockHitPosition = pos; + int Normal = GetHitNormal(a_Start, End, pos ); + if(Normal > 0) + { + HitNormal = m_NormalTable[Normal-1]; + } + return 1; + } + } + return 0; +} + +// return 1 = hit, other is not hit +int LinesCross(float x0,float y0,float x1,float y1,float x2,float y2,float x3,float y3) +{ + //float linx, liny; + + float d=(x1-x0)*(y3-y2)-(y1-y0)*(x3-x2); + if (abs(d)<0.001) {return 0;} + float AB=((y0-y2)*(x3-x2)-(x0-x2)*(y3-y2))/d; + if (AB>=0.0 && AB<=1.0) + { + float CD=((y0-y2)*(x1-x0)-(x0-x2)*(y1-y0))/d; + if (CD>=0.0 && CD<=1.0) + { + //linx=x0+AB*(x1-x0); + //liny=y0+AB*(y1-y0); + return 1; + } + } + return 0; +} + +// intersect3D_SegmentPlane(): intersect a segment and a plane +// Input: a_Ray = a segment, and a_Plane = a plane = {Point V0; Vector n;} +// Output: *I0 = the intersect point (when it exists) +// Return: 0 = disjoint (no intersection) +// 1 = intersection in the unique point *I0 +// 2 = the segment lies in the plane +int cTracer::intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f & a_End, const Vector3f & a_PlanePos, const Vector3f & a_PlaneNormal ) +{ + Vector3f u = a_End - a_Origin;//a_Ray.P1 - S.P0; + Vector3f w = a_Origin - a_PlanePos;//S.P0 - Pn.V0; + + float D = a_PlaneNormal.Dot( u );//dot(Pn.n, u); + float N = -(a_PlaneNormal.Dot( w ) );//-dot(a_Plane.n, w); + + const float EPSILON = 0.0001f; + if (fabs(D) < EPSILON) { // segment is parallel to plane + if (N == 0) // segment lies in plane + return 2; + return 0; // no intersection + } + // they are not parallel + // compute intersect param + float sI = N / D; + if (sI < 0 || sI > 1) + return 0; // no intersection + + //Vector3f I ( a_Ray->GetOrigin() + sI * u );//S.P0 + sI * u; // compute segment intersect point + RealHit = a_Origin + u * sI; + return 1; +} + +int cTracer::GetHitNormal(const Vector3f & start, const Vector3f & end, const Vector3i & a_BlockPos) +{ + Vector3i SmallBlockPos = a_BlockPos; + char BlockID = m_World->GetBlock( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z ); + + if( BlockID == E_BLOCK_AIR || IsBlockWater(BlockID)) + return 0; + + Vector3f BlockPos; + BlockPos = Vector3f(SmallBlockPos); + + Vector3f Look = (end - start); + Look.Normalize(); + + float dot = Look.Dot( Vector3f(-1, 0, 0) ); // first face normal is x -1 + if(dot < 0) + { + int Lines = LinesCross( start.x, start.y, end.x, end.y, BlockPos.x, BlockPos.y, BlockPos.x, BlockPos.y + 1 ); + if(Lines == 1) + { + Lines = LinesCross( start.x, start.z, end.x, end.z, BlockPos.x, BlockPos.z, BlockPos.x, BlockPos.z + 1 ); + if(Lines == 1) + { + intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(-1, 0, 0) ); + return 1; + } + } + } + dot = Look.Dot( Vector3f(0, 0, -1) ); // second face normal is z -1 + if(dot < 0) + { + int Lines = LinesCross( start.z, start.y, end.z, end.y, BlockPos.z, BlockPos.y, BlockPos.z, BlockPos.y + 1 ); + if(Lines == 1) + { + Lines = LinesCross( start.z, start.x, end.z, end.x, BlockPos.z, BlockPos.x, BlockPos.z, BlockPos.x + 1 ); + if(Lines == 1) + { + intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(0, 0, -1) ); + return 2; + } + } + } + dot = Look.Dot( Vector3f(1, 0, 0) ); // third face normal is x 1 + if(dot < 0) + { + int Lines = LinesCross( start.x, start.y, end.x, end.y, BlockPos.x + 1, BlockPos.y, BlockPos.x + 1, BlockPos.y + 1 ); + if(Lines == 1) + { + Lines = LinesCross( start.x, start.z, end.x, end.z, BlockPos.x + 1, BlockPos.z, BlockPos.x + 1, BlockPos.z + 1 ); + if(Lines == 1) + { + intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(1, 0, 0), Vector3f(1, 0, 0) ); + return 3; + } + } + } + dot = Look.Dot( Vector3f(0, 0, 1) ); // fourth face normal is z 1 + if(dot < 0) + { + int Lines = LinesCross( start.z, start.y, end.z, end.y, BlockPos.z + 1, BlockPos.y, BlockPos.z + 1, BlockPos.y + 1 ); + if(Lines == 1) + { + Lines = LinesCross( start.z, start.x, end.z, end.x, BlockPos.z + 1, BlockPos.x, BlockPos.z + 1, BlockPos.x + 1 ); + if(Lines == 1) + { + intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(0, 0, 1), Vector3f(0, 0, 1) ); + return 4; + } + } + } + dot = Look.Dot( Vector3f(0, 1, 0) ); // fifth face normal is y 1 + if(dot < 0) + { + int Lines = LinesCross( start.y, start.x, end.y, end.x, BlockPos.y + 1, BlockPos.x, BlockPos.y + 1, BlockPos.x + 1 ); + if(Lines == 1) + { + Lines = LinesCross( start.y, start.z, end.y, end.z, BlockPos.y + 1, BlockPos.z, BlockPos.y + 1, BlockPos.z + 1 ); + if(Lines == 1) + { + intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(0, 1, 0), Vector3f(0, 1, 0) ); + return 5; + } + } + } + dot = Look.Dot( Vector3f(0, -1, 0) ); // sixth face normal is y -1 + if(dot < 0) + { + int Lines = LinesCross( start.y, start.x, end.y, end.x, BlockPos.y, BlockPos.x, BlockPos.y, BlockPos.x + 1 ); + if(Lines == 1) + { + Lines = LinesCross( start.y, start.z, end.y, end.z, BlockPos.y, BlockPos.z, BlockPos.y, BlockPos.z + 1 ); + if(Lines == 1) + { + intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(0, -1, 0) ); + return 6; + } + } + } + return 0; +} diff --git a/source/Tracer.h b/source/Tracer.h new file mode 100644 index 000000000..fa9d3c673 --- /dev/null +++ b/source/Tracer.h @@ -0,0 +1,34 @@ +#pragma once + +#include "Vector3i.h" +#include "Vector3f.h" + + +class cWorld; +class cTracer //tolua_export +{ //tolua_export +public: //tolua_export + Vector3f DotPos; + Vector3f BoxOffset; + cTracer( cWorld* a_World); //tolua_export + ~cTracer(); //tolua_export + int Trace( const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance ); //tolua_export + void SetValues( const Vector3f & a_Start, const Vector3f & a_Direction ); //tolua_export + Vector3f BlockHitPosition; //tolua_export + Vector3f HitNormal; //tolua_export + Vector3f RealHit; //tolua_export +private: + int intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f & a_End, const Vector3f & a_PlanePos, const Vector3f & a_PlaneNormal ); + int GetHitNormal( const Vector3f & start, const Vector3f & end, const Vector3i & a_BlockPos); + float SigNum( float a_Num ); + cWorld* m_World; + + Vector3f m_NormalTable[6]; + + Vector3f dir; + Vector3f tDelta; + Vector3i pos; + Vector3i end1; + Vector3i step; + Vector3f tMax; +}; //tolua_export \ No newline at end of file diff --git a/source/UI/SlotArea.cpp b/source/UI/SlotArea.cpp index 655f85524..73d26eb27 100644 --- a/source/UI/SlotArea.cpp +++ b/source/UI/SlotArea.cpp @@ -5,13 +5,13 @@ #include "Globals.h" #include "SlotArea.h" -#include "../cPlayer.h" -#include "../cChestEntity.h" -#include "../cFurnaceEntity.h" -#include "../Items/Item.h" +#include "../Player.h" +#include "../ChestEntity.h" +#include "../FurnaceEntity.h" +#include "../Items/ItemHandler.h" #include "Window.h" #include "../CraftingRecipes.h" -#include "../cRoot.h" +#include "../Root.h" diff --git a/source/UI/SlotArea.h b/source/UI/SlotArea.h index 79f95c5d5..e356fbdd8 100644 --- a/source/UI/SlotArea.h +++ b/source/UI/SlotArea.h @@ -8,7 +8,7 @@ #pragma once -#include "../cItem.h" +#include "../Item.h" diff --git a/source/UI/Window.cpp b/source/UI/Window.cpp index e6c7a9feb..b3d72570c 100644 --- a/source/UI/Window.cpp +++ b/source/UI/Window.cpp @@ -2,15 +2,15 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Window.h" -#include "../cItem.h" -#include "../cClientHandle.h" -#include "../cPlayer.h" -#include "../cPickup.h" -#include "../cInventory.h" +#include "../Item.h" +#include "../ClientHandle.h" +#include "../Player.h" +#include "../Pickup.h" +#include "../Inventory.h" #include "WindowOwner.h" -#include "../items/Item.h" +#include "../items/ItemHandler.h" #include "SlotArea.h" -#include "../cChestEntity.h" +#include "../ChestEntity.h" diff --git a/source/UI/Window.h b/source/UI/Window.h index b3df1bc86..8caab3bd1 100644 --- a/source/UI/Window.h +++ b/source/UI/Window.h @@ -9,7 +9,7 @@ #pragma once -#include "../cItem.h" +#include "../Item.h" diff --git a/source/UI/WindowOwner.h b/source/UI/WindowOwner.h index e0cc8da8a..7ad4a5739 100644 --- a/source/UI/WindowOwner.h +++ b/source/UI/WindowOwner.h @@ -1,8 +1,8 @@ #pragma once -#include "../cBlockEntity.h" -#include "../cEntity.h" +#include "../BlockEntity.h" +#include "../Entity.h" #include "Window.h" /* diff --git a/source/Vine.h b/source/Vine.h new file mode 100644 index 000000000..d4bc99c1a --- /dev/null +++ b/source/Vine.h @@ -0,0 +1,23 @@ +#pragma once + +class cVine //tolua_export +{ //tolua_export +public: + + static char DirectionToMetaData( char a_Direction ) //tolua_export + { //tolua_export + switch (a_Direction) + { + case 0x2: + return 0x1; + case 0x3: + return 0x4; + case 0x4: + return 0x8; + case 0x5: + return 0x2; + default: + return 0xf; + }; + } //tolua_export +}; //tolua_export \ No newline at end of file diff --git a/source/WaterSimulator.cpp b/source/WaterSimulator.cpp new file mode 100644 index 000000000..48389ccd4 --- /dev/null +++ b/source/WaterSimulator.cpp @@ -0,0 +1,22 @@ +#include "Globals.h" +#include "WaterSimulator.h" +#include "Defines.h" +#include "World.h" + + + +cWaterSimulator::cWaterSimulator(cWorld *a_World) + : cFluidSimulator(a_World) +{ + m_FluidBlock = E_BLOCK_WATER; + m_StationaryFluidBlock = E_BLOCK_STATIONARY_WATER; + m_MaxHeight = 7; + m_FlowReduction = 1; +} + + +bool cWaterSimulator::IsAllowedBlock(char a_BlockID) +{ + return IsBlockWater(a_BlockID); +} + diff --git a/source/WaterSimulator.h b/source/WaterSimulator.h new file mode 100644 index 000000000..aa81a74f6 --- /dev/null +++ b/source/WaterSimulator.h @@ -0,0 +1,11 @@ +#pragma once +#include "FluidSimulator.h" + +class cWaterSimulator : public cFluidSimulator +{ +public: + cWaterSimulator( cWorld* a_World ); + + virtual bool IsAllowedBlock( char a_BlockID ); + +}; \ No newline at end of file diff --git a/source/WebAdmin.cpp b/source/WebAdmin.cpp new file mode 100644 index 000000000..577fbbd5f --- /dev/null +++ b/source/WebAdmin.cpp @@ -0,0 +1,365 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "WebAdmin.h" +#include "StringMap.h" + +#include "WebPlugin.h" + +#include "PluginManager.h" +#include "Plugin.h" + +#include "World.h" +#include "Player.h" +#include "Server.h" +#include "Root.h" + +#include "../iniFile/iniFile.h" + +#ifdef _WIN32 + #include +#else + #include +#endif + + + + + +/// Helper class - appends all player names together in a HTML list +class cPlayerAccum : + public cPlayerListCallback +{ + virtual bool Item(cPlayer * a_Player) override + { + m_Contents.append("
  • "); + m_Contents.append(a_Player->GetName()); + m_Contents.append("
  • "); + return false; + } + +public: + + AString m_Contents; +} ; + + + + + +cWebAdmin * WebAdmin = NULL; + + + + + +cWebAdmin::cWebAdmin( int a_Port /* = 8080 */ ) + : m_Port( a_Port ) + , m_bConnected( false ) +{ + WebAdmin = this; + m_Event = new cEvent(); + Init( m_Port ); +} + +cWebAdmin::~cWebAdmin() +{ + WebAdmin = 0; + m_WebServer->Stop(); + + while( m_Plugins.begin() != m_Plugins.end() ) + { + delete *m_Plugins.begin(); + //m_Plugins.remove( *m_Plugins.begin() ); + } + delete m_WebServer; + delete m_IniFile; + + m_Event->Wait(); + delete m_Event; +} + +void cWebAdmin::AddPlugin( cWebPlugin * a_Plugin ) +{ + m_Plugins.remove( a_Plugin ); + m_Plugins.push_back( a_Plugin ); +} + +void cWebAdmin::RemovePlugin( cWebPlugin * a_Plugin ) +{ + m_Plugins.remove( a_Plugin ); +} + + + + + +void cWebAdmin::Request_Handler(webserver::http_request* r) +{ + if( WebAdmin == 0 ) return; + LOG("Path: %s", r->path_.c_str() ); + + if (r->path_ == "/") + { + r->answer_ += "

    MCServer WebAdmin

    "; + r->answer_ += "
    "; + r->answer_ += "
    "; + r->answer_ += ""; + r->answer_ += "
    "; + r->answer_ += "
    "; + return; + } + + if (r->path_.empty() || r->path_[0] != '/') + { + r->answer_ += "

    Bad request

    "; + r->answer_ += "

    "; + r->answer_ = r->path_; // TODO: Shouldn't we sanitize this? Possible security issue. + r->answer_ += "

    "; + return; + } + + AStringVector Split = StringSplit(r->path_.substr(1), "/"); + + if (Split.empty() || (Split[0] != "webadmin" && Split[0] != "~webadmin")) + { + r->answer_ += "

    Bad request

    "; + return; + } + + if (!r->authentication_given_) + { + r->answer_ += "no auth"; + r->auth_realm_ = "MCServer WebAdmin"; + } + + bool bDontShowTemplate = false; + if (Split[0] == "~webadmin") + { + bDontShowTemplate = true; + } + + std::string UserPassword = WebAdmin->m_IniFile->GetValue( "User:"+r->username_, "Password", ""); + if ((UserPassword != "") && (r->password_ == UserPassword)) + { + std::string BaseURL = "./"; + if (Split.size() > 1) + { + for (unsigned int i = 0; i < Split.size(); i++) + { + BaseURL += "../"; + } + BaseURL += "webadmin/"; + } + + std::string Menu; + std::string Content; + std::string Template = bDontShowTemplate ? "{CONTENT}" : WebAdmin->GetTemplate(); + std::string FoundPlugin; + + for (PluginList::iterator itr = WebAdmin->m_Plugins.begin(); itr != WebAdmin->m_Plugins.end(); ++itr) + { + cWebPlugin* WebPlugin = *itr; + std::list< std::pair > NameList = WebPlugin->GetTabNames(); + for( std::list< std::pair >::iterator Names = NameList.begin(); Names != NameList.end(); ++Names ) + { + Menu += "
  • " + (*Names).first + "
  • "; + } + } + + HTTPRequest Request; + Request.Username = r->username_; + Request.Method = r->method_; + Request.Params = r->params_; + Request.PostParams = r->params_post_; + Request.Path = r->path_.substr(1); + + for( unsigned int i = 0; i < r->multipart_formdata_.size(); ++i ) + { + webserver::formdata& fd = r->multipart_formdata_[i]; + + HTTPFormData HTTPfd;//( fd.value_ ); + HTTPfd.Value = fd.value_; + HTTPfd.Type = fd.content_type_; + HTTPfd.Name = fd.name_; + LOGINFO("Form data name: %s", fd.name_.c_str() ); + Request.FormData[ fd.name_ ] = HTTPfd; + } + + if( Split.size() > 1 ) + { + for( PluginList::iterator itr = WebAdmin->m_Plugins.begin(); itr != WebAdmin->m_Plugins.end(); ++itr ) + { + if( (*itr)->GetName() == Split[1] ) + { + Content = (*itr)->HandleWebRequest( &Request ); + cWebPlugin* WebPlugin = *itr; + FoundPlugin = WebPlugin->GetName(); + AString TabName = WebPlugin->GetTabNameForRequest( &Request ).first; + if( TabName.empty() == false ) + { + FoundPlugin += " - " + TabName; + } + break; + } + } + } + + if( FoundPlugin.empty() ) // Default page + { + Content.clear(); + FoundPlugin = "Current Game"; + Content += "

    Server Name:

    "; + Content += "

    " + std::string( cRoot::Get()->GetServer()->GetServerID() ) + "

    "; + + Content += "

    Plugins:

      "; + cPluginManager* PM = cRoot::Get()->GetPluginManager(); + if( PM ) + { + const cPluginManager::PluginList & List = PM->GetAllPlugins(); + for( cPluginManager::PluginList::const_iterator itr = List.begin(); itr != List.end(); ++itr ) + { + AString VersionNum; + AppendPrintf(Content, "
    • %s V.%i
    • ", (*itr)->GetName().c_str(), (*itr)->GetVersion()); + } + } + Content += "
    "; + Content += "

    Players:

      "; + + cPlayerAccum PlayerAccum; + cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players + if( World != NULL ) + { + World->ForEachPlayer(PlayerAccum); + Content.append(PlayerAccum.m_Contents); + } + Content += "

    "; + } + + + + if (bDontShowTemplate == false && Split.size() > 1) + { + Content += "\n

    Go back

    "; + } + + // mem usage +#ifndef _WIN32 + rusage resource_usage; + if (getrusage(RUSAGE_SELF, &resource_usage) != 0) + { + ReplaceString( Template, std::string("{MEM}"), "Error :(" ); + } + else + { + AString MemUsage; + Printf(MemUsage, "%0.2f", ((double)resource_usage.ru_maxrss / 1024 / 1024) ); + ReplaceString(Template, std::string("{MEM}"), MemUsage); + } +#else + HANDLE hProcess = GetCurrentProcess(); + PROCESS_MEMORY_COUNTERS pmc; + if( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc) ) ) + { + AString MemUsage; + Printf(MemUsage, "%0.2f", (pmc.WorkingSetSize / 1024.f / 1024.f) ); + ReplaceString( Template, "{MEM}", MemUsage ); + } +#endif + // end mem usage + + ReplaceString( Template, "{USERNAME}", r->username_ ); + ReplaceString( Template, "{MENU}", Menu ); + ReplaceString( Template, "{PLUGIN_NAME}", FoundPlugin ); + ReplaceString( Template, "{CONTENT}", Content ); + ReplaceString( Template, "{TITLE}", "MCServer" ); + + AString NumChunks; + Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount()); + ReplaceString(Template, "{NUMCHUNKS}", NumChunks); + + r->answer_ = Template; + } + else + { + r->answer_ += "Wrong username/password"; + r->auth_realm_ = "MCServer WebAdmin"; + } +} + + + + + +bool cWebAdmin::Init( int a_Port ) +{ + m_Port = a_Port; + + m_IniFile = new cIniFile("webadmin.ini"); + if( m_IniFile->ReadFile() ) + { + m_Port = m_IniFile->GetValueI("WebAdmin", "Port", 8080 ); + } + + LOG("Starting WebAdmin on port %i", m_Port); + +#ifdef _WIN32 + HANDLE hThread = CreateThread( + NULL, // default security + 0, // default stack size + ListenThread, // name of the thread function + this, // thread parameters + 0, // default startup flags + NULL); + CloseHandle( hThread ); // Just close the handle immediately +#else + pthread_t LstnThread; + pthread_create( &LstnThread, 0, ListenThread, this); +#endif + + return true; +} + + + + + +#ifdef _WIN32 +DWORD WINAPI cWebAdmin::ListenThread(LPVOID lpParam) +#else +void *cWebAdmin::ListenThread( void *lpParam ) +#endif +{ + cWebAdmin* self = (cWebAdmin*)lpParam; + + self->m_WebServer = new webserver(self->m_Port, Request_Handler ); + if (!self->m_WebServer->Begin()) + { + LOGWARN("WebServer failed to start! WebAdmin is disabled"); + } + + self->m_Event->Set(); + return 0; +} + + + + + +std::string cWebAdmin::GetTemplate() +{ + std::string retVal = ""; + + char SourceFile[] = "webadmin/template.html"; + + cFile f; + if (!f.Open(SourceFile, cFile::fmRead)) + { + return ""; + } + + // copy the file into the buffer: + f.ReadRestOfFile(retVal); + + return retVal; +} \ No newline at end of file diff --git a/source/WebAdmin.h b/source/WebAdmin.h new file mode 100644 index 000000000..f50de3f64 --- /dev/null +++ b/source/WebAdmin.h @@ -0,0 +1,67 @@ +#pragma once + +#include "../WebServer/WebServer.h" +#include "OSSupport/Socket.h" + +class cStringMap; + +struct HTTPFormData //tolua_export +{ //tolua_export + std::string Name; //tolua_export + std::string Value; //tolua_export + std::string Type; //tolua_export +};//tolua_export + +struct HTTPRequest //tolua_export +{ //tolua_export + typedef std::map< std::string, std::string > StringStringMap; + typedef std::map< std::string, HTTPFormData > FormDataMap; + std::string Method; //tolua_export + std::string Path; //tolua_export + StringStringMap Params; // >> EXPORTED IN MANUALBINDINGS << + StringStringMap PostParams; // >> EXPORTED IN MANUALBINDINGS << + std::string Username; //tolua_export + FormDataMap FormData; // >> EXPORTED IN MANUALBINDINGS << +}; //tolua_export + +struct lua_State; +class cEvent; +class cIniFile; +class cWebPlugin; +class cWebAdmin +{ +public: + cWebAdmin( int a_Port = 8080 ); + ~cWebAdmin(); + + bool Init( int a_Port ); + + void AddPlugin( cWebPlugin* a_Plugin ); + void RemovePlugin( cWebPlugin* a_Plugin ); + + typedef std::list< cWebPlugin* > PluginList; + PluginList GetPlugins() { return m_Plugins; } + + static void Request_Handler(webserver::http_request* r); +private: + +#ifdef _WIN32 + static DWORD WINAPI ListenThread(LPVOID lpParam); +#else + static void *ListenThread( void *lpParam ); +#endif + + std::string GetTemplate(); + + int m_Port; + + bool m_bConnected; + cSocket m_ListenSocket; + + cIniFile* m_IniFile; + PluginList m_Plugins; + + cEvent* m_Event; + + webserver* m_WebServer; +}; \ No newline at end of file diff --git a/source/WebPlugin.cpp b/source/WebPlugin.cpp new file mode 100644 index 000000000..93619ef14 --- /dev/null +++ b/source/WebPlugin.cpp @@ -0,0 +1,109 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "WebPlugin.h" +#include "WebAdmin.h" +#include "Server.h" +#include "Root.h" + + + + + +cWebPlugin::cWebPlugin() +{ + LOG("cWebPlugin::cWebPlugin()"); + cWebAdmin* WebAdmin = cRoot::Get()->GetWebAdmin(); + if( WebAdmin ) WebAdmin->AddPlugin( this ); +} + + + + + +cWebPlugin::~cWebPlugin() +{ + LOG("~cWebPlugin::cWebPlugin()"); + cWebAdmin* WebAdmin = cRoot::Get()->GetWebAdmin(); + if( WebAdmin ) WebAdmin->RemovePlugin( this ); + + for( TabList::iterator itr = m_Tabs.begin(); itr != m_Tabs.end(); ++itr ) + { + delete *itr; + } + m_Tabs.clear(); +} + + + + + +std::list< std::pair > cWebPlugin::GetTabNames() +{ + std::list< std::pair< AString, AString > > NameList; + for( TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr ) + { + std::pair< AString, AString > StringPair; + StringPair.first = (*itr)->Title; + StringPair.second = (*itr)->SafeTitle; + NameList.push_back( StringPair ); + } + return NameList; +} + + + + + +std::pair< AString, AString > cWebPlugin::GetTabNameForRequest( HTTPRequest* a_Request ) +{ + std::pair< AString, AString > Names; + AStringVector Split = StringSplit(a_Request->Path, "/"); + + if( Split.size() > 1 ) + { + sWebPluginTab* Tab = 0; + if( Split.size() > 2 ) // If we got the tab name, show that page + { + for( TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr ) + { + if( (*itr)->SafeTitle.compare( Split[2] ) == 0 ) // This is the one! Rawr + { + Tab = *itr; + break; + } + } + } + else // Otherwise show the first tab + { + if( GetTabs().size() > 0 ) + Tab = *GetTabs().begin(); + } + + if( Tab ) + { + Names.first = Tab->Title; + Names.second = Tab->SafeTitle; + } + } + + return Names; +} + + + + +AString cWebPlugin::SafeString( const AString & a_String ) +{ + AString RetVal; + for( unsigned int i = 0; i < a_String.size(); ++i ) + { + char c = a_String[i]; + if( c == ' ' ) + { + c = '_'; + } + RetVal.push_back( c ); + } + return RetVal; +} \ No newline at end of file diff --git a/source/WebPlugin.h b/source/WebPlugin.h new file mode 100644 index 000000000..35af88167 --- /dev/null +++ b/source/WebPlugin.h @@ -0,0 +1,47 @@ + +#pragma once + +struct lua_State; +struct HTTPRequest; + + + + + +// tolua_begin +class cWebPlugin +{ +public: + // tolua_end + cWebPlugin(); + virtual ~cWebPlugin(); + + virtual const AString & GetName(void) const = 0; + // tolua_begin + + virtual AString HandleWebRequest( HTTPRequest * a_Request ) = 0; + + static AString SafeString( const AString & a_String ); + //tolua_end + + struct sWebPluginTab + { + std::string Title; + std::string SafeTitle; + + int UserData; + }; + + typedef std::list< sWebPluginTab* > TabList; + TabList & GetTabs() { return m_Tabs; } + + std::list< std::pair > GetTabNames(); + std::pair< AString, AString > GetTabNameForRequest( HTTPRequest* a_Request ); + +private: + TabList m_Tabs; +}; // tolua_export + + + + diff --git a/source/World.cpp b/source/World.cpp new file mode 100644 index 000000000..9b241c8ee --- /dev/null +++ b/source/World.cpp @@ -0,0 +1,2097 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "BlockID.h" +#include "World.h" +#include "Redstone.h" +#include "ChunkDef.h" +#include "ClientHandle.h" +#include "Pickup.h" +#include "Player.h" +#include "Server.h" +#include "Item.h" +#include "Root.h" +#include "../iniFile/iniFile.h" +#include "ChunkMap.h" +#include "SimulatorManager.h" +#include "WaterSimulator.h" +#include "LavaSimulator.h" +#include "FireSimulator.h" +#include "SandSimulator.h" +#include "RedstoneSimulator.h" + +// Mobs: +#include "Mobs/Chicken.h" +#include "Mobs/Spider.h" +#include "Mobs/Cow.h" +#include "Mobs/Squid.h" +#include "Mobs/Wolf.h" +#include "Mobs/Slime.h" +#include "Mobs/Skeleton.h" +#include "Mobs/Silverfish.h" +#include "Mobs/Pig.h" +#include "Mobs/Sheep.h" +#include "Mobs/Zombie.h" +#include "Mobs/Enderman.h" +#include "Mobs/Creeper.h" +#include "Mobs/Cavespider.h" +#include "Mobs/Ghast.h" +#include "Mobs/Zombiepigman.h" + +#include "OSSupport/MakeDir.h" +#include "MersenneTwister.h" +#include "Tracer.h" +#include "Generating/Trees.h" +#include "PluginManager.h" +#include "blocks/BlockHandler.h" +#include "Vector3d.h" + +#include "tolua++.h" + +#ifndef _WIN32 + #include +#endif + + + + + +/// Up to this many m_SpreadQueue elements are handled each world tick +const int MAX_LIGHTING_SPREAD_PER_TICK = 10; + + + + + +float cWorld::m_Time = 0.f; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorldLoadProgress: + +/// A simple thread that displays the progress of world loading / saving in cWorld::InitializeSpawn() +class cWorldLoadProgress : + public cIsThread +{ +public: + cWorldLoadProgress(cWorld * a_World) : + cIsThread("cWorldLoadProgress"), + m_World(a_World) + { + Start(); + } + + void Stop(void) + { + m_ShouldTerminate = true; + Wait(); + } + +protected: + + cWorld * m_World; + + virtual void Execute(void) override + { + for (;;) + { + LOG("%d chunks to load, %d chunks to generate", + m_World->GetStorage().GetLoadQueueLength(), + m_World->GetGenerator().GetQueueLength() + ); + + // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish + for (int i = 0; i < 20; i++) + { + cSleep::MilliSleep(100); + if (m_ShouldTerminate) + { + return; + } + } + } // for (-ever) + } + +} ; + + + + + +/// A simple thread that displays the progress of world lighting in cWorld::InitializeSpawn() +class cWorldLightingProgress : + public cIsThread +{ +public: + cWorldLightingProgress(cLightingThread * a_Lighting) : + cIsThread("cWorldLightingProgress"), + m_Lighting(a_Lighting) + { + Start(); + } + + void Stop(void) + { + m_ShouldTerminate = true; + Wait(); + } + +protected: + + cLightingThread * m_Lighting; + + virtual void Execute(void) override + { + for (;;) + { + LOG("%d chunks remaining to light", m_Lighting->GetQueueLength() + ); + + // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish + for (int i = 0; i < 20; i++) + { + cSleep::MilliSleep(100); + if (m_ShouldTerminate) + { + return; + } + } + } // for (-ever) + } + +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorld: + +cWorld* cWorld::GetWorld() +{ + LOGWARN("WARNING: Using deprecated function cWorld::GetWorld() use cRoot::Get()->GetDefaultWorld() instead!"); + return cRoot::Get()->GetDefaultWorld(); +} + + + + + +cWorld::~cWorld() +{ + { + cCSLock Lock(m_CSEntities); + while( m_AllEntities.begin() != m_AllEntities.end() ) + { + cEntity* Entity = *m_AllEntities.begin(); + m_AllEntities.remove( Entity ); + if ( !Entity->IsDestroyed() ) + { + Entity->Destroy(); + } + delete Entity; + } + } + + delete m_SimulatorManager; + delete m_SandSimulator; + delete m_WaterSimulator; + delete m_LavaSimulator; + delete m_FireSimulator; + delete m_RedstoneSimulator; + + UnloadUnusedChunks(); + + m_Storage.WaitForFinish(); + + delete m_ChunkMap; +} + + + + + +cWorld::cWorld( const AString & a_WorldName ) + : m_SpawnMonsterTime( 0.f ) + , m_RSList ( 0 ) + , m_Weather ( eWeather_Sunny ) +{ + LOG("cWorld::cWorld(%s)", a_WorldName.c_str()); + m_WorldName = a_WorldName; + m_IniFileName = m_WorldName + "/world.ini"; + + cMakeDir::MakeDir(m_WorldName.c_str()); + + MTRand r1; + m_SpawnX = (double)((r1.randInt() % 1000) - 500); + m_SpawnY = cChunkDef::Height; + m_SpawnZ = (double)((r1.randInt() % 1000) - 500); + m_GameMode = eGameMode_Creative; + + AString StorageSchema("Default"); + + cIniFile IniFile(m_IniFileName); + IniFile.ReadFile(); + m_SpawnX = IniFile.GetValueSetF("SpawnPosition", "X", m_SpawnX); + m_SpawnY = IniFile.GetValueSetF("SpawnPosition", "Y", m_SpawnY); + m_SpawnZ = IniFile.GetValueSetF("SpawnPosition", "Z", m_SpawnZ); + StorageSchema = IniFile.GetValueSet ("Storage", "Schema", StorageSchema); + m_MaxCactusHeight = IniFile.GetValueSetI("Plants", "MaxCactusHeight", 3); + m_MaxSugarcaneHeight = IniFile.GetValueSetI("Plants", "MaxSugarcaneHeight", 3); + m_IsCropsBonemealable = IniFile.GetValueSetB("Plants", "IsCropsBonemealable", true); + m_IsGrassBonemealable = IniFile.GetValueSetB("Plants", "IsGrassBonemealable", true); + m_IsSaplingBonemealable = IniFile.GetValueSetB("Plants", "IsSaplingBonemealable", true); + m_IsMelonStemBonemealable = IniFile.GetValueSetB("Plants", "IsMelonStemBonemealable", true); + m_IsMelonBonemealable = IniFile.GetValueSetB("Plants", "IsMelonBonemealable", false); + m_IsPumpkinStemBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinStemBonemealable", true); + m_IsPumpkinBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinBonemealable", false); + m_IsSugarcaneBonemealable = IniFile.GetValueSetB("Plants", "IsSugarcaneBonemealable", false); + m_IsCactusBonemealable = IniFile.GetValueSetB("Plants", "IsCactusBonemealable", false); + + m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode ); + + if (!IniFile.WriteFile()) + { + LOG("WARNING: Could not write to %s", m_IniFileName.c_str()); + } + + m_Lighting.Start(this); + m_Storage.Start(this, StorageSchema); + m_Generator.Start(this, IniFile); + + m_bAnimals = true; + m_SpawnMonsterRate = 10; + cIniFile IniFile2("settings.ini"); + if( IniFile2.ReadFile() ) + { + m_bAnimals = IniFile2.GetValueB("Monsters", "AnimalsOn", true ); + m_SpawnMonsterRate = (float)IniFile2.GetValueF("Monsters", "AnimalSpawnInterval", 10); + SetMaxPlayers(IniFile2.GetValueI("Server", "MaxPlayers", 9001)); + m_Description = IniFile2.GetValue("Server", "Description", "MCServer! - It's OVER 9000!").c_str(); + } + + m_ChunkMap = new cChunkMap(this ); + + m_ChunkSender.Start(this); + + m_Time = 0; + m_WorldTimeFraction = 0.f; + m_WorldTime = 0; + m_LastSave = 0; + m_LastUnload = 0; + + //preallocate some memory for ticking blocks so we donīt need to allocate that often + m_BlockTickQueue.reserve(1000); + m_BlockTickQueueCopy.reserve(1000); + + //Simulators: + m_WaterSimulator = new cWaterSimulator( this ); + m_LavaSimulator = new cLavaSimulator( this ); + m_SandSimulator = new cSandSimulator(this); + m_FireSimulator = new cFireSimulator(this); + m_RedstoneSimulator = new cRedstoneSimulator(this); + + m_SimulatorManager = new cSimulatorManager(); + m_SimulatorManager->RegisterSimulator(m_WaterSimulator, 6); + m_SimulatorManager->RegisterSimulator(m_LavaSimulator, 12); + m_SimulatorManager->RegisterSimulator(m_SandSimulator, 1); + m_SimulatorManager->RegisterSimulator(m_FireSimulator, 10); + m_SimulatorManager->RegisterSimulator(m_RedstoneSimulator, 1); +} + + + + + +void cWorld::SetWeather(eWeather a_Weather) +{ + switch (a_Weather) + { + case eWeather_Sunny: + case eWeather_Rain: + case eWeather_ThunderStorm: + { + m_Weather = a_Weather; + BroadcastWeather(a_Weather); + break; + } + + default: + { + LOGWARN("Trying to set unknown weather %d", a_Weather); + break; + } + } +} + + + + + +void cWorld::CastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) +{ + BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cWorld::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + return m_ChunkMap->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ); +} + + + + + +void cWorld::InitializeSpawn(void) +{ + int ChunkX = 0, ChunkY = 0, ChunkZ = 0; + BlockToChunk( (int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ, ChunkX, ChunkY, ChunkZ ); + + // For the debugging builds, don't make the server build too much world upon start: + #ifdef _DEBUG + int ViewDist = 9; + #else + int ViewDist = 20; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is + #endif // _DEBUG + + LOG("Preparing spawn area in world \"%s\"...", m_WorldName.c_str()); + for (int x = 0; x < ViewDist; x++) + { + for (int z = 0; z < ViewDist; z++) + { + m_ChunkMap->TouchChunk( x + ChunkX-(ViewDist - 1) / 2, ZERO_CHUNK_Y, z + ChunkZ-(ViewDist - 1) / 2 ); // Queue the chunk in the generator / loader + } + } + + { + // Display progress during this process: + cWorldLoadProgress Progress(this); + + // Wait for the loader to finish loading + m_Storage.WaitForQueuesEmpty(); + + // Wait for the generator to finish generating + m_Generator.WaitForQueueEmpty(); + + Progress.Stop(); + } + + // Light all chunks that have been newly generated: + LOG("Lighting spawn area in world \"%s\"...", m_WorldName.c_str()); + + for (int x = 0; x < ViewDist; x++) + { + int ChX = x + ChunkX-(ViewDist - 1) / 2; + for (int z = 0; z < ViewDist; z++) + { + int ChZ = z + ChunkZ-(ViewDist - 1) / 2; + if (!m_ChunkMap->IsChunkLighted(ChX, ChZ)) + { + m_Lighting.QueueChunk(ChX, ChZ); // Queue the chunk in the lighting thread + } + } // for z + } // for x + + { + cWorldLightingProgress Progress(&m_Lighting); + m_Lighting.WaitForQueueEmpty(); + Progress.Stop(); + } + + // TODO: Better spawn detection - move spawn out of the water if it isn't set in the INI already + m_SpawnY = (double)GetHeight( (int)m_SpawnX, (int)m_SpawnZ ) + 1.6f; // +1.6f eye height +} + + + + + +void cWorld::StopThreads(void) +{ + m_Generator.Stop(); + m_ChunkSender.Stop(); +} + + + + + +void cWorld::Tick(float a_Dt) +{ + m_Time += a_Dt / 1000.f; + + CurrentTick++; + + bool bSendTime = false; + m_WorldTimeFraction += a_Dt / 1000.f; + while (m_WorldTimeFraction > 1.f) + { + m_WorldTimeFraction -= 1.f; + m_WorldTime += 20; + bSendTime = true; + } + m_WorldTime %= 24000; // 24000 units in a day + + if (bSendTime) + { + BroadcastTimeUpdate(); + } + + { + cCSLock Lock(m_CSEntities); + for (cEntityList::iterator itr = m_AllEntities.begin(); itr != m_AllEntities.end();) + { + if ((*itr)->IsDestroyed()) + { + LOGD("Destroying entity #%i", (*itr)->GetUniqueID()); + cEntity * RemoveMe = *itr; + itr = m_AllEntities.erase( itr ); + m_RemoveEntityQueue.push_back( RemoveMe ); + continue; + } + (*itr)->Tick(a_Dt); + itr++; + } + } + + m_ChunkMap->Tick(a_Dt, m_TickRand); + + TickQueuedBlocks(a_Dt); + + GetSimulatorManager()->Simulate(a_Dt); + + TickWeather(a_Dt); + + // Asynchronously set blocks: + sSetBlockList FastSetBlockQueueCopy; + { + cCSLock Lock(m_CSFastSetBlock); + std::swap(FastSetBlockQueueCopy, m_FastSetBlockQueue); + } + m_ChunkMap->FastSetBlocks(FastSetBlockQueueCopy); + if (!FastSetBlockQueueCopy.empty()) + { + // Some blocks failed, store them for next tick: + cCSLock Lock(m_CSFastSetBlock); + m_FastSetBlockQueue.splice(m_FastSetBlockQueue.end(), FastSetBlockQueueCopy); + } + + if( m_Time - m_LastSave > 60 * 5 ) // Save each 5 minutes + { + SaveAllChunks(); + } + + if( m_Time - m_LastUnload > 10 ) // Unload every 10 seconds + { + UnloadUnusedChunks(); + } + + // Delete entities queued for removal: + for (cEntityList::iterator itr = m_RemoveEntityQueue.begin(); itr != m_RemoveEntityQueue.end(); ++itr) + { + delete *itr; + } + m_RemoveEntityQueue.clear(); + + TickSpawnMobs(a_Dt); + + std::vector m_RSList_copy(m_RSList); + + m_RSList.clear(); + + std::vector::const_iterator cii; // FIXME - Please rename this variable, WTF is cii??? Use human readable variable names or common abbreviations (i, idx, itr, iter) + for(cii=m_RSList_copy.begin(); cii!=m_RSList_copy.end();) + { + int tempX = *cii;cii++; + int tempY = *cii;cii++; + int tempZ = *cii;cii++; + int state = *cii;cii++; + + if ( (state == 11111) && ( (int)GetBlock( tempX, tempY, tempZ ) == E_BLOCK_REDSTONE_TORCH_OFF ) ) + { + FastSetBlock( tempX, tempY, tempZ, E_BLOCK_REDSTONE_TORCH_ON, (int)GetBlockMeta( tempX, tempY, tempZ ) ); + cRedstone Redstone(this); + Redstone.ChangeRedstone( tempX, tempY, tempZ, true ); + } + else if ( (state == 00000) && ( (int)GetBlock( tempX, tempY, tempZ ) == E_BLOCK_REDSTONE_TORCH_ON ) ) + { + FastSetBlock( tempX, tempY, tempZ, E_BLOCK_REDSTONE_TORCH_OFF, (int)GetBlockMeta( tempX, tempY, tempZ ) ); + cRedstone Redstone(this); + Redstone.ChangeRedstone( tempX, tempY, tempZ, false ); + } + } + m_RSList_copy.erase(m_RSList_copy.begin(),m_RSList_copy.end()); +} + + + + + +void cWorld::ChangeWeather() +{ + unsigned randWeather = (m_TickRand.randInt() % 99); + + if (GetWeather() == eWeather_Sunny) + { + if (randWeather < 20) + { + LOG("Starting rainstorm!"); + SetWeather( eWeather_Rain ); + } + } + + else if (GetWeather() == eWeather_Rain) + { + if (randWeather < 5) + { + LOG("Thunderstorm!"); + SetWeather( eWeather_ThunderStorm ); + } + + else if (randWeather < 60) + { + LOG("Back to sunshine"); + SetWeather( eWeather_Sunny ); + } + } + + else if (GetWeather() == eWeather_ThunderStorm) + { + if (randWeather < 70) + { + SetWeather(eWeather_Sunny); + LOG("Thunder ended abruptly, returning to lovely sunshine"); + } + else if (randWeather < 85) + { + SetWeather(eWeather_Rain); + LOG("Thunder ended, but rain persists."); + } + else + { + return; + } + } +} + + + + + +void cWorld::TickWeather(float a_Dt) +{ + if (m_WeatherInterval == 0) + { + ChangeWeather(); + + cRoot::Get()->GetPluginManager()->CallHookWeatherChanged(this); + + switch (GetWeather()) + { + case eWeather_Sunny: + m_WeatherInterval = 14400 + (m_TickRand.randInt() % 4800); // 12 - 16 minutes + break; + case eWeather_Rain: + m_WeatherInterval = 9600 + (m_TickRand.randInt() % 7200); // 8 - 14 minutes + break; + case eWeather_ThunderStorm: + m_WeatherInterval = 2400 + (m_TickRand.randInt() % 4800); // 2 - 6 minutes + break; + default: + LOG("Unknown weather occurred"); + break; + } + } + + else + { + m_WeatherInterval--; + } + + if ( GetWeather() == 2 ) // if thunderstorm + { + if (m_TickRand.randInt() % 199 == 0) // 0.5% chance per tick of thunderbolt + { + CastThunderbolt ( 0, 0, 0 ); // TODO: find random possitions near players to cast thunderbolts. + } + } +} + + + + + +void cWorld::TickSpawnMobs(float a_Dt) +{ + if (!m_bAnimals || (m_Time - m_SpawnMonsterTime <= m_SpawnMonsterRate)) + { + return; + } + + m_SpawnMonsterTime = m_Time; + Vector3d SpawnPos; + { + cCSLock Lock(m_CSPlayers); + if ( m_Players.size() <= 0) + { + return; + } + int RandomPlayerIdx = m_TickRand.randInt() & m_Players.size(); + cPlayerList::iterator itr = m_Players.begin(); + for( int i = 1; i < RandomPlayerIdx; i++ ) + { + itr++; + } + SpawnPos = (*itr)->GetPosition(); + } + + cMonster * Monster = NULL; + int dayRand = m_TickRand.randInt() % 6; + int nightRand = m_TickRand.randInt() % 10; + + SpawnPos += Vector3d( (double)(m_TickRand.randInt() % 64) - 32, (double)(m_TickRand.randInt() % 64) - 32, (double)(m_TickRand.randInt() % 64) - 32 ); + int Height = GetHeight( (int)SpawnPos.x, (int)SpawnPos.z ); + + if (m_WorldTime >= 12000 + 1000) + { + if (GetBiomeAt((int)SpawnPos.x, (int)SpawnPos.z) == biHell) // Spawn nether mobs + { + if (nightRand == 1) + Monster = new cZombie(); + else if (nightRand == 5) + Monster = new cGhast(); + else if (nightRand == 6) + Monster = new cZombiepigman(); + } + else + { + if (nightRand == 0) //random percent to spawn for night + Monster = new cSpider(); + else if (nightRand == 2) + Monster = new cEnderman(); + else if (nightRand == 3) + Monster = new cCreeper(); + else if (nightRand == 4) + Monster = new cCavespider(); + else if (nightRand == 7) + Monster = new cSlime(); + else if (nightRand == 8) + Monster = new cSilverfish(); + else if (nightRand == 9) + Monster = new cSkeleton(); + } + //end random percent to spawn for night + } + else + { + if (dayRand == 0) //random percent to spawn for day + Monster = new cChicken(); + else if (dayRand == 1) + Monster = new cCow(); + else if (dayRand == 2) + Monster = new cPig(); + else if (dayRand == 3) + Monster = new cSheep(); + else if (dayRand == 4) + Monster = new cSquid(); + else if (dayRand == 5) + Monster = new cWolf(); + //end random percent to spawn for day + } + + if( Monster ) + { + Monster->Initialize(this); + Monster->TeleportTo(SpawnPos.x, (double)(Height) + 2, SpawnPos.z); + BroadcastSpawn(*Monster); + } +} + + + + + +bool cWorld::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback) +{ + return m_ChunkMap->ForEachChestInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback) +{ + return m_ChunkMap->ForEachFurnaceInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) +{ + return m_ChunkMap->DoWithChestAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cWorld::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) +{ + return m_ChunkMap->DoWithFurnaceAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + +bool cWorld::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) +{ + return m_ChunkMap->GetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); +} + + + + + +void cWorld::GrowTree( int a_X, int a_Y, int a_Z ) +{ + if (GetBlock(a_X, a_Y, a_Z) == E_BLOCK_SAPLING) + { + // There is a sapling here, grow a tree according to its type: + GrowTreeFromSapling(a_X, a_Y, a_Z, GetBlockMeta(a_X, a_Y, a_Z)); + } + else + { + // There is nothing here, grow a tree based on the current biome here: + GrowTreeByBiome(a_X, a_Y, a_Z); + } +} + + + + + +void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, char a_SaplingMeta) +{ + cNoise Noise(m_Generator.GetSeed()); + sSetBlockVector Logs, Other; + switch (a_SaplingMeta & 0x07) + { + case E_META_SAPLING_APPLE: GetAppleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldTime & 0xffffffff), Logs, Other); break; + case E_META_SAPLING_BIRCH: GetBirchTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldTime & 0xffffffff), Logs, Other); break; + case E_META_SAPLING_CONIFER: GetConiferTreeImage(a_X, a_Y, a_Z, Noise, (int)(m_WorldTime & 0xffffffff), Logs, Other); break; + case E_META_SAPLING_JUNGLE: GetJungleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldTime & 0xffffffff), Logs, Other); break; + } + Other.insert(Other.begin(), Logs.begin(), Logs.end()); + Logs.clear(); + GrowTreeImage(Logs); +} + + + + + +void cWorld::GrowTreeByBiome(int a_X, int a_Y, int a_Z) +{ + cNoise Noise(m_Generator.GetSeed()); + sSetBlockVector Logs, Other; + GetTreeImageByBiome(a_X, a_Y, a_Z, Noise, (int)(m_WorldTime & 0xffffffff), (EMCSBiome)GetBiomeAt(a_X, a_Z), Logs, Other); + Other.insert(Other.begin(), Logs.begin(), Logs.end()); + Logs.clear(); + GrowTreeImage(Other); +} + + + + + +void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks) +{ + // Check that the tree has place to grow + + // Make a copy of the log blocks: + sSetBlockVector b2; + for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) + { + if (itr->BlockType == E_BLOCK_LOG) + { + b2.push_back(*itr); + } + } // for itr - a_Blocks[] + + // Query blocktypes and metas at those log blocks: + if (!GetBlocks(b2, false)) + { + return; + } + + // Check that at each log's coord there's an block allowed to be overwritten: + for (sSetBlockVector::const_iterator itr = b2.begin(); itr != b2.end(); ++itr) + { + switch (itr->BlockType) + { + CASE_TREE_ALLOWED_BLOCKS: + { + break; + } + default: + { + return; + } + } + } // for itr - b2[] + + // All ok, replace blocks with the tree image: + m_ChunkMap->ReplaceTreeBlocks(a_Blocks); +} + + + + + +bool cWorld::GrowPlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal) +{ + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); + switch (BlockType) + { + case E_BLOCK_CROPS: + { + if (a_IsByBonemeal && !m_IsGrassBonemealable) + { + return false; + } + if (BlockMeta < 7) + { + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + } + return true; + } + + case E_BLOCK_MELON_STEM: + { + if (BlockMeta < 7) + { + if (a_IsByBonemeal && !m_IsMelonStemBonemealable) + { + return false; + } + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + } + else + { + if (a_IsByBonemeal && !m_IsMelonBonemealable) + { + return false; + } + GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType); + } + return true; + } + + case E_BLOCK_PUMPKIN_STEM: + { + if (BlockMeta < 7) + { + if (a_IsByBonemeal && !m_IsPumpkinStemBonemealable) + { + return false; + } + FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); + } + else + { + if (a_IsByBonemeal && !m_IsPumpkinBonemealable) + { + return false; + } + GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType); + } + return true; + } + + case E_BLOCK_SAPLING: + { + if (a_IsByBonemeal && !m_IsSaplingBonemealable) + { + return false; + } + GrowTreeFromSapling(a_BlockX, a_BlockY, a_BlockZ, BlockMeta); + return true; + } + + case E_BLOCK_GRASS: + { + if (a_IsByBonemeal && !m_IsGrassBonemealable) + { + return false; + } + MTRand r1; + for (int i = 0; i < 60; i++) + { + int OfsX = (r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3; + int OfsY = r1.randInt(3) + r1.randInt(3) - 3; + int OfsZ = (r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3; + BLOCKTYPE Ground = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ); + if (Ground != E_BLOCK_GRASS) + { + continue; + } + BLOCKTYPE Above = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ); + if (Above != E_BLOCK_AIR) + { + continue; + } + BLOCKTYPE SpawnType; + NIBBLETYPE SpawnMeta = 0; + switch (r1.randInt(10)) + { + case 0: SpawnType = E_BLOCK_YELLOW_FLOWER; break; + case 1: SpawnType = E_BLOCK_RED_ROSE; break; + default: + { + SpawnType = E_BLOCK_TALL_GRASS; + SpawnMeta = E_META_TALL_GRASS_GRASS; + break; + } + } // switch (random spawn block) + FastSetBlock(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ, SpawnType, SpawnMeta); + } // for i - 50 times + return true; + } + + case E_BLOCK_SUGARCANE: + { + if (a_IsByBonemeal && !m_IsSugarcaneBonemealable) + { + return false; + } + m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, m_MaxSugarcaneHeight); + return true; + } + + case E_BLOCK_CACTUS: + { + if (a_IsByBonemeal && !m_IsCactusBonemealable) + { + return false; + } + m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, m_MaxCactusHeight); + return true; + } + } // switch (BlockType) + return false; +} + + + + + +void cWorld::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockType) +{ + MTRand Rand; + m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, Rand); +} + + + + + +int cWorld::GetBiomeAt (int a_BlockX, int a_BlockZ) +{ + return m_ChunkMap->GetBiomeAt(a_BlockX, a_BlockZ); +} + + + + + +void cWorld::SetBlock( int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) +{ + if(a_BlockType == E_BLOCK_AIR) + { + BlockHandler(GetBlock(a_X, a_Y, a_Z))->OnDestroyed(this, a_X, a_Y, a_Z); + } + m_ChunkMap->SetBlock(a_X, a_Y, a_Z, a_BlockType, a_BlockMeta); + + GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z); + BlockHandler(a_BlockType)->OnPlaced(this, a_X, a_Y, a_Z, a_BlockMeta); +} + + + + + +void cWorld::FastSetBlock( int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) +{ + cCSLock Lock(m_CSFastSetBlock); + m_FastSetBlockQueue.push_back(sSetBlock(a_X, a_Y, a_Z, a_BlockType, a_BlockMeta)); +} + + + + + +BLOCKTYPE cWorld::GetBlock(int a_X, int a_Y, int a_Z) +{ + // First check if it isn't queued in the m_FastSetBlockQueue: + { + int X = a_X, Y = a_Y, Z = a_Z; + int ChunkX, ChunkY, ChunkZ; + AbsoluteToRelative(X, Y, Z, ChunkX, ChunkY, ChunkZ); + + cCSLock Lock(m_CSFastSetBlock); + for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr) + { + if ((itr->x == X) && (itr->y == Y) && (itr->z == Z) && (itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) + { + return itr->BlockType; + } + } // for itr - m_FastSetBlockQueue[] + } + + return m_ChunkMap->GetBlock(a_X, a_Y, a_Z); +} + + + + + +NIBBLETYPE cWorld::GetBlockMeta( int a_X, int a_Y, int a_Z ) +{ + // First check if it isn't queued in the m_FastSetBlockQueue: + { + cCSLock Lock(m_CSFastSetBlock); + for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr) + { + if ((itr->x == a_X) && (itr->y == a_Y) && (itr->y == a_Y)) + { + return itr->BlockMeta; + } + } // for itr - m_FastSetBlockQueue[] + } + + return m_ChunkMap->GetBlockMeta(a_X, a_Y, a_Z); +} + + + + + +void cWorld::SetBlockMeta( int a_X, int a_Y, int a_Z, NIBBLETYPE a_MetaData ) +{ + m_ChunkMap->SetBlockMeta(a_X, a_Y, a_Z, a_MetaData); +} + + + + + +NIBBLETYPE cWorld::GetBlockSkyLight( int a_X, int a_Y, int a_Z ) +{ + return m_ChunkMap->GetBlockSkyLight(a_X, a_Y, a_Z); +} + + + + + +void cWorld::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +{ + m_ChunkMap->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, (BLOCKTYPE &)a_BlockType, (NIBBLETYPE &)a_BlockMeta); +} + + + + + +void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed) +{ + MTRand r1; + a_FlyAwaySpeed /= 1000; // Pre-divide, so that we don't have to divide each time inside the loop + for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr) + { + float SpeedX = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500)); + float SpeedY = (float)(a_FlyAwaySpeed * r1.randInt(1000)); + float SpeedZ = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500)); + cPickup * Pickup = new cPickup( + (int)(a_BlockX * 32) + r1.randInt(16) + r1.randInt(16), + (int)(a_BlockY * 32) + r1.randInt(16) + r1.randInt(16), + (int)(a_BlockZ * 32) + r1.randInt(16) + r1.randInt(16), + *itr, SpeedX, SpeedY, SpeedZ + ); + Pickup->Initialize(this); + } +} + + + + + +void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ) +{ + MTRand r1; + for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr) + { + cPickup * Pickup = new cPickup( + (int)(a_BlockX * 32) + r1.randInt(16) + r1.randInt(16), + (int)(a_BlockY * 32) + r1.randInt(16) + r1.randInt(16), + (int)(a_BlockZ * 32) + r1.randInt(16) + r1.randInt(16), + *itr, (float)a_SpeedX, (float)a_SpeedY, (float)a_SpeedZ + ); + Pickup->Initialize(this); + } +} + + + + + +void cWorld::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType) +{ + m_ChunkMap->ReplaceBlocks(a_Blocks, a_FilterBlockType); +} + + + + + +bool cWorld::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) +{ + return m_ChunkMap->GetBlocks(a_Blocks, a_ContinueOnFailure); +} + + + + + +bool cWorld::DigBlock( int a_X, int a_Y, int a_Z) +{ + cBlockHandler *Handler = cBlockHandler::GetBlockHandler(GetBlock(a_X, a_Y, a_Z)); + Handler->OnDestroyed(this, a_X, a_Y, a_Z); + return m_ChunkMap->DigBlock(a_X, a_Y, a_Z); +} + + + + + +void cWorld::SendBlockTo( int a_X, int a_Y, int a_Z, cPlayer * a_Player ) +{ + m_ChunkMap->SendBlockTo(a_X, a_Y, a_Z, a_Player); +} + + + + + +int cWorld::GetHeight( int a_X, int a_Z ) +{ + return m_ChunkMap->GetHeight(a_X, a_Z); +} + + + + + +void cWorld::BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendChat(a_Message); + } +} + + + + + +void cWorld::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastPlayerAnimation(a_Player, a_Animation, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityEquipment(a_Entity, a_SlotNum, a_Item, a_Exclude); +} + + + + + +void cWorld::BroadcastTeleportEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendTeleportEntity(a_Entity); + } +} + + + + + +void cWorld::BroadcastEntRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); +} + + + + + +void cWorld::BroadcastEntRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntRelMove(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); +} + + + + + +void cWorld::BroadcastEntLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntLook(a_Entity, a_Exclude); +} + + + + + +void cWorld::BroadcastEntHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntHeadLook(a_Entity, a_Exclude); +} + + + + + +void cWorld::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType, a_Exclude); +} + + + + + +void cWorld::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastDestroyEntity(a_Entity, a_Exclude); +} + + + + + +void cWorld::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastEntityStatus(a_Entity, a_Status, a_Exclude); +} + + + + + +void cWorld::BroadcastMetadata(const cPawn & a_Pawn, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastMetadata(a_Pawn, a_Exclude); +} + + + + + +void cWorld::BroadcastSpawn(cEntity & a_Entity, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastSpawn(a_Entity, a_Exclude); +} + + + + + +void cWorld::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastCollectPickup(a_Pickup, a_Player, a_Exclude); +} + + + + + +void cWorld::BroadcastWeather(eWeather a_Weather, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendWeather(a_Weather); + } +} + + + + + +void cWorld::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); +} + + + + + +void cWorld::BroadcastTimeUpdate(const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendTimeUpdate(m_WorldTime); + } +} + + + + + +void cWorld::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastChunkData(a_ChunkX, a_ChunkZ, a_Serializer, a_Exclude); +} + + + + + +void cWorld::BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendPlayerListItem(a_Player, a_IsOnline); + } +} + + + + + +void cWorld::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch, a_Exclude); +} + + + + + +void cWorld::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); +} + + + + + +void cWorld::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client) +{ + m_ChunkMap->SendBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Client); +} + + + + + +void cWorld::MarkChunkDirty (int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + m_ChunkMap->MarkChunkDirty (a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +void cWorld::MarkChunkSaving(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + m_ChunkMap->MarkChunkSaving(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +void cWorld::MarkChunkSaved (int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + m_ChunkMap->MarkChunkSaved (a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +void cWorld::SetChunkData( + int a_ChunkX, int a_ChunkY, int a_ChunkZ, + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const cChunkDef::HeightMap * a_HeightMap, + const cChunkDef::BiomeMap * a_BiomeMap, + cEntityList & a_Entities, + cBlockEntityList & a_BlockEntities, + bool a_MarkDirty +) +{ + // Validate biomes, if needed: + cChunkDef::BiomeMap BiomeMap; + const cChunkDef::BiomeMap * Biomes = a_BiomeMap; + if (a_BiomeMap == NULL) + { + // The biomes are not assigned, get them from the generator: + Biomes = &BiomeMap; + m_Generator.GenerateBiomes(a_ChunkX, a_ChunkZ, BiomeMap); + } + + m_ChunkMap->SetChunkData( + a_ChunkX, a_ChunkY, a_ChunkZ, + a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, + a_HeightMap, *Biomes, + a_Entities, a_BlockEntities, + a_MarkDirty + ); + + // If a client is requesting this chunk, send it to them: + if (m_ChunkMap->HasChunkAnyClients(a_ChunkX, a_ChunkY, a_ChunkZ)) + { + m_ChunkSender.ChunkReady(a_ChunkX, a_ChunkY, a_ChunkZ); + } + + // Notify the lighting thread that the chunk has become valid (in case it is a neighbor of a postponed chunk): + m_Lighting.ChunkReady(a_ChunkX, a_ChunkZ); +} + + + + + +void cWorld::ChunkLighted( + int a_ChunkX, int a_ChunkZ, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight +) +{ + m_ChunkMap->ChunkLighted(a_ChunkX, a_ChunkZ, a_BlockLight, a_SkyLight); +} + + + + + +bool cWorld::GetChunkData(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkDataCallback & a_Callback) +{ + return m_ChunkMap->GetChunkData(a_ChunkX, a_ChunkY, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::GetChunkBlockTypes(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockTypes) +{ + return m_ChunkMap->GetChunkBlockTypes(a_ChunkX, a_ChunkY, a_ChunkZ, a_BlockTypes); +} + + + + + +bool cWorld::GetChunkBlockData(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData) +{ + return m_ChunkMap->GetChunkBlockData(a_ChunkX, a_ChunkY, a_ChunkZ, a_BlockData); +} + + + + + +bool cWorld::IsChunkValid(int a_ChunkX, int a_ChunkY, int a_ChunkZ) const +{ + return m_ChunkMap->IsChunkValid(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +bool cWorld::HasChunkAnyClients(int a_ChunkX, int a_ChunkY, int a_ChunkZ) const +{ + return m_ChunkMap->HasChunkAnyClients(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +void cWorld::UnloadUnusedChunks(void ) +{ + m_LastUnload = m_Time; + m_ChunkMap->UnloadUnusedChunks(); +} + + + + + +void cWorld::CollectPickupsByPlayer(cPlayer * a_Player) +{ + m_ChunkMap->CollectPickupsByPlayer(a_Player); +} + + + + + +void cWorld::SetMaxPlayers(int iMax) +{ + m_MaxPlayers = MAX_PLAYERS; + if (iMax > 0 && iMax < MAX_PLAYERS) + { + m_MaxPlayers = iMax; + } +} + + + + + +void cWorld::AddPlayer( cPlayer* a_Player ) +{ + cCSLock Lock(m_CSPlayers); + + ASSERT(std::find(m_Players.begin(), m_Players.end(), a_Player) == m_Players.end()); // Is it already in the list? HOW? + + m_Players.remove( a_Player ); // Make sure the player is registered only once + m_Players.push_back( a_Player ); +} + + + + + +void cWorld::RemovePlayer( cPlayer* a_Player ) +{ + cCSLock Lock(m_CSPlayers); + m_Players.remove( a_Player ); +} + + + + + +bool cWorld::ForEachPlayer(cPlayerListCallback & a_Callback) +{ + // Calls the callback for each player in the list + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(), itr2 = itr; itr != m_Players.end(); itr = itr2) + { + ++itr2; + if (a_Callback.Item(*itr)) + { + return false; + } + } // for itr - m_Players[] + return true; +} + + + + + +bool cWorld::DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback) +{ + // Calls the callback for each player in the list + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + if (NoCaseCompare((*itr)->GetName(), a_PlayerName) == 0) + { + a_Callback.Item(*itr); + return true; + } + } // for itr - m_Players[] + return false; +} + + + + + +bool cWorld::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback) +{ + cPlayer* BestMatch = 0; + unsigned int BestRating = 0; + unsigned int NumMatches = 0; + unsigned int NameLength = a_PlayerName.length(); + + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + unsigned int Rating = RateCompareString (a_PlayerName, (*itr)->GetName()); + if (Rating > 0 && Rating >= BestRating) + { + BestMatch = *itr; + if( Rating > BestRating ) NumMatches = 0; + BestRating = Rating; + ++NumMatches; + } + if (Rating == NameLength) // Perfect match + { + break; + } + } // for itr - m_Players[] + + if (NumMatches == 1) + { + LOG("Compared %s and %s with rating %i", a_PlayerName.c_str(), BestMatch->GetName().c_str(), BestRating ); + return a_Callback.Item (BestMatch); + } + return false; +} + + + + + +// TODO: This interface is dangerous! +cPlayer * cWorld::FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit) +{ + cTracer LineOfSight(this); + + float ClosestDistance = a_SightLimit; + cPlayer* ClosestPlayer = NULL; + + cCSLock Lock(m_CSPlayers); + for (cPlayerList::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + Vector3f Pos = (*itr)->GetPosition(); + float Distance = (Pos - a_Pos).Length(); + + if (Distance <= a_SightLimit) + { + if (!LineOfSight.Trace(a_Pos,(Pos - a_Pos),(int)(Pos - a_Pos).Length())) + { + if (Distance < ClosestDistance) + { + ClosestDistance = Distance; + ClosestPlayer = *itr; + } + } + } + } + return ClosestPlayer; +} + + + + + +void cWorld::SendPlayerList(cPlayer * a_DestPlayer) +{ + // Sends the playerlist to a_DestPlayer + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch != NULL) && !ch->IsDestroyed()) + { + a_DestPlayer->GetClientHandle()->SendPlayerListItem(*(*itr), true); + } + } +} + + + + + +void cWorld::RemoveEntityFromChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + m_ChunkMap->RemoveEntityFromChunk(a_Entity, a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +void cWorld::MoveEntityToChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + m_ChunkMap->MoveEntityToChunk(a_Entity, a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +bool cWorld::ForEachEntity(cEntityCallback & a_Callback) +{ + cCSLock Lock(m_CSEntities); + for (cEntityList::iterator itr = m_AllEntities.begin(), itr2 = itr; itr != m_AllEntities.end(); itr = itr2) + { + ++itr2; + if (a_Callback.Item(*itr)) + { + return false; + } + } // for itr - m_AllEntities[] + return false; +} + + + + + +bool cWorld::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback) +{ + return m_ChunkMap->ForEachEntityInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::DoWithEntityByID( int a_UniqueID, cEntityCallback & a_Callback ) +{ + cCSLock Lock(m_CSEntities); + for (cEntityList::iterator itr = m_AllEntities.begin(); itr != m_AllEntities.end(); ++itr ) + { + if( (*itr)->GetUniqueID() == a_UniqueID ) + { + return a_Callback.Item(*itr); + } + } // for itr - m_AllEntities[] + return false; +} + + + + + +void cWorld::CompareChunkClients(int a_ChunkX1, int a_ChunkY1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkY2, int a_ChunkZ2, cClientDiffCallback & a_Callback) +{ + m_ChunkMap->CompareChunkClients(a_ChunkX1, a_ChunkY1, a_ChunkZ1, a_ChunkX2, a_ChunkY2, a_ChunkZ2, a_Callback); +} + + + + + +bool cWorld::AddChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) +{ + return m_ChunkMap->AddChunkClient(a_ChunkX, a_ChunkY, a_ChunkZ, a_Client); +} + + + + + +void cWorld::RemoveChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) +{ + m_ChunkMap->RemoveChunkClient(a_ChunkX, a_ChunkY, a_ChunkZ, a_Client); +} + + + + + +void cWorld::RemoveClientFromChunks(cClientHandle * a_Client) +{ + m_ChunkMap->RemoveClientFromChunks(a_Client); +} + + + + + +void cWorld::SendChunkTo(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) +{ + m_ChunkSender.QueueSendChunkTo(a_ChunkX, a_ChunkY, a_ChunkZ, a_Client); +} + + + + + +void cWorld::RemoveClientFromChunkSender(cClientHandle * a_Client) +{ + m_ChunkSender.RemoveClient(a_Client); +} + + + + + +void cWorld::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +bool cWorld::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + return m_ChunkMap->LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +void cWorld::LoadChunks(const cChunkCoordsList & a_Chunks) +{ + m_ChunkMap->LoadChunks(a_Chunks); +} + + + + + +void cWorld::ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +{ + m_ChunkMap->ChunkLoadFailed(a_ChunkX, a_ChunkY, a_ChunkZ); +} + + + + + +void cWorld::UpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) +{ + AString Line1(a_Line1); + AString Line2(a_Line2); + AString Line3(a_Line3); + AString Line4(a_Line4); + if (cRoot::Get()->GetPluginManager()->CallHookUpdatingSign(this, a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4, a_Player)) + { + return; + } + m_ChunkMap->UpdateSign(a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4); + cRoot::Get()->GetPluginManager()->CallHookUpdatedSign(this, a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4, a_Player); +} + + + + + +void cWorld::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay) +{ + m_ChunkMap->ChunksStay(a_Chunks, a_Stay); +} + + + + + +void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ) +{ + m_ChunkMap->MarkChunkRegenerating(a_ChunkX, a_ChunkZ); + + // Trick: use Y=1 to force the chunk generation even though the chunk data is already present + m_Generator.QueueGenerateChunk(a_ChunkX, 1, a_ChunkZ); +} + + + + + +void cWorld::GenerateChunk(int a_ChunkX, int a_ChunkZ) +{ + m_Generator.QueueGenerateChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); +} + + + + + +void cWorld::QueueLightChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback) +{ + m_Lighting.QueueChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + +bool cWorld::IsChunkLighted(int a_ChunkX, int a_ChunkZ) +{ + return m_ChunkMap->IsChunkLighted(a_ChunkX, a_ChunkZ); +} + + + + + +bool cWorld::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback) +{ + return m_ChunkMap->ForEachChunkInRect(a_MinChunkX, a_MaxChunkX, a_MinChunkZ, a_MaxChunkZ, a_Callback); +} + + + + + +void cWorld::SaveAllChunks(void) +{ + LOGINFO("Saving all chunks..."); + m_LastSave = m_Time; + m_ChunkMap->SaveAllChunks(); + m_Storage.QueueSavedMessage(); +} + + + + + +/************************************************************************/ +/* Get and set */ +/************************************************************************/ +// void cWorld::AddClient( cClientHandle* a_Client ) +// { +// m_m_Clients.push_back( a_Client ); +// } +// cWorld::ClientList & cWorld::GetClients() +// { +// return m_m_Clients; +// } + + + + + +void cWorld::AddEntity( cEntity* a_Entity ) +{ + cCSLock Lock(m_CSEntities); + m_AllEntities.push_back( a_Entity ); +} + + + + + +unsigned int cWorld::GetNumPlayers() +{ + cCSLock Lock(m_CSPlayers); + return m_Players.size(); +} + + + + + +int cWorld::GetNumChunks(void) const +{ + return m_ChunkMap->GetNumChunks(); +} + + + + + +void cWorld::GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue) +{ + m_ChunkMap->GetChunkStats(a_NumValid, a_NumDirty); + a_NumInLightingQueue = (int) m_Lighting.GetQueueLength(); +} + + + + +void cWorld::TickQueuedBlocks(float a_Dt) +{ + if(m_BlockTickQueue.empty()) + return; + m_BlockTickQueueCopy.clear(); + m_BlockTickQueue.swap(m_BlockTickQueueCopy); + + for(std::vector::iterator itr = m_BlockTickQueueCopy.begin(); itr != m_BlockTickQueueCopy.end(); itr++) + { + BlockTickQueueItem *Block = (*itr); + Block->ToWait -= a_Dt; + if(Block->ToWait <= 0) + { + BlockHandler(GetBlock(Block->X, Block->Y, Block->Z))->OnUpdate(this, Block->X, Block->Y, Block->Z); + delete Block; //We donīt have to remove it from the vector, this will happen automatically on the next tick + }else{ + m_BlockTickQueue.push_back(Block); //Keep the block in the queue + } + } + +} + + +void cWorld::QueueBlockForTick(int a_X, int a_Y, int a_Z, float a_Time) +{ + BlockTickQueueItem *Block = new BlockTickQueueItem; + Block->X = a_X; + Block->Y = a_Y; + Block->Z = a_Z; + Block->ToWait = a_Time; + + m_BlockTickQueue.push_back(Block); +} + + +bool cWorld::IsBlockDirectlyWatered(int a_X, int a_Y, int a_Z) +{ + return IsBlockWater(GetBlock(a_X - 1, a_Y, a_Z)) + || IsBlockWater(GetBlock(a_X + 1, a_Y, a_Z)) + || IsBlockWater(GetBlock(a_X, a_Y, a_Z - 1)) + || IsBlockWater(GetBlock(a_X, a_Y, a_Z + 1)); +} \ No newline at end of file diff --git a/source/World.h b/source/World.h new file mode 100644 index 000000000..1cbd6a2fb --- /dev/null +++ b/source/World.h @@ -0,0 +1,477 @@ + +#pragma once + +#ifndef _WIN32 + #include "BlockID.h" +#else + enum ENUM_ITEM_ID; +#endif + +#define MAX_PLAYERS 65535 + +#include "SimulatorManager.h" +#include "MersenneTwister.h" +#include "ChunkMap.h" +#include "WorldStorage/WorldStorage.h" +#include "Generating/ChunkGenerator.h" +#include "Vector3i.h" +#include "Vector3f.h" +#include "ChunkSender.h" +#include "Defines.h" +#include "LightingThread.h" +#include "Item.h" + + + + + +class cRedstone; +class cFireSimulator; +class cWaterSimulator; +class cLavaSimulator; +class cSandSimulator; +class cRedstoneSimulator; +class cItem; +class cPlayer; +class cClientHandle; +class cEntity; +class cBlockEntity; +class cWorldGenerator; // The generator that actually generates the chunks for a single world +class cChunkGenerator; // The thread responsible for generating chunks +class cChestEntity; +class cFurnaceEntity; + +typedef std::list< cPlayer * > cPlayerList; + +typedef cItemCallback cPlayerListCallback; +typedef cItemCallback cEntityCallback; +typedef cItemCallback cChestCallback; +typedef cItemCallback cFurnaceCallback; + + + + + + + +class cWorld //tolua_export +{ //tolua_export +public: + + OBSOLETE static cWorld * GetWorld(); + + // Return time in seconds + inline static float GetTime() //tolua_export + { + return m_Time; + } + long long GetWorldTime(void) const { return m_WorldTime; } //tolua_export + + eGameMode GetGameMode(void) const { return m_GameMode; } //tolua_export + + void SetWorldTime(long long a_WorldTime) { m_WorldTime = a_WorldTime; } //tolua_export + + int GetHeight( int a_X, int a_Z ); //tolua_export + + void BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude = NULL); + void BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); + void BroadcastTeleportEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + void BroadcastEntRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); + void BroadcastEntLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntHeadLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL); + void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); + void BroadcastMetadata (const cPawn & a_Pawn, const cClientHandle * a_Exclude = NULL); + void BroadcastSpawn (cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); + void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); + void BroadcastWeather (eWeather a_Weather, const cClientHandle * a_Exclude = NULL); + void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); + void BroadcastTimeUpdate (const cClientHandle * a_Exclude = NULL); + void BroadcastChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); + void BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude = NULL); + void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8 + + /// If there is a block entity at the specified coods, sends it to all clients except a_Exclude + void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); + + /// If there is a block entity at the specified coords, sends it to the client specified + void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); + + void MarkChunkDirty (int a_ChunkX, int a_ChunkY, int a_ChunkZ); + void MarkChunkSaving(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + void MarkChunkSaved (int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /** Sets the chunk data as either loaded from the storage or generated. + a_BlockLight and a_BlockSkyLight are optional, if not present, chunk will be marked as unlighted. + a_BiomeMap is optional, if not present, biomes will be calculated by the generator + a_HeightMap is optional, if not present, will be calculated. + If a_MarkDirty is set, the chunk is set as dirty (used after generating) + */ + void SetChunkData( + int a_ChunkX, int a_ChunkY, int a_ChunkZ, + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight, + const cChunkDef::HeightMap * a_HeightMap, + const cChunkDef::BiomeMap * a_BiomeMap, + cEntityList & a_Entities, + cBlockEntityList & a_BlockEntities, + bool a_MarkDirty + ); + + void ChunkLighted( + int a_ChunkX, int a_ChunkZ, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_SkyLight + ); + + bool GetChunkData (int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkDataCallback & a_Callback); + + /// Gets the chunk's blocks, only the block types + bool GetChunkBlockTypes(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockTypes); + + /// Gets the chunk's blockdata, the entire 4 arrays (Types, Meta, Light, SkyLight) + bool GetChunkBlockData (int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData); + + bool IsChunkValid (int a_ChunkX, int a_ChunkY, int a_ChunkZ) const; + bool HasChunkAnyClients(int a_ChunkX, int a_ChunkY, int a_ChunkZ) const; + + void UnloadUnusedChunks(void); // tolua_export + + void CollectPickupsByPlayer(cPlayer * a_Player); + + // MOTD + const AString & GetDescription(void) const {return m_Description; } // FIXME: This should not be in cWorld + + // Max Players + unsigned int GetMaxPlayers(void) const {return m_MaxPlayers; } //tolua_export + void SetMaxPlayers(int iMax); //tolua_export + + void AddPlayer( cPlayer* a_Player ); + void RemovePlayer( cPlayer* a_Player ); + + /// Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true + bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + + /// Calls the callback for the player of the given name; returns true if the player was found and the callback called, false if player not found. Callback return ignored + bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + + /// Finds a player from a partial or complete player name and calls the callback - case-insensitive + bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + + unsigned int GetNumPlayers(); //tolua_export + + // TODO: This interface is dangerous - rewrite to DoWithClosestPlayer(pos, sight, action) + cPlayer * FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit); + + void SendPlayerList(cPlayer * a_DestPlayer); // Sends playerlist to the player + + void AddEntity( cEntity* a_Entity ); + + /// Removes the entity from the chunk specified + void RemoveEntityFromChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Moves the entity from its current chunk to the new chunk specified + void MoveEntityToChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true + bool ForEachEntity(cEntityCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true + bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param + bool DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback); // TODO: Exported in ManualBindings.cpp + + /// Compares clients of two chunks, calls the callback accordingly + void CompareChunkClients(int a_ChunkX1, int a_ChunkY1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkY2, int a_ChunkZ2, cClientDiffCallback & a_Callback); + + /// Adds client to a chunk, if not already present; returns true if added, false if present + bool AddChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes client from the chunk specified + void RemoveChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes the client from all chunks it is present in + void RemoveClientFromChunks(cClientHandle * a_Client); + + /// Sends the chunk to the client specified, if the chunk is valid. If not valid, the request is postponed (ChunkSender will send that chunk when it becomes valid+lighted) + void SendChunkTo(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes client from ChunkSender's queue of chunks to be sent + void RemoveClientFromChunkSender(cClientHandle * a_Client); + + /// Touches the chunk, causing it to be loaded or generated + void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) + bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() + void LoadChunks(const cChunkCoordsList & a_Chunks); + + /// Marks the chunk as failed-to-load: + void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + + /// Updates the sign, askin gplugins for permission forst. a_Player is the player who changed the sign, may be NULL + void UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = NULL); //tolua_export + + /// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable. To be used only by cChunkStay! + void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true); + + /// Regenerate the given chunk: + void RegenerateChunk(int a_ChunkX, int a_ChunkZ); //tolua_export + + /// Generates the given chunk, if not already generated + void GenerateChunk(int a_ChunkX, int a_ChunkZ); //tolua_export + + /// Queues a chunk for lighting; a_Callback is called after the chunk is lighted + void QueueLightChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = NULL); + + bool IsChunkLighted(int a_ChunkX, int a_ChunkZ); + + /// Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully + bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback); + + void SetBlock (int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ); //tolua_export + void FastSetBlock (int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ); //tolua_export + BLOCKTYPE GetBlock(int a_X, int a_Y, int a_Z ); //tolua_export + BLOCKTYPE GetBlock(const Vector3i & a_Pos ) { return GetBlock( a_Pos.x, a_Pos.y, a_Pos.z ); } //tolua_export + NIBBLETYPE GetBlockMeta(int a_X, int a_Y, int a_Z ); //tolua_export + NIBBLETYPE GetBlockMeta(const Vector3i & a_Pos ) { return GetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z ); } //tolua_export + void SetBlockMeta(int a_X, int a_Y, int a_Z, NIBBLETYPE a_MetaData ); //tolua_export + void SetBlockMeta(const Vector3i & a_Pos, NIBBLETYPE a_MetaData ) { SetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z, a_MetaData ); } //tolua_export + NIBBLETYPE GetBlockSkyLight( int a_X, int a_Y, int a_Z ); //tolua_export + // TODO: NIBBLETYPE GetBlockActualLight(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export + void GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); // tolua_export + + /// Spawns item pickups for each item in the list. May compress pickups if too many entities: + void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed = 1.0); + + /// Spawns item pickups for each item in the list. May compress pickups if too many entities. All pickups get the speed specified: + void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ); + + /// Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType + void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType); + + /// Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. + bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure); + + bool DigBlock (int a_X, int a_Y, int a_Z); //tolua_export + void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player ); //tolua_export + + const double & GetSpawnX(void) const { return m_SpawnX; } // tolua_export + const double & GetSpawnY(void) const { return m_SpawnY; } // tolua_export + const double & GetSpawnZ(void) const { return m_SpawnZ; } // tolua_export + + inline cSimulatorManager *GetSimulatorManager() { return m_SimulatorManager; } + inline cWaterSimulator *GetWaterSimulator() { return m_WaterSimulator; } + inline cLavaSimulator *GetLavaSimulator() { return m_LavaSimulator; } + + /// Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true + bool ForEachChestInChunk (int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true + bool ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found + bool DoWithChestAt (int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords, true if found + bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp + + /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found + bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // tolua_export + + /// a_Player is using block entity at [x, y, z], handle that: + void UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) {m_ChunkMap->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); } + + void GrowTree (int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export + void GrowTreeFromSapling(int a_BlockX, int a_BlockY, int a_BlockZ, char a_SaplingMeta); // tolua_export + void GrowTreeByBiome (int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export + + void GrowTreeImage(const sSetBlockVector & a_Blocks); + + /// Grows the plant at the specified block to its ripe stage (bonemeal used); returns false if the block is not growable. If a_IsBonemeal is true, block is not grown if not allowed in world.ini + bool GrowPlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal = false); // tolua_export + + /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem) + void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockType); // tolua_export + + int GetBiomeAt (int a_BlockX, int a_BlockZ); // tolua_export + + const AString & GetName(void) const { return m_WorldName; } //tolua_export + const AString & GetIniFileName(void) const {return m_IniFileName; } + + inline static void AbsoluteToRelative( int & a_X, int & a_Y, int & a_Z, int & a_ChunkX, int & a_ChunkY, int & a_ChunkZ ) + { + // TODO: Use floor() instead of weird if statements + // Also fix Y + a_ChunkX = a_X/cChunkDef::Width; + if(a_X < 0 && a_X % cChunkDef::Width != 0) a_ChunkX--; + a_ChunkY = 0; + a_ChunkZ = a_Z/cChunkDef::Width; + if(a_Z < 0 && a_Z % cChunkDef::Width != 0) a_ChunkZ--; + + a_X = a_X - a_ChunkX*cChunkDef::Width; + a_Y = a_Y - a_ChunkY*cChunkDef::Height; + a_Z = a_Z - a_ChunkZ*cChunkDef::Width; + } + + inline static void BlockToChunk( int a_X, int a_Y, int a_Z, int & a_ChunkX, int & a_ChunkY, int & a_ChunkZ ) + { + // TODO: Use floor() instead of weird if statements + // Also fix Y + (void)a_Y; // not unused anymore + a_ChunkX = a_X/cChunkDef::Width; + if(a_X < 0 && a_X % cChunkDef::Width != 0) a_ChunkX--; + a_ChunkY = 0; + a_ChunkZ = a_Z/cChunkDef::Width; + if(a_Z < 0 && a_Z % cChunkDef::Width != 0) a_ChunkZ--; + } + + void SaveAllChunks(void); //tolua_export + + /// Returns the number of chunks loaded + int GetNumChunks() const; //tolua_export + + /// Returns the number of chunks loaded and dirty, and in the lighting queue + void GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue); + + // Various queues length queries (cannot be const, they lock their CS): + inline int GetGeneratorQueueLength (void) { return m_Generator.GetQueueLength(); } // tolua_export + inline int GetLightingQueueLength (void) { return m_Lighting.GetQueueLength(); } // tolua_export + inline int GetStorageLoadQueueLength(void) { return m_Storage.GetLoadQueueLength(); } // tolua_export + inline int GetStorageSaveQueueLength(void) { return m_Storage.GetSaveQueueLength(); } // tolua_export + + void Tick(float a_Dt); + + void InitializeSpawn(void); + + /// Stops threads that belong to this world (part of deinit) + void StopThreads(void); + void TickQueuedBlocks(float a_Dt); + + struct BlockTickQueueItem + { + int X; + int Y; + int Z; + float ToWait; + }; + + void QueueBlockForTick(int a_BlockX, int a_BlockY, int a_BlockZ, float a_Time); + + void CastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ); //tolua_export + void SetWeather ( eWeather a_Weather ); //tolua_export + void ChangeWeather(); //tolua_export + eWeather GetWeather() { return m_Weather; }; //tolua_export + + cChunkGenerator & GetGenerator(void) { return m_Generator; } + cWorldStorage & GetStorage (void) { return m_Storage; } + cChunkMap * GetChunkMap (void) { return m_ChunkMap; } + + /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call + void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export + + int GetMaxSugarcaneHeight(void) const { return m_MaxSugarcaneHeight; } // tolua_export + int GetMaxCactusHeight (void) const { return m_MaxCactusHeight; } // tolua_export + + bool IsBlockDirectlyWatered(int a_X, int a_Y, int a_Z); + +private: + + friend class cRoot; + + // This random generator is to be used only in the Tick() method, and thus only in the World-Tick-thread (MTRand is not exactly thread-safe) + MTRand m_TickRand; + + double m_SpawnX; + double m_SpawnY; + double m_SpawnZ; + + float m_LastUnload; + float m_LastSave; + static float m_Time; // Time in seconds + long long m_WorldTime; // Time in seconds*20, this is sent to clients (is wrapped) + unsigned long long CurrentTick; + eGameMode m_GameMode; + float m_WorldTimeFraction; // When this > 1.f m_WorldTime is incremented by 20 + + // The cRedstone class simulates redstone and needs access to m_RSList + friend class cRedstone; + std::vector m_RSList; + + std::vector m_BlockTickQueue; + std::vector m_BlockTickQueueCopy; //Second is for safely removing the objects from the queue + + cSimulatorManager * m_SimulatorManager; + cSandSimulator * m_SandSimulator; + cWaterSimulator * m_WaterSimulator; + cLavaSimulator * m_LavaSimulator; + cFireSimulator * m_FireSimulator; + cRedstoneSimulator * m_RedstoneSimulator; + + cCriticalSection m_CSClients; + cCriticalSection m_CSEntities; + cCriticalSection m_CSPlayers; + + cWorldStorage m_Storage; + + AString m_Description; + + unsigned int m_MaxPlayers; + + cChunkMap * m_ChunkMap; + + bool m_bAnimals; + float m_SpawnMonsterTime; + float m_SpawnMonsterRate; + + eWeather m_Weather; + int m_WeatherInterval; + + int m_MaxCactusHeight; + int m_MaxSugarcaneHeight; + bool m_IsCropsBonemealable; + bool m_IsGrassBonemealable; + bool m_IsSaplingBonemealable; + bool m_IsMelonStemBonemealable; + bool m_IsMelonBonemealable; + bool m_IsPumpkinStemBonemealable; + bool m_IsPumpkinBonemealable; + bool m_IsSugarcaneBonemealable; + bool m_IsCactusBonemealable; + + cEntityList m_RemoveEntityQueue; + cEntityList m_AllEntities; + cClientHandleList m_Clients; + cPlayerList m_Players; + + cCriticalSection m_CSFastSetBlock; + sSetBlockList m_FastSetBlockQueue; + + cChunkGenerator m_Generator; + + cChunkSender m_ChunkSender; + cLightingThread m_Lighting; + + AString m_WorldName; + AString m_IniFileName; + + cWorld(const AString & a_WorldName); + ~cWorld(); + + void TickWeather(float a_Dt); // Handles weather each tick + void TickSpawnMobs(float a_Dt); // Handles mob spawning each tick + + void RemoveEntity( cEntity * a_Entity ); +}; //tolua_export + + + + diff --git a/source/WorldStorage/WSSAnvil.cpp b/source/WorldStorage/WSSAnvil.cpp index b39dee7c7..2036da92d 100644 --- a/source/WorldStorage/WSSAnvil.cpp +++ b/source/WorldStorage/WSSAnvil.cpp @@ -5,16 +5,16 @@ #include "Globals.h" #include "WSSAnvil.h" -#include "../cWorld.h" +#include "../World.h" #include "zlib.h" #include "../BlockID.h" -#include "../cChestEntity.h" -#include "../cFurnaceEntity.h" -#include "../cSignEntity.h" -#include "../cNoteEntity.h" -#include "../cItem.h" +#include "../ChestEntity.h" +#include "../FurnaceEntity.h" +#include "../SignEntity.h" +#include "../NoteEntity.h" +#include "../Item.h" #include "../StringCompression.h" -#include "../cEntity.h" +#include "../Entity.h" #include "../OSSupport/MakeDir.h" #include "FastNBT.h" diff --git a/source/WorldStorage/WSSCompact.cpp b/source/WorldStorage/WSSCompact.cpp index e604ca9b9..addb2a1cd 100644 --- a/source/WorldStorage/WSSCompact.cpp +++ b/source/WorldStorage/WSSCompact.cpp @@ -5,14 +5,14 @@ #include "Globals.h" #include "WSSCompact.h" -#include "../cWorld.h" +#include "../World.h" #include "zlib.h" #include #include "../StringCompression.h" -#include "../cChestEntity.h" -#include "../cSignEntity.h" -#include "../cFurnaceEntity.h" -#include "../cNoteEntity.h" +#include "../ChestEntity.h" +#include "../SignEntity.h" +#include "../FurnaceEntity.h" +#include "../NoteEntity.h" #include "../BlockID.h" diff --git a/source/WorldStorage/WorldStorage.cpp b/source/WorldStorage/WorldStorage.cpp index 503d49527..a11956e5c 100644 --- a/source/WorldStorage/WorldStorage.cpp +++ b/source/WorldStorage/WorldStorage.cpp @@ -9,10 +9,10 @@ #include "WorldStorage.h" #include "WSSCompact.h" #include "WSSAnvil.h" -#include "../cWorld.h" +#include "../World.h" #include "../Generating/ChunkGenerator.h" -#include "../cEntity.h" -#include "../cBlockEntity.h" +#include "../Entity.h" +#include "../BlockEntity.h" #include "../BlockID.h" diff --git a/source/blocks/Block.cpp b/source/blocks/Block.cpp deleted file mode 100644 index 98868de7d..000000000 --- a/source/blocks/Block.cpp +++ /dev/null @@ -1,445 +0,0 @@ -#include "Globals.h" -#include "Block.h" -#include "../cItem.h" -#include "../cWorld.h" -#include "BlockSand.h" -#include "BlockGravel.h" -#include "BlockDoor.h" -#include "BlockFire.h" -#include "BlockRedstone.h" -#include "BlockRedstoneTorch.h" -#include "BlockRedstoneRepeater.h" -#include "BlockPiston.h" -#include "BlockWorkbench.h" -#include "BlockEntity.h" -#include "BlockVine.h" -#include "BlockTallGrass.h" -#include "BlockSnow.h" -#include "BlockCloth.h" -#include "BlockSlab.h" -#include "BlockDirt.h" -#include "BlockTorch.h" -#include "BlockWood.h" -#include "BlockLeaves.h" -#include "BlockSapling.h" -#include "BlockFluid.h" -#include "BlockChest.h" -#include "BlockFurnace.h" -#include "BlockDispenser.h" -#include "BlockStairs.h" -#include "BlockLadder.h" -#include "BlockSign.h" -#include "BlockCrops.h" -#include "BlockSugarcane.h" -#include "BlockFlower.h" -#include "BlockMushroom.h" -#include "BlockCactus.h" -#include "BlockStems.h" -#include "BlockGlowstone.h" -#include "BlockStone.h" -#include "BlockMelon.h" -#include "BlockIce.h" -#include "BlockOre.h" -#include "BlockNote.h" - - - - - -bool cBlockHandler::m_HandlerInitialized = false; -cBlockHandler *cBlockHandler::m_BlockHandler[256]; - - - - - -cBlockHandler *cBlockHandler::GetBlockHandler(BLOCKTYPE a_BlockID) -{ - if (!m_HandlerInitialized) - { - //We have to initialize - memset(m_BlockHandler, 0, sizeof(m_BlockHandler)); - m_HandlerInitialized = true; - } - if (m_BlockHandler[a_BlockID] != NULL) - { - return m_BlockHandler[a_BlockID]; - } - - return m_BlockHandler[a_BlockID] = CreateBlockHandler(a_BlockID); -} - - - - - -cBlockHandler *cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockID) -{ - switch(a_BlockID) - { - case E_BLOCK_SAND: - return new cBlockSandHandler(a_BlockID); - case E_BLOCK_GRAVEL: - return new cBlockGravelHandler(a_BlockID); - case E_BLOCK_WOODEN_DOOR: - case E_BLOCK_IRON_DOOR: - return new cBlockDoorHandler(a_BlockID); - case E_BLOCK_FIRE: - return new cBlockFireHandler(a_BlockID); - case E_BLOCK_REDSTONE_TORCH_ON: - case E_BLOCK_REDSTONE_TORCH_OFF: - return new cBlockRedstoneTorchHandler(a_BlockID); - case E_BLOCK_REDSTONE_WIRE: - return new cBlockRedstoneHandler(a_BlockID); - case E_BLOCK_PISTON: - case E_BLOCK_STICKY_PISTON: - return new cBlockPistonHandler(a_BlockID); - case E_BLOCK_REDSTONE_REPEATER_ON: - case E_BLOCK_REDSTONE_REPEATER_OFF: - return new cBlockRedstoneRepeaterHandler(a_BlockID); - case E_BLOCK_WORKBENCH: - return new cBlockWorkbenchHandler(a_BlockID); - case E_BLOCK_SNOW: - return new cBlockSnowHandler(a_BlockID); - case E_BLOCK_TALL_GRASS: - return new cBlockTallGrassHandler(a_BlockID); - case E_BLOCK_VINES: - return new cBlockVineHandler(a_BlockID); - case ::E_BLOCK_WOOL: - return new cBlockClothHandler(a_BlockID); - case E_BLOCK_WOODEN_SLAB: - case E_BLOCK_STONE_SLAB: - case E_BLOCK_DOUBLE_WOODEN_SLAB: - case E_BLOCK_DOUBLE_STONE_SLAB: - return new cBlockSlabHandler(a_BlockID); - case E_BLOCK_LOG: - case E_BLOCK_PLANKS: - return new cBlockWoodHandler(a_BlockID); - case E_BLOCK_TORCH: - return new cBlockTorchHandler(a_BlockID); - case E_BLOCK_DIRT: - case E_BLOCK_GRASS: - return new cBlockDirtHandler(a_BlockID); - case E_BLOCK_LEAVES: - return new cBlockLeavesHandler(a_BlockID); - case E_BLOCK_SAPLING: - return new cBlockSaplingHandler(a_BlockID); - case E_BLOCK_WATER: - case E_BLOCK_STATIONARY_WATER: - case E_BLOCK_STATIONARY_LAVA: - case E_BLOCK_LAVA: - return new cBlockFluidHandler(a_BlockID); - case E_BLOCK_DISPENSER: - return new cBlockDispenserHandler(a_BlockID); - case E_BLOCK_FURNACE: - case E_BLOCK_LIT_FURNACE: - return new cBlockFurnaceHandler(a_BlockID); - case E_BLOCK_CHEST: - return new cBlockChestHandler(a_BlockID); - case E_BLOCK_ICE: - return new cBlockIceHandler(a_BlockID); - case E_BLOCK_LADDER: - return new cBlockLadderHandler(a_BlockID); - case E_BLOCK_COBBLESTONE_STAIRS: - case E_BLOCK_BRICK_STAIRS: - case E_BLOCK_STONE_BRICK_STAIRS: - case E_BLOCK_NETHER_BRICK_STAIRS: - case E_BLOCK_WOODEN_STAIRS: - return new cBlockStairsHandler(a_BlockID); - case E_BLOCK_SIGN_POST: - case E_BLOCK_WALLSIGN: - return new cBlockSignHandler(a_BlockID); - case E_BLOCK_CROPS: - return new cBlockCropsHandler(a_BlockID); - case E_BLOCK_SUGARCANE: - return new cBlockSugarcaneHandler(a_BlockID); - case E_BLOCK_YELLOW_FLOWER: - case E_BLOCK_RED_ROSE: - return new cBlockFlowerHandler(a_BlockID); - case E_BLOCK_BROWN_MUSHROOM: - case E_BLOCK_RED_MUSHROOM: - return new cBlockMushroomHandler(a_BlockID); - case E_BLOCK_CACTUS: - return new cBlockCactusHandler(a_BlockID); - case E_BLOCK_MELON_STEM: - case E_BLOCK_PUMPKIN_STEM: - return new cBlockStemsHandler(a_BlockID); - case E_BLOCK_GLOWSTONE: - return new cBlockGlowstoneHandler(a_BlockID); - case E_BLOCK_DIAMOND_ORE: - case E_BLOCK_GOLD_ORE: - case E_BLOCK_REDSTONE_ORE: - case E_BLOCK_REDSTONE_ORE_GLOWING: - case E_BLOCK_EMERALD_ORE: - case E_BLOCK_IRON_ORE: - case E_BLOCK_LAPIS_ORE: - case E_BLOCK_COAL_ORE: - return new cBlockOreHandler(a_BlockID); - case E_BLOCK_STONE: - case E_BLOCK_COBBLESTONE: - return new cBlockStoneHandler(a_BlockID); - case E_BLOCK_MELON: - return new cBlockMelonHandler(a_BlockID); - case E_BLOCK_NOTE_BLOCK: - return new cBlockNoteHandler(a_BlockID); - default: - return new cBlockHandler(a_BlockID); - break; - } -} - - - - - -void cBlockHandler::Deinit() -{ - for(int i = 0; i < 256; i++) - { - delete m_BlockHandler[i]; - } - m_HandlerInitialized = false; -} - - - - - -cBlockHandler::cBlockHandler(BLOCKTYPE a_BlockID) -{ - m_BlockID = a_BlockID; -} - - - - - -void cBlockHandler::OnUpdate(cWorld *a_World, int a_X, int a_Y, int a_Z) -{ -} - - - - - -void cBlockHandler::OnPlacedByPlayer(cWorld *a_World, cPlayer * a_Player, int a_X, int a_Y, int a_Z, int a_Dir) -{ -} - - - - - -void cBlockHandler::OnDestroyedByPlayer(cWorld *a_World, cPlayer * a_Player, int a_X, int a_Y, int a_Z) -{ -} - - - - - -void cBlockHandler::OnPlaced(cWorld *a_World, int a_X, int a_Y, int a_Z, int a_Dir) -{ - //Notify the neighbors - NeighborChanged(a_World, a_X - 1, a_Y, a_Z); - NeighborChanged(a_World, a_X + 1, a_Y, a_Z); - NeighborChanged(a_World, a_X, a_Y - 1, a_Z); - NeighborChanged(a_World, a_X, a_Y + 1, a_Z); - NeighborChanged(a_World, a_X, a_Y, a_Z - 1); - NeighborChanged(a_World, a_X, a_Y, a_Z + 1); -} - - - - - -void cBlockHandler::OnDestroyed(cWorld *a_World, int a_X, int a_Y, int a_Z) -{ - //Notify the neighbors - NeighborChanged(a_World, a_X - 1, a_Y, a_Z); - NeighborChanged(a_World, a_X + 1, a_Y, a_Z); - NeighborChanged(a_World, a_X, a_Y - 1, a_Z); - NeighborChanged(a_World, a_X, a_Y + 1, a_Z); - NeighborChanged(a_World, a_X, a_Y, a_Z - 1); - NeighborChanged(a_World, a_X, a_Y, a_Z + 1); -} - - - - - -void cBlockHandler::NeighborChanged(cWorld *a_World, int a_X, int a_Y, int a_Z) -{ - GetBlockHandler(a_World->GetBlock(a_X, a_Y, a_Z))->OnNeighborChanged(a_World, a_X, a_Y, a_Z); -} - - - - - -void cBlockHandler::OnNeighborChanged(cWorld *a_World, int a_X, int a_Y, int a_Z) -{ -} - - - - - -void cBlockHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_X, int a_Y, int a_Z) -{ -} - - - - - -void cBlockHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_X, int a_Y, int a_Z) -{ -} - - - - - -void cBlockHandler::PlaceBlock(cWorld *a_World, cPlayer *a_Player, NIBBLETYPE a_BlockMeta, int a_X, int a_Y, int a_Z, char a_Dir) -{ - a_World->SetBlock(a_X, a_Y, a_Z, m_BlockID, a_BlockMeta); - OnPlacedByPlayer(a_World, a_Player, a_X, a_Y, a_Z, a_Dir); -} - - - - - -char cBlockHandler::GetDropCount() -{ - return 1; -} - - - - - -int cBlockHandler::GetDropID() -{ - return m_BlockID; -} - - - - - -NIBBLETYPE cBlockHandler::GetDropMeta(NIBBLETYPE a_BlockMeta) -{ - return a_BlockMeta; //This keeps most textures. The few other blocks have to override this -} - - - - - -void cBlockHandler::DropBlock(cWorld *a_World, int a_X, int a_Y, int a_Z) -{ - cItems Drops; - NIBBLETYPE Meta = a_World->GetBlockMeta(a_X, a_Y, a_Z); - char DropCount = GetDropCount(); - short DropItem = (short)GetDropID(); - if (DropCount > 0 && (DropItem != E_ITEM_EMPTY)) - { - Drops.push_back(cItem(DropItem, DropCount, GetDropMeta(Meta))); - a_World->SpawnItemPickups(Drops, a_X, a_Y, a_Z); - } -} - - - - - -AString cBlockHandler::GetStepSound() { - return "step.stone"; -} - - - - - -bool cBlockHandler::CanBePlacedAt(cWorld *a_World, int a_X, int a_Y, int a_Z, char a_Dir) -{ - return CanBeAt(a_World, a_X, a_Y, a_Z); -} - - - - - -bool cBlockHandler::CanBeAt(cWorld *a_World, int a_X, int a_Y, int a_Z) -{ - return true; -} - - - - - -bool cBlockHandler::IsUseable() -{ - return false; -} - - - - - -bool cBlockHandler::IsClickedThrough() -{ - return false; -} - - - - - -bool cBlockHandler::IgnoreBuildCollision() -{ - return m_BlockID == E_BLOCK_AIR; -} - - - - - -bool cBlockHandler::NeedsRandomTicks() -{ - return false; -} - - - - - -bool cBlockHandler::AllowBlockOnTop() -{ - return true; -} - - - - - -bool cBlockHandler::CanBePlacedOnSide() -{ - return true; -} - - - - - -bool cBlockHandler::DropOnUnsuitable() -{ - return true; -} - - - - diff --git a/source/blocks/Block.h b/source/blocks/Block.h deleted file mode 100644 index 859870c4a..000000000 --- a/source/blocks/Block.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once -#include "../Defines.h" - -class cWorld; -class cPlayer; - - - -class cBlockHandler -{ -public: - cBlockHandler(BLOCKTYPE a_BlockID); - - // Called when the block gets ticked either by a random tick or by a queued tick - virtual void OnUpdate(cWorld *a_World, int a_X, int a_Y, int a_Z); - - // Will be called by cBlockHandler::PlaceBlock after the player has placed a new block - virtual void OnPlacedByPlayer(cWorld *a_World, cPlayer * a_Player, int a_X, int a_Y, int a_Z, int a_Dir); - // Will be called before the player has destroyed a block - virtual void OnDestroyedByPlayer(cWorld *a_World, cPlayer * a_Player, int a_X, int a_Y, int a_Z); - // Will be called when a new block was placed. Will be called before OnPlacedByPlayer - virtual void OnPlaced(cWorld *a_World, int a_X, int a_Y, int a_Z, int a_Dir); - // Will be called before a block gets destroyed / replaced with air - virtual void OnDestroyed(cWorld *a_World, int a_X, int a_Y, int a_Z); - // Will be called when a direct neighbor of this block has been changed (The position is the own position, not the neighbor position) - virtual void OnNeighborChanged(cWorld *a_World, int a_X, int a_Y, int a_Z); - // Notifies all neighbors of the give block about a change - static void NeighborChanged(cWorld *a_World, int a_X, int a_Y, int a_Z); - // Will be called while the player diggs the block. - virtual void OnDigging(cWorld *a_World, cPlayer *a_Player, int a_X, int a_Y, int a_Z); - // Will be called if the user right clicks the block and the block is useable - virtual void OnUse(cWorld *a_World, cPlayer *a_Player, int a_X, int a_Y, int a_Z); - // This function handles the real block placement for the give block by a player and also calls the OnPlacedByPlayer function - virtual void PlaceBlock(cWorld *a_World, cPlayer *a_Player, NIBBLETYPE a_BlockMeta, int a_X, int a_Y, int a_Z, char a_Dir); - - // Indicates how much items are dropped DEFAULT: 1 - virtual char GetDropCount(); - // Indicates the id dropped by this block DEFAULT: BlockID - virtual int GetDropID(); - // Indicates the Drop Meta data based on the block meta DEFAULT: BlockMeta - virtual NIBBLETYPE GetDropMeta(NIBBLETYPE a_BlockMeta); - // This function handles the dropping of a block based on the Drop id, drop count and drop meta. This will not destroy the block - virtual void DropBlock(cWorld *a_World, int a_X, int a_Y, int a_Z); - /// Returns step sound name of block - virtual AString GetStepSound(); - - // Indicates whether this block needs random ticks DEFAULT: False - virtual bool NeedsRandomTicks(); - - /// Checks if the block can stay at the specified coords in the world - virtual bool CanBeAt(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Checks if the block can be placed at this point. Default: CanBeAt(...) NOTE: This call doesn't actually place the block - virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); - - /// Called when the player tries to place a block on top of this block (Only if he aims directly on this block); return false to disallow - virtual bool AllowBlockOnTop(void); - - /// Called to check whether this block supports a rclk action. If it returns true, OnUse() is called - virtual bool IsUseable(void); - - // Indicates whether the client will click through this block. For example digging a fire will hit the block below the fire so fire is clicked through - virtual bool IsClickedThrough(void); - - // Checks if the player can build "inside" this block. For example blocks placed "on" snow will be placed at the same position. So: Snow ignores Build collision - virtual bool IgnoreBuildCollision(void); - - /// Indicates this block can be placed on the side of other blocks. Default: true - virtual bool CanBePlacedOnSide(); - - /// Does this block drop if it gets destroyed by an unsuitable situation? Default: true - virtual bool DropOnUnsuitable(); - - - // Static function to get the blockhandler for an specific block id - static cBlockHandler * GetBlockHandler(BLOCKTYPE a_BlockID); - - // Deletes all initialised block handlers - static void Deinit(); - -protected: - BLOCKTYPE m_BlockID; - // Creates a new blockhandler for the given block id. For internal use only, use GetBlockHandler instead. - static cBlockHandler *CreateBlockHandler(BLOCKTYPE a_BlockID); - static cBlockHandler *m_BlockHandler[256]; - static bool m_HandlerInitialized; //used to detect if the blockhandlers are initialized -}; - -// Shortcut to get the blockhandler for a specific block -inline cBlockHandler *BlockHandler(BLOCKTYPE a_BlockID) { return cBlockHandler::GetBlockHandler(a_BlockID); } \ No newline at end of file diff --git a/source/blocks/BlockCactus.h b/source/blocks/BlockCactus.h index 9da9d796b..964646299 100644 --- a/source/blocks/BlockCactus.h +++ b/source/blocks/BlockCactus.h @@ -1,6 +1,6 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" diff --git a/source/blocks/BlockChest.h b/source/blocks/BlockChest.h index 65c0ffa5c..db67fe1c8 100644 --- a/source/blocks/BlockChest.h +++ b/source/blocks/BlockChest.h @@ -1,8 +1,8 @@ #pragma once #include "BlockEntity.h" -#include "../cWorld.h" -#include "../cPiston.h" -#include "../cPlayer.h" +#include "../World.h" +#include "../Piston.h" +#include "../Player.h" class cBlockChestHandler : public cBlockEntityHandler { diff --git a/source/blocks/BlockCloth.h b/source/blocks/BlockCloth.h index d83b18ebb..452ad2237 100644 --- a/source/blocks/BlockCloth.h +++ b/source/blocks/BlockCloth.h @@ -1,5 +1,5 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" class cBlockClothHandler : public cBlockHandler diff --git a/source/blocks/BlockCrops.h b/source/blocks/BlockCrops.h index ebf911131..b30139e79 100644 --- a/source/blocks/BlockCrops.h +++ b/source/blocks/BlockCrops.h @@ -1,7 +1,7 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" #include "../MersenneTwister.h" -#include "../cWorld.h" +#include "../World.h" class cBlockCropsHandler : public cBlockHandler { diff --git a/source/blocks/BlockDirt.h b/source/blocks/BlockDirt.h index 28993d29c..5a2487f1b 100644 --- a/source/blocks/BlockDirt.h +++ b/source/blocks/BlockDirt.h @@ -1,7 +1,7 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" #include "../MersenneTwister.h" -#include "../cWorld.h" +#include "../World.h" class cBlockDirtHandler : public cBlockHandler { diff --git a/source/blocks/BlockDispenser.h b/source/blocks/BlockDispenser.h index f3ad20d5c..eee75a39a 100644 --- a/source/blocks/BlockDispenser.h +++ b/source/blocks/BlockDispenser.h @@ -1,8 +1,8 @@ #pragma once #include "BlockEntity.h" -#include "../cWorld.h" -#include "../cPiston.h" -#include "../cPlayer.h" +#include "../World.h" +#include "../Piston.h" +#include "../Player.h" class cBlockDispenserHandler : public cBlockEntityHandler { diff --git a/source/blocks/BlockDoor.cpp b/source/blocks/BlockDoor.cpp index 283f546b6..deed8a256 100644 --- a/source/blocks/BlockDoor.cpp +++ b/source/blocks/BlockDoor.cpp @@ -1,9 +1,9 @@ #include "Globals.h" #include "BlockDoor.h" -#include "../cItem.h" -#include "../cWorld.h" -#include "../cDoors.h" -#include "../cPlayer.h" +#include "../Item.h" +#include "../World.h" +#include "../Doors.h" +#include "../Player.h" cBlockDoorHandler::cBlockDoorHandler(BLOCKTYPE a_BlockID) diff --git a/source/blocks/BlockDoor.h b/source/blocks/BlockDoor.h index 6af8bd955..3316f50a4 100644 --- a/source/blocks/BlockDoor.h +++ b/source/blocks/BlockDoor.h @@ -1,5 +1,5 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" class cBlockDoorHandler : public cBlockHandler diff --git a/source/blocks/BlockEntity.h b/source/blocks/BlockEntity.h index bfc3241d3..caf6ee342 100644 --- a/source/blocks/BlockEntity.h +++ b/source/blocks/BlockEntity.h @@ -1,7 +1,7 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" diff --git a/source/blocks/BlockFire.h b/source/blocks/BlockFire.h index 1b9f68060..1fecf9a14 100644 --- a/source/blocks/BlockFire.h +++ b/source/blocks/BlockFire.h @@ -1,5 +1,5 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" class cBlockFireHandler : public cBlockHandler diff --git a/source/blocks/BlockFlower.h b/source/blocks/BlockFlower.h index 920cf1c8f..35a55fcac 100644 --- a/source/blocks/BlockFlower.h +++ b/source/blocks/BlockFlower.h @@ -1,5 +1,5 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" class cBlockFlowerHandler : public cBlockHandler diff --git a/source/blocks/BlockFluid.h b/source/blocks/BlockFluid.h index 6883ce715..c5ac660b0 100644 --- a/source/blocks/BlockFluid.h +++ b/source/blocks/BlockFluid.h @@ -1,5 +1,5 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" class cBlockFluidHandler : public cBlockHandler diff --git a/source/blocks/BlockFurnace.h b/source/blocks/BlockFurnace.h index d0455e805..4157b5049 100644 --- a/source/blocks/BlockFurnace.h +++ b/source/blocks/BlockFurnace.h @@ -1,8 +1,8 @@ #pragma once #include "BlockEntity.h" -#include "../cWorld.h" -#include "../cPiston.h" -#include "../cPlayer.h" +#include "../World.h" +#include "../Piston.h" +#include "../Player.h" class cBlockFurnaceHandler : public cBlockEntityHandler { diff --git a/source/blocks/BlockGlowstone.h b/source/blocks/BlockGlowstone.h index ecd34a1d9..0b906e2fe 100644 --- a/source/blocks/BlockGlowstone.h +++ b/source/blocks/BlockGlowstone.h @@ -1,5 +1,5 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" class cBlockGlowstoneHandler : public cBlockHandler diff --git a/source/blocks/BlockGravel.h b/source/blocks/BlockGravel.h index d4f36a894..da47db9bb 100644 --- a/source/blocks/BlockGravel.h +++ b/source/blocks/BlockGravel.h @@ -1,5 +1,5 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" class cBlockGravelHandler : public cBlockHandler diff --git a/source/blocks/BlockHandler.cpp b/source/blocks/BlockHandler.cpp new file mode 100644 index 000000000..f45f40f9e --- /dev/null +++ b/source/blocks/BlockHandler.cpp @@ -0,0 +1,445 @@ +#include "Globals.h" +#include "BlockHandler.h" +#include "../Item.h" +#include "../World.h" +#include "BlockSand.h" +#include "BlockGravel.h" +#include "BlockDoor.h" +#include "BlockFire.h" +#include "BlockRedstone.h" +#include "BlockRedstoneTorch.h" +#include "BlockRedstoneRepeater.h" +#include "BlockPiston.h" +#include "BlockWorkbench.h" +#include "BlockEntity.h" +#include "BlockVine.h" +#include "BlockTallGrass.h" +#include "BlockSnow.h" +#include "BlockCloth.h" +#include "BlockSlab.h" +#include "BlockDirt.h" +#include "BlockTorch.h" +#include "BlockWood.h" +#include "BlockLeaves.h" +#include "BlockSapling.h" +#include "BlockFluid.h" +#include "BlockChest.h" +#include "BlockFurnace.h" +#include "BlockDispenser.h" +#include "BlockStairs.h" +#include "BlockLadder.h" +#include "BlockSign.h" +#include "BlockCrops.h" +#include "BlockSugarcane.h" +#include "BlockFlower.h" +#include "BlockMushroom.h" +#include "BlockCactus.h" +#include "BlockStems.h" +#include "BlockGlowstone.h" +#include "BlockStone.h" +#include "BlockMelon.h" +#include "BlockIce.h" +#include "BlockOre.h" +#include "BlockNote.h" + + + + + +bool cBlockHandler::m_HandlerInitialized = false; +cBlockHandler *cBlockHandler::m_BlockHandler[256]; + + + + + +cBlockHandler *cBlockHandler::GetBlockHandler(BLOCKTYPE a_BlockID) +{ + if (!m_HandlerInitialized) + { + //We have to initialize + memset(m_BlockHandler, 0, sizeof(m_BlockHandler)); + m_HandlerInitialized = true; + } + if (m_BlockHandler[a_BlockID] != NULL) + { + return m_BlockHandler[a_BlockID]; + } + + return m_BlockHandler[a_BlockID] = CreateBlockHandler(a_BlockID); +} + + + + + +cBlockHandler *cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockID) +{ + switch(a_BlockID) + { + case E_BLOCK_SAND: + return new cBlockSandHandler(a_BlockID); + case E_BLOCK_GRAVEL: + return new cBlockGravelHandler(a_BlockID); + case E_BLOCK_WOODEN_DOOR: + case E_BLOCK_IRON_DOOR: + return new cBlockDoorHandler(a_BlockID); + case E_BLOCK_FIRE: + return new cBlockFireHandler(a_BlockID); + case E_BLOCK_REDSTONE_TORCH_ON: + case E_BLOCK_REDSTONE_TORCH_OFF: + return new cBlockRedstoneTorchHandler(a_BlockID); + case E_BLOCK_REDSTONE_WIRE: + return new cBlockRedstoneHandler(a_BlockID); + case E_BLOCK_PISTON: + case E_BLOCK_STICKY_PISTON: + return new cBlockPistonHandler(a_BlockID); + case E_BLOCK_REDSTONE_REPEATER_ON: + case E_BLOCK_REDSTONE_REPEATER_OFF: + return new cBlockRedstoneRepeaterHandler(a_BlockID); + case E_BLOCK_WORKBENCH: + return new cBlockWorkbenchHandler(a_BlockID); + case E_BLOCK_SNOW: + return new cBlockSnowHandler(a_BlockID); + case E_BLOCK_TALL_GRASS: + return new cBlockTallGrassHandler(a_BlockID); + case E_BLOCK_VINES: + return new cBlockVineHandler(a_BlockID); + case ::E_BLOCK_WOOL: + return new cBlockClothHandler(a_BlockID); + case E_BLOCK_WOODEN_SLAB: + case E_BLOCK_STONE_SLAB: + case E_BLOCK_DOUBLE_WOODEN_SLAB: + case E_BLOCK_DOUBLE_STONE_SLAB: + return new cBlockSlabHandler(a_BlockID); + case E_BLOCK_LOG: + case E_BLOCK_PLANKS: + return new cBlockWoodHandler(a_BlockID); + case E_BLOCK_TORCH: + return new cBlockTorchHandler(a_BlockID); + case E_BLOCK_DIRT: + case E_BLOCK_GRASS: + return new cBlockDirtHandler(a_BlockID); + case E_BLOCK_LEAVES: + return new cBlockLeavesHandler(a_BlockID); + case E_BLOCK_SAPLING: + return new cBlockSaplingHandler(a_BlockID); + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_STATIONARY_LAVA: + case E_BLOCK_LAVA: + return new cBlockFluidHandler(a_BlockID); + case E_BLOCK_DISPENSER: + return new cBlockDispenserHandler(a_BlockID); + case E_BLOCK_FURNACE: + case E_BLOCK_LIT_FURNACE: + return new cBlockFurnaceHandler(a_BlockID); + case E_BLOCK_CHEST: + return new cBlockChestHandler(a_BlockID); + case E_BLOCK_ICE: + return new cBlockIceHandler(a_BlockID); + case E_BLOCK_LADDER: + return new cBlockLadderHandler(a_BlockID); + case E_BLOCK_COBBLESTONE_STAIRS: + case E_BLOCK_BRICK_STAIRS: + case E_BLOCK_STONE_BRICK_STAIRS: + case E_BLOCK_NETHER_BRICK_STAIRS: + case E_BLOCK_WOODEN_STAIRS: + return new cBlockStairsHandler(a_BlockID); + case E_BLOCK_SIGN_POST: + case E_BLOCK_WALLSIGN: + return new cBlockSignHandler(a_BlockID); + case E_BLOCK_CROPS: + return new cBlockCropsHandler(a_BlockID); + case E_BLOCK_SUGARCANE: + return new cBlockSugarcaneHandler(a_BlockID); + case E_BLOCK_YELLOW_FLOWER: + case E_BLOCK_RED_ROSE: + return new cBlockFlowerHandler(a_BlockID); + case E_BLOCK_BROWN_MUSHROOM: + case E_BLOCK_RED_MUSHROOM: + return new cBlockMushroomHandler(a_BlockID); + case E_BLOCK_CACTUS: + return new cBlockCactusHandler(a_BlockID); + case E_BLOCK_MELON_STEM: + case E_BLOCK_PUMPKIN_STEM: + return new cBlockStemsHandler(a_BlockID); + case E_BLOCK_GLOWSTONE: + return new cBlockGlowstoneHandler(a_BlockID); + case E_BLOCK_DIAMOND_ORE: + case E_BLOCK_GOLD_ORE: + case E_BLOCK_REDSTONE_ORE: + case E_BLOCK_REDSTONE_ORE_GLOWING: + case E_BLOCK_EMERALD_ORE: + case E_BLOCK_IRON_ORE: + case E_BLOCK_LAPIS_ORE: + case E_BLOCK_COAL_ORE: + return new cBlockOreHandler(a_BlockID); + case E_BLOCK_STONE: + case E_BLOCK_COBBLESTONE: + return new cBlockStoneHandler(a_BlockID); + case E_BLOCK_MELON: + return new cBlockMelonHandler(a_BlockID); + case E_BLOCK_NOTE_BLOCK: + return new cBlockNoteHandler(a_BlockID); + default: + return new cBlockHandler(a_BlockID); + break; + } +} + + + + + +void cBlockHandler::Deinit() +{ + for(int i = 0; i < 256; i++) + { + delete m_BlockHandler[i]; + } + m_HandlerInitialized = false; +} + + + + + +cBlockHandler::cBlockHandler(BLOCKTYPE a_BlockID) +{ + m_BlockID = a_BlockID; +} + + + + + +void cBlockHandler::OnUpdate(cWorld *a_World, int a_X, int a_Y, int a_Z) +{ +} + + + + + +void cBlockHandler::OnPlacedByPlayer(cWorld *a_World, cPlayer * a_Player, int a_X, int a_Y, int a_Z, int a_Dir) +{ +} + + + + + +void cBlockHandler::OnDestroyedByPlayer(cWorld *a_World, cPlayer * a_Player, int a_X, int a_Y, int a_Z) +{ +} + + + + + +void cBlockHandler::OnPlaced(cWorld *a_World, int a_X, int a_Y, int a_Z, int a_Dir) +{ + //Notify the neighbors + NeighborChanged(a_World, a_X - 1, a_Y, a_Z); + NeighborChanged(a_World, a_X + 1, a_Y, a_Z); + NeighborChanged(a_World, a_X, a_Y - 1, a_Z); + NeighborChanged(a_World, a_X, a_Y + 1, a_Z); + NeighborChanged(a_World, a_X, a_Y, a_Z - 1); + NeighborChanged(a_World, a_X, a_Y, a_Z + 1); +} + + + + + +void cBlockHandler::OnDestroyed(cWorld *a_World, int a_X, int a_Y, int a_Z) +{ + //Notify the neighbors + NeighborChanged(a_World, a_X - 1, a_Y, a_Z); + NeighborChanged(a_World, a_X + 1, a_Y, a_Z); + NeighborChanged(a_World, a_X, a_Y - 1, a_Z); + NeighborChanged(a_World, a_X, a_Y + 1, a_Z); + NeighborChanged(a_World, a_X, a_Y, a_Z - 1); + NeighborChanged(a_World, a_X, a_Y, a_Z + 1); +} + + + + + +void cBlockHandler::NeighborChanged(cWorld *a_World, int a_X, int a_Y, int a_Z) +{ + GetBlockHandler(a_World->GetBlock(a_X, a_Y, a_Z))->OnNeighborChanged(a_World, a_X, a_Y, a_Z); +} + + + + + +void cBlockHandler::OnNeighborChanged(cWorld *a_World, int a_X, int a_Y, int a_Z) +{ +} + + + + + +void cBlockHandler::OnDigging(cWorld *a_World, cPlayer *a_Player, int a_X, int a_Y, int a_Z) +{ +} + + + + + +void cBlockHandler::OnUse(cWorld *a_World, cPlayer *a_Player, int a_X, int a_Y, int a_Z) +{ +} + + + + + +void cBlockHandler::PlaceBlock(cWorld *a_World, cPlayer *a_Player, NIBBLETYPE a_BlockMeta, int a_X, int a_Y, int a_Z, char a_Dir) +{ + a_World->SetBlock(a_X, a_Y, a_Z, m_BlockID, a_BlockMeta); + OnPlacedByPlayer(a_World, a_Player, a_X, a_Y, a_Z, a_Dir); +} + + + + + +char cBlockHandler::GetDropCount() +{ + return 1; +} + + + + + +int cBlockHandler::GetDropID() +{ + return m_BlockID; +} + + + + + +NIBBLETYPE cBlockHandler::GetDropMeta(NIBBLETYPE a_BlockMeta) +{ + return a_BlockMeta; //This keeps most textures. The few other blocks have to override this +} + + + + + +void cBlockHandler::DropBlock(cWorld *a_World, int a_X, int a_Y, int a_Z) +{ + cItems Drops; + NIBBLETYPE Meta = a_World->GetBlockMeta(a_X, a_Y, a_Z); + char DropCount = GetDropCount(); + short DropItem = (short)GetDropID(); + if (DropCount > 0 && (DropItem != E_ITEM_EMPTY)) + { + Drops.push_back(cItem(DropItem, DropCount, GetDropMeta(Meta))); + a_World->SpawnItemPickups(Drops, a_X, a_Y, a_Z); + } +} + + + + + +AString cBlockHandler::GetStepSound() { + return "step.stone"; +} + + + + + +bool cBlockHandler::CanBePlacedAt(cWorld *a_World, int a_X, int a_Y, int a_Z, char a_Dir) +{ + return CanBeAt(a_World, a_X, a_Y, a_Z); +} + + + + + +bool cBlockHandler::CanBeAt(cWorld *a_World, int a_X, int a_Y, int a_Z) +{ + return true; +} + + + + + +bool cBlockHandler::IsUseable() +{ + return false; +} + + + + + +bool cBlockHandler::IsClickedThrough() +{ + return false; +} + + + + + +bool cBlockHandler::IgnoreBuildCollision() +{ + return m_BlockID == E_BLOCK_AIR; +} + + + + + +bool cBlockHandler::NeedsRandomTicks() +{ + return false; +} + + + + + +bool cBlockHandler::AllowBlockOnTop() +{ + return true; +} + + + + + +bool cBlockHandler::CanBePlacedOnSide() +{ + return true; +} + + + + + +bool cBlockHandler::DropOnUnsuitable() +{ + return true; +} + + + + diff --git a/source/blocks/BlockHandler.h b/source/blocks/BlockHandler.h new file mode 100644 index 000000000..859870c4a --- /dev/null +++ b/source/blocks/BlockHandler.h @@ -0,0 +1,90 @@ +#pragma once +#include "../Defines.h" + +class cWorld; +class cPlayer; + + + +class cBlockHandler +{ +public: + cBlockHandler(BLOCKTYPE a_BlockID); + + // Called when the block gets ticked either by a random tick or by a queued tick + virtual void OnUpdate(cWorld *a_World, int a_X, int a_Y, int a_Z); + + // Will be called by cBlockHandler::PlaceBlock after the player has placed a new block + virtual void OnPlacedByPlayer(cWorld *a_World, cPlayer * a_Player, int a_X, int a_Y, int a_Z, int a_Dir); + // Will be called before the player has destroyed a block + virtual void OnDestroyedByPlayer(cWorld *a_World, cPlayer * a_Player, int a_X, int a_Y, int a_Z); + // Will be called when a new block was placed. Will be called before OnPlacedByPlayer + virtual void OnPlaced(cWorld *a_World, int a_X, int a_Y, int a_Z, int a_Dir); + // Will be called before a block gets destroyed / replaced with air + virtual void OnDestroyed(cWorld *a_World, int a_X, int a_Y, int a_Z); + // Will be called when a direct neighbor of this block has been changed (The position is the own position, not the neighbor position) + virtual void OnNeighborChanged(cWorld *a_World, int a_X, int a_Y, int a_Z); + // Notifies all neighbors of the give block about a change + static void NeighborChanged(cWorld *a_World, int a_X, int a_Y, int a_Z); + // Will be called while the player diggs the block. + virtual void OnDigging(cWorld *a_World, cPlayer *a_Player, int a_X, int a_Y, int a_Z); + // Will be called if the user right clicks the block and the block is useable + virtual void OnUse(cWorld *a_World, cPlayer *a_Player, int a_X, int a_Y, int a_Z); + // This function handles the real block placement for the give block by a player and also calls the OnPlacedByPlayer function + virtual void PlaceBlock(cWorld *a_World, cPlayer *a_Player, NIBBLETYPE a_BlockMeta, int a_X, int a_Y, int a_Z, char a_Dir); + + // Indicates how much items are dropped DEFAULT: 1 + virtual char GetDropCount(); + // Indicates the id dropped by this block DEFAULT: BlockID + virtual int GetDropID(); + // Indicates the Drop Meta data based on the block meta DEFAULT: BlockMeta + virtual NIBBLETYPE GetDropMeta(NIBBLETYPE a_BlockMeta); + // This function handles the dropping of a block based on the Drop id, drop count and drop meta. This will not destroy the block + virtual void DropBlock(cWorld *a_World, int a_X, int a_Y, int a_Z); + /// Returns step sound name of block + virtual AString GetStepSound(); + + // Indicates whether this block needs random ticks DEFAULT: False + virtual bool NeedsRandomTicks(); + + /// Checks if the block can stay at the specified coords in the world + virtual bool CanBeAt(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ); + + /// Checks if the block can be placed at this point. Default: CanBeAt(...) NOTE: This call doesn't actually place the block + virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); + + /// Called when the player tries to place a block on top of this block (Only if he aims directly on this block); return false to disallow + virtual bool AllowBlockOnTop(void); + + /// Called to check whether this block supports a rclk action. If it returns true, OnUse() is called + virtual bool IsUseable(void); + + // Indicates whether the client will click through this block. For example digging a fire will hit the block below the fire so fire is clicked through + virtual bool IsClickedThrough(void); + + // Checks if the player can build "inside" this block. For example blocks placed "on" snow will be placed at the same position. So: Snow ignores Build collision + virtual bool IgnoreBuildCollision(void); + + /// Indicates this block can be placed on the side of other blocks. Default: true + virtual bool CanBePlacedOnSide(); + + /// Does this block drop if it gets destroyed by an unsuitable situation? Default: true + virtual bool DropOnUnsuitable(); + + + // Static function to get the blockhandler for an specific block id + static cBlockHandler * GetBlockHandler(BLOCKTYPE a_BlockID); + + // Deletes all initialised block handlers + static void Deinit(); + +protected: + BLOCKTYPE m_BlockID; + // Creates a new blockhandler for the given block id. For internal use only, use GetBlockHandler instead. + static cBlockHandler *CreateBlockHandler(BLOCKTYPE a_BlockID); + static cBlockHandler *m_BlockHandler[256]; + static bool m_HandlerInitialized; //used to detect if the blockhandlers are initialized +}; + +// Shortcut to get the blockhandler for a specific block +inline cBlockHandler *BlockHandler(BLOCKTYPE a_BlockID) { return cBlockHandler::GetBlockHandler(a_BlockID); } \ No newline at end of file diff --git a/source/blocks/BlockIce.h b/source/blocks/BlockIce.h index d6bb63b0a..cc5cc8920 100644 --- a/source/blocks/BlockIce.h +++ b/source/blocks/BlockIce.h @@ -1,7 +1,7 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" #include "../MersenneTwister.h" -#include "../cWorld.h" +#include "../World.h" class cBlockIceHandler : public cBlockHandler { diff --git a/source/blocks/BlockLadder.h b/source/blocks/BlockLadder.h index e30fc884d..ee48df848 100644 --- a/source/blocks/BlockLadder.h +++ b/source/blocks/BlockLadder.h @@ -1,7 +1,7 @@ #pragma once -#include "Block.h" -#include "../cWorld.h" -#include "../cLadder.h" +#include "BlockHandler.h" +#include "../World.h" +#include "../Ladder.h" class cBlockLadderHandler : public cBlockHandler { diff --git a/source/blocks/BlockLeaves.h b/source/blocks/BlockLeaves.h index c2ec697ae..65c47f8de 100644 --- a/source/blocks/BlockLeaves.h +++ b/source/blocks/BlockLeaves.h @@ -1,7 +1,7 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" #include "../MersenneTwister.h" -#include "../cWorld.h" +#include "../World.h" #include "../BlockArea.h" diff --git a/source/blocks/BlockMelon.h b/source/blocks/BlockMelon.h index 3abcc6efb..87fb7e1e8 100644 --- a/source/blocks/BlockMelon.h +++ b/source/blocks/BlockMelon.h @@ -1,5 +1,5 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" class cBlockMelonHandler : public cBlockHandler diff --git a/source/blocks/BlockMushroom.h b/source/blocks/BlockMushroom.h index ca787dce6..c4119bad0 100644 --- a/source/blocks/BlockMushroom.h +++ b/source/blocks/BlockMushroom.h @@ -1,5 +1,5 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" class cBlockMushroomHandler : public cBlockHandler diff --git a/source/blocks/BlockNote.h b/source/blocks/BlockNote.h index 2c439f623..896a5d03b 100644 --- a/source/blocks/BlockNote.h +++ b/source/blocks/BlockNote.h @@ -1,5 +1,5 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" #include "BlockEntity.h" class cBlockNoteHandler : public cBlockEntityHandler diff --git a/source/blocks/BlockOre.h b/source/blocks/BlockOre.h index 4788c9b89..556a215ce 100644 --- a/source/blocks/BlockOre.h +++ b/source/blocks/BlockOre.h @@ -1,7 +1,7 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" #include "../MersenneTwister.h" -#include "../cWorld.h" +#include "../World.h" class cBlockOreHandler : public cBlockHandler { diff --git a/source/blocks/BlockPiston.cpp b/source/blocks/BlockPiston.cpp index c2fcdd1f2..a4ed13d88 100644 --- a/source/blocks/BlockPiston.cpp +++ b/source/blocks/BlockPiston.cpp @@ -1,10 +1,10 @@ #include "Globals.h" #include "BlockPiston.h" -#include "../cItem.h" -#include "../cWorld.h" -#include "../cRedstone.h" -#include "../cPlayer.h" -#include "../cPiston.h" +#include "../Item.h" +#include "../World.h" +#include "../Redstone.h" +#include "../Player.h" +#include "../Piston.h" diff --git a/source/blocks/BlockPiston.h b/source/blocks/BlockPiston.h index 1493ff27e..dbce9bae5 100644 --- a/source/blocks/BlockPiston.h +++ b/source/blocks/BlockPiston.h @@ -1,5 +1,5 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" class cBlockPistonHandler : public cBlockHandler diff --git a/source/blocks/BlockRedstone.cpp b/source/blocks/BlockRedstone.cpp index 58edaa1cd..7653436f4 100644 --- a/source/blocks/BlockRedstone.cpp +++ b/source/blocks/BlockRedstone.cpp @@ -1,9 +1,9 @@ #include "Globals.h" #include "BlockRedstone.h" -#include "../cItem.h" -#include "../cWorld.h" -#include "../cRedstone.h" -#include "../cTorch.h" +#include "../Item.h" +#include "../World.h" +#include "../Redstone.h" +#include "../Torch.h" cBlockRedstoneHandler::cBlockRedstoneHandler(BLOCKTYPE a_BlockID) : cBlockHandler(a_BlockID) diff --git a/source/blocks/BlockRedstone.h b/source/blocks/BlockRedstone.h index dd2c75fc4..9b83b1b29 100644 --- a/source/blocks/BlockRedstone.h +++ b/source/blocks/BlockRedstone.h @@ -1,6 +1,6 @@ #pragma once -#include "Block.h" -#include "../cWorld.h" +#include "BlockHandler.h" +#include "../World.h" class cBlockRedstoneHandler : public cBlockHandler { diff --git a/source/blocks/BlockRedstoneRepeater.cpp b/source/blocks/BlockRedstoneRepeater.cpp index 4921cd6f7..861f6e221 100644 --- a/source/blocks/BlockRedstoneRepeater.cpp +++ b/source/blocks/BlockRedstoneRepeater.cpp @@ -1,9 +1,9 @@ #include "Globals.h" #include "BlockRedstoneRepeater.h" -#include "../cItem.h" -#include "../cWorld.h" -#include "../cRedstone.h" -#include "../cPlayer.h" +#include "../Item.h" +#include "../World.h" +#include "../Redstone.h" +#include "../Player.h" cBlockRedstoneRepeaterHandler::cBlockRedstoneRepeaterHandler(BLOCKTYPE a_BlockID) : cBlockHandler(a_BlockID) diff --git a/source/blocks/BlockRedstoneRepeater.h b/source/blocks/BlockRedstoneRepeater.h index c2ed30e2c..a928f554f 100644 --- a/source/blocks/BlockRedstoneRepeater.h +++ b/source/blocks/BlockRedstoneRepeater.h @@ -1,6 +1,6 @@ #pragma once -#include "Block.h" -#include "../cWorld.h" +#include "BlockHandler.h" +#include "../World.h" class cBlockRedstoneRepeaterHandler : public cBlockHandler { diff --git a/source/blocks/BlockRedstoneTorch.h b/source/blocks/BlockRedstoneTorch.h index 42d3a0366..eeb2afc5c 100644 --- a/source/blocks/BlockRedstoneTorch.h +++ b/source/blocks/BlockRedstoneTorch.h @@ -2,7 +2,7 @@ #pragma once #include "BlockRedstone.h" #include "BlockTorch.h" -#include "../cTorch.h" +#include "../Torch.h" diff --git a/source/blocks/BlockSand.h b/source/blocks/BlockSand.h index 71096c5a7..69fda5ec6 100644 --- a/source/blocks/BlockSand.h +++ b/source/blocks/BlockSand.h @@ -1,5 +1,5 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" class cBlockSandHandler : public cBlockHandler diff --git a/source/blocks/BlockSapling.h b/source/blocks/BlockSapling.h index e4ca998cb..c9862349a 100644 --- a/source/blocks/BlockSapling.h +++ b/source/blocks/BlockSapling.h @@ -1,6 +1,6 @@ #pragma once -#include "Block.h" -#include "../cWorld.h" +#include "BlockHandler.h" +#include "../World.h" class cBlockSaplingHandler : public cBlockHandler { diff --git a/source/blocks/BlockSign.h b/source/blocks/BlockSign.h index b0f9d7f7f..dd82605b3 100644 --- a/source/blocks/BlockSign.h +++ b/source/blocks/BlockSign.h @@ -1,8 +1,8 @@ #pragma once -#include "Block.h" -#include "../cWorld.h" -#include "../cSign.h" -#include "../cPlayer.h" +#include "BlockHandler.h" +#include "../World.h" +#include "../Sign.h" +#include "../Player.h" class cBlockSignHandler : public cBlockHandler { diff --git a/source/blocks/BlockSlab.h b/source/blocks/BlockSlab.h index 11d1933c3..ad9970d67 100644 --- a/source/blocks/BlockSlab.h +++ b/source/blocks/BlockSlab.h @@ -1,5 +1,5 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" class cBlockSlabHandler : public cBlockHandler diff --git a/source/blocks/BlockSnow.h b/source/blocks/BlockSnow.h index ba9d35183..c8e6b4c1c 100644 --- a/source/blocks/BlockSnow.h +++ b/source/blocks/BlockSnow.h @@ -1,5 +1,5 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" class cBlockSnowHandler : public cBlockHandler diff --git a/source/blocks/BlockStairs.h b/source/blocks/BlockStairs.h index 4e06a6e3f..4d0007bc5 100644 --- a/source/blocks/BlockStairs.h +++ b/source/blocks/BlockStairs.h @@ -1,6 +1,6 @@ #pragma once -#include "Block.h" -#include "../cStairs.h" +#include "BlockHandler.h" +#include "../Stairs.h" class cBlockStairsHandler : public cBlockHandler { diff --git a/source/blocks/BlockStems.h b/source/blocks/BlockStems.h index aa6229536..be4519a52 100644 --- a/source/blocks/BlockStems.h +++ b/source/blocks/BlockStems.h @@ -1,7 +1,7 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" #include "../MersenneTwister.h" -#include "../cWorld.h" +#include "../World.h" class cBlockStemsHandler : public cBlockHandler { diff --git a/source/blocks/BlockStone.h b/source/blocks/BlockStone.h index bd644a3a4..8b645bf1e 100644 --- a/source/blocks/BlockStone.h +++ b/source/blocks/BlockStone.h @@ -1,7 +1,7 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" #include "../MersenneTwister.h" -#include "../cWorld.h" +#include "../World.h" class cBlockStoneHandler : public cBlockHandler { diff --git a/source/blocks/BlockSugarcane.h b/source/blocks/BlockSugarcane.h index 4b0cb5408..1d477b42b 100644 --- a/source/blocks/BlockSugarcane.h +++ b/source/blocks/BlockSugarcane.h @@ -1,6 +1,6 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" diff --git a/source/blocks/BlockTallGrass.h b/source/blocks/BlockTallGrass.h index 6e7854db8..f5bb1b373 100644 --- a/source/blocks/BlockTallGrass.h +++ b/source/blocks/BlockTallGrass.h @@ -1,5 +1,5 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" class cBlockTallGrassHandler : public cBlockHandler diff --git a/source/blocks/BlockTorch.h b/source/blocks/BlockTorch.h index f3b172bf9..450b5ecab 100644 --- a/source/blocks/BlockTorch.h +++ b/source/blocks/BlockTorch.h @@ -1,8 +1,8 @@ #pragma once -#include "Block.h" -#include "../cTorch.h" -#include "../cWorld.h" +#include "BlockHandler.h" +#include "../Torch.h" +#include "../World.h" diff --git a/source/blocks/BlockVine.h b/source/blocks/BlockVine.h index 6b8d85339..56317219a 100644 --- a/source/blocks/BlockVine.h +++ b/source/blocks/BlockVine.h @@ -1,6 +1,6 @@ #pragma once -#include "Block.h" -#include "../cVine.h" +#include "BlockHandler.h" +#include "../Vine.h" class cBlockVineHandler : public cBlockHandler { diff --git a/source/blocks/BlockWood.h b/source/blocks/BlockWood.h index 5e6615c1b..d71d5d71c 100644 --- a/source/blocks/BlockWood.h +++ b/source/blocks/BlockWood.h @@ -1,5 +1,5 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" class cBlockWoodHandler : public cBlockHandler diff --git a/source/blocks/BlockWorkbench.h b/source/blocks/BlockWorkbench.h index 776628382..c1c004dc5 100644 --- a/source/blocks/BlockWorkbench.h +++ b/source/blocks/BlockWorkbench.h @@ -1,7 +1,7 @@ #pragma once -#include "Block.h" +#include "BlockHandler.h" #include "../UI/Window.h" -#include "../cPlayer.h" +#include "../Player.h" diff --git a/source/cAuthenticator.cpp b/source/cAuthenticator.cpp deleted file mode 100644 index 171c90598..000000000 --- a/source/cAuthenticator.cpp +++ /dev/null @@ -1,284 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cAuthenticator.h" -#include "OSSupport/BlockingTCPLink.h" -#include "cRoot.h" -#include "cServer.h" - -#include "../iniFile/iniFile.h" - -#include - - - - - -#define DEFAULT_AUTH_SERVER "session.minecraft.net" -#define DEFAULT_AUTH_ADDRESS "/game/checkserver.jsp?user=%USERNAME%&serverId=%SERVERID%" -#define MAX_REDIRECTS 10 - - - - - -cAuthenticator::cAuthenticator(void) : - super("cAuthenticator"), - m_Server(DEFAULT_AUTH_SERVER), - m_Address(DEFAULT_AUTH_ADDRESS), - m_ShouldAuthenticate(true) -{ - ReadINI(); -} - - - - - -cAuthenticator::~cAuthenticator() -{ - Stop(); -} - - - - - -/// Read custom values from INI -void cAuthenticator::ReadINI(void) -{ - cIniFile IniFile("settings.ini"); - if (!IniFile.ReadFile()) - { - return; - } - - m_Server = IniFile.GetValue("Authentication", "Server"); - m_Address = IniFile.GetValue("Authentication", "Address"); - m_ShouldAuthenticate = IniFile.GetValueB("Authentication", "Authenticate", true); - bool bSave = false; - - if (m_Server.length() == 0) - { - m_Server = DEFAULT_AUTH_SERVER; - IniFile.SetValue("Authentication", "Server", m_Server); - bSave = true; - } - if (m_Address.length() == 0) - { - m_Address = DEFAULT_AUTH_ADDRESS; - IniFile.SetValue("Authentication", "Address", m_Address); - bSave = true; - } - - if (bSave) - { - IniFile.SetValueB("Authentication", "Authenticate", m_ShouldAuthenticate); - IniFile.WriteFile(); - } -} - - - - - -/// Queues a request for authenticating a user. If the auth fails, the user is kicked -void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash) -{ - if (!m_ShouldAuthenticate) - { - cRoot::Get()->AuthenticateUser(a_ClientID); - return; - } - - cCSLock Lock(m_CS); - m_Queue.push_back(cUser(a_ClientID, a_UserName, a_ServerHash)); - m_QueueNonempty.Set(); -} - - - - - -void cAuthenticator::Stop(void) -{ - m_ShouldTerminate = true; - m_QueueNonempty.Set(); - Wait(); -} - - - - - -void cAuthenticator::Execute(void) -{ - for (;;) - { - cCSLock Lock(m_CS); - while (!m_ShouldTerminate && (m_Queue.size() == 0)) - { - cCSUnlock Unlock(Lock); - m_QueueNonempty.Wait(); - } - if (m_ShouldTerminate) - { - return; - } - ASSERT(!m_Queue.empty()); - - int ClientID = m_Queue.front().m_ClientID; - AString UserName = m_Queue.front().m_Name; - AString ActualAddress = m_Address; - ReplaceString(ActualAddress, "%USERNAME%", UserName); - ReplaceString(ActualAddress, "%SERVERID%", m_Queue.front().m_ServerID); - m_Queue.pop_front(); - Lock.Unlock(); - - if (!AuthFromAddress(m_Server, ActualAddress, UserName)) - { - cRoot::Get()->KickUser(ClientID, "Failed to authenticate account!"); - } - else - { - cRoot::Get()->AuthenticateUser(ClientID); - } - } // for (-ever) -} - - - - - -bool cAuthenticator::AuthFromAddress(const AString & a_Server, const AString & a_Address, const AString & a_UserName, int a_Level /* = 1 */) -{ - // Returns true if the user authenticated okay, false on error; iLevel is the recursion deptht (bails out if too deep) - - cBlockingTCPLink Link; - if (!Link.Connect(a_Server.c_str(), 80)) - { - LOGERROR("cAuthenticator: cannot connect to auth server \"%s\", kicking user \"%s\"", a_Server.c_str(), a_Server.c_str()); - return false; - } - - Link.SendMessage( AString( "GET " + a_Address + " HTTP/1.1\r\n" ).c_str()); - Link.SendMessage( AString( "User-Agent: MCServer\r\n" ).c_str()); - Link.SendMessage( AString( "Host: " + a_Server + "\r\n" ).c_str()); - //Link.SendMessage( AString( "Host: session.minecraft.net\r\n" ).c_str()); - Link.SendMessage( AString( "Accept: */*\r\n" ).c_str()); - Link.SendMessage( AString( "Connection: close\r\n" ).c_str()); //Close so we donīt have to mess with the Content-Length :) - Link.SendMessage( AString( "\r\n" ).c_str()); - AString DataRecvd; - Link.ReceiveData(DataRecvd); - Link.CloseSocket(); - - std::stringstream ss(DataRecvd); - - // Parse the data received: - std::string temp; - ss >> temp; - bool bRedirect = false; - bool bOK = false; - if ((temp.compare("HTTP/1.1") == 0) || (temp.compare("HTTP/1.0") == 0)) - { - int code; - ss >> code; - if (code == 302) - { - // redirect blabla - LOGINFO("Need to redirect!"); - if (a_Level > MAX_REDIRECTS) - { - LOGERROR("cAuthenticator: received too many levels of redirection from auth server \"%s\" for user \"%s\", bailing out and kicking the user", a_Server.c_str(), a_UserName.c_str()); - return false; - } - bRedirect = true; - } - else if (code == 200) - { - LOGINFO("Got 200 OK :D"); - bOK = true; - } - } - else - { - LOGERROR("cAuthenticator: cannot parse auth reply from server \"%s\" for user \"%s\", kicking the user.", a_Server.c_str(), a_UserName.c_str()); - return false; - } - - if( bRedirect ) - { - AString Location; - // Search for "Location:" - bool bFoundLocation = false; - while( !bFoundLocation && ss.good() ) - { - char c = 0; - while( c != '\n' ) - { - ss.get( c ); - } - AString Name; - ss >> Name; - if (Name.compare("Location:") == 0) - { - bFoundLocation = true; - ss >> Location; - } - } - if (!bFoundLocation) - { - LOGERROR("cAuthenticator: received invalid redirection from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str()); - return false; - } - - Location = Location.substr(strlen("http://"), std::string::npos); // Strip http:// - std::string Server = Location.substr( 0, Location.find( "/" ) ); // Only leave server address - Location = Location.substr( Server.length(), std::string::npos); - return AuthFromAddress(Server, Location, a_UserName, a_Level + 1); - } - - if (!bOK) - { - LOGERROR("cAuthenticator: received an error from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str()); - return false; - } - - // Header says OK, so receive the rest. - // Go past header, double \n means end of headers - char c = 0; - while (ss.good()) - { - while (c != '\n') - { - ss.get(c); - } - ss.get(c); - if( c == '\n' || c == '\r' || ss.peek() == '\r' || ss.peek() == '\n' ) - break; - } - if (!ss.good()) - { - LOGERROR("cAuthenticator: error while parsing response body from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str()); - return false; - } - - std::string Result; - ss >> Result; - LOGINFO("Got result: %s", Result.c_str()); - //if (Result.compare("3") == 0) // FIXME: Quick and dirty hack to support auth - //Lapayo: Wtf 3? - if (Result.compare("YES") == 0) //Works well - { - LOGINFO("Result was \"YES\", so player is authenticated!"); - return true; - } - - - LOGINFO("Result was \"%s\", so player is NOT authenticated!", Result.c_str()); - return false; -} - - - - diff --git a/source/cAuthenticator.h b/source/cAuthenticator.h deleted file mode 100644 index c9e647329..000000000 --- a/source/cAuthenticator.h +++ /dev/null @@ -1,90 +0,0 @@ - -// cAuthenticator.h - -// Interfaces to the cAuthenticator class representing the thread that authenticates users against the official MC server -// Authentication prevents "hackers" from joining with an arbitrary username (possibly impersonating the server admins) -// For more info, see http://wiki.vg/Session#Server_operation -// In MCS, authentication is implemented as a single thread that receives queued auth requests and dispatches them one by one. - - - - - -#pragma once -#ifndef CAUTHENTICATOR_H_INCLUDED -#define CAUTHENTICATOR_H_INCLUDED - -#include "OSSupport/IsThread.h" - - - - - -// fwd: "cRoot.h" -class cRoot; - - - - - -class cAuthenticator : - public cIsThread -{ - typedef cIsThread super; - -public: - cAuthenticator(void); - ~cAuthenticator(); - - /// (Re-)read server and address from INI: - void ReadINI(void); - - /// Queues a request for authenticating a user. If the auth fails, the user is kicked - void Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash); - - // Stops the authenticator thread - void Stop(void); - -private: - - class cUser - { - public: - int m_ClientID; - AString m_Name; - AString m_ServerID; - - cUser(int a_ClientID, const AString & a_Name, const AString & a_ServerID) : - m_ClientID(a_ClientID), - m_Name(a_Name), - m_ServerID(a_ServerID) - { - } - } ; - - typedef std::deque cUserList; - - cCriticalSection m_CS; - cUserList m_Queue; - cEvent m_QueueNonempty; - - AString m_Server; - AString m_Address; - bool m_ShouldAuthenticate; - - // cIsThread override: - virtual void Execute(void) override; - - // Returns true if the user authenticated okay, false on error; iLevel is the recursion deptht (bails out if too deep) - bool AuthFromAddress(const AString & a_Server, const AString & a_Address, const AString & a_UserName, int a_Level = 1); -}; - - - - - -#endif // CAUTHENTICATOR_H_INCLUDED - - - - diff --git a/source/cBlockEntity.h b/source/cBlockEntity.h deleted file mode 100644 index f5730d67e..000000000 --- a/source/cBlockEntity.h +++ /dev/null @@ -1,78 +0,0 @@ - -#pragma once - -#include "cClientHandle.h" -#include "cWorld.h" - - - - - -#ifndef _WIN32 -#include "BlockID.h" -#else -enum ENUM_BLOCK_ID; -#endif - - - - - -namespace Json -{ - class Value; -}; - -class cPlayer; -class cWorld; -class cPacket; - - - - - -class cBlockEntity -{ -protected: - cBlockEntity(ENUM_BLOCK_ID a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) - : m_PosX( a_BlockX ) - , m_PosY( a_BlockY ) - , m_PosZ( a_BlockZ ) - , m_BlockType( a_BlockType ) - , m_World( a_World ) - {} -public: - virtual ~cBlockEntity() {}; - virtual void Destroy() {}; - - // Position, in absolute block coordinates: - int GetPosX() { return m_PosX; } - int GetPosY() { return m_PosY; } - int GetPosZ() { return m_PosZ; } - - ENUM_BLOCK_ID GetBlockType() { return m_BlockType; } - - cWorld * GetWorld(void) const {return m_World; } - - virtual void SaveToJson (Json::Value & a_Value ) = 0; - - virtual void UsedBy( cPlayer * a_Player ) = 0; - - /** Sends the packet defining the block entity to the client specified. - To send to all eligible clients, use cWorld::BroadcastBlockEntity() - */ - virtual void SendTo(cClientHandle & a_Client) = 0; - -protected: - int m_PosX; // Position in absolute block coordinates - int m_PosY; - int m_PosZ; - - ENUM_BLOCK_ID m_BlockType; - - cWorld * m_World; -}; - - - - diff --git a/source/cChatColor.cpp b/source/cChatColor.cpp deleted file mode 100644 index 262130897..000000000 --- a/source/cChatColor.cpp +++ /dev/null @@ -1,39 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cChatColor.h" - -const std::string cChatColor::Color = "\xc2\xa7"; // or in other words: "§" in UTF-8 -const std::string cChatColor::Delimiter = "\xc2\xa7"; // or in other words: "§" in UTF-8 -const std::string cChatColor::Black = cChatColor::Color + "0"; -const std::string cChatColor::Navy = cChatColor::Color + "1"; -const std::string cChatColor::Green = cChatColor::Color + "2"; -const std::string cChatColor::Blue = cChatColor::Color + "3"; -const std::string cChatColor::Red = cChatColor::Color + "4"; -const std::string cChatColor::Purple = cChatColor::Color + "5"; -const std::string cChatColor::Gold = cChatColor::Color + "6"; -const std::string cChatColor::LightGray = cChatColor::Color + "7"; -const std::string cChatColor::Gray = cChatColor::Color + "8"; -const std::string cChatColor::DarkPurple = cChatColor::Color + "9"; -const std::string cChatColor::LightGreen = cChatColor::Color + "a"; -const std::string cChatColor::LightBlue = cChatColor::Color + "b"; -const std::string cChatColor::Rose = cChatColor::Color + "c"; -const std::string cChatColor::LightPurple = cChatColor::Color + "d"; -const std::string cChatColor::Yellow = cChatColor::Color + "e"; -const std::string cChatColor::White = cChatColor::Color + "f"; - -const std::string cChatColor::Random = cChatColor::Color + "k"; -const std::string cChatColor::Bold = cChatColor::Color + "l"; -const std::string cChatColor::Strikethrough = cChatColor::Color + "m"; -const std::string cChatColor::Underlined = cChatColor::Color + "n"; -const std::string cChatColor::Italic = cChatColor::Color + "o"; -const std::string cChatColor::Plain = cChatColor::Color + "r"; - -const std::string cChatColor::MakeColor( char a_Color ) -{ - return cChatColor::Color + a_Color; -} - - - - diff --git a/source/cChatColor.h b/source/cChatColor.h deleted file mode 100644 index 85b10f400..000000000 --- a/source/cChatColor.h +++ /dev/null @@ -1,43 +0,0 @@ - -#pragma once - - - - - -// tolua_begin -class cChatColor -{ -public: - static const std::string Color; - static const std::string Delimiter; - - static const std::string Black; - static const std::string Navy; - static const std::string Green; - static const std::string Blue; - static const std::string Red; - static const std::string Purple; - static const std::string Gold; - static const std::string LightGray; - static const std::string Gray; - static const std::string DarkPurple; - static const std::string LightGreen; - static const std::string LightBlue; - static const std::string Rose; - static const std::string LightPurple; - static const std::string Yellow; - static const std::string White; - - // Styles ( source: http://wiki.vg/Chat ) - static const std::string Random; - static const std::string Bold; - static const std::string Strikethrough; - static const std::string Underlined; - static const std::string Italic; - static const std::string Plain; - - static const std::string MakeColor( char a_Color ); -}; - -// tolua_end diff --git a/source/cChestEntity.cpp b/source/cChestEntity.cpp deleted file mode 100644 index 659bd7083..000000000 --- a/source/cChestEntity.cpp +++ /dev/null @@ -1,225 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cChestEntity.h" -#include "cItem.h" -#include "cClientHandle.h" -#include "cPlayer.h" -#include "UI/Window.h" -#include "cWorld.h" -#include "cRoot.h" -#include "cPickup.h" -#include - - - - - -class cWorld; -class cRoot; - - - - - -cChestEntity::cChestEntity(int a_X, int a_Y, int a_Z, cWorld * a_World) - : cBlockEntity( E_BLOCK_CHEST, a_X, a_Y, a_Z, a_World) - , m_TopChest( false ) - , m_JoinedChest( NULL ) -{ - m_Content = new cItem[ c_ChestHeight * c_ChestWidth ]; - SetBlockEntity(this); // cBlockEntityWindowOwner -} - - - - - -cChestEntity::~cChestEntity() -{ - if( GetWindow() ) - { - GetWindow()->OwnerDestroyed(); - } - - if( m_Content ) - { - delete [] m_Content; - } -} - - - - - -void cChestEntity::Destroy() -{ - // Drop items - cItems Pickups; - for( int i = 0; i < c_ChestHeight * c_ChestWidth; ++i ) - { - if( !m_Content[i].IsEmpty() ) - { - Pickups.push_back(m_Content[i]); - m_Content[i].Empty(); - } - } - m_World->SpawnItemPickups(Pickups, m_PosX, m_PosY, m_PosZ); - if (m_JoinedChest) - { - m_JoinedChest->RemoveJoinedChest(this); - } -} - - - - - -const cItem * cChestEntity::GetSlot( int a_Slot ) const -{ - if( a_Slot > -1 && a_Slot < c_ChestHeight*c_ChestWidth ) - { - return &m_Content[ a_Slot ]; - } - return 0; -} - - - - - -void cChestEntity::SetSlot(int a_Slot, const cItem & a_Item) -{ - if ((a_Slot > -1) && (a_Slot < c_ChestHeight * c_ChestWidth)) - { - m_Content[a_Slot] = a_Item; - } -} - - - - - -#define READ(File, Var) \ - if (File.Read(&Var, sizeof(Var)) != sizeof(Var)) \ - { \ - LOGERROR("ERROR READING cChestEntity %s FROM FILE (line %d)", #Var, __LINE__); \ - return false; \ - } - - - - - - -bool cChestEntity::LoadFromJson( const Json::Value& a_Value ) -{ - m_PosX = a_Value.get("x", 0).asInt(); - m_PosY = a_Value.get("y", 0).asInt(); - m_PosZ = a_Value.get("z", 0).asInt(); - - Json::Value AllSlots = a_Value.get("Slots", 0); - int SlotIdx = 0; - for( Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr ) - { - cItem Item; - Item.FromJson( *itr ); - SetSlot( SlotIdx, Item ); - SlotIdx++; - } - return true; -} - - - - - -void cChestEntity::SaveToJson( Json::Value& a_Value ) -{ - a_Value["x"] = m_PosX; - a_Value["y"] = m_PosY; - a_Value["z"] = m_PosZ; - - unsigned int NumSlots = c_ChestHeight*c_ChestWidth; - Json::Value AllSlots; - for(unsigned int i = 0; i < NumSlots; i++) - { - Json::Value Slot; - const cItem * Item = GetSlot( i ); - if( Item ) Item->GetJson( Slot ); - AllSlots.append( Slot ); - } - a_Value["Slots"] = AllSlots; -} - - - - - -void cChestEntity::SendTo(cClientHandle & a_Client) -{ - // The chest entity doesn't need anything sent to the client when it's created / gets in the viewdistance - // All the actual handling is in the cWindow UI code that gets called when the chest is rclked - - UNUSED(a_Client); -} - - - - - -void cChestEntity::UsedBy(cPlayer * a_Player) -{ - if (GetWindow() == NULL) - { - OpenWindow(new cChestWindow(m_PosX, m_PosY, m_PosZ, this)); - } - if (GetWindow()) - { - if( a_Player->GetWindow() != GetWindow() ) - { - a_Player->OpenWindow( GetWindow() ); - GetWindow()->SendWholeWindow(*a_Player->GetClientHandle()); - } - } - - // This is rather a hack - // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now - // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first. - // The few false positives aren't much to worry about - int ChunkX, ChunkY = 0, ChunkZ; - cChunkDef::BlockToChunk(m_PosX, m_PosY, m_PosZ, ChunkX, ChunkZ); - m_World->MarkChunkDirty(ChunkX, ChunkY, ChunkZ); -} - - - - - -cItem * cChestEntity::GetContents(bool a_OnlyThis) -{ - if (m_JoinedChest && !a_OnlyThis) - { - // TODO: "Combined" memory leaks here - cItem * Combined = new cItem[GetChestHeight() * c_ChestWidth]; - cItem * first = (m_TopChest) ? GetContents(true) : m_JoinedChest->GetContents(true); - cItem * second = (!m_TopChest) ? GetContents(true) : m_JoinedChest->GetContents(true); - for (int i = 0; i < GetChestHeight() * c_ChestWidth; i++) - { - int index = i % (c_ChestHeight * c_ChestWidth); - if (i < c_ChestHeight * c_ChestWidth) - Combined[index] = first[index]; - else - Combined[index] = second[index]; - } - return Combined; - } - else - { - return m_Content; - } -} - - - - diff --git a/source/cChestEntity.h b/source/cChestEntity.h deleted file mode 100644 index 2f6301479..000000000 --- a/source/cChestEntity.h +++ /dev/null @@ -1,65 +0,0 @@ - -#pragma once - -#include "cBlockEntity.h" -#include "UI/WindowOwner.h" - - - - - -namespace Json -{ - class Value; -}; - -class cClientHandle; -class cServer; -class cItem; -class cNBTData; - - - - - -class cChestEntity : // tolua_export - public cBlockEntity, // tolua_export - public cBlockEntityWindowOwner // tolua_export -{ // tolua_export -public: - cChestEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); - virtual ~cChestEntity(); - virtual void Destroy(); - - void HandleData( cNBTData* a_NBTData ); - - const cItem * GetSlot( int a_Slot ) const; //tolua_export - void SetSlot(int a_Slot, const cItem & a_Item ); //tolua_export - - bool LoadFromJson( const Json::Value& a_Value ); - virtual void SaveToJson(Json::Value& a_Value ) override; - - virtual void SendTo(cClientHandle & a_Client) override; - - virtual void UsedBy( cPlayer * a_Player ); //tolua_export - - cChestEntity * GetJoinedChest() { return m_JoinedChest; } // NOTE: Is this a safe function? Should it be exported to Lua? - void SetJoinedChest(cChestEntity *a_Chest) { m_JoinedChest = a_Chest; } - void RemoveJoinedChest(cChestEntity *a_Chest) { if (m_JoinedChest && m_JoinedChest == a_Chest) { m_JoinedChest = NULL; m_TopChest = false; } } - - int GetChestHeight() { return ((m_JoinedChest) ? c_ChestHeight * 2 : c_ChestHeight); } //tolua_export - cItem * GetContents(bool a_OnlyThis = false); - - static const int c_ChestWidth = 9; - static const int c_ChestHeight = 3; - -private: - - cItem * m_Content; - bool m_TopChest; - cChestEntity * m_JoinedChest; -}; //tolua_export - - - - diff --git a/source/cChunk.cpp b/source/cChunk.cpp deleted file mode 100644 index d2d680794..000000000 --- a/source/cChunk.cpp +++ /dev/null @@ -1,1852 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#ifndef _WIN32 - #include -#endif - - -#include "cChunk.h" -#include "cWorld.h" -#include "cWaterSimulator.h" -#include "cLavaSimulator.h" -#include "cClientHandle.h" -#include "cServer.h" -#include "zlib.h" -#include "Defines.h" -#include "cChestEntity.h" -#include "cFurnaceEntity.h" -#include "cSignEntity.h" -#include "cNoteEntity.h" -#include "cTorch.h" -#include "cLadder.h" -#include "cPickup.h" -#include "cRedstone.h" -#include "cItem.h" -#include "cNoise.h" -#include "cRoot.h" -#include "MersenneTwister.h" -#include "cPlayer.h" -#include "BlockArea.h" -#include "cPluginManager.h" -#include "blocks/Block.h" - -#include - - - - - - - - - -extern bool g_bWaterPhysics; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// sSetBlock: - -sSetBlock::sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) // absolute block position - : x( a_BlockX ) - , y( a_BlockY ) - , z( a_BlockZ ) - , BlockType( a_BlockType ) - , BlockMeta( a_BlockMeta ) -{ - cChunkDef::AbsoluteToRelative(x, y, z, ChunkX, ChunkZ); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cChunk: - -cChunk::cChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkMap * a_ChunkMap, cWorld * a_World) - : m_PosX( a_ChunkX ) - , m_PosY( a_ChunkY ) - , m_PosZ( a_ChunkZ ) - , m_BlockTickX( 0 ) - , m_BlockTickY( 0 ) - , m_BlockTickZ( 0 ) - , m_World( a_World ) - , m_ChunkMap(a_ChunkMap) - , m_IsValid(false) - , m_IsLightValid(false) - , m_IsDirty(false) - , m_IsSaving(false) - , m_StayCount(0) -{ - // LOGINFO("### new cChunk (%i, %i) at %p, thread 0x%x ###", a_X, a_Z, this, GetCurrentThreadId()); -} - - - - - -cChunk::~cChunk() -{ - // LOGINFO("### delete cChunk() (%i, %i) from %p, thread 0x%x ###", m_PosX, m_PosZ, this, GetCurrentThreadId() ); - - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - delete *itr; - } - m_BlockEntities.clear(); - - // Remove and destroy all entities that are not players: - cEntityList Entities; - for (cEntityList::const_iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) - { - if ((*itr)->GetEntityType() != cEntity::eEntityType_Player) - { - Entities.push_back(*itr); - } - } - for (cEntityList::iterator itr = Entities.begin(); itr != Entities.end(); ++itr) - { - (*itr)->RemoveFromChunk(); - (*itr)->Destroy(); - } - m_Entities.clear(); -} - - - - - -void cChunk::SetValid(void) -{ - m_IsValid = true; - - m_World->GetChunkMap()->ChunkValidated(); -} - - - - - -void cChunk::MarkRegenerating(void) -{ - // Tell all clients attached to this chunk that they want this chunk: - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) - { - (*itr)->AddWantedChunk(m_PosX, m_PosZ); - } // for itr - m_LoadedByClient[] -} - - - - - -bool cChunk::CanUnload(void) -{ - return m_LoadedByClient.empty() && !m_IsDirty && (m_StayCount == 0); -} - - - - - -void cChunk::MarkSaving(void) -{ - m_IsSaving = true; -} - - - - - -void cChunk::MarkSaved(void) -{ - if (!m_IsSaving) - { - return; - } - m_IsDirty = false; -} - - - - - -void cChunk::MarkLoaded(void) -{ - m_IsDirty = false; - SetValid(); -} - - - - - -void cChunk::MarkLoadFailed(void) -{ - if (m_IsValid) - { - return; - } - - m_HasLoadFailed = true; -} - - - - - -void cChunk::GetAllData(cChunkDataCallback & a_Callback) -{ - a_Callback.HeightMap (&m_HeightMap); - a_Callback.BiomeData (&m_BiomeMap); - a_Callback.BlockTypes (m_BlockTypes); - a_Callback.BlockMeta (m_BlockMeta); - a_Callback.LightIsValid (m_IsLightValid); - a_Callback.BlockLight (m_BlockLight); - a_Callback.BlockSkyLight(m_BlockSkyLight); - - for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) - { - a_Callback.Entity(*itr); - } - - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - a_Callback.BlockEntity(*itr); - } -} - - - - - -void cChunk::SetAllData( - const BLOCKTYPE * a_BlockTypes, - const NIBBLETYPE * a_BlockMeta, - const NIBBLETYPE * a_BlockLight, - const NIBBLETYPE * a_BlockSkyLight, - const HeightMap * a_HeightMap, - const BiomeMap & a_BiomeMap, - cEntityList & a_Entities, - cBlockEntityList & a_BlockEntities -) -{ - memcpy(m_BiomeMap, a_BiomeMap, sizeof(m_BiomeMap)); - - if (a_HeightMap != NULL) - { - memcpy(m_HeightMap, a_HeightMap, sizeof(m_HeightMap)); - } - - memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes)); - memcpy(m_BlockMeta, a_BlockMeta, sizeof(m_BlockMeta)); - if (a_BlockLight != NULL) - { - memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight)); - } - if (a_BlockSkyLight != NULL) - { - memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight)); - } - - m_IsLightValid = (a_BlockLight != NULL) && (a_BlockSkyLight != NULL); - - if (a_HeightMap == NULL) - { - CalculateHeightmap(); - } - - // Append entities to current entity list: - m_Entities.splice(m_Entities.end(), a_Entities); - - // Clear the block entities present - either the loader / saver has better, or we'll create empty ones: - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - delete *itr; - } - std::swap(a_BlockEntities, m_BlockEntities); - - // Create block entities that the loader didn't load; fill them with defaults - CreateBlockEntities(); - - m_HasLoadFailed = false; -} - - - - - -void cChunk::SetLight( - const cChunkDef::BlockNibbles & a_BlockLight, - const cChunkDef::BlockNibbles & a_SkyLight -) -{ - // TODO: We might get cases of wrong lighting when a chunk changes in the middle of a lighting calculation. - // Postponing until we see how bad it is :) - memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight)); - memcpy(m_BlockSkyLight, a_SkyLight, sizeof(m_BlockSkyLight)); - m_IsLightValid = true; -} - - - - - -void cChunk::GetBlockTypes(BLOCKTYPE * a_BlockTypes) -{ - memcpy(a_BlockTypes, m_BlockTypes, NumBlocks); -} - - - - - -void cChunk::GetBlockData(BLOCKTYPE * a_BlockData) -{ - memcpy(a_BlockData, m_BlockTypes, NumBlocks); - memcpy(a_BlockData + MetaOffset, m_BlockMeta, NumBlocks / 2); - memcpy(a_BlockData + LightOffset, m_BlockLight, NumBlocks / 2); - memcpy(a_BlockData + SkyLightOffset, m_BlockSkyLight, NumBlocks / 2); -} - - - - - -/// Returns true if there is a block entity at the coords specified -bool cChunk::HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - if ( - ((*itr)->GetPosX() == a_BlockX) && - ((*itr)->GetPosY() == a_BlockY) && - ((*itr)->GetPosZ() == a_BlockZ) - ) - { - return true; - } - } // for itr - m_BlockEntities[] - return false; -} - - - - - -/// Sets or resets the internal flag that prevents chunk from being unloaded -void cChunk::Stay(bool a_Stay) -{ - m_StayCount += (a_Stay ? 1 : -1); - ASSERT(m_StayCount >= 0); -} - - - - - -void cChunk::Tick(float a_Dt, MTRand & a_TickRandom) -{ - BroadcastPendingBlockChanges(); - - // Unload the chunk from all clients that have queued unloading: - for (cClientHandleList::iterator itr = m_UnloadQuery.begin(), end = m_UnloadQuery.end(); itr != end; ++itr) - { - (*itr)->SendUnloadChunk(m_PosX, m_PosZ); - } - m_UnloadQuery.clear(); - - CheckBlocks(); - - TickBlocks(a_TickRandom); - - // Tick block entities (furnaces) - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - if ((*itr)->GetBlockType() == E_BLOCK_FURNACE) - { - m_IsDirty = ((cFurnaceEntity *)(*itr))->Tick( a_Dt ) | m_IsDirty; - } - } -} - - - - - -void cChunk::BroadcastPendingBlockChanges(void) -{ - sSetBlockVector Changes; - { - cCSLock Lock(m_CSBlockLists); - if (m_PendingSendBlocks.empty()) - { - return; - } - Changes.reserve(m_PendingSendBlocks.size()); - for (std::vector::iterator itr = m_PendingSendBlocks.begin(), end = m_PendingSendBlocks.end(); itr != end; ++itr) - { - unsigned int index = *itr; - Vector3i RelPos = IndexToCoordinate(index); - Changes.push_back(sSetBlock(m_PosX, m_PosZ, RelPos.x, RelPos.y, RelPos.z, GetBlock(index), GetMeta(index))); - } // for itr - m_PendingSendBlocks[] - m_PendingSendBlocks.clear(); - } - - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) - { - (*itr)->SendBlockChanges(m_PosX, m_PosZ, Changes); - } -} - - - - - -void cChunk::CheckBlocks(void) -{ - cCSLock Lock2(m_CSBlockLists); - unsigned int NumTickBlocks = m_ToTickBlocks.size(); - Lock2.Unlock(); - - if (NumTickBlocks == 0) - { - return; - } - - Lock2.Lock(); - std::deque< unsigned int > ToTickBlocks = m_ToTickBlocks; - m_ToTickBlocks.clear(); - Lock2.Unlock(); - - for (std::deque< unsigned int >::const_iterator itr = ToTickBlocks.begin(); itr != ToTickBlocks.end(); ++itr) - { - unsigned int index = (*itr); - Vector3i BlockPos = IndexToCoordinate(index); - Vector3i WorldPos = PositionToWorldPosition( BlockPos ); - - cBlockHandler * Handler = BlockHandler(GetBlock(index)); - if(!Handler->CanBeAt(m_World, WorldPos.x, WorldPos.y, WorldPos.z)) - { - if(Handler->DropOnUnsuitable()) - { - Handler->DropBlock(m_World, WorldPos.x, WorldPos.y, WorldPos.z); - } - - m_World->SetBlock(WorldPos.x, WorldPos.y, WorldPos.z, E_BLOCK_AIR, 0); - } - } // for itr - ToTickBlocks[] -} - - - - - -void cChunk::TickBlocks(MTRand & a_TickRandom) -{ - // Tick dem blocks - // _X: We must limit the random number or else we get a nasty int overflow bug ( http://forum.mc-server.org/showthread.php?tid=457 ) - int RandomX = a_TickRandom.randInt(0x00ffffff); - int RandomY = a_TickRandom.randInt(0x00ffffff); - int RandomZ = a_TickRandom.randInt(0x00ffffff); - int TickX = m_BlockTickX; - int TickY = m_BlockTickY; - int TickZ = m_BlockTickZ; - - // This for loop looks disgusting, but it actually does a simple thing - first processes m_BlockTick, then adds random to it - // This is so that SetNextBlockTick() works - for (int i = 0; i < 50; i++, - - // This weird construct (*2, then /2) is needed, - // otherwise the blocktick distribution is too biased towards even coords! - - TickX = (TickX + RandomX) % (Width * 2), - TickY = (TickY + RandomY) % (Height * 2), - TickZ = (TickZ + RandomZ) % (Width * 2), - m_BlockTickX = TickX / 2, - m_BlockTickY = TickY / 2, - m_BlockTickZ = TickZ / 2 - ) - { - - if (m_BlockTickY > cChunkDef::GetHeight(m_HeightMap, m_BlockTickX, m_BlockTickZ)) - { - continue; // It's all air up here - } - - unsigned int Index = MakeIndexNoCheck( m_BlockTickX, m_BlockTickY, m_BlockTickZ ); - BLOCKTYPE ID = m_BlockTypes[Index]; - switch( ID ) - { - case E_BLOCK_CROPS: - { - NIBBLETYPE Meta = GetMeta(Index); - if (Meta < 7) - { - FastSetBlock(m_BlockTickX, m_BlockTickY, m_BlockTickZ, E_BLOCK_CROPS, ++Meta); - } - break; - } - - case E_BLOCK_PUMPKIN_STEM: - case E_BLOCK_MELON_STEM: TickMelonPumpkin(m_BlockTickX, m_BlockTickY, m_BlockTickZ, Index, ID, a_TickRandom); break; - case E_BLOCK_FARMLAND: TickFarmland (m_BlockTickX, m_BlockTickY, m_BlockTickZ); break; - case E_BLOCK_SUGARCANE: GrowSugarcane (m_BlockTickX, m_BlockTickY, m_BlockTickZ, 1); break; - case E_BLOCK_CACTUS: GrowCactus (m_BlockTickX, m_BlockTickY, m_BlockTickZ, 1); break; - - - default: - { - cBlockHandler * Handler = BlockHandler(ID); - ASSERT(Handler != NULL); // Happenned on server restart, FS #243 - if (Handler->NeedsRandomTicks()) - { - Handler->OnUpdate(m_World, m_BlockTickX + m_PosX * Width, m_BlockTickY, m_BlockTickZ + m_PosZ * Width); - } - break; - } - } - } -} - - - - - -void cChunk::TickMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, int a_BlockIdx, BLOCKTYPE a_BlockType, MTRand & a_TickRandom) -{ - NIBBLETYPE Meta = GetMeta(a_BlockIdx); - if (Meta < 7) - { - FastSetBlock(m_BlockTickX, m_BlockTickY, m_BlockTickZ, a_BlockType, ++Meta); - return; - } - GrowMelonPumpkin(a_RelX, a_RelY, a_RelZ, a_BlockType, a_TickRandom); -} - - - - - -void cChunk::TickFarmland(int a_RelX, int a_RelY, int a_RelZ) -{ - // TODO: Rain hydrates blocks, too. Check world weather, don't search for water if raining. - // NOTE: The desert biomes do not get precipitation, so another check needs to be made. - - // Search for water in a close proximity: - // Ref.: http://www.minecraftwiki.net/wiki/Farmland#Hydrated_Farmland_Tiles - bool Found = false; - for (int y = a_RelY; y <= a_RelY + 1; y++) - { - for (int z = a_RelZ - 4; z <= a_RelZ + 4; z++) - { - for (int x = a_RelX - 4; x <= a_RelX + 4; x++) - { - BLOCKTYPE BlockType; - NIBBLETYPE Meta; // unused - - if (!UnboundedRelGetBlock(x, y, z, BlockType, Meta)) - { - // Too close to an unloaded chunk, we might miss a water block there, so don't tick at all - return; - } - if ( - (BlockType == E_BLOCK_WATER) || - (BlockType == E_BLOCK_STATIONARY_WATER) - ) - { - Found = true; - break; - } - } // for x - if (Found) - { - break; - } - } // for z - if (Found) - { - break; - } - } // for y - - NIBBLETYPE BlockMeta = GetMeta(a_RelX, a_RelY, a_RelZ); - - if (Found) - { - // Water was found, hydrate the block until hydration reaches 7: - if (BlockMeta < 7) - { - FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_FARMLAND, ++BlockMeta); - } - return; - } - - // Water wasn't found, de-hydrate block: - if (BlockMeta > 0) - { - FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_FARMLAND, --BlockMeta); - return; - } - - // Farmland too dry. If nothing is growing on top, turn back to dirt: - - switch (GetBlock(a_RelX, a_RelY + 1, a_RelZ)) - { - case E_BLOCK_CROPS: - case E_BLOCK_MELON_STEM: - case E_BLOCK_PUMPKIN_STEM: - break; - default: - FastSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_DIRT, 0); - break; - } -} - - - - - - -void cChunk::GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_TickRandom) -{ - // Convert the stem BlockType into produce BlockType - BLOCKTYPE ProduceType; - switch (a_BlockType) - { - case E_BLOCK_MELON_STEM: ProduceType = E_BLOCK_MELON; break; - case E_BLOCK_PUMPKIN_STEM: ProduceType = E_BLOCK_PUMPKIN; break; - default: - { - ASSERT(!"Unhandled blocktype in TickMelonPumpkin()"); - return; - } - } - - // Check if there's another melon / pumpkin around that stem, if so, abort: - bool IsValid; - BLOCKTYPE BlockType[4]; - NIBBLETYPE BlockMeta; // unused - IsValid = UnboundedRelGetBlock(a_RelX + 1, a_RelY, a_RelZ, BlockType[0], BlockMeta); - IsValid = IsValid && UnboundedRelGetBlock(a_RelX - 1, a_RelY, a_RelZ, BlockType[1], BlockMeta); - IsValid = IsValid && UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ + 1, BlockType[2], BlockMeta); - IsValid = IsValid && UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ - 1, BlockType[3], BlockMeta); - if ( - !IsValid || - (BlockType[0] == ProduceType) || - (BlockType[1] == ProduceType) || - (BlockType[2] == ProduceType) || - (BlockType[3] == ProduceType) - ) - { - // Neighbors not valid or already taken by the same produce - return; - } - - // Pick a direction in which to place the produce: - int x = 0, z = 0; - int CheckType = a_TickRandom.randInt(3); // The index to the neighbors array which should be checked for emptiness - switch (CheckType) - { - case 0: x = 1; break; - case 1: x = -1; break; - case 2: z = 1; break; - case 3: z = -1; break; - } - - // Check that the block in that direction is empty: - switch (BlockType[CheckType]) - { - case E_BLOCK_AIR: - case E_BLOCK_SNOW: - case E_BLOCK_TALL_GRASS: - case E_BLOCK_DEAD_BUSH: - { - break; - } - default: return; - } - - // Check if there's soil under the neighbor. We already know the neighbors are valid. Place produce if ok - BLOCKTYPE Soil; - UnboundedRelGetBlock(a_RelX + x, a_RelY - 1, a_RelZ + z, Soil, BlockMeta); - switch (Soil) - { - case E_BLOCK_DIRT: - case E_BLOCK_GRASS: - case E_BLOCK_FARMLAND: - { - // Place a randomly-facing produce: - UnboundedRelFastSetBlock(a_RelX + x, a_RelY, a_RelZ + z, ProduceType, (NIBBLETYPE)(a_TickRandom.randInt(4) % 4)); - break; - } - } -} - - - - - -void cChunk::GrowSugarcane(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) -{ - // Check the total height of the sugarcane blocks here: - int Top = a_RelY + 1; - while ( - (Top < cChunkDef::Height) && - (GetBlock(a_RelX, Top, a_RelZ) == E_BLOCK_SUGARCANE) - ) - { - ++Top; - } - int Bottom = a_RelY - 1; - while ( - (Bottom > 0) && - (GetBlock(a_RelX, Bottom, a_RelZ) == E_BLOCK_SUGARCANE) - ) - { - --Bottom; - } - - // Grow by at most a_NumBlocks, but no more than max height: - int ToGrow = std::min(a_NumBlocks, m_World->GetMaxSugarcaneHeight() + 1 - (Top - Bottom)); - for (int i = 0; i < ToGrow; i++) - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if (UnboundedRelGetBlock(a_RelX, Top + i, a_RelZ, BlockType, BlockMeta) && (BlockType == E_BLOCK_AIR)) - { - UnboundedRelFastSetBlock(a_RelX, Top + i, a_RelZ, E_BLOCK_SUGARCANE, 0); - } - else - { - break; - } - } // for i -} - - - - - -void cChunk::GrowCactus(int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks) -{ - // Check the total height of the sugarcane blocks here: - int Top = a_RelY + 1; - while ( - (Top < cChunkDef::Height) && - (GetBlock(a_RelX, Top, a_RelZ) == E_BLOCK_CACTUS) - ) - { - ++Top; - } - int Bottom = a_RelY - 1; - while ( - (Bottom > 0) && - (GetBlock(a_RelX, Bottom, a_RelZ) == E_BLOCK_CACTUS) - ) - { - --Bottom; - } - - // Grow by at most a_NumBlocks, but no more than max height: - int ToGrow = std::min(a_NumBlocks, m_World->GetMaxCactusHeight() + 1 - (Top - Bottom)); - for (int i = 0; i < ToGrow; i++) - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if (UnboundedRelGetBlock(a_RelX, Top + i, a_RelZ, BlockType, BlockMeta) && (BlockType == E_BLOCK_AIR)) - { - // TODO: Check the surrounding blocks, if they aren't air, break the cactus block into pickups (and continue breaking blocks above in the next loop iterations) - UnboundedRelFastSetBlock(a_RelX, Top + i, a_RelZ, E_BLOCK_CACTUS, 0); - } - else - { - break; - } - } // for i -} - - - - - -bool cChunk::UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) -{ - if ((a_RelY < 0) || (a_RelY > cChunkDef::Height)) - { - return false; - } - - if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)) - { - int BlockIdx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); - a_BlockType = GetBlock(BlockIdx); - a_BlockMeta = GetMeta(BlockIdx); - return true; - } - return m_ChunkMap->LockedGetBlock( - m_PosX * cChunkDef::Width + a_RelX, - ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, - m_PosZ * cChunkDef::Width + a_RelZ, - a_BlockType, a_BlockMeta - ); -} - - - - - -bool cChunk::UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)) - { - SetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); - return true; - } - return m_ChunkMap->LockedSetBlock( - m_PosX * cChunkDef::Width + a_RelX, - ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, - m_PosZ * cChunkDef::Width + a_RelZ, - a_BlockType, a_BlockMeta - ); -} - - - - - -bool cChunk::UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - if ((a_RelX >= 0) && (a_RelX < cChunkDef::Width) && (a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)) - { - FastSetBlock(a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta); - return true; - } - return m_ChunkMap->LockedFastSetBlock( - m_PosX * cChunkDef::Width + a_RelX, - ZERO_CHUNK_Y * cChunkDef::Height + a_RelY, - m_PosZ * cChunkDef::Width + a_RelZ, - a_BlockType, a_BlockMeta - ); -} - - - - - -int cChunk::GetHeight( int a_X, int a_Z ) -{ - ASSERT((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width)); - - if ((a_X >= 0) && (a_X < Width) && (a_Z >= 0) && (a_Z < Width)) - { - return m_HeightMap[a_X + a_Z * Width]; - } - return 0; -} - - - - - -void cChunk::CreateBlockEntities(void) -{ - for (int x = 0; x < Width; x++) - { - for (int z = 0; z < Width; z++) - { - for (int y = 0; y < Height; y++) - { - ENUM_BLOCK_ID BlockType = (ENUM_BLOCK_ID)cChunkDef::GetBlock(m_BlockTypes, x, y, z); - switch ( BlockType ) - { - case E_BLOCK_CHEST: - { - if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) - { - m_BlockEntities.push_back( new cChestEntity( x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) ); - } - break; - } - - case E_BLOCK_FURNACE: - { - if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) - { - m_BlockEntities.push_back( new cFurnaceEntity( x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) ); - } - break; - } - - case E_BLOCK_SIGN_POST: - case E_BLOCK_WALLSIGN: - { - if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) - { - m_BlockEntities.push_back( new cSignEntity( BlockType, x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) ); - } - break; - } - - case E_BLOCK_NOTE_BLOCK: - { - if (!HasBlockEntityAt(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width)) - { - m_BlockEntities.push_back(new cNoteEntity(x + m_PosX * Width, y + m_PosY * Height, z + m_PosZ * Width, m_World) ); - } - break; - } - } // switch (BlockType) - } // for y - } // for z - } // for x -} - - - - - -void cChunk::CalculateHeightmap() -{ - for (int x = 0; x < Width; x++) - { - for (int z = 0; z < Width; z++) - { - for (int y = Height - 1; y > -1; y--) - { - int index = MakeIndex( x, y, z ); - if (m_BlockTypes[index] != E_BLOCK_AIR) - { - m_HeightMap[x + z * Width] = (unsigned char)y; - break; - } - } // for y - } // for z - } // for x -} - - - - - -void cChunk::SetBlock( int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) -{ - if (a_RelX < 0 || a_RelX >= Width || a_RelY < 0 || a_RelY >= Height || a_RelZ < 0 || a_RelZ >= Width) - { - return; // Clip - } - - ASSERT(IsValid()); // Is this chunk loaded / generated? - - int index = MakeIndexNoCheck( a_RelX, a_RelY, a_RelZ ); - BLOCKTYPE OldBlockMeta = GetNibble( m_BlockMeta, index ); - BLOCKTYPE OldBlockType = m_BlockTypes[index]; - m_BlockTypes[index] = a_BlockType; - - SetNibble( m_BlockMeta, index, a_BlockMeta ); - - if ((OldBlockType == a_BlockType) && (OldBlockMeta == a_BlockMeta)) - { - return; - } - - MarkDirty(); - - { - cCSLock Lock(m_CSBlockLists); - m_PendingSendBlocks.push_back( index ); - } - - // ONLY recalculate lighting if it's necessary! - if( - (g_BlockLightValue[ OldBlockType ] != g_BlockLightValue[ a_BlockType ]) || - (g_BlockSpreadLightFalloff[ OldBlockType ] != g_BlockSpreadLightFalloff[ a_BlockType ]) || - (g_BlockTransparent[ OldBlockType ] != g_BlockTransparent[ a_BlockType ] ) - ) - { - m_IsLightValid = false; - } - - // Update heightmap, if needed: - if (a_RelY >= m_HeightMap[a_RelX + a_RelZ * Width]) - { - if (a_BlockType != E_BLOCK_AIR) - { - SetHeight(m_HeightMap, a_RelX, a_RelZ, a_RelY); - } - else - { - for (int y = a_RelY - 1; y > 0; --y) - { - if (cChunkDef::GetBlock(m_BlockTypes, a_RelX, y, a_RelZ) != E_BLOCK_AIR) - { - SetHeight(m_HeightMap, a_RelX, a_RelZ, y); - break; - } - } // for y - column in m_BlockData - } - } - - m_ToTickBlocks.push_back( MakeIndex( a_RelX, a_RelY, a_RelZ ) ); - m_ToTickBlocks.push_back( MakeIndex( a_RelX + 1, a_RelY, a_RelZ ) ); - m_ToTickBlocks.push_back( MakeIndex( a_RelX - 1, a_RelY, a_RelZ ) ); - m_ToTickBlocks.push_back( MakeIndex( a_RelX, a_RelY + 1, a_RelZ ) ); - m_ToTickBlocks.push_back( MakeIndex( a_RelX, a_RelY - 1, a_RelZ ) ); - m_ToTickBlocks.push_back( MakeIndex( a_RelX, a_RelY, a_RelZ + 1 ) ); - m_ToTickBlocks.push_back( MakeIndex( a_RelX, a_RelY, a_RelZ - 1 ) ); - - Vector3i WorldPos = PositionToWorldPosition( a_RelX, a_RelY, a_RelZ ); - cBlockEntity* BlockEntity = GetBlockEntity( WorldPos ); - if( BlockEntity ) - { - BlockEntity->Destroy(); - RemoveBlockEntity( BlockEntity ); - delete BlockEntity; - } - switch( a_BlockType ) - { - case E_BLOCK_CHEST: - { - AddBlockEntity( new cChestEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World) ); - break; - } - case E_BLOCK_FURNACE: - { - AddBlockEntity( new cFurnaceEntity( WorldPos.x, WorldPos.y, WorldPos.z, m_World) ); - break; - } - case E_BLOCK_SIGN_POST: - case E_BLOCK_WALLSIGN: - { - AddBlockEntity( new cSignEntity( (ENUM_BLOCK_ID)a_BlockType, WorldPos.x, WorldPos.y, WorldPos.z, m_World) ); - break; - } - case E_BLOCK_NOTE_BLOCK: - { - AddBlockEntity(new cNoteEntity(WorldPos.x, WorldPos.y, WorldPos.z, m_World)); - break; - } - } // switch (a_BlockType) -} - - - - - -void cChunk::FastSetBlock( int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta) -{ - ASSERT(!((a_X < 0 || a_X >= Width || a_Y < 0 || a_Y >= Height || a_Z < 0 || a_Z >= Width))); - - ASSERT(IsValid()); - - const int index = MakeIndexNoCheck( a_X, a_Y, a_Z ); - const BLOCKTYPE OldBlock = m_BlockTypes[index]; - const BLOCKTYPE OldBlockMeta = GetNibble( m_BlockMeta, index ); - if ((OldBlock == a_BlockType) && (OldBlockMeta == a_BlockMeta)) - { - return; - } - - MarkDirty(); - - m_BlockTypes[index] = a_BlockType; - - { - cCSLock Lock(m_CSBlockLists); - m_PendingSendBlocks.push_back( index ); - } - - SetNibble( m_BlockMeta, index, a_BlockMeta ); - - // ONLY recalculate lighting if it's necessary! - if( - (g_BlockLightValue[ OldBlock ] != g_BlockLightValue[ a_BlockType ]) || - (g_BlockSpreadLightFalloff[ OldBlock ] != g_BlockSpreadLightFalloff[ a_BlockType ]) || - (g_BlockTransparent[ OldBlock ] != g_BlockTransparent[ a_BlockType ] ) - ) - { - m_IsLightValid = false; - } - - // Update heightmap, if needed: - if (a_Y >= m_HeightMap[a_X + a_Z * Width]) - { - if (a_BlockType != E_BLOCK_AIR) - { - m_HeightMap[a_X + a_Z * Width] = (unsigned char)a_Y; - } - else - { - for (int y = a_Y - 1; y > 0; --y) - { - if (m_BlockTypes[MakeIndexNoCheck(a_X, y, a_Z)] != E_BLOCK_AIR) - { - m_HeightMap[a_X + a_Z * Width] = (unsigned char)y; - break; - } - } // for y - column in m_BlockData - } - } -} - - - - - -void cChunk::SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client) -{ - unsigned int index = MakeIndex(a_RelX, a_RelY, a_RelZ); - if (index == INDEX_OUT_OF_RANGE) - { - LOGWARN("cChunk::SendBlockTo Index out of range!"); - return; - } - - if (a_Client == NULL) - { - // Queue the block for all clients in the chunk (will be sent in Tick()) - m_PendingSendBlocks.push_back(index); - return; - } - - Vector3i wp = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ); - a_Client->SendBlockChange(wp.x, wp.y, wp.z, GetBlock(index), GetMeta(index)); -} - - - - - -void cChunk::AddBlockEntity( cBlockEntity* a_BlockEntity ) -{ - cCSLock Lock(m_CSBlockLists); - m_BlockEntities.push_back( a_BlockEntity ); -} - - - - - -cBlockEntity * cChunk::GetBlockEntity(int a_X, int a_Y, int a_Z) -{ - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - if ( - ((*itr)->GetPosX() == a_X) && - ((*itr)->GetPosY() == a_Y) && - ((*itr)->GetPosZ() == a_Z) - ) - { - return *itr; - } - } // for itr - m_BlockEntities[] - - return NULL; -} - - - - - -void cChunk::UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z) -{ - cBlockEntity * be = GetBlockEntity(a_X, a_Y, a_Z); - if (be != NULL) - { - be->UsedBy(a_Player); - } -} - - - - - -void cChunk::CollectPickupsByPlayer(cPlayer * a_Player) -{ - double PosX = a_Player->GetPosX(); - double PosY = a_Player->GetPosY(); - double PosZ = a_Player->GetPosZ(); - - for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) - { - if ( (*itr)->GetEntityType() != cEntity::eEntityType_Pickup ) - { - continue; // Only pickups - } - float DiffX = (float)((*itr)->GetPosX() - PosX ); - float DiffY = (float)((*itr)->GetPosY() - PosY ); - float DiffZ = (float)((*itr)->GetPosZ() - PosZ ); - float SqrDist = DiffX * DiffX + DiffY * DiffY + DiffZ * DiffZ; - if (SqrDist < 1.5f * 1.5f) // 1.5 block - { - MarkDirty(); - (reinterpret_cast(*itr))->CollectedBy( a_Player ); - } - } -} - - - - - -void cChunk::UpdateSign(int a_PosX, int a_PosY, int a_PosZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) -{ - // Also sends update packets to all clients in the chunk - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - if ( - ((*itr)->GetPosX() == a_PosX) && - ((*itr)->GetPosY() == a_PosY) && - ((*itr)->GetPosZ() == a_PosZ) && - ( - ((*itr)->GetBlockType() == E_BLOCK_WALLSIGN) || - ((*itr)->GetBlockType() == E_BLOCK_SIGN_POST) - ) - ) - { - MarkDirty(); - (reinterpret_cast(*itr))->SetLines(a_Line1, a_Line2, a_Line3, a_Line4); - m_World->BroadcastBlockEntity(a_PosX, a_PosY, a_PosZ); - } - } // for itr - m_BlockEntities[] -} - - - - - -void cChunk::RemoveBlockEntity( cBlockEntity* a_BlockEntity ) -{ - cCSLock Lock(m_CSBlockLists); - MarkDirty(); - m_BlockEntities.remove( a_BlockEntity ); -} - - - - - -bool cChunk::AddClient(cClientHandle* a_Client) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) - { - if (a_Client == *itr) - { - // Already there, nothing needed - return false; - } - } - m_LoadedByClient.push_back( a_Client ); - - for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr ) - { - LOGD("cChunk: Entity #%d (%s) at [%i, %i, %i] spawning for player \"%s\"", (*itr)->GetUniqueID(), (*itr)->GetClass(), m_PosX, m_PosY, m_PosZ, a_Client->GetUsername().c_str()); - (*itr)->SpawnOn(*a_Client); - } - return true; -} - - - - - -void cChunk::RemoveClient( cClientHandle* a_Client ) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) - { - if (*itr != a_Client) - { - continue; - } - - m_LoadedByClient.erase(itr); - - if (!a_Client->IsDestroyed()) - { - for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr ) - { - LOGD("chunk [%i, %i] destroying entity #%i for player \"%s\"", m_PosX, m_PosZ, (*itr)->GetUniqueID(), a_Client->GetUsername().c_str() ); - a_Client->SendDestroyEntity(*(*itr)); - } - } - return; - } // for itr - m_LoadedByClient[] -} - - - - - -bool cChunk::HasClient( cClientHandle* a_Client ) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) - { - if ((*itr) == a_Client) - { - return true; - } - } - return false; -} - - - - - -bool cChunk::HasAnyClients(void) -{ - return !m_LoadedByClient.empty(); -} - - - - - -void cChunk::AddEntity( cEntity * a_Entity) -{ - if (a_Entity->GetEntityType() != cEntity::eEntityType_Player) - { - MarkDirty(); - } - m_Entities.push_back( a_Entity ); -} - - - - - -void cChunk::RemoveEntity(cEntity * a_Entity) -{ - size_t SizeBefore = m_Entities.size(); - m_Entities.remove(a_Entity); - size_t SizeAfter = m_Entities.size(); - - if (SizeBefore != SizeAfter) - { - // Mark as dirty if it was a server-generated entity: - if (a_Entity->GetEntityType() != cEntity::eEntityType_Player) - { - MarkDirty(); - } - } -} - - - - - -bool cChunk::ForEachEntity(cEntityCallback & a_Callback) -{ - // The entity list is locked by the parent chunkmap's CS - for (cEntityList::iterator itr = m_Entities.begin(), itr2 = itr; itr != m_Entities.end(); itr = itr2) - { - ++itr2; - if (a_Callback.Item(*itr)) - { - return false; - } - } // for itr - m_Entitites[] - return true; -} - - - - - -bool cChunk::ForEachChest(cChestCallback & a_Callback) -{ - // The blockentity list is locked by the parent chunkmap's CS - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) - { - ++itr2; - if ((*itr)->GetBlockType() != E_BLOCK_CHEST) - { - continue; - } - if (a_Callback.Item((cChestEntity *)*itr)) - { - return false; - } - } // for itr - m_BlockEntitites[] - return true; -} - - - - - -bool cChunk::ForEachFurnace(cFurnaceCallback & a_Callback) -{ - // The blockentity list is locked by the parent chunkmap's CS - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) - { - ++itr2; - switch ((*itr)->GetBlockType()) - { - case E_BLOCK_FURNACE: - case E_BLOCK_LIT_FURNACE: - { - break; - } - default: - { - continue; - } - } - if (a_Callback.Item((cFurnaceEntity *)*itr)) - { - return false; - } - } // for itr - m_BlockEntitites[] - return true; -} - - - - - -bool cChunk::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) -{ - // The blockentity list is locked by the parent chunkmap's CS - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) - { - ++itr2; - if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) - { - continue; - } - if ((*itr)->GetBlockType() != E_BLOCK_CHEST) - { - // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out - return false; - } - - // The correct block entity is here - if (a_Callback.Item((cChestEntity *)*itr)) - { - return false; - } - return true; - } // for itr - m_BlockEntitites[] - - // Not found: - return false; -} - - - - - -bool cChunk::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) -{ - // The blockentity list is locked by the parent chunkmap's CS - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) - { - ++itr2; - if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) - { - continue; - } - switch ((*itr)->GetBlockType()) - { - case E_BLOCK_FURNACE: - case E_BLOCK_LIT_FURNACE: - { - break; - } - default: - { - // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out - return false; - } - } // switch (BlockType) - - // The correct block entity is here, - if (a_Callback.Item((cFurnaceEntity *)*itr)) - { - return false; - } - return true; - } // for itr - m_BlockEntitites[] - - // Not found: - return false; -} - - - - - -bool cChunk::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) -{ - // The blockentity list is locked by the parent chunkmap's CS - for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) - { - if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) - { - continue; - } - switch ((*itr)->GetBlockType()) - { - case E_BLOCK_WALLSIGN: - case E_BLOCK_SIGN_POST: - { - a_Line1 = ((cSignEntity *)*itr)->GetLine(0); - a_Line2 = ((cSignEntity *)*itr)->GetLine(1); - a_Line3 = ((cSignEntity *)*itr)->GetLine(2); - a_Line4 = ((cSignEntity *)*itr)->GetLine(3); - return true; - } - } // switch (BlockType) - - // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out - return false; - } // for itr - m_BlockEntitites[] - - // Not found: - return false; -} - - - - - -BLOCKTYPE cChunk::GetBlock( int a_X, int a_Y, int a_Z ) -{ - if ((a_X < 0) || (a_X >= Width) || (a_Y < 0) || (a_Y >= Height) || (a_Z < 0) || (a_Z >= Width)) return 0; // Clip - - return m_BlockTypes[ MakeIndexNoCheck( a_X, a_Y, a_Z ) ]; -} - - - - - -BLOCKTYPE cChunk::GetBlock( int a_BlockIdx ) -{ - if( a_BlockIdx < 0 || a_BlockIdx >= NumBlocks ) return 0; - return m_BlockTypes[ a_BlockIdx ]; -} - - - - - -void cChunk::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) -{ - int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ); - a_BlockType = cChunkDef::GetBlock (m_BlockTypes, a_RelX, a_RelY, a_RelZ); - a_BlockMeta = cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); -} - - - - - -void cChunk::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendPlayerAnimation(a_Player, a_Animation); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastEntRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendEntRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastEntRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendEntRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastEntLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendEntLook(a_Entity); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastEntHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendEntHeadLook(a_Entity); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendDestroyEntity(a_Entity); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendEntityStatus(a_Entity, a_Status); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastMetadata(const cPawn & a_Pawn, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendMetadata(a_Pawn); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastSpawn(cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - a_Entity.SpawnOn(*(*itr)); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendCollectPickup(a_Pickup, a_Player); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastChunkData(cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) -{ - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - (*itr)->SendChunkData(m_PosX, m_PosZ, a_Serializer); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) -{ - // We can operate on entity pointers, we're inside the ChunkMap's CS lock which guards the list - cBlockEntity * Entity = GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); - if (Entity == NULL) - { - return; - } - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) - { - if (*itr == a_Exclude) - { - continue; - } - Entity->SendTo(*(*itr)); - } // for itr - LoadedByClient[] -} - - - - - -void cChunk::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client) -{ - cBlockEntity * Entity = GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); - if (Entity == NULL) - { - return; - } - Entity->SendTo(a_Client); -} - - - - - -void cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ, int & a_BlockX, int & a_BlockY, int & a_BlockZ) -{ - a_BlockY = a_RelY; - a_BlockX = m_PosX * Width + a_RelX; - a_BlockZ = m_PosZ * Width + a_RelZ; -} - - - - - -Vector3i cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ) -{ - return Vector3i(m_PosX * Width + a_RelX, m_PosY * Height + a_RelY, m_PosZ * Width + a_RelZ); -} - - - - - -#if !C_CHUNK_USE_INLINE -# include "cChunk.inl.h" -#endif - - - - diff --git a/source/cChunk.h b/source/cChunk.h deleted file mode 100644 index d094a3a64..000000000 --- a/source/cChunk.h +++ /dev/null @@ -1,321 +0,0 @@ - -#pragma once - -#include "cEntity.h" -#include "ChunkDef.h" - - - - - -#define C_CHUNK_USE_INLINE 1 - -// Do not touch -#if C_CHUNK_USE_INLINE - #define __C_CHUNK_INLINE__ inline -#else - #define __C_CHUNK_INLINE__ -#endif - - - - - -namespace Json -{ - class Value; -}; - - - - - -class cWorld; -class cFurnaceEntity; -class cClientHandle; -class cServer; -class MTRand; -class cPlayer; -class cChunkMap; -class cChestEntity; -class cFurnaceEntity; -class cBlockArea; -class cPawn; -class cPickup; -class cChunkDataSerializer; - -typedef std::list cClientHandleList; -typedef cItemCallback cEntityCallback; -typedef cItemCallback cChestCallback; -typedef cItemCallback cFurnaceCallback; - - - - -// This class is not to be used directly -// Instead, call actions on cChunkMap (such as cChunkMap::SetBlock() etc.) -class cChunk : - public cChunkDef // The inheritance is "misused" here only to inherit the functions and constants defined in cChunkDef -{ -public: - cChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkMap * a_ChunkMap, cWorld * a_World); - ~cChunk(); - - bool IsValid(void) const {return m_IsValid; } // Returns true if the chunk block data is valid (loaded / generated) - void SetValid(void); // Also wakes up any calls to cChunkMap::GetHeight() - void MarkRegenerating(void); // Marks all clients attached to this chunk as wanting this chunk - bool IsDirty(void) const {return m_IsDirty; } // Returns true if the chunk has changed since it was last saved - bool HasLoadFailed(void) const {return m_HasLoadFailed; } // Returns true if the chunk failed to load and hasn't been generated since then - bool CanUnload(void); - - bool IsLightValid(void) const {return m_IsLightValid; } - - /* - To save a chunk, the WSSchema must: - 1. Mark the chunk as being saved (MarkSaving() ) - 2. Get the chunk's data using GetAllData() - 3. Mark the chunk as saved (MarkSaved() ) - If anywhere inside this sequence another thread mmodifies the chunk, the chunk will not get marked as saved in MarkSaved() - */ - void MarkSaving(void); // Marks the chunk as being saved. - void MarkSaved(void); // Marks the chunk as saved, if it didn't change from the last call to MarkSaving() - void MarkLoaded(void); // Marks the chunk as freshly loaded. Fails if the chunk is already valid - void MarkLoadFailed(void); // Marks the chunk as failed to load. Ignored is the chunk is already valid - - /// Gets all chunk data, calls the a_Callback's methods for each data type - void GetAllData(cChunkDataCallback & a_Callback); - - /// Sets all chunk data - void SetAllData( - const BLOCKTYPE * a_BlockTypes, - const NIBBLETYPE * a_BlockMeta, - const NIBBLETYPE * a_BlockLight, - const NIBBLETYPE * a_BlockSkyLight, - const cChunkDef::HeightMap * a_HeightMap, - const cChunkDef::BiomeMap & a_BiomeMap, - cEntityList & a_Entities, - cBlockEntityList & a_BlockEntities - ); - - void SetLight( - const cChunkDef::BlockNibbles & a_BlockLight, - const cChunkDef::BlockNibbles & a_SkyLight - ); - - /// Copies m_BlockData into a_BlockTypes, only the block types - void GetBlockTypes(BLOCKTYPE * a_BlockTypes); - - /// Copies entire block data into a_BlockData, the entire 4 arrays (Type, Meta, Light, SkyLight) - void GetBlockData(BLOCKTYPE * a_BlockData); - - /// Returns true if there is a block entity at the coords specified - bool HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Sets or resets the internal flag that prevents chunk from being unloaded - void Stay(bool a_Stay = true); - - void Tick(float a_Dt, MTRand & a_TickRandom); - - int GetPosX() { return m_PosX; } - int GetPosY() { return m_PosY; } - int GetPosZ() { return m_PosZ; } - cWorld * GetWorld() { return m_World; } - - // OBSOLETE void SendTo( cClientHandle * a_Client ); - - void SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ); - // SetBlock() does a lot of work (heightmap, tickblocks, blockentities) so a BlockIdx version doesn't make sense - void SetBlock( const Vector3i & a_RelBlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) { SetBlock( a_RelBlockPos.x, a_RelBlockPos.y, a_RelBlockPos.z, a_BlockType, a_BlockMeta ); } - void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta ); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc. - BLOCKTYPE GetBlock( int a_X, int a_Y, int a_Z ); - BLOCKTYPE GetBlock( int a_BlockIdx ); - void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); - - EMCSBiome GetBiomeAt(int a_RelX, int a_RelZ) const {return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ); } - - void CollectPickupsByPlayer(cPlayer * a_Player); - void UpdateSign(int a_PosX, int a_PosY, int a_PosZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); // Also sends update packets to all clients in the chunk - - int GetHeight( int a_X, int a_Z ); - - void SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client); - - /// Adds a client to the chunk; returns true if added, false if already there - bool AddClient (cClientHandle* a_Client ); - - void RemoveClient (cClientHandle* a_Client ); - bool HasClient (cClientHandle* a_Client ); - bool HasAnyClients(void); // Returns true if theres any client in the chunk; false otherwise - - void AddEntity( cEntity * a_Entity); - void RemoveEntity( cEntity * a_Entity); - - /// Calls the callback for each entity; returns true if all entities processed, false if the callback aborted by returning true - bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible - - /// Calls the callback for each chest; returns true if all chests processed, false if the callback aborted by returning true - bool ForEachChest(cChestCallback & a_Callback); // Lua-accessible - - /// Calls the callback for each furnace; returns true if all furnaces processed, false if the callback aborted by returning true - bool ForEachFurnace(cFurnaceCallback & a_Callback); // Lua-accessible - - /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found - bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible - - /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords, true if found - bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Lua-accessible - - /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found - bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible - - void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); // [x, y, z] in world block coords - - void CalculateLighting(); // Recalculate right now - void CalculateHeightmap(); - - void BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); - void BroadcastEntRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); - void BroadcastEntRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); - void BroadcastEntLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntHeadLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL); - void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); - void BroadcastMetadata (const cPawn & a_Pawn, const cClientHandle * a_Exclude = NULL); - void BroadcastSpawn (cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); - void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); - void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); - void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8 - void BroadcastChunkData (cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); - - void SendBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); - - Vector3i PositionToWorldPosition(const Vector3i & a_RelPos) - { - return PositionToWorldPosition(a_RelPos.x, a_RelPos.y, a_RelPos.z); - } - - void PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ, int & a_BlockX, int & a_BlockY, int & a_BlockZ); - Vector3i PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ ); - - inline void MarkDirty(void) - { - m_IsDirty = true; - m_IsSaving = false; - } - - /// Sets the blockticking to start at the specified block. Only one blocktick may be set, second call overwrites the first call - inline void SetNextBlockTick(int a_RelX, int a_RelY, int a_RelZ) - { - m_BlockTickX = a_RelX; - m_BlockTickY = a_RelY; - m_BlockTickZ = a_RelZ; - } - - inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) {return cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); } - inline NIBBLETYPE GetMeta(int a_BlockIdx) {return cChunkDef::GetNibble(m_BlockMeta, a_BlockIdx); } - inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ, a_Meta); } - - inline NIBBLETYPE GetLight(int a_RelX, int a_RelY, int a_RelZ) {return cChunkDef::GetNibble(m_BlockLight, a_RelX, a_RelY, a_RelZ); } - inline NIBBLETYPE GetSkyLight(int a_RelX, int a_RelY, int a_RelZ) {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ); } - -private: - - friend class cChunkMap; - - bool m_IsValid; // True if the chunk is loaded / generated - bool m_IsLightValid; // True if the blocklight and skylight are calculated - bool m_IsDirty; // True if the chunk has changed since it was last saved - bool m_IsSaving; // True if the chunk is being saved - bool m_HasLoadFailed; // True if chunk failed to load and hasn't been generated yet since then - - cCriticalSection m_CSBlockLists; - std::deque< unsigned int > m_ToTickBlocks; - std::vector< unsigned int > m_PendingSendBlocks; - - // A critical section is not needed, because all chunk access is protected by its parent ChunkMap's csLayers - cClientHandleList m_LoadedByClient; - cClientHandleList m_UnloadQuery; - cEntityList m_Entities; - cBlockEntityList m_BlockEntities; - - /// Number of times the chunk has been requested to stay (by various cChunkStay objects); if zero, the chunk can be unloaded - int m_StayCount; - - int m_PosX, m_PosY, m_PosZ; - cWorld * m_World; - cChunkMap * m_ChunkMap; - - // TODO: Make these pointers and don't allocate what isn't needed - BLOCKTYPE m_BlockTypes [cChunkDef::NumBlocks]; - NIBBLETYPE m_BlockMeta [cChunkDef::NumBlocks / 2]; - NIBBLETYPE m_BlockLight [cChunkDef::NumBlocks / 2]; - NIBBLETYPE m_BlockSkyLight[cChunkDef::NumBlocks / 2]; - - cChunkDef::HeightMap m_HeightMap; - cChunkDef::BiomeMap m_BiomeMap; - - int m_BlockTickX, m_BlockTickY, m_BlockTickZ; - - void RemoveBlockEntity( cBlockEntity* a_BlockEntity ); - void AddBlockEntity( cBlockEntity* a_BlockEntity ); - cBlockEntity * GetBlockEntity( int a_X, int a_Y, int a_Z ); - cBlockEntity * GetBlockEntity( const Vector3i & a_BlockPos ) { return GetBlockEntity( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z ); } - - void SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff); - - void CreateBlockEntities(void); - - // Makes a copy of the list - cClientHandleList GetAllClients(void) const {return m_LoadedByClient; } - - /// Sends m_PendingSendBlocks to all clients - void BroadcastPendingBlockChanges(void); - - /// Checks the block scheduled for checking in m_ToTickBlocks[] - void CheckBlocks(void); - - void TickBlocks (MTRand & a_TickRandom); - void TickGrass (int a_RelX, int a_RelY, int a_RelZ, MTRand & a_TickRandom); - void TickMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, int a_BlockIdx, BLOCKTYPE a_BlockType, MTRand & a_TickRandom); - void TickFarmland (int a_RelX, int a_RelY, int a_RelZ); - - /// Grows sugarcane by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking) - void GrowSugarcane (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks); - - /// Grows cactus by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking) - void GrowCactus (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks); - - /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem) - void GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_Random); - - /// Checks if a leaves block at the specified coords has a log up to 4 blocks away connected by other leaves blocks (false if no log) - bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ); - - /// Same as GetBlock(), but relative coords needn't be in this chunk (uses m_ChunkMap in such a case); returns true on success; only usable in Tick() - bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); - - /// Same as SetBlock(), but relative coords needn't be in this chunk (uses m_ChunkMap in such a case); returns true on success; only usable in Tick() - bool UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - - /// Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_ChunkMap in such a case); returns true on success; only usable in Tick() - bool UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); -}; - -typedef cChunk * cChunkPtr; - -typedef std::list cChunkPtrList; - - - - - -#if C_CHUNK_USE_INLINE - #include "cChunk.inl.h" -#endif - - - - diff --git a/source/cChunk.inl.h b/source/cChunk.inl.h deleted file mode 100644 index fb9c4dad1..000000000 --- a/source/cChunk.inl.h +++ /dev/null @@ -1,34 +0,0 @@ - -#ifndef __C_CHUNK_INL_H__ -#define __C_CHUNK_INL_H__ - -#ifndef MAX -# define MAX(a,b) (((a)>(b))?(a):(b)) -#endif - - - - - -__C_CHUNK_INLINE__ -void cChunk::SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff) -{ - unsigned char CurrentLight = cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z ); - cChunkDef::SetNibble( a_LightBuffer, a_X-1, a_Y, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X-1, a_Y, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); - cChunkDef::SetNibble( a_LightBuffer, a_X+1, a_Y, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X+1, a_Y, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); - cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y-1, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y-1, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); - cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y+1, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y+1, a_Z ), MAX(0,CurrentLight-a_Falloff) ) ); - cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y, a_Z-1, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z-1 ), MAX(0,CurrentLight-a_Falloff) ) ); - cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y, a_Z+1, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z+1 ), MAX(0,CurrentLight-a_Falloff) ) ); - MarkDirty(); -} - - - - - -#endif - - - - diff --git a/source/cChunkMap.cpp b/source/cChunkMap.cpp deleted file mode 100644 index a53357c50..000000000 --- a/source/cChunkMap.cpp +++ /dev/null @@ -1,1874 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cChunkMap.h" -#include "cWorld.h" -#include "cRoot.h" -#include "cPlayer.h" -#include "BlockID.h" -#include "cItem.h" -#include "cPickup.h" -#include "cChunk.h" -#include "Generating/Trees.h" // used in cChunkMap::ReplaceTreeBlocks() for tree block discrimination - -#ifndef _WIN32 - #include // abs -#endif - -#include "zlib.h" -#include - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cChunkMap: - -cChunkMap::cChunkMap(cWorld * a_World ) - : m_World( a_World ) -{ -} - - - - - -cChunkMap::~cChunkMap() -{ - cCSLock Lock(m_CSLayers); - for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - delete *itr; - } // for itr - m_Layers[] -} - - - - - -void cChunkMap::RemoveLayer( cChunkLayer* a_Layer ) -{ - cCSLock Lock(m_CSLayers); - m_Layers.remove(a_Layer); -} - - - - - -cChunkMap::cChunkLayer * cChunkMap::GetLayer(int a_LayerX, int a_LayerZ) -{ - cCSLock Lock(m_CSLayers); - for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - if (((*itr)->GetX() == a_LayerX) && ((*itr)->GetZ() == a_LayerZ)) - { - return *itr; - } - } - - // Not found, create new: - cChunkLayer * Layer = new cChunkLayer(a_LayerX, a_LayerZ, this); - if (Layer == NULL) - { - LOGERROR("cChunkMap: Cannot create new layer, server out of memory?"); - return NULL; - } - m_Layers.push_back(Layer); - return Layer; -} - - - - - -cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk( int a_ChunkX, int a_ChunkZ ) -{ - const int LayerX = (int)(floorf((float)a_ChunkX / (float)(LAYER_SIZE))); - const int LayerZ = (int)(floorf((float)a_ChunkZ / (float)(LAYER_SIZE))); - return GetLayer( LayerX, LayerZ ); -} - - - - - -cChunkPtr cChunkMap::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) -{ - // No need to lock m_CSLayers, since it's already locked by the operation that called us - cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); - if (Layer == NULL) - { - // An error must have occurred, since layers are automatically created if they don't exist - return NULL; - } - - cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); - if (Chunk == NULL) - { - return NULL; - } - if (!(Chunk->IsValid())) - { - m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ, true); - } - return Chunk; -} - - - - - -cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) -{ - // No need to lock m_CSLayers, since it's already locked by the operation that called us - cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); - if (Layer == NULL) - { - // An error must have occurred, since layers are automatically created if they don't exist - return NULL; - } - - cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); - if (Chunk == NULL) - { - return NULL; - } - if (!(Chunk->IsValid())) - { - m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ, false); - } - - return Chunk; -} - - - - - -cChunkPtr cChunkMap::GetChunkNoLoad( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) -{ - // No need to lock m_CSLayers, since it's already locked by the operation that called us - cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); - if (Layer == NULL) - { - // An error must have occurred, since layers are automatically created if they don't exist - return NULL; - } - - return Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -bool cChunkMap::LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) -{ - // We already have m_CSLayers locked since this can be called only from within the tick thread - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk == NULL) - { - return false; - } - - int Index = cChunkDef::MakeIndexNoCheck(a_BlockX, a_BlockY, a_BlockZ); - a_BlockType = Chunk->GetBlock(Index); - a_BlockMeta = Chunk->GetMeta(Index); - return true; -} - - - - - -bool cChunkMap::LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - // We already have m_CSLayers locked since this can be called only from within the tick thread - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk == NULL) - { - return false; - } - - Chunk->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); - return true; -} - - - - - -bool cChunkMap::LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - // We already have m_CSLayers locked since this can be called only from within the tick thread - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk == NULL) - { - return false; - } - - Chunk->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); - return true; -} - - - - - -void cChunkMap::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Player.GetChunkX(), a_Player.GetChunkY(), a_Player.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastPlayerAnimation(a_Player, a_Animation, a_Exclude); -} - - - - - -void cChunkMap::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkY(), a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastEntityEquipment(a_Entity, a_SlotNum, a_Item, a_Exclude); -} - - - - - - -void cChunkMap::BroadcastEntRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkY(), a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastEntRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); -} - - - - - - -void cChunkMap::BroadcastEntRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkY(), a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastEntRelMove(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); -} - - - - - - -void cChunkMap::BroadcastEntLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkY(), a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastEntLook(a_Entity, a_Exclude); -} - - - - - - -void cChunkMap::BroadcastEntHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkY(), a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastEntHeadLook(a_Entity, a_Exclude); -} - - - - - - -void cChunkMap::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - int x, y, z, ChunkX, ChunkZ; - x = a_BlockX; - y = a_BlockY; - z = a_BlockZ; - cChunkDef::BlockToChunk(x, y, z, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType, a_Exclude); -} - - - - - - -void cChunkMap::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkY(), a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastDestroyEntity(a_Entity, a_Exclude); -} - - - - - - -void cChunkMap::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkY(), a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastEntityStatus(a_Entity, a_Status, a_Exclude); -} - - - - - - -void cChunkMap::BroadcastMetadata(const cPawn & a_Pawn, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Pawn.GetChunkX(), a_Pawn.GetChunkY(), a_Pawn.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastMetadata(a_Pawn, a_Exclude); -} - - - - - -void cChunkMap::BroadcastSpawn(cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkY(), a_Entity.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastSpawn(a_Entity, a_Exclude); -} - - - - - -void cChunkMap::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Pickup.GetChunkX(), a_Pickup.GetChunkY(), a_Pickup.GetChunkZ()); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastCollectPickup(a_Pickup, a_Player, a_Exclude); -} - - - - - -void cChunkMap::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); -} - - - - - -void cChunkMap::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ; - - cChunkDef::BlockToChunk(a_SrcX / 8, a_SrcY / 8, a_SrcZ / 8, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch, a_Exclude); -} - - - - - -void cChunkMap::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, 0, a_ChunkZ); - if (Chunk == NULL) - { - return; - } - // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastChunkData(a_Serializer, a_Exclude); -} - - - - - -void cChunkMap::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return; - } - Chunk->BroadcastBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); -} - - - - - -void cChunkMap::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client) -{ - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return; - } - Chunk->SendBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Client); -} - - - - - -void cChunkMap::UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) -{ - // a_Player rclked block entity at the coords specified, handle it - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return; - } - Chunk->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cChunkMap::MarkChunkDirty (int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return; - } - Chunk->MarkDirty(); -} - - - - - -void cChunkMap::MarkChunkSaving(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return; - } - Chunk->MarkSaving(); -} - - - - - -void cChunkMap::MarkChunkSaved (int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return; - } - Chunk->MarkSaved(); -} - - - - - -void cChunkMap::SetChunkData( - int a_ChunkX, int a_ChunkY, int a_ChunkZ, - const BLOCKTYPE * a_BlockTypes, - const NIBBLETYPE * a_BlockMeta, - const NIBBLETYPE * a_BlockLight, - const NIBBLETYPE * a_BlockSkyLight, - const cChunkDef::HeightMap * a_HeightMap, - const cChunkDef::BiomeMap & a_BiomeMap, - cEntityList & a_Entities, - cBlockEntityList & a_BlockEntities, - bool a_MarkDirty -) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, a_ChunkZ); - if (Chunk == NULL) - { - return; - } - Chunk->SetAllData(a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, a_HeightMap, a_BiomeMap, a_Entities, a_BlockEntities); - Chunk->SetValid(); - - if (a_MarkDirty) - { - Chunk->MarkDirty(); - } -} - - - - - -void cChunkMap::ChunkLighted( - int a_ChunkX, int a_ChunkZ, - const cChunkDef::BlockNibbles & a_BlockLight, - const cChunkDef::BlockNibbles & a_SkyLight -) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if (Chunk == NULL) - { - return; - } - Chunk->SetLight(a_BlockLight, a_SkyLight); - Chunk->MarkDirty(); -} - - - - - -bool cChunkMap::GetChunkData(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkDataCallback & a_Callback) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return false; - } - Chunk->GetAllData(a_Callback); - return true; -} - - - - - -bool cChunkMap::GetChunkBlockTypes(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockTypes) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return false; - } - Chunk->GetBlockTypes(a_BlockTypes); - return true; -} - - - - - -bool cChunkMap::GetChunkBlockData(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return false; - } - Chunk->GetBlockData(a_BlockData); - return true; -} - - - - - -bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, a_ChunkZ); - return (Chunk != NULL) && Chunk->IsValid(); -} - - - - - -bool cChunkMap::HasChunkAnyClients(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); - return (Chunk != NULL) && Chunk->HasAnyClients(); -} - - - - - -int cChunkMap::GetHeight(int a_BlockX, int a_BlockZ) -{ - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ, BlockY = 0; - cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk == NULL) - { - return 0; - } - - // Wait for the chunk to become valid: - while (!Chunk->IsValid()) - { - GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); // Re-queue (in case it managed to get unloaded before we caught it - cCSUnlock Unlock(Lock); - m_evtChunkValid.Wait(); - } - - return Chunk->GetHeight(a_BlockX, a_BlockZ); -} - - - - - -void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList) -{ - sSetBlockList Failed; - - // Process all items from a_BlockList, either successfully or by placing into Failed - while (!a_BlockList.empty()) - { - int ChunkX = a_BlockList.front().ChunkX; - int ChunkZ = a_BlockList.front().ChunkZ; - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk != NULL) && Chunk->IsValid()) - { - for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();) - { - if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) - { - Chunk->FastSetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); - itr = a_BlockList.erase(itr); - } - else - { - ++itr; - } - } // for itr - a_BlockList[] - } - else - { - // The chunk is not valid, move all blocks within this chunk to Failed - for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();) - { - if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) - { - Failed.push_back(*itr); - itr = a_BlockList.erase(itr); - } - else - { - ++itr; - } - } // for itr - a_BlockList[] - } - } - - // Return the failed: - std::swap(Failed, a_BlockList); -} - - - - - -void cChunkMap::CollectPickupsByPlayer(cPlayer * a_Player) -{ - int BlockX = (int)(a_Player->GetPosX()); // Truncating doesn't matter much; we're scanning entire chunks anyway - int BlockY = (int)(a_Player->GetPosY()); - int BlockZ = (int)(a_Player->GetPosZ()); - int ChunkX, ChunkZ, ChunkY = ZERO_CHUNK_Y; - cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); - int OtherChunkX = ChunkX + ((BlockX > 8) ? 1 : -1); - int OtherChunkZ = ChunkZ + ((BlockZ > 8) ? 1 : -1); - - // We suppose that each player keeps their chunks in memory, therefore it makes little sense to try to re-load or even generate them. - // The only time the chunks are not valid is when the player is downloading the initial world and they should not call this at that moment - - cCSLock Lock(m_CSLayers); - GetChunkNoLoad(ChunkX, ChunkY, ChunkZ)->CollectPickupsByPlayer(a_Player); - - // Check the neighboring chunks as well: - GetChunkNoLoad(OtherChunkX, ChunkY, ChunkZ )->CollectPickupsByPlayer(a_Player); - GetChunkNoLoad(OtherChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player); - GetChunkNoLoad(ChunkX, ChunkY, ChunkZ )->CollectPickupsByPlayer(a_Player); - GetChunkNoLoad(ChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player); -} - - - - - -BLOCKTYPE cChunkMap::GetBlock(int a_X, int a_Y, int a_Z) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkZ ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk != NULL) && Chunk->IsValid()) - { - return Chunk->GetBlock(a_X, a_Y, a_Z); - } - return 0; -} - - - - - -BLOCKTYPE cChunkMap::GetBlockMeta(int a_X, int a_Y, int a_Z) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkZ ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); - if ((Chunk != NULL) && Chunk->IsValid() ) - { - return Chunk->GetMeta(a_X, a_Y, a_Z); - } - return 0; -} - - - - - -BLOCKTYPE cChunkMap::GetBlockSkyLight(int a_X, int a_Y, int a_Z) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkZ ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); - if ((Chunk != NULL) && Chunk->IsValid() ) - { - return Chunk->GetSkyLight( a_X, a_Y, a_Z ); - } - return 0; -} - - - - - -void cChunkMap::SetBlockMeta(int a_X, int a_Y, int a_Z, NIBBLETYPE a_BlockMeta) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkZ ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); - if ((Chunk != NULL) && Chunk->IsValid() ) - { - Chunk->SetMeta(a_X, a_Y, a_Z, a_BlockMeta); - Chunk->MarkDirty(); - Chunk->SendBlockTo( a_X, a_Y, a_Z, NULL ); - } -} - - - - - -void cChunkMap::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta) -{ - int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; - cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); - if ((Chunk != NULL) && Chunk->IsValid()) - { - Chunk->SetBlock(X, Y, Z, a_BlockType, a_BlockMeta ); - } -} - - - - - -void cChunkMap::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) -{ - int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; - cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); - if ((Chunk != NULL) && Chunk->IsValid()) - { - Chunk->GetBlockTypeMeta(X, Y, Z, a_BlockType, a_BlockMeta); - } -} - - - - - -void cChunkMap::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType) -{ - cCSLock Lock(m_CSLayers); - for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) - { - cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - continue; - } - if (Chunk->GetBlock(itr->x, itr->y, itr->z) == a_FilterBlockType) - { - Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); - } - } -} - - - - - -void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks) -{ - cCSLock Lock(m_CSLayers); - for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) - { - cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - continue; - } - switch (Chunk->GetBlock(itr->x, itr->y, itr->z)) - { - CASE_TREE_OVERWRITTEN_BLOCKS: - { - Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); - break; - } - case E_BLOCK_LEAVES: - { - if (itr->BlockType == E_BLOCK_LOG) - { - Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); - } - break; - } - } - } // for itr - a_Blocks[] -} - - - - - -EMCSBiome cChunkMap::GetBiomeAt (int a_BlockX, int a_BlockZ) -{ - int ChunkX, ChunkZ, X = a_BlockX, Y = 0, Z = a_BlockZ; - cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); - if ((Chunk != NULL) && Chunk->IsValid()) - { - return Chunk->GetBiomeAt(X, Z); - } - else - { - return m_World->GetGenerator().GetBiomeAt(a_BlockX, a_BlockZ); - } -} - - - - - -bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) -{ - bool res = true; - cCSLock Lock(m_CSLayers); - for (sSetBlockVector::iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) - { - cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - if (!a_ContinueOnFailure) - { - return false; - } - res = false; - continue; - } - int idx = cChunkDef::MakeIndexNoCheck(itr->x, itr->y, itr->z); - itr->BlockType = Chunk->GetBlock(idx); - itr->BlockMeta = Chunk->GetMeta(idx); - } - return res; -} - - - - - -bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z) -{ - int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkZ; - - cChunkDef::AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkZ ); - - { - cCSLock Lock(m_CSLayers); - cChunkPtr DestChunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); - if ((DestChunk == NULL) || !DestChunk->IsValid()) - { - return false; - } - - DestChunk->SetBlock(PosX, PosY, PosZ, E_BLOCK_AIR, 0 ); - } - - m_World->GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z); - - return true; -} - - - - - -void cChunkMap::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_X, a_Y, a_Z, ChunkX, ChunkZ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk->IsValid()) - { - Chunk->SendBlockTo(a_X, a_Y, a_Z, a_Player->GetClientHandle()); - } -} - - - - - -void cChunkMap::CompareChunkClients(int a_ChunkX1, int a_ChunkY1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkY2, int a_ChunkZ2, cClientDiffCallback & a_Callback) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk1 = GetChunkNoGen(a_ChunkX1, a_ChunkY1, a_ChunkZ1); - if (Chunk1 == NULL) - { - return; - } - cChunkPtr Chunk2 = GetChunkNoGen(a_ChunkX2, a_ChunkY2, a_ChunkZ2); - if (Chunk2 == NULL) - { - return; - } - - cClientHandleList Clients1(Chunk1->GetAllClients()); - cClientHandleList Clients2(Chunk2->GetAllClients()); - - // Find "removed" clients: - for (cClientHandleList::iterator itr1 = Clients1.begin(); itr1 != Clients1.end(); ++itr1) - { - bool Found = false; - for (cClientHandleList::iterator itr2 = Clients2.begin(); itr2 != Clients2.end(); ++itr2) - { - if (*itr1 == *itr2) - { - Found = true; - break; - } - } // for itr2 - Clients2[] - if (!Found) - { - a_Callback.Removed(*itr1); - } - } // for itr1 - Clients1[] - - // Find "added" clients: - for (cClientHandleList::iterator itr2 = Clients2.begin(); itr2 != Clients2.end(); ++itr2) - { - bool Found = false; - for (cClientHandleList::iterator itr1 = Clients1.begin(); itr1 != Clients1.end(); ++itr1) - { - if (*itr1 == *itr2) - { - Found = true; - break; - } - } // for itr1 - Clients1[] - if (!Found) - { - a_Callback.Added(*itr2); - } - } // for itr2 - Clients2[] -} - - - - - -bool cChunkMap::AddChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); - if (Chunk == NULL) - { - return false; - } - return Chunk->AddClient(a_Client); -} - - - - - -void cChunkMap::RemoveChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); - if (Chunk == NULL) - { - return; - } - Chunk->RemoveClient(a_Client); -} - - - - - -void cChunkMap::RemoveClientFromChunks(cClientHandle * a_Client) -{ - cCSLock Lock(m_CSLayers); - - for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - (*itr)->RemoveClient(a_Client); - } // for itr - m_Layers[] -} - - - - - -void cChunkMap::MoveEntityToChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr OldChunk = GetChunkNoGen(a_Entity->GetChunkX(), a_Entity->GetChunkY(), a_Entity->GetChunkZ()); - if (OldChunk != NULL) - { - OldChunk->RemoveEntity(a_Entity); - } - cChunkPtr NewChunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); - if (NewChunk != NULL) - { - NewChunk->AddEntity(a_Entity); - } -} - - - - - -void cChunkMap::RemoveEntityFromChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return; - } - Chunk->RemoveEntity(a_Entity); -} - - - - - -bool cChunkMap::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->ForEachEntity(a_Callback); -} - - - - - -bool cChunkMap::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->ForEachChest(a_Callback); -} - - - - - -bool cChunkMap::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->ForEachFurnace(a_Callback); -} - - - - - -bool cChunkMap::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) -{ - int ChunkX, ChunkZ; - int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; - cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->DoWithChestAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); -} - - - - - -bool cChunkMap::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) -{ - int ChunkX, ChunkZ; - int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; - cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->DoWithFurnaceAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); -} - - - - - -bool cChunkMap::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) -{ - int ChunkX, ChunkZ; - int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; - cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk == NULL) && !Chunk->IsValid()) - { - return false; - } - return Chunk->GetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); -} - - - - - -void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -/// Loads the chunk synchronously, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) -bool cChunkMap::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - { - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); - if (Chunk == NULL) - { - // Internal error - return false; - } - if (Chunk->IsValid()) - { - // Already loaded - return true; - } - if (Chunk->HasLoadFailed()) - { - // Already tried loading and it failed - return false; - } - } - return m_World->GetStorage().LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -/// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() -void cChunkMap::LoadChunks(const cChunkCoordsList & a_Chunks) -{ - for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr) - { - LoadChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); - } // for itr - a_Chunks[] -} - - - - - -void cChunkMap::ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, a_ChunkZ); - if (Chunk == NULL) - { - return; - } - Chunk->MarkLoadFailed(); -} - - - - - -void cChunkMap::UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) -{ - cCSLock Lock(m_CSLayers); - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(a_X, a_Y, a_Z, ChunkX, ChunkZ); - cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if ((Chunk == NULL) || !Chunk->IsValid()) - { - return; - } - Chunk->UpdateSign(a_X, a_Y, a_Z, a_Line1, a_Line2, a_Line3, a_Line4); -} - - - - - -void cChunkMap::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay) -{ - cCSLock Lock(m_CSLayers); - for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr) - { - cChunkPtr Chunk = GetChunkNoLoad(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); - if (Chunk == NULL) - { - continue; - } - Chunk->Stay(a_Stay); - } -} - - - - - -void cChunkMap::MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if (Chunk == NULL) - { - // Not present - return; - } - Chunk->MarkRegenerating(); -} - - - - - -bool cChunkMap::IsChunkLighted(int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); - if (Chunk == NULL) - { - // Not present - return false; - } - return Chunk->IsLightValid(); -} - - - - - -bool cChunkMap::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback) -{ - bool Result = true; - cCSLock Lock(m_CSLayers); - for (int z = a_MinChunkZ; z <= a_MaxChunkZ; z++) - { - for (int x = a_MinChunkX; x <= a_MaxChunkX; x++) - { - cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z); - if ((Chunk == NULL) || (!Chunk->IsValid())) - { - // Not present / not valid - Result = false; - continue; - } - if (!a_Callback.Coords(x, z)) - { - continue; - } - Chunk->GetAllData(a_Callback); - } - } - return Result; -} - - - - - -void cChunkMap::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) -{ - a_NumChunksValid = 0; - a_NumChunksDirty = 0; - cCSLock Lock(m_CSLayers); - for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - int NumValid = 0, NumDirty = 0; - (*itr)->GetChunkStats(NumValid, NumDirty); - a_NumChunksValid += NumValid; - a_NumChunksDirty += NumDirty; - } // for itr - m_Layers[] -} - - - - - -void cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk != NULL) - { - Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Rand); - } -} - - - - - -void cChunkMap::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk != NULL) - { - Chunk->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); - } -} - - - - - -void cChunkMap::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk != NULL) - { - Chunk->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow); - } -} - - - - - -void cChunkMap::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - - cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); - if (Chunk != NULL) - { - Chunk->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ); - } -} - - - - - -void cChunkMap::Tick( float a_Dt, MTRand & a_TickRandom ) -{ - cCSLock Lock(m_CSLayers); - for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - (*itr)->Tick(a_Dt, a_TickRandom); - } // for itr - m_Layers -} - - - - - -void cChunkMap::UnloadUnusedChunks() -{ - cCSLock Lock(m_CSLayers); - for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - (*itr)->UnloadUnusedChunks(); - } // for itr - m_Layers -} - - - - - -void cChunkMap::SaveAllChunks(void) -{ - cCSLock Lock(m_CSLayers); - for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - (*itr)->Save(); - } // for itr - m_Layers[] -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cChunkMap::cChunkLayer: - -cChunkMap::cChunkLayer::cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent) - : m_LayerX( a_LayerX ) - , m_LayerZ( a_LayerZ ) - , m_Parent( a_Parent ) - , m_NumChunksLoaded( 0 ) -{ - memset(m_Chunks, 0, sizeof(m_Chunks)); -} - - - - - -cChunkMap::cChunkLayer::~cChunkLayer() -{ - for (int i = 0; i < ARRAYCOUNT(m_Chunks); ++i) - { - delete m_Chunks[i]; - } // for i - m_Chunks[] -} - - - - - -cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) -{ - // Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check - - const int LocalX = a_ChunkX - m_LayerX * LAYER_SIZE; - const int LocalZ = a_ChunkZ - m_LayerZ * LAYER_SIZE; - - if (!((LocalX < LAYER_SIZE) && (LocalZ < LAYER_SIZE) && (LocalX > -1) && (LocalZ > -1))) - { - ASSERT(!"Asking a cChunkLayer for a chunk that doesn't belong to it!"); - return NULL; - } - - int Index = LocalX + LocalZ * LAYER_SIZE; - if (m_Chunks[Index] == NULL) - { - m_Chunks[Index] = new cChunk(a_ChunkX, 0, a_ChunkZ, m_Parent, m_Parent->GetWorld()); - } - return m_Chunks[Index]; -} - - - - - -void cChunkMap::cChunkLayer::Tick(float a_Dt, MTRand & a_TickRand) -{ - for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) - { - // Only tick chunks that are valid and have clients: - if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) - { - m_Chunks[i]->Tick(a_Dt, a_TickRand); - } - } // for i - m_Chunks[] -} - - - - - -void cChunkMap::cChunkLayer::RemoveClient(cClientHandle * a_Client) -{ - for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) - { - if (m_Chunks[i] != NULL) - { - m_Chunks[i]->RemoveClient(a_Client); - } - } // for i - m_Chunks[] -} - - - - - -int cChunkMap::cChunkLayer::GetNumChunksLoaded(void) const -{ - int NumChunks = 0; - for ( int i = 0; i < ARRAYCOUNT(m_Chunks); ++i ) - { - if (m_Chunks[i] != NULL) - { - NumChunks++; - } - } // for i - m_Chunks[] - return NumChunks; -} - - - - - -void cChunkMap::cChunkLayer::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) const -{ - int NumValid = 0; - int NumDirty = 0; - for ( int i = 0; i < ARRAYCOUNT(m_Chunks); ++i ) - { - if (m_Chunks[i] == NULL) - { - continue; - } - NumValid++; - if (m_Chunks[i]->IsDirty()) - { - NumDirty++; - } - } // for i - m_Chunks[] - a_NumChunksValid = NumValid; - a_NumChunksDirty = NumDirty; -} - - - - - -void cChunkMap::cChunkLayer::Save(void) -{ - cWorld * World = m_Parent->GetWorld(); - for (int i = 0; i < ARRAYCOUNT(m_Chunks); ++i) - { - if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->IsDirty()) - { - World->GetStorage().QueueSaveChunk(m_Chunks[i]->GetPosX(), m_Chunks[i]->GetPosY(), m_Chunks[i]->GetPosZ()); - } - } // for i - m_Chunks[] -} - - - - - -void cChunkMap::cChunkLayer::UnloadUnusedChunks(void) -{ - for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) - { - if ((m_Chunks[i] != NULL) && (m_Chunks[i]->CanUnload())) - { - // The cChunk destructor calls our GetChunk() while removing its entities - // so we still need to be able to return the chunk. Therefore we first delete, then NULLify - // Doing otherwise results in bug http://forum.mc-server.org/showthread.php?tid=355 - delete m_Chunks[i]; - m_Chunks[i] = NULL; - } - } // for i - m_Chunks[] -} - - - - - -int cChunkMap::GetNumChunks(void) -{ - cCSLock Lock(m_CSLayers); - int NumChunks = 0; - for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) - { - NumChunks += (*itr)->GetNumChunksLoaded(); - } - return NumChunks; -} - - - - - -void cChunkMap::ChunkValidated(void) -{ - m_evtChunkValid.Set(); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cChunkStay: - -cChunkStay::cChunkStay(cWorld * a_World) : - m_World(a_World), - m_IsEnabled(false) -{ -} - - - - - -cChunkStay::~cChunkStay() -{ - Clear(); -} - - - - - -void cChunkStay::Clear(void) -{ - if (m_IsEnabled) - { - Disable(); - } - m_Chunks.clear(); -} - - - - - -void cChunkStay::Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - ASSERT(!m_IsEnabled); - - for (cChunkCoordsList::const_iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr) - { - if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ)) - { - // Already present - return; - } - } // for itr - Chunks[] - m_Chunks.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); -} - - - - - -void cChunkStay::Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - ASSERT(!m_IsEnabled); - - for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr) - { - if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ)) - { - // Found, un-"stay" - m_Chunks.erase(itr); - return; - } - } // for itr - m_Chunks[] -} - - - - - -void cChunkStay::Enable(void) -{ - ASSERT(!m_IsEnabled); - - m_World->ChunksStay(*this, true); - m_IsEnabled = true; -} - - - - - -void cChunkStay::Load(void) -{ - for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr) - { - m_World->TouchChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ); - } // for itr - m_Chunks[] -} - - - - - -void cChunkStay::Disable(void) -{ - ASSERT(m_IsEnabled); - - m_World->ChunksStay(*this, false); - m_IsEnabled = false; -} - - - - diff --git a/source/cChunkMap.h b/source/cChunkMap.h deleted file mode 100644 index 4165be089..000000000 --- a/source/cChunkMap.h +++ /dev/null @@ -1,345 +0,0 @@ - -// cChunkMap.h - -// Interfaces to the cChunkMap class representing the chunk storage for a single world - -#pragma once - -#include "ChunkDef.h" - - - - - -class cWorld; -class cItem; -class MTRand; -class cChunkStay; -class cChunk; -class cPlayer; -class cChestEntity; -class cFurnaceEntity; -class cPawn; -class cPickup; -class cChunkDataSerializer; - -typedef std::list cClientHandleList; -typedef cChunk * cChunkPtr; -typedef cItemCallback cEntityCallback; -typedef cItemCallback cChestCallback; -typedef cItemCallback cFurnaceCallback; - - - - - -class cChunkMap -{ -public: - - static const int LAYER_SIZE = 32; - - cChunkMap(cWorld* a_World ); - ~cChunkMap(); - - /// Broadcasts an a_Player's animation to all clients in the chunk where a_Player is - void BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL); - - /// Broadcasts an entity equipment change to all clients in the chunk where a_Entity is - void BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); - - /// Broadcasts a RelEntMoveLook packet to all clients in the chunk where a_Entity is - void BroadcastEntRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); - - /// Broadcasts a RelEntMove packet to all clients in the chunk where a_Entity is - void BroadcastEntRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); - - /// Broadcasts a EntLook packet to all clients in the chunk where a_Entity is - void BroadcastEntLook(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - - /// Broadcasts a EntHeadLook packet to all clients in the chunk where a_Entity is - void BroadcastEntHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - - /// Broadcasts a BlockAction packet to all clients who are in the specified chunk - void BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL); - - void BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - - void BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); - - void BroadcastMetadata(const cPawn & a_Pawn, const cClientHandle * a_Exclude = NULL); - - void BroadcastSpawn(cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - - void BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); - - void BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); - - void BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8 - - void BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); - - /// Broadcasts the block entity, if it is at the coords specified, to all clients except a_Exclude - void BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude); - - /// Sends the block entity, if it is at the coords specified, to a_Client - void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); - - /// a_Player rclked block entity at the coords specified, handle it - void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); - - void MarkChunkDirty (int a_ChunkX, int a_ChunkY, int a_ChunkZ); - void MarkChunkSaving (int a_ChunkX, int a_ChunkY, int a_ChunkZ); - void MarkChunkSaved (int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /** Sets the chunk data as either loaded from the storage or generated. - a_BlockLight and a_BlockSkyLight are optional, if not present, chunk will be marked as unlighted. - a_BiomeMap is optional, if not present, biomes will be calculated by the generator - a_HeightMap is optional, if not present, will be calculated. - If a_MarkDirty is set, the chunk is set as dirty (used after generating) - */ - void SetChunkData( - int a_ChunkX, int a_ChunkY, int a_ChunkZ, - const BLOCKTYPE * a_BlockTypes, - const NIBBLETYPE * a_BlockMeta, - const NIBBLETYPE * a_BlockLight, - const NIBBLETYPE * a_BlockSkyLight, - const cChunkDef::HeightMap * a_HeightMap, - const cChunkDef::BiomeMap & a_BiomeMap, - cEntityList & a_Entities, - cBlockEntityList & a_BlockEntities, - bool a_MarkDirty - ); - - void ChunkLighted( - int a_ChunkX, int a_ChunkZ, - const cChunkDef::BlockNibbles & a_BlockLight, - const cChunkDef::BlockNibbles & a_SkyLight - ); - - bool GetChunkData (int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkDataCallback & a_Callback); - - /// Gets the chunk's blocks, only the block types - bool GetChunkBlockTypes (int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_Blocks); - - /// Gets the chunk's block data, the entire 4 arrays (Types, Meta, Light, SkyLight) - bool GetChunkBlockData (int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData); - - bool IsChunkValid (int a_ChunkX, int a_ChunkY, int a_ChunkZ); - bool HasChunkAnyClients (int a_ChunkX, int a_ChunkY, int a_ChunkZ); - int GetHeight (int a_BlockX, int a_BlockZ); - void FastSetBlocks (sSetBlockList & a_BlockList); - void CollectPickupsByPlayer(cPlayer * a_Player); - BLOCKTYPE GetBlock (int a_X, int a_Y, int a_Z); - BLOCKTYPE GetBlockMeta (int a_X, int a_Y, int a_Z); - BLOCKTYPE GetBlockSkyLight (int a_X, int a_Y, int a_Z); - void SetBlockMeta (int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockMeta); - void SetBlock (int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta); - void GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); - - /// Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType - void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType); - - /// Special function used for growing trees, replaces only blocks that tree may overwrite - void ReplaceTreeBlocks(const sSetBlockVector & a_Blocks); - - EMCSBiome GetBiomeAt (int a_BlockX, int a_BlockZ); - - /// Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. - bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure); - - 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); - - /// Compares clients of two chunks, calls the callback accordingly - void CompareChunkClients(int a_ChunkX1, int a_ChunkY1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkY2, int a_ChunkZ2, cClientDiffCallback & a_Callback); - - /// Adds client to a chunk, if not already present; returns true if added, false if present - bool AddChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); - - /// Removes the client from the chunk - void RemoveChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); - - /// Removes the client from all chunks it is present in - void RemoveClientFromChunks(cClientHandle * a_Client); - - /// Moves the entity from its current chunk to the new chunk specified - void MoveEntityToChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Removes the entity from the chunk specified - void RemoveEntityFromChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true - bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Lua-accessible - - /// Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true - bool ForEachChestInChunk (int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Lua-accessible - - /// Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true - bool ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback); // Lua-accessible - - /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found - bool DoWithChestAt (int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible - - /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords, true if found - bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Lua-accessible - - /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found - bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible - - /// Touches the chunk, causing it to be loaded or generated - void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) - bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() - void LoadChunks(const cChunkCoordsList & a_Chunks); - - /// Marks the chunk as failed-to-load: - void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - void UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); - - /// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable; to be used only by cChunkStay! - void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true); - - /// Marks the chunk as being regenerated - all its clients want that chunk again (used by cWorld::RegenerateChunk() ) - void MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ); - - bool IsChunkLighted(int a_ChunkX, int a_ChunkZ); - - /// Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully - bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback); - - /// Returns the number of valid chunks and the number of dirty chunks - void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty); - - /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem) - void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand); - - /// Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height of 3 - void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); - - /// Grows a cactus present at the block specified by the amount of blocks specified, up to the max height of 3 - void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow); - - /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call - void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); - - void Tick( float a_Dt, MTRand & a_TickRand ); - - void UnloadUnusedChunks(); - void SaveAllChunks(); - - cWorld * GetWorld() { return m_World; } - - int GetNumChunks(void); - - void ChunkValidated(void); // Called by chunks that have become valid - -private: - - friend class cChunk; // The chunks can manipulate neighbors while in their Tick() method, using LockedGetBlock() and LockedSetBlock() - - class cChunkLayer - { - public: - cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent); - ~cChunkLayer(); - - /// Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check - cChunkPtr GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ); - - int GetX(void) const {return m_LayerX; } - int GetZ(void) const {return m_LayerZ; } - - int GetNumChunksLoaded(void) const ; - - void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) const; - - void Save(void); - void UnloadUnusedChunks(void); - - void Tick( float a_Dt, MTRand & a_TickRand ); - - void RemoveClient(cClientHandle * a_Client); - - protected: - - cChunkPtr m_Chunks[LAYER_SIZE * LAYER_SIZE]; - int m_LayerX; - int m_LayerZ; - cChunkMap * m_Parent; - int m_NumChunksLoaded; - }; - - typedef std::list cChunkLayerList; - // TODO: Use smart pointers for cChunkLayerList as well, so that ticking and saving needn't lock the entire layerlist - // This however means that cChunkLayer needs to interlock its m_Chunks[] - - cChunkLayer * GetLayerForChunk( int a_ChunkX, int a_ChunkZ ); // Creates the layer if it doesn't already exist - cChunkLayer * GetLayer( int a_LayerX, int a_LayerZ ); // Creates the layer if it doesn't already exist - void RemoveLayer( cChunkLayer* a_Layer ); - - cCriticalSection m_CSLayers; - cChunkLayerList m_Layers; - cEvent m_evtChunkValid; // Set whenever any chunk becomes valid, via ChunkValidated() - - cWorld * m_World; - - cChunkPtr GetChunk (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading / generating if not valid - cChunkPtr GetChunkNoGen (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading if not valid; doesn't generate - cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Doesn't load, doesn't generate - - /// Gets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) - bool LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); - - /// Sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) - bool LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - - /// Fast-sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) - bool LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); -}; - - - - - -/** Makes chunks stay loaded until this object is cleared or destroyed -Works by setting internal flags in the cChunk that it should not be unloaded. -To optimize for speed, cChunkStay has an Enabled flag, it will "stay" the chunks only when enabled and it will refuse manipulations when enabled -The object itself is not made thread-safe, it's supposed to be used from a single thread only. -*/ -class cChunkStay -{ -public: - cChunkStay(cWorld * a_World); - ~cChunkStay(); - - void Clear(void); - - void Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - void Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - void Enable(void); - void Disable(void); - - /// Queues each chunk in m_Chunks[] for loading / generating - void Load(void); - - // Allow cChunkStay be passed to functions expecting a const cChunkCoordsList & - operator const cChunkCoordsList(void) const {return m_Chunks; } - -protected: - - cWorld * m_World; - - bool m_IsEnabled; - - cChunkCoordsList m_Chunks; -} ; - - - - diff --git a/source/cClientHandle.cpp b/source/cClientHandle.cpp deleted file mode 100644 index 2d97cfa01..000000000 --- a/source/cClientHandle.cpp +++ /dev/null @@ -1,1651 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cClientHandle.h" -#include "cServer.h" -#include "cWorld.h" -#include "cPickup.h" -#include "cPluginManager.h" -#include "cPlayer.h" -#include "cInventory.h" -#include "cChestEntity.h" -#include "cSignEntity.h" -#include "UI/Window.h" -#include "cItem.h" -#include "cTorch.h" -#include "cDoors.h" -#include "cLadder.h" -#include "cVine.h" -#include "cSign.h" -#include "cRedstone.h" -#include "cPiston.h" -#include "Mobs/Monster.h" -#include "cChatColor.h" -#include "OSSupport/Socket.h" -#include "OSSupport/Timer.h" -#include "items/Item.h" -#include "blocks/Block.h" - -#include "cTracer.h" -#include "Vector3f.h" -#include "Vector3d.h" - -#include "cRoot.h" - -#include "cAuthenticator.h" -#include "MersenneTwister.h" - -#include "Protocol/ProtocolRecognizer.h" - - - - - -#define AddPistonDir(x, y, z, dir, amount) switch (dir) { case 0: (y)-=(amount); break; case 1: (y)+=(amount); break;\ - case 2: (z)-=(amount); break; case 3: (z)+=(amount); break;\ - case 4: (x)-=(amount); break; case 5: (x)+=(amount); break; } - - - - - -/// If the number of queued outgoing packets reaches this, the client will be kicked -#define MAX_OUTGOING_PACKETS 2000 - - - - - -#define RECI_RAND_MAX (1.f/RAND_MAX) -inline int fRadRand(MTRand & r1, int a_BlockCoord) -{ - return a_BlockCoord * 32 + (int)(16 * ((float)r1.rand() * RECI_RAND_MAX) * 16 - 8); -} - - - - - -int cClientHandle::s_ClientCount = 0; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cClientHandle: - -cClientHandle::cClientHandle(const cSocket & a_Socket, int a_ViewDistance) - : m_ViewDistance(a_ViewDistance) - , m_Socket(a_Socket) - , m_OutgoingData(64 KiB) - , m_bDestroyed(false) - , m_Player(NULL) - , m_bKicking(false) - , m_TimeLastPacket(cWorld::GetTime()) - , m_bKeepThreadGoing(true) - , m_Ping(1000) - , m_PingID(1) - , m_State(csConnected) - , m_LastStreamedChunkX(0x7fffffff) // bogus chunk coords to force streaming upon login - , m_LastStreamedChunkZ(0x7fffffff) - , m_ShouldCheckDownloaded(false) - , m_UniqueID(0) -{ - m_Protocol = new cProtocolRecognizer(this); - - s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread - m_UniqueID = s_ClientCount; - - cTimer t1; - m_LastPingTime = t1.GetNowTime(); - - LOGD("New ClientHandle created at %p", this); -} - - - - - -cClientHandle::~cClientHandle() -{ - LOGD("Deleting client \"%s\" at %p", GetUsername().c_str(), this); - - // Remove from cSocketThreads, we're not to be called anymore: - cRoot::Get()->GetServer()->ClientDestroying(this); - - { - cCSLock Lock(m_CSChunkLists); - m_LoadedChunks.clear(); - m_ChunksToSend.clear(); - } - - if (m_Player != NULL) - { - cWorld * World = m_Player->GetWorld(); - if (!m_Username.empty() && (World != NULL)) - { - // Send the Offline PlayerList packet: - World->BroadcastPlayerListItem(*m_Player, false, this); - - // Send the Chat packet: - AString Left(m_Username + " left the game!"); - World->BroadcastChat(Left, this); - } - if (World != NULL) - { - World->RemovePlayer(m_Player); - } - } - - if (m_Socket.IsValid()) - { - if (!m_bKicking) - { - SendDisconnect("Server shut down? Kthnxbai"); - } - } - - if (m_Player != NULL) - { - m_Player->Destroy(); - m_Player = NULL; - } - - // Queue all remaining outgoing packets to cSocketThreads: - { - cCSLock Lock(m_CSOutgoingData); - AString Data; - m_OutgoingData.ReadAll(Data); - m_OutgoingData.CommitRead(); - cRoot::Get()->GetServer()->WriteToClient(&m_Socket, Data); - } - - // Queue the socket to close as soon as it sends all outgoing data: - cRoot::Get()->GetServer()->QueueClientClose(&m_Socket); - - // We need to remove the socket from SocketThreads because we own it and it gets destroyed after this destructor finishes - // TODO: The socket needs to stay alive, someone else has to own it - cRoot::Get()->GetServer()->RemoveClient(&m_Socket); - - delete m_Protocol; - m_Protocol = NULL; - - LOGD("ClientHandle at %p deleted", this); -} - - - - - -void cClientHandle::Destroy() -{ - // Setting m_bDestroyed was moved to the bottom of Destroy(), - // otherwise the destructor may be called within another thread before the client is removed from chunks - // http://forum.mc-server.org/showthread.php?tid=366 - - if ((m_Player != NULL) && (m_Player->GetWorld() != NULL)) - { - RemoveFromAllChunks(); - m_Player->GetWorld()->RemoveClientFromChunkSender(this); - } - - m_bDestroyed = true; -} - - - - - -void cClientHandle::Kick(const AString & a_Reason) -{ - if (m_State >= csAuthenticating) // Don't log pings - { - LOG("Kicking user \"%s\" for \"%s\"", m_Username.c_str(), a_Reason.c_str()); - } - SendDisconnect(a_Reason); - m_bKicking = true; -} - - - - - -void cClientHandle::Authenticate(void) -{ - if (m_State != csAuthenticating) - { - return; - } - - ASSERT( m_Player == NULL ); - - // Spawn player (only serversided, so data is loaded) - m_Player = new cPlayer(this, GetUsername()); - - cWorld * World = cRoot::Get()->GetWorld(m_Player->GetLoadedWorldName()); - if (World == NULL) - { - World = cRoot::Get()->GetDefaultWorld(); - } - - if (m_Player->GetGameMode() == eGameMode_NotSet) - { - m_Player->LoginSetGameMode(World->GetGameMode()); - } - - m_Player->SetIP (m_Socket.GetIPString()); - - cRoot::Get()->GetPluginManager()->CallHook(cPluginManager::HOOK_PLAYER_SPAWN, 1, m_Player); - - m_ConfirmPosition = m_Player->GetPosition(); - - // Return a server login packet - m_Protocol->SendLogin(*m_Player, *World); - - // Send Weather if raining: - if ((World->GetWeather() == 1) || (World->GetWeather() == 2)) - { - m_Protocol->SendWeather(World->GetWeather()); - } - - // Send time - m_Protocol->SendTimeUpdate(World->GetWorldTime()); - - // Send inventory - m_Player->GetInventory().SendWholeInventory(*this); - - // Send health - m_Player->SendHealth(); - - m_Player->Initialize(World); - StreamChunks(); - m_State = csDownloadingWorld; - - // Broadcast this player's spawning to all other players in the same chunk - m_Player->GetWorld()->BroadcastSpawn(*m_Player, this); -} - - - - - -void cClientHandle::StreamChunks(void) -{ - if (m_State < csAuthenticating) - { - return; - } - - ASSERT(m_Player != NULL); - - int ChunkPosX = FAST_FLOOR_DIV(m_Player->GetPosX(), cChunkDef::Width); - int ChunkPosZ = FAST_FLOOR_DIV(m_Player->GetPosZ(), cChunkDef::Width); - if ((ChunkPosX == m_LastStreamedChunkX) && (ChunkPosZ == m_LastStreamedChunkZ)) - { - // Already streamed for this position - return; - } - m_LastStreamedChunkX = ChunkPosX; - m_LastStreamedChunkZ = ChunkPosZ; - - LOGD("Streaming chunks centered on [%d, %d], view distance %d", ChunkPosX, ChunkPosZ, m_ViewDistance); - - cWorld * World = m_Player->GetWorld(); - ASSERT(World != NULL); - - // Remove all loaded chunks that are no longer in range; deferred to out-of-CS: - cChunkCoordsList RemoveChunks; - { - cCSLock Lock(m_CSChunkLists); - for (cChunkCoordsList::iterator itr = m_LoadedChunks.begin(); itr != m_LoadedChunks.end();) - { - int RelX = (*itr).m_ChunkX - ChunkPosX; - int RelZ = (*itr).m_ChunkZ - ChunkPosZ; - if ((RelX > m_ViewDistance) || (RelX < -m_ViewDistance) || (RelZ > m_ViewDistance) || (RelZ < -m_ViewDistance)) - { - RemoveChunks.push_back(*itr); - itr = m_LoadedChunks.erase(itr); - } - else - { - ++itr; - } - } // for itr - m_LoadedChunks[] - for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end();) - { - int RelX = (*itr).m_ChunkX - ChunkPosX; - int RelZ = (*itr).m_ChunkZ - ChunkPosZ; - if ((RelX > m_ViewDistance) || (RelX < -m_ViewDistance) || (RelZ > m_ViewDistance) || (RelZ < -m_ViewDistance)) - { - itr = m_ChunksToSend.erase(itr); - } - else - { - ++itr; - } - } // for itr - m_ChunksToSend[] - } - for (cChunkCoordsList::iterator itr = RemoveChunks.begin(); itr != RemoveChunks.end(); ++itr) - { - World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ, this); - m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ); - } // for itr - RemoveChunks[] - - // Add all chunks that are in range and not yet in m_LoadedChunks: - // Queue these smartly - from the center out to the edge - for (int d = 0; d <= m_ViewDistance; ++d) // cycle through (square) distance, from nearest to furthest - { - // For each distance add chunks in a hollow square centered around current position: - for (int i = -d; i <= d; ++i) - { - StreamChunk(ChunkPosX + d, ZERO_CHUNK_Y, ChunkPosZ + i); - StreamChunk(ChunkPosX - d, ZERO_CHUNK_Y, ChunkPosZ + i); - } // for i - for (int i = -d + 1; i < d; ++i) - { - StreamChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ + d); - StreamChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ - d); - } // for i - } // for d - - // Touch chunks GENERATEDISTANCE ahead to let them generate: - for (int d = m_ViewDistance + 1; d <= m_ViewDistance + GENERATEDISTANCE; ++d) // cycle through (square) distance, from nearest to furthest - { - // For each distance touch chunks in a hollow square centered around current position: - for (int i = -d; i <= d; ++i) - { - World->TouchChunk(ChunkPosX + d, ZERO_CHUNK_Y, ChunkPosZ + i); - World->TouchChunk(ChunkPosX - d, ZERO_CHUNK_Y, ChunkPosZ + i); - } // for i - for (int i = -d + 1; i < d; ++i) - { - World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ + d); - World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ - d); - } // for i - } // for d -} - - - - -void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cWorld * World = m_Player->GetWorld(); - ASSERT(World != NULL); - - if (World->AddChunkClient(a_ChunkX, a_ChunkY, a_ChunkZ, this)) - { - { - cCSLock Lock(m_CSChunkLists); - m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); - m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); - } - World->SendChunkTo(a_ChunkX, a_ChunkY, a_ChunkZ, this); - } -} - - - - - -// Removes the client from all chunks. Used when switching worlds or destroying the player -void cClientHandle::RemoveFromAllChunks() -{ - cWorld * World = m_Player->GetWorld(); - if (World != NULL) - { - World->RemoveClientFromChunks(this); - } - - { - cCSLock Lock(m_CSChunkLists); - m_LoadedChunks.clear(); - m_ChunksToSend.clear(); - } -} - - - - - -void cClientHandle::HandlePing(void) -{ - // Somebody tries to retrieve information about the server - AString Reply; - Printf(Reply, "%s%s%i%s%i", - cRoot::Get()->GetDefaultWorld()->GetDescription().c_str(), - cChatColor::Delimiter.c_str(), - cRoot::Get()->GetDefaultWorld()->GetNumPlayers(), - cChatColor::Delimiter.c_str(), - cRoot::Get()->GetDefaultWorld()->GetMaxPlayers() - ); - Kick(Reply.c_str()); -} - - - - - -bool cClientHandle::HandleLogin(int a_ProtocolVersion, const AString & a_Username) -{ - LOGD("LOGIN %s", a_Username.c_str()); - m_Username = a_Username; - - if (cRoot::Get()->GetPluginManager()->CallHookLogin(this, a_ProtocolVersion, a_Username)) - { - Destroy(); - return false; - } - - // Schedule for authentication; until then, let them wait (but do not block) - m_State = csAuthenticating; - cRoot::Get()->GetAuthenticator().Authenticate(GetUniqueID(), GetUsername(), m_Protocol->GetAuthServerID()); - return true; -} - - - - - -void cClientHandle::HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem) -{ - // This is for creative Inventory changes - if (m_Player->GetGameMode() != eGameMode_Creative) - { - LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in creative mode. Ignoring.", m_Username.c_str()); - return; - } - if (m_Player->GetWindow()->GetWindowType() != cWindow::Inventory) - { - LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in the inventory window. Ignoring.", m_Username.c_str()); - return; - } - - m_Player->GetWindow()->Clicked(*m_Player, 0, a_SlotNum, false, false, a_HeldItem); -} - - - - - -void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround) -{ - /* - // TODO: Invalid stance check - if ((a_PosY >= a_Stance) || (a_Stance > a_PosY + 1.65)) - { - LOGD("Invalid stance"); - SendPlayerMoveLook(); - return; - } - */ - - // LOGD("recv player pos: {%0.2f %0.2f %0.2f}, ground: %d", a_PosX, a_PosY, a_PosZ, a_IsOnGround ? 1 : 0); - Vector3d Pos(a_PosX, a_PosY, a_PosZ); - if ((m_Player->GetPosition() - Pos).SqrLength() > 100 * 100) - { - LOGD("Too far away (%0.2f), \"repairing\" the client", (m_Player->GetPosition() - Pos).Length()); - SendPlayerMoveLook(); - return; - } - m_Player->MoveTo(Pos); - m_Player->SetStance(a_Stance); - m_Player->SetTouchGround(a_IsOnGround); -} - - - - - -void cClientHandle::HandleBlockDig(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) -{ - if (!CheckBlockInteractionsRate()) - { - return; - } - - LOGD("OnBlockDig: {%i, %i, %i}; Face: %i; Stat: %i", - a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status - ); - - // Do we want plugins to disable tossing items? Probably no, so toss item before asking plugins for permission - if (a_Status == DIG_STATUS_DROP_HELD) // Drop held item - { - m_Player->TossItem(false); - return; - } - - if (a_Status == DIG_STATUS_SHOOT_EAT) - { - LOGINFO("BlockDig: Status SHOOT/EAT not implemented"); - return; - } - - cWorld * World = m_Player->GetWorld(); - BLOCKTYPE OldBlock; - NIBBLETYPE OldMeta; - World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, OldBlock, OldMeta); - - if (cRoot::Get()->GetPluginManager()->CallHookBlockDig(m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status, OldBlock, OldMeta)) - { - // The plugin doesn't agree with the digging, replace the block on the client and quit: - World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - return; - } - - bool bBroken = ( - (a_Status == DIG_STATUS_FINISHED) || - (g_BlockOneHitDig[(int)OldBlock]) || - ((a_Status == DIG_STATUS_STARTED) && (m_Player->GetGameMode() == 1)) - ); - - cItem & Equipped = m_Player->GetInventory().GetEquippedItem(); - cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Equipped.m_ItemID); - - if (bBroken) - { - ItemHandler->OnBlockDestroyed(World, m_Player, &Equipped, a_BlockX, a_BlockY, a_BlockZ); - - BlockHandler(OldBlock)->OnDestroyedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); - World->DigBlock(a_BlockX, a_BlockY, a_BlockZ); - } - else - { - cBlockHandler * Handler = cBlockHandler::GetBlockHandler(OldBlock); - Handler->OnDigging(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); - - ItemHandler->OnDiggingBlock(World, m_Player, &Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - - - // Check for clickthrough-blocks: - int pX = a_BlockX; - int pY = a_BlockY; - int pZ = a_BlockZ; - AddDirection(pX, pY, pZ, a_BlockFace); - - Handler = cBlockHandler::GetBlockHandler(World->GetBlock(pX, pY, pZ)); - if (Handler->IsClickedThrough()) - { - Handler->OnDigging(World, m_Player, pX, pY, pZ); - } - } -} - - - - - -void cClientHandle::HandleBlockPlace(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) -{ - LOGD("HandleBlockPlace: {%d, %d, %d}, face %d, itemtype: %d", - a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_HeldItem.m_ItemType - ); - - if (!CheckBlockInteractionsRate()) - { - return; - } - - cItem & Equipped = m_Player->GetInventory().GetEquippedItem(); - - if ((Equipped.m_ItemType != a_HeldItem.m_ItemType) && (a_HeldItem.m_ItemType != -1)) - { - // Only compare ItemType, not meta (torches have different metas) - // The -1 check is there because sometimes the client sends -1 instead of the held item - // ( http://forum.mc-server.org/showthread.php?tid=549&pid=4502#pid4502 ) - LOGWARN("Player %s tried to place a block that was not equipped (exp %d, got %d)", - m_Username.c_str(), Equipped.m_ItemType, a_HeldItem.m_ItemType - ); - - // Let's send the current world block to the client, so that it can immediately "let the user know" that they haven't placed the block - if (a_BlockFace > -1) - { - AddDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - } - return; - } - - if (cRoot::Get()->GetPluginManager()->CallHookBlockPlace(m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, Equipped)) - { - if (a_BlockFace > -1) - { - AddDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - } - return; - } - - cWorld * World = m_Player->GetWorld(); - - cBlockHandler *Handler = cBlockHandler::GetBlockHandler(World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)); - if (Handler->IsUseable()) - { - Handler->OnUse(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); - } - else - { - cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Equipped.m_ItemID); - - if (ItemHandler->OnItemUse(World, m_Player, &Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) - { - // Nothing here :P - } - else if (ItemHandler->IsPlaceable()) - { - if (a_BlockFace < 0) - { - // clicked in air - return; - } - - BLOCKTYPE ClickedBlock = World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - cBlockHandler *Handler = cBlockHandler::GetBlockHandler(ClickedBlock); - - if(Handler->IgnoreBuildCollision()) - { - Handler->OnDestroyedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ); - // World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - } - else - { - AddDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - // Check for Blocks not allowing placement on top - if ((a_BlockFace == BLOCK_FACE_TOP) && !Handler->AllowBlockOnTop()) - { - // Resend the old block - // Some times the client still places the block O.o - - World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - return; - } - - - BLOCKTYPE PlaceBlock = m_Player->GetWorld()->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - if (!BlockHandler(PlaceBlock)->IgnoreBuildCollision()) - { - // Tried to place a block *into* another? - return; // Happens when you place a block aiming at side of block like torch or stem - } - } - - cBlockHandler * NewBlock = BlockHandler(ItemHandler->GetBlockType()); - - // Cannot be placed on the side of an other block - if ((a_BlockFace != BLOCK_FACE_TOP) && !NewBlock->CanBePlacedOnSide()) - { - return; - } - - if (NewBlock->CanBePlacedAt(World, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) - { - ItemHandler->PlaceBlock(World, m_Player, &m_Player->GetInventory().GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - // Step sound with 0.8f pitch is used as block placement sound - World->BroadcastSoundEffect(NewBlock->GetStepSound(),a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 0.8f); - } - else - { - World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); // Send the old block back to the player - return; - } - - } - else if (ItemHandler->IsFood()) - { - cItem Item; - Item.m_ItemID = Equipped.m_ItemID; - Item.m_ItemCount = 1; - if (ItemHandler->EatItem(m_Player, &Item)) - { - ItemHandler->OnFoodEaten(World, m_Player, &Item); - m_Player->GetInventory().RemoveItem(Item); - return; - } - } - } -} - - - - - -void cClientHandle::HandleChat(const AString & a_Message) -{ - if (!cRoot::Get()->GetServer()->Command(*this, a_Message)) - { - AString Msg; - Printf(Msg, "<%s%s%s> %s", - m_Player->GetColor().c_str(), - m_Player->GetName().c_str(), - cChatColor::White.c_str(), - a_Message.c_str() - ); - m_Player->GetWorld()->BroadcastChat(Msg); - } -} - - - - - -void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsOnGround) -{ - m_Player->SetRotation (a_Rotation); - m_Player->SetPitch (a_Pitch); - m_Player->SetTouchGround(a_IsOnGround); - m_Player->WrapRotation(); -} - - - - - -void cClientHandle::HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround) -{ - /* - // TODO: Invalid stance check - if ((a_PosY >= a_Stance) || (a_Stance > a_PosY + 1.65)) - { - LOGD("Invalid stance"); - SendPlayerMoveLook(); - return; - } - */ - switch (m_State) - { - case csPlaying: - { - m_Player->MoveTo(Vector3d(a_PosX, a_PosY, a_PosZ)); - m_Player->SetStance (a_Stance); - m_Player->SetTouchGround(a_IsOnGround); - m_Player->SetRotation (a_Rotation); - m_Player->SetPitch (a_Pitch); - m_Player->WrapRotation(); - break; - } - - case csDownloadingWorld: - { - Vector3d ReceivedPosition = Vector3d(a_PosX, a_PosY, a_PosZ); - // LOGD("Received MoveLook confirmation: {%0.2f %0.2f %0.2f}", a_PosX, a_PosY, a_PosZ); - - // Test the distance between points with a small/large enough value instead of comparing directly. Floating point inaccuracies might screw stuff up - double Dist = (ReceivedPosition - m_ConfirmPosition).SqrLength(); - if (Dist < 1.0) - { - if (ReceivedPosition.Equals(m_ConfirmPosition)) - { - LOGINFO("Exact position confirmed by client!"); - } - m_State = csPlaying; - } - else - { - LOGWARNING("Player \"%s\" sent a weird position confirmation %.2f blocks away, retrying", m_Username.c_str(), sqrt(Dist)); - LOGD(" Expected pos: {%0.2f, %0.2f, %0.2f}", m_ConfirmPosition.x, m_ConfirmPosition.y, m_ConfirmPosition.z); - LOGD(" Received pos: {%0.2f, %0.2f, %0.2f}", a_PosX, a_PosY, a_PosZ); - m_ConfirmPosition = m_Player->GetPosition(); - SendPlayerMoveLook(); - } - break; - } - } -} - - - - - -void cClientHandle::HandleAnimation(char a_Animation) -{ - m_Player->GetWorld()->BroadcastPlayerAnimation(*m_Player, a_Animation, this); -} - - - - - -void cClientHandle::HandleSlotSelected(short a_SlotNum) -{ - m_Player->GetInventory().SetEquippedSlot(a_SlotNum); - m_Player->GetWorld()->BroadcastEntityEquipment(*m_Player, 0, m_Player->GetInventory().GetEquippedItem(), this); -} - - - - - -void cClientHandle::HandleWindowClose(char a_WindowID) -{ - m_Player->CloseWindow(a_WindowID); -} - - - - - -void cClientHandle::HandleWindowClick(char a_WindowID, short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_HeldItem) -{ - LOGD("WindowClick: WinID %d, SlotNum %d, IsRclk %d, IsShift %d, Item %s x %d", - a_WindowID, a_SlotNum, a_IsRightClick, a_IsShiftPressed, - ItemToString(a_HeldItem).c_str(), a_HeldItem.m_ItemCount - ); - - cWindow * Window = m_Player->GetWindow(); - if (Window == NULL) - { - LOGWARNING("Player \"%s\" clicked in a non-existent window. Ignoring", m_Username.c_str()); - return; - } - - Window->Clicked(*m_Player, a_WindowID, a_SlotNum, a_IsRightClick, a_IsShiftPressed, a_HeldItem); -} - - - - - -void cClientHandle::HandleUpdateSign( - int a_BlockX, int a_BlockY, int a_BlockZ, - const AString & a_Line1, const AString & a_Line2, - const AString & a_Line3, const AString & a_Line4 -) -{ - cWorld * World = m_Player->GetWorld(); - World->UpdateSign(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, m_Player); -} - - - - - -void cClientHandle::HandleUseEntity(int a_TargetEntityID, bool a_IsLeftClick) -{ - if (!a_IsLeftClick) - { - // TODO: we don't handle right-clicking yet - return; - } - - class cDamageEntity : public cEntityCallback - { - virtual bool Item(cEntity * a_Entity) override - { - if (a_Entity->IsA("cPawn")) - { - reinterpret_cast(a_Entity)->TakeDamage(Damage, Instigator); - } - return true; - } - public: - int Damage; - cEntity * Instigator; - } Callback; - - Callback.Damage = 1; // TODO: Find proper damage from current item equipped - Callback.Instigator = m_Player; - - cWorld * World = m_Player->GetWorld(); - World->DoWithEntityByID(a_TargetEntityID, Callback); -} - - - - - -void cClientHandle::HandleRespawn(void) -{ - m_Player->Respawn(); -} - - - - - -void cClientHandle::HandleDisconnect(const AString & a_Reason) -{ - LOGD("Received d/c packet from \"%s\" with reason \"%s\"", m_Username.c_str(), a_Reason.c_str()); - if (!cRoot::Get()->GetPluginManager()->CallHookDisconnect(m_Player, a_Reason)) - { - AString DisconnectMessage; - Printf(DisconnectMessage, "%s disconnected: %s", m_Username.c_str(), a_Reason.c_str()); - m_Player->GetWorld()->BroadcastChat(DisconnectMessage, this); - } - Destroy(); -} - - - - - -void cClientHandle::HandleKeepAlive(int a_KeepAliveID) -{ - if (a_KeepAliveID == m_PingID) - { - cTimer t1; - m_Ping = (short)((t1.GetNowTime() - m_PingStartTime) / 2); - } -} - - - - - -bool cClientHandle::HandleHandshake(const AString & a_Username) -{ - if (!cRoot::Get()->GetPluginManager()->CallHookHandshake(this, a_Username)) - { - if (cRoot::Get()->GetDefaultWorld()->GetNumPlayers() >= cRoot::Get()->GetDefaultWorld()->GetMaxPlayers()) - { - Kick("The server is currently full :(-- Try again later"); - return false; - } - } - return true; -} - - - - - -void cClientHandle::SendData(const char * a_Data, int a_Size) -{ - { - cCSLock Lock(m_CSOutgoingData); - - // _X 2012_09_06: We need an overflow buffer, usually when streaming the initial chunks - if (m_OutgoingDataOverflow.empty()) - { - // No queued overflow data; if this packet fits into the ringbuffer, put it in, otherwise put it in the overflow buffer: - int CanFit = m_OutgoingData.GetFreeSpace(); - if (CanFit > a_Size) - { - CanFit = a_Size; - } - if (CanFit > 0) - { - m_OutgoingData.Write(a_Data, CanFit); - } - if (a_Size > CanFit) - { - m_OutgoingDataOverflow.append(a_Data + CanFit, a_Size - CanFit); - } - } - else - { - // There is a queued overflow. Append to it, then send as much from its front as possible - m_OutgoingDataOverflow.append(a_Data, a_Size); - int CanFit = m_OutgoingData.GetFreeSpace(); - if (CanFit > 128) - { - // No point in moving the data over if it's not large enough - too much effort for too little an effect - m_OutgoingData.Write(m_OutgoingDataOverflow.data(), CanFit); - m_OutgoingDataOverflow.erase(0, CanFit); - } - } - } // Lock(m_CSOutgoingData) - - // Notify SocketThreads that we have something to write: - cRoot::Get()->GetServer()->NotifyClientWrite(this); -} - - - - - -bool cClientHandle::CheckBlockInteractionsRate(void) -{ - ASSERT(m_Player != NULL); - ASSERT(m_Player->GetWorld() != NULL); - int LastActionCnt = m_Player->GetLastBlockActionCnt(); - if ((m_Player->GetWorld()->GetTime() - m_Player->GetLastBlockActionTime()) < 0.1) - { - // Limit the number of block interactions per tick - m_Player->SetLastBlockActionTime(); //Player tried to interact with a block. Reset last block interation time. - m_Player->SetLastBlockActionCnt(LastActionCnt + 1); - if (m_Player->GetLastBlockActionCnt() > MAXBLOCKCHANGEINTERACTIONS) - { - // Kick if more than MAXBLOCKCHANGEINTERACTIONS per tick - LOGWARN("Player %s tried to interact with a block too quickly! (could indicate bot) Was Kicked.", m_Username.c_str()); - Kick("You're a baaaaaad boy!"); - return false; - } - } - else - { - m_Player->SetLastBlockActionCnt(0); // Reset count - m_Player->SetLastBlockActionTime(); // Player tried to interact with a block. Reset last block interation time. - } - return true; -} - - - - - -void cClientHandle::Tick(float a_Dt) -{ - (void)a_Dt; - if (cWorld::GetTime() - m_TimeLastPacket > 30.f) // 30 seconds time-out - { - SendDisconnect("Nooooo!! You timed out! D: Come back!"); - - // TODO: Cannot sleep in the tick thread! - cSleep::MilliSleep(1000); // Give packet some time to be received - - Destroy(); - } - - if ((m_State == csDownloadingWorld) && m_ShouldCheckDownloaded) - { - CheckIfWorldDownloaded(); - m_ShouldCheckDownloaded = false; - } - - cTimer t1; - // Send ping packet - if ( - (m_Player != NULL) && // Is logged in? - (m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime()) - ) - { - m_PingID++; - m_PingStartTime = t1.GetNowTime(); - m_Protocol->SendKeepAlive(m_PingID); - m_LastPingTime = m_PingStartTime; - } -} - - - - - -void cClientHandle::SendDisconnect(const AString & a_Reason) -{ - LOGD("Sending a DC: \"%s\"", a_Reason.c_str()); - m_Protocol->SendDisconnect(a_Reason); -} - - - - - -void cClientHandle::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item) -{ - m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item); -} - - - - - -void cClientHandle::SendChat(const AString & a_Message) -{ - m_Protocol->SendChat(a_Message); -} - - - - - -void cClientHandle::SendPlayerAnimation(const cPlayer & a_Player, char a_Animation) -{ - m_Protocol->SendPlayerAnimation(a_Player, a_Animation); -} - - - - - -void cClientHandle::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) -{ - m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item); -} - - - - - -void cClientHandle::SendWindowOpen(char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots) -{ - m_Protocol->SendWindowOpen(a_WindowID, a_WindowType, a_WindowTitle, a_NumSlots); -} - - - - - -void cClientHandle::SendWindowClose(char a_WindowID) -{ - m_Protocol->SendWindowClose(a_WindowID); -} - - - - - -void cClientHandle::SendWholeInventory(const cInventory & a_Inventory) -{ - m_Protocol->SendWholeInventory(a_Inventory); -} - - - - - -void cClientHandle::SendWholeInventory(const cWindow & a_Window) -{ - m_Protocol->SendWholeInventory(a_Window); -} - - - - - -void cClientHandle::SendTeleportEntity(const cEntity & a_Entity) -{ - m_Protocol->SendTeleportEntity(a_Entity); -} - - - - - -void cClientHandle::SendPlayerListItem(const cPlayer & a_Player, bool a_IsOnline) -{ - m_Protocol->SendPlayerListItem(a_Player, a_IsOnline); -} - - - - - -void cClientHandle::SendPlayerPosition(void) -{ - m_Protocol->SendPlayerPosition(); -} - - - - - -void cClientHandle::SendEntRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) -{ - ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self - - m_Protocol->SendEntRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ); -} - - - - - -void cClientHandle::SendEntRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ) -{ - ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self - - m_Protocol->SendEntRelMove(a_Entity, a_RelX, a_RelY, a_RelZ); -} - - - - - -void cClientHandle::SendEntLook(const cEntity & a_Entity) -{ - ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self - - m_Protocol->SendEntLook(a_Entity); -} - - - - - -void cClientHandle::SendEntHeadLook(const cEntity & a_Entity) -{ - ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self - - m_Protocol->SendEntHeadLook(a_Entity); -} - - - - - -void cClientHandle::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) -{ - m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType); -} - - - - - -void cClientHandle::SendHealth(void) -{ - m_Protocol->SendHealth(); -} - - - - - -void cClientHandle::SendRespawn(void) -{ - m_Protocol->SendRespawn(); -} - - - - - -void cClientHandle::SendGameMode(eGameMode a_GameMode) -{ - m_Protocol->SendGameMode(a_GameMode); -} - - - - - -void cClientHandle::SendDestroyEntity(const cEntity & a_Entity) -{ - m_Protocol->SendDestroyEntity(a_Entity); -} - - - - - -void cClientHandle::SendPlayerMoveLook(void) -{ - /* - LOGD("Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d", - m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0 - ); - */ - m_Protocol->SendPlayerMoveLook(); -} - - - - - -void cClientHandle::SendEntityStatus(const cEntity & a_Entity, char a_Status) -{ - m_Protocol->SendEntityStatus(a_Entity, a_Status); -} - - - - - -void cClientHandle::SendMetadata(const cPawn & a_Pawn) -{ - m_Protocol->SendMetadata(a_Pawn); -} - - - - - -void cClientHandle::SendInventoryProgress(char a_WindowID, short a_ProgressBar, short a_Value) -{ - m_Protocol->SendInventoryProgress(a_WindowID, a_ProgressBar, a_Value); -} - - - - - -void cClientHandle::SendPlayerSpawn(const cPlayer & a_Player) -{ - if (a_Player.GetUniqueID() == m_Player->GetUniqueID()) - { - // Do NOT send this packet to myself - return; - } - - LOG("Spawning player \"%s\" on client \"%s\" @ %s", - a_Player.GetName().c_str(), GetPlayer()->GetName().c_str(), GetIPString().c_str() - ); - - m_Protocol->SendPlayerSpawn(a_Player); -} - - - - - -void cClientHandle::SendPickupSpawn(const cPickup & a_Pickup) -{ - m_Protocol->SendPickupSpawn(a_Pickup); -} - - - - - -void cClientHandle::SendSpawnMob(const cMonster & a_Mob) -{ - m_Protocol->SendSpawnMob(a_Mob); -} - - - - - -void cClientHandle::SendUpdateSign( - int a_BlockX, int a_BlockY, int a_BlockZ, - const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 -) -{ - m_Protocol->SendUpdateSign( - a_BlockX, a_BlockY, a_BlockZ, - a_Line1, a_Line2, a_Line3, a_Line4 - ); -} - - - - - -void cClientHandle::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) -{ - m_Protocol->SendCollectPickup(a_Pickup, a_Player); -} - - - - - -void cClientHandle::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) -{ - m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); -} - - - - - -void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) -{ - m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes); -} - - - - - -void cClientHandle::SendUnloadChunk(int a_ChunkX, int a_ChunkZ) -{ - m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ); -} - - - - - -void cClientHandle::SendWeather(eWeather a_Weather) -{ - m_Protocol->SendWeather(a_Weather); -} - - - - - -void cClientHandle::SendTimeUpdate(Int64 a_WorldTime) -{ - m_Protocol->SendTimeUpdate(a_WorldTime); -} - - - - - -void cClientHandle::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cClientHandle::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) -{ - m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); -} - - - - - -void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) -{ - // Check chunks being sent, erase them from m_ChunksToSend: - bool Found = false; - { - cCSLock Lock(m_CSChunkLists); - for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end(); ++itr) - { - if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ)) - { - m_ChunksToSend.erase(itr); - - // Make the tick thread check if all the needed chunks have been downloaded - // -- needed to offload this from here due to a deadlock possibility - m_ShouldCheckDownloaded = true; - - Found = true; - break; - } - } // for itr - m_ChunksToSend[] - } - if (!Found) - { - // This just sometimes happens. If you have a reliably replicatable situation for this, go ahead and fix it - // It's not a big issue anyway, just means that some chunks may be compressed several times - // LOGD("Refusing to send chunk [%d, %d] to client \"%s\" at [%d, %d].", ChunkX, ChunkZ, m_Username.c_str(), m_Player->GetChunkX(), m_Player->GetChunkZ()); - return; - } - - m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer); -} - - - - - -void cClientHandle::CheckIfWorldDownloaded(void) -{ - if (m_State != csDownloadingWorld) - { - return; - } - - bool ShouldSendConfirm = false; - { - cCSLock Lock(m_CSChunkLists); - ShouldSendConfirm = m_ChunksToSend.empty(); - } - - if (ShouldSendConfirm) - { - SendConfirmPosition(); - } -} - - - - - -void cClientHandle::SendConfirmPosition(void) -{ - LOG("Spawning player \"%s\" at {%.2f, %.2f, %.2f}", - m_Username.c_str(), m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ() - ); - - m_State = csConfirmingPos; - - if (!cRoot::Get()->GetPluginManager()->CallHook(cPluginManager::HOOK_PLAYER_JOIN, 1, m_Player)) - { - // Broadcast that this player has joined the game! Yay~ - cRoot::Get()->GetServer()->BroadcastChat(m_Username + " joined the game!", this); - } - - SendPlayerMoveLook(); -} - - - - - -const AString & cClientHandle::GetUsername(void) const -{ - return m_Username; -} - - - - - -void cClientHandle::SetViewDistance(int a_ViewDistance) -{ - if (a_ViewDistance < MIN_VIEW_DISTANCE) - { - a_ViewDistance = MIN_VIEW_DISTANCE; - } - if (a_ViewDistance > MAX_VIEW_DISTANCE) - { - a_ViewDistance = MAX_VIEW_DISTANCE; - } - m_ViewDistance = a_ViewDistance; - - // Need to re-stream chunks for the change to become apparent: - StreamChunks(); -} - - - - - -bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - cCSLock Lock(m_CSChunkLists); - return (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)) != m_ChunksToSend.end()); -} - - - - - -void cClientHandle::AddWantedChunk(int a_ChunkX, int a_ChunkZ) -{ - LOGD("Adding chunk [%d, %d] to wanted chunks for client %p", a_ChunkX, a_ChunkZ, this); - cCSLock Lock(m_CSChunkLists); - if (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)) == m_ChunksToSend.end()) - { - m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); - } -} - - - - - -void cClientHandle::PacketBufferFull(void) -{ - // Too much data in the incoming queue, the server is probably too busy, kick the client: - LOGERROR("Too much data in queue for client \"%s\" @ %s, kicking them.", m_Username.c_str(), m_Socket.GetIPString().c_str()); - SendDisconnect("Server busy"); - // TODO: QueueDestroy(); - cSleep::MilliSleep(1000); // Give packet some time to be received - Destroy(); -} - - - - - -void cClientHandle::PacketUnknown(unsigned char a_PacketType) -{ - LOGERROR("Unknown packet type 0x%02x from client \"%s\" @ %s", a_PacketType, m_Username.c_str(), m_Socket.GetIPString().c_str()); - - AString Reason; - Printf(Reason, "[C->S] Unknown PacketID: 0x%02x", a_PacketType); - SendDisconnect(Reason); - // TODO: QueueDestroy(); - cSleep::MilliSleep(1000); // Give packet some time to be received - Destroy(); -} - - - - - -void cClientHandle::PacketError(unsigned char a_PacketType) -{ - LOGERROR("Protocol error while parsing packet type 0x%02x; disconnecting client \"%s\"", a_PacketType, m_Username.c_str()); - SendDisconnect("Protocol error"); - // TODO: QueueDestroy(); - cSleep::MilliSleep(1000); // Give packet some time to be received - Destroy(); -} - - - - - -void cClientHandle::DataReceived(const char * a_Data, int a_Size) -{ - // Data is received from the client, hand it off to the protocol: - m_Protocol->DataReceived(a_Data, a_Size); - m_TimeLastPacket = cWorld::GetTime(); -} - - - - - -void cClientHandle::GetOutgoingData(AString & a_Data) -{ - // Data can be sent to client - { - cCSLock Lock(m_CSOutgoingData); - m_OutgoingData.ReadAll(a_Data); - m_OutgoingData.CommitRead(); - a_Data.append(m_OutgoingDataOverflow); - m_OutgoingDataOverflow.clear(); - } - - // Disconnect player after all packets have been sent - if (m_bKicking && a_Data.empty()) - { - Destroy(); - } -} - - - - - -void cClientHandle::SocketClosed(void) -{ - // The socket has been closed for any reason - - // TODO - /* - self->Destroy(); - LOG("Client \"%s\" disconnected", GetLogName().c_str()); - */ -} - - - - - - diff --git a/source/cClientHandle.h b/source/cClientHandle.h deleted file mode 100644 index 83d0fd9c0..000000000 --- a/source/cClientHandle.h +++ /dev/null @@ -1,254 +0,0 @@ - -// cClientHandle.h - -// Interfaces to the cClientHandle class representing a client connected to this server. The client need not be a player yet - - - - - -#pragma once -#ifndef CCLIENTHANDLE_H_INCLUDED -#define CCLIENTHANDLE_H_INCLUDED - -#include "Defines.h" -#include "Vector3d.h" -#include "OSSupport/SocketThreads.h" -#include "ChunkDef.h" -#include "ByteBuffer.h" - - - - - -class cChunkDataSerializer; -class cInventory; -class cMonster; -class cPawn; -class cPickup; -class cPlayer; -class cProtocol; -class cRedstone; -class cWindow; - - - - -class cClientHandle : // tolua_export - public cSocketThreads::cCallback -{ // tolua_export -public: - enum ENUM_PRIORITY - { - E_PRIORITY_LOW, - E_PRIORITY_NORMAL - }; - - static const int MAXBLOCKCHANGEINTERACTIONS = 20; // 5 didn't help, 10 still doesn't work in Creative, 20 seems to have done the trick - - static const int DEFAULT_VIEW_DISTANCE = 9; // The default ViewDistance (used when no value is set in Settings.ini) - static const int MAX_VIEW_DISTANCE = 10; - static const int MIN_VIEW_DISTANCE = 4; - - cClientHandle(const cSocket & a_Socket, int a_ViewDistance); - ~cClientHandle(); - - cSocket & GetSocket (void) { return m_Socket; } - const AString & GetIPString(void) const { return m_Socket.GetIPString(); } - - cPlayer* GetPlayer() { return m_Player; } // tolua_export - - void Kick(const AString & a_Reason); //tolua_export - void Authenticate(void); // Called by cAuthenticator when the user passes authentication - - void StreamChunks(void); - - // Removes the client from all chunks. Used when switching worlds or destroying the player - void RemoveFromAllChunks(void); - - inline bool IsLoggedIn(void) const { return m_State >= csAuthenticating; } - - void Tick(float a_Dt); - - bool IsDestroyed() { return m_bDestroyed; } - void Destroy(); - - bool IsPlaying(void) const {return (m_State == csPlaying); } - - void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType); - void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes); - void SendChat (const AString & a_Message); - void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer); - void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player); - void SendDestroyEntity (const cEntity & a_Entity); - void SendDisconnect (const AString & a_Reason); - void SendEntHeadLook (const cEntity & a_Entity); - void SendEntLook (const cEntity & a_Entity); - void SendEntRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ); - void SendEntRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ); - void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item); - void SendEntityStatus (const cEntity & a_Entity, char a_Status); - void SendGameMode (eGameMode a_GameMode); - void SendHealth (void); - void SendInventoryProgress(char a_WindowID, short a_Progressbar, short a_Value); - void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item); - void SendMetadata (const cPawn & a_Entity); - void SendPickupSpawn (const cPickup & a_Pickup); - void SendPlayerAnimation (const cPlayer & a_Player, char a_Animation); - void SendPlayerListItem (const cPlayer & a_Player, bool a_IsOnline); - void SendPlayerMoveLook (void); - void SendPlayerPosition (void); - void SendPlayerSpawn (const cPlayer & a_Player); - void SendRespawn (void); - void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch); // a_Src coords are Block * 8 - void SendSpawnMob (const cMonster & a_Mob); - void SendTeleportEntity (const cEntity & a_Entity); - void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ); - void SendTimeUpdate (Int64 a_WorldTime); - void SendUnloadChunk (int a_ChunkX, int a_ChunkZ); - void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); - void SendWeather (eWeather a_Weather); - void SendWholeInventory (const cInventory & a_Inventory); - void SendWholeInventory (const cWindow & a_Window); - void SendWindowClose (char a_WindowID); - void SendWindowOpen (char a_WindowID, char a_WindowType, const AString & a_WindowTitle, char a_NumSlots); - - const AString & GetUsername(void) const; //tolua_export - - inline short GetPing() const { return m_Ping; } //tolua_export - - void SetViewDistance(int a_ViewDistance); //tolua_export - int GetViewDistance() { return m_ViewDistance; }//tolua_export - - int GetUniqueID() const { return m_UniqueID; } //tolua_export - - /// Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend) - bool WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend) - void AddWantedChunk(int a_ChunkX, int a_ChunkZ); - - // Calls that cProtocol descendants use to report state: - void PacketBufferFull(void); - void PacketUnknown(unsigned char a_PacketType); - void PacketError(unsigned char a_PacketType); - - // Calls that cProtocol descendants use for handling packets: - void HandlePing (void); - void HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem); - void HandlePlayerPos (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround); - void HandleBlockDig (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status); - void HandleBlockPlace (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem); - void HandleChat (const AString & a_Message); - void HandlePlayerLook (float a_Rotation, float a_Pitch, bool a_IsOnGround); - void HandlePlayerMoveLook (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround); // While m_bPositionConfirmed (normal gameplay) - void HandleAnimation (char a_Animation); - void HandleSlotSelected (short a_SlotNum); - void HandleWindowClose (char a_WindowID); - void HandleWindowClick (char a_WindowID, short a_SlotNum, bool a_IsRightClick, bool a_IsShiftPressed, const cItem & a_HeldItem); - void HandleUpdateSign ( - int a_BlockX, int a_BlockY, int a_BlockZ, - const AString & a_Line1, const AString & a_Line2, - const AString & a_Line3, const AString & a_Line4 - ); - void HandleUseEntity (int a_TargetEntityID, bool a_IsLeftClick); - void HandleRespawn (void); - void HandleDisconnect (const AString & a_Reason); - void HandleKeepAlive (int a_KeepAliveID); - bool HandleHandshake (const AString & a_Username); - - /** Called when the protocol has finished logging the user in. - Return true to allow the user in; false to kick them. - */ - bool HandleLogin(int a_ProtocolVersion, const AString & a_Username); - - void SendData(const char * a_Data, int a_Size); -private: - - int m_ViewDistance; // Number of chunks the player can see in each direction; 4 is the minimum ( http://wiki.vg/Protocol_FAQ#.E2.80.A6all_connecting_clients_spasm_and_jerk_uncontrollably.21 ) - - static const int GENERATEDISTANCE = 2; // Server generates this many chunks AHEAD of player sight. 2 is the minimum, since foliage is generated 1 step behind chunk terrain generation - - int m_ProtocolVersion; - AString m_Username; - AString m_Password; - - cCriticalSection m_CSChunkLists; - cChunkCoordsList m_LoadedChunks; // Chunks that the player belongs to - cChunkCoordsList m_ChunksToSend; // Chunks that need to be sent to the player (queued because they weren't generated yet or there's not enough time to send them) - - cSocket m_Socket; - cProtocol * m_Protocol; - - cCriticalSection m_CSOutgoingData; - cByteBuffer m_OutgoingData; - AString m_OutgoingDataOverflow; //< For data that didn't fit into the m_OutgoingData ringbuffer temporarily - - cCriticalSection m_CriticalSection; - - Vector3d m_ConfirmPosition; - - bool m_bDestroyed; - cPlayer * m_Player; - bool m_bKicking; - - // Chunk position when the last StreamChunks() was called; used to avoid re-streaming while in the same chunk - int m_LastStreamedChunkX; - int m_LastStreamedChunkZ; - - float m_TimeLastPacket; - - short m_Ping; - int m_PingID; - long long m_PingStartTime; - long long m_LastPingTime; - static const unsigned short PING_TIME_MS = 1000; //minecraft sends 1 per 20 ticks (1 second or every 1000 ms) - - enum eState - { - csConnected, // The client has just connected, waiting for their handshake / login - csAuthenticating, // The client has logged in, waiting for external authentication - csDownloadingWorld, // The client is waiting for chunks, we're waiting for the loader to provide and send them - csConfirmingPos, // The client has been sent the position packet, waiting for them to repeat the position back - csPlaying, // Normal gameplay - - // TODO: Add Kicking and Destroyed here as well - } ; - - eState m_State; - - bool m_bKeepThreadGoing; - - /// If set to true during csDownloadingWorld, the tick thread calls CheckIfWorldDownloaded() - bool m_ShouldCheckDownloaded; - - /// Returns true if the rate block interactions is within a reasonable limit (bot protection) - bool CheckBlockInteractionsRate(void); - - /// Checks whether all loaded chunks have been sent to the client; if so, sends the position to confirm - void CheckIfWorldDownloaded(void); - - /// Sends the PlayerMoveLook packet that the client needs to reply to for the game to start - void SendConfirmPosition(void); - - /// Adds a single chunk to be streamed to the client; used by StreamChunks() - void StreamChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - // cSocketThreads::cCallback overrides: - virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client - virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client - virtual void SocketClosed (void) override; // The socket has been closed for any reason - - static int s_ClientCount; - int m_UniqueID; -}; // tolua_export - - - - -#endif // CCLIENTHANDLE_H_INCLUDED - - - - diff --git a/source/cCuboid.cpp b/source/cCuboid.cpp deleted file mode 100644 index 86600f6b6..000000000 --- a/source/cCuboid.cpp +++ /dev/null @@ -1,19 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cCuboid.h" - - - - - -void cCuboid::Sort() -{ - if( p1.x > p2.x ) std::swap( p1.x, p2.x ); - if( p1.y > p2.y ) std::swap( p1.y, p2.y ); - if( p1.z > p2.z ) std::swap( p1.z, p2.z ); -} - - - - diff --git a/source/cCuboid.h b/source/cCuboid.h deleted file mode 100644 index a07d665fc..000000000 --- a/source/cCuboid.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "Vector3i.h" -#include "Vector3d.h" - -class cCuboid //tolua_export -{ //tolua_export -public: //tolua_export - cCuboid() {} //tolua_export - cCuboid( const cCuboid & a_Cuboid ) : p1( a_Cuboid.p1 ), p2( a_Cuboid.p2 ) {} //tolua_export - cCuboid( const Vector3i & a_p1, const Vector3i & a_p2 ) : p1( a_p1 ), p2( a_p2 ) {} //tolua_export - - - Vector3i p1, p2; //tolua_export - - void Sort(); //tolua_export - - bool IsInside( const Vector3i & v ) const //tolua_export - { //tolua_export - if( v.x >= p1.x && v.x <= p2.x - && v.y >= p1.y && v.y <= p2.y - && v.z >= p1.z && v.z <= p2.z ) - { - return true; - } - return false; - } //tolua_export - - bool IsInside( const Vector3d & v ) const //tolua_export - { //tolua_export - if( v.x >= p1.x && v.x <= p2.x - && v.y >= p1.y && v.y <= p2.y - && v.z >= p1.z && v.z <= p2.z ) - { - return true; - } - return false; - } //tolua_export - -}; //tolua_export \ No newline at end of file diff --git a/source/cDoors.h b/source/cDoors.h deleted file mode 100644 index 71fec0d39..000000000 --- a/source/cDoors.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - - -class cDoors //tolua_export -{ //tolua_export -public: - static char RotationToMetaData( float a_Rotation ) //tolua_export - { //tolua_export - a_Rotation += 90 + 45; // So its not aligned with axis - if( a_Rotation > 360.f ) a_Rotation -= 360.f; - if( a_Rotation >= 0.f && a_Rotation < 90.f ) - return 0x0; - else if( a_Rotation >= 180 && a_Rotation < 270 ) - return 0x2; - else if( a_Rotation >= 90 && a_Rotation < 180 ) - return 0x1; - else - return 0x3; - } //tolua_export - - static char ChangeStateMetaData( char a_MetaData ) //tolua_export - { //tolua_export - - a_MetaData ^= 4; //XOR bit 2 aka 3. bit (Door open state) - - return a_MetaData; - } //tolua_export - - static void ChangeDoor(cWorld *a_World, int a_X, int a_Y, int a_Z) //tolua_export - { //tolua_export - char OldMetaData = a_World->GetBlockMeta(a_X, a_Y, a_Z); - - a_World->SetBlockMeta(a_X, a_Y, a_Z, ChangeStateMetaData ( OldMetaData ) ); - - - if (OldMetaData & 8) - { //current block is top of the door - char BottomBlock = a_World->GetBlock(a_X, a_Y - 1, a_Z); - char BottomMeta = a_World->GetBlockMeta(a_X, a_Y - 1, a_Z); - - if (IsDoor(BottomBlock) && !(BottomMeta & 8)) - { - a_World->SetBlockMeta(a_X, a_Y - 1, a_Z, ChangeStateMetaData ( BottomMeta ) ); - } - } else { //current block is bottom of the door - char TopBlock = a_World->GetBlock(a_X, a_Y + 1, a_Z); - char TopMeta = a_World->GetBlockMeta(a_X, a_Y + 1, a_Z); - - if (IsDoor(TopBlock) && (TopMeta & 8)) - { - a_World->SetBlockMeta(a_X, a_Y + 1, a_Z, ChangeStateMetaData ( TopMeta ) ); - } - } - } //tolua_export - - inline static bool IsDoor(char a_Block) - { - return (a_Block == E_BLOCK_WOODEN_DOOR || a_Block == E_BLOCK_IRON_DOOR); - } - -}; //tolua_export \ No newline at end of file diff --git a/source/cEntity.cpp b/source/cEntity.cpp deleted file mode 100644 index e6ed2baaa..000000000 --- a/source/cEntity.cpp +++ /dev/null @@ -1,344 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cEntity.h" -#include "cWorld.h" -#include "cServer.h" -#include "cRoot.h" -#include "Vector3d.h" -#include "Vector3f.h" -#include "Matrix4f.h" -#include "cReferenceManager.h" -#include "cClientHandle.h" - - - - - -int cEntity::m_EntityCount = 0; -cCriticalSection cEntity::m_CSCount; - - - - - -cEntity::cEntity(const double & a_X, const double & a_Y, const double & a_Z) - : m_UniqueID( 0 ) - , m_Referencers( new cReferenceManager( cReferenceManager::RFMNGR_REFERENCERS ) ) - , m_References( new cReferenceManager( cReferenceManager::RFMNGR_REFERENCES ) ) - , m_ChunkX( 0 ) - , m_ChunkY( 0 ) - , m_ChunkZ( 0 ) - , m_Pos( a_X, a_Y, a_Z ) - , m_bDirtyPosition( true ) - , m_bDirtyOrientation( true ) - , m_bDestroyed( false ) - , m_EntityType( eEntityType_Entity ) - , m_World( 0 ) - , m_bRemovedFromChunk( false ) - , m_FireDamageInterval(0.f) - , m_BurnPeriod(0.f) -{ - cCSLock Lock(m_CSCount); - m_EntityCount++; - m_UniqueID = m_EntityCount; -} - - - - - -cEntity::~cEntity() -{ - LOG("Deleting entity %d at pos {%.2f, %.2f} ~ [%d, %d]; ptr %p", - m_UniqueID, - m_Pos.x, m_Pos.z, - (int)(m_Pos.x / cChunkDef::Width), (int)(m_Pos.z / cChunkDef::Width), - this - ); - - if( !m_bDestroyed || !m_bRemovedFromChunk ) - { - LOGERROR("ERROR: Entity deallocated without being destroyed %i or unlinked %i", m_bDestroyed, m_bRemovedFromChunk ); - ASSERT(!"Entity deallocated without being destroyed or unlinked"); - } - delete m_Referencers; - delete m_References; -} - - - - - -CLASS_DEF_GETCLASS(cEntity); - - - - - -void cEntity::Initialize(cWorld * a_World) -{ - m_World = a_World; - m_World->AddEntity( this ); - - MoveToCorrectChunk(true); -} - - - - - -void cEntity::WrapRotation() -{ - while (m_Rot.x > 180.f) m_Rot.x-=360.f; // Wrap it - while (m_Rot.x < -180.f) m_Rot.x+=360.f; - while (m_Rot.y > 180.f) m_Rot.y-=360.f; - while (m_Rot.y < -180.f) m_Rot.y+=360.f; -} - - - - - -void cEntity::MoveToCorrectChunk(bool a_bIgnoreOldChunk) -{ - ASSERT(m_World != NULL); // Entity needs a world to move to a chunk - if( !m_World ) return; - - int ChunkX = 0, ChunkY = 0, ChunkZ = 0; - cWorld::BlockToChunk( (int)m_Pos.x, (int)m_Pos.y, (int)m_Pos.z, ChunkX, ChunkY, ChunkZ ); - if (!a_bIgnoreOldChunk && (m_ChunkX == ChunkX) && (m_ChunkY == ChunkY) && (m_ChunkZ == ChunkZ)) - { - return; - } - - class cMover : - public cClientDiffCallback - { - virtual void Removed(cClientHandle * a_Client) override - { - if (m_IgnoreOldChunk) - { - return; - } - a_Client->SendDestroyEntity(*m_Entity); - } - - virtual void Added(cClientHandle * a_Client) override - { - m_Entity->SpawnOn(*a_Client); - } - - bool m_IgnoreOldChunk; - cEntity * m_Entity; - - public: - cMover(cEntity * a_Entity, bool a_IgnoreOldChunk) : - m_IgnoreOldChunk(a_IgnoreOldChunk), - m_Entity(a_Entity) - {} - } Mover(this, a_bIgnoreOldChunk); - - m_World->CompareChunkClients(m_ChunkX, m_ChunkY, m_ChunkZ, ChunkX, ChunkY, ChunkZ, Mover); - m_World->MoveEntityToChunk(this, ChunkX, ChunkY, ChunkZ); - - m_ChunkX = ChunkX; - m_ChunkY = ChunkY; - m_ChunkZ = ChunkZ; -} - - - - - -void cEntity::Destroy() -{ - if (m_bDestroyed) - { - return; - } - if (!m_bRemovedFromChunk) - { - RemoveFromChunk(); - } - - m_World->BroadcastDestroyEntity(*this); - - m_bDestroyed = true; - - Destroyed(); -} - - - - - -void cEntity::RemoveFromChunk(void) -{ - if (m_World == NULL) - { - return; - } - - m_World->RemoveEntityFromChunk(this, m_ChunkX, m_ChunkY, m_ChunkZ); - m_bRemovedFromChunk = true; -} - - - - - -bool cEntity::IsA( const char* a_EntityType ) -{ - //LOG("IsA( cEntity ) : %s", a_EntityType); - if( strcmp( a_EntityType, "cEntity" ) == 0 ) return true; - return false; -} - - - - - -////////////////////////////////////////////////////////////////////////// -// Set orientations -void cEntity::SetRot( const Vector3f & a_Rot ) -{ - m_Rot = a_Rot; - m_bDirtyOrientation = true; -} - - - - - -void cEntity::SetRotation( float a_Rotation ) -{ - m_Rot.x = a_Rotation; - m_bDirtyOrientation = true; -} - - - - - -void cEntity::SetPitch( float a_Pitch ) -{ - m_Rot.y = a_Pitch; - m_bDirtyOrientation = true; -} - - - - - -void cEntity::SetRoll( float a_Roll ) -{ - m_Rot.z = a_Roll; - m_bDirtyOrientation = true; -} - - - - - -////////////////////////////////////////////////////////////////////////// -// Get look vector (this is NOT a rotation!) -Vector3f cEntity::GetLookVector() -{ - Matrix4f m; - m.Init( Vector3f(), 0, m_Rot.x, -m_Rot.y ); - Vector3f Look = m.Transform( Vector3f(0, 0, 1) ); - LOG("Look: %0.1f %0.1f %0.1f", Look.x, Look.y, Look.z ); - return Look; -} - - - - - -////////////////////////////////////////////////////////////////////////// -// Set position -void cEntity::SetPosition( const Vector3d & a_Pos ) -{ - m_Pos = a_Pos; - MoveToCorrectChunk(); - m_bDirtyPosition = true; -} - - - - - -void cEntity::SetPosition( const double & a_PosX, const double & a_PosY, const double & a_PosZ ) -{ - m_Pos.Set( a_PosX, a_PosY, a_PosZ ); - MoveToCorrectChunk(); - m_bDirtyPosition = true; -} - - - - - -void cEntity::SetPosX( const double & a_PosX ) -{ - m_Pos.x = a_PosX; - MoveToCorrectChunk(); - m_bDirtyPosition = true; -} - - - - - -void cEntity::SetPosY( const double & a_PosY ) -{ - m_Pos.y = a_PosY; - MoveToCorrectChunk(); - m_bDirtyPosition = true; -} - - - - - -void cEntity::SetPosZ( const double & a_PosZ ) -{ - m_Pos.z = a_PosZ; - MoveToCorrectChunk(); - m_bDirtyPosition = true; -} - - - - - -////////////////////////////////////////////////////////////////////////// -// Reference stuffs -void cEntity::AddReference( cEntity*& a_EntityPtr ) -{ - m_References->AddReference( a_EntityPtr ); - a_EntityPtr->ReferencedBy( a_EntityPtr ); -} - - - - - -void cEntity::ReferencedBy( cEntity*& a_EntityPtr ) -{ - m_Referencers->AddReference( a_EntityPtr ); -} - - - - - -void cEntity::Dereference( cEntity*& a_EntityPtr ) -{ - m_Referencers->Dereference( a_EntityPtr ); -} - - - - diff --git a/source/cEntity.h b/source/cEntity.h deleted file mode 100644 index 15c351a09..000000000 --- a/source/cEntity.h +++ /dev/null @@ -1,172 +0,0 @@ - -#pragma once - - - - -#include "Vector3d.h" -#include "Vector3f.h" - - - - - -#define CLASS_PROT_ISA() virtual bool IsA( const char* a_EntityType ); -#define CLASS_PROT_GETCLASS() virtual const char* GetClass(); - -/* Can't use this (yet) because of tolua */ -#define CLASS_PROTOTYPE() \ - CLASS_PROT_ISA(); \ - CLASS_PROT_GETCLASS(); - -#define CLASS_DEF_ISA( classname, superclass ) \ - bool classname::IsA( const char* a_EntityType ) \ - { \ - if( strcmp( a_EntityType, #classname ) == 0 ) return true; \ - return superclass::IsA( a_EntityType ); \ - } - -#define CLASS_DEF_GETCLASS( classname ) \ - const char* classname::GetClass() \ - { \ - return #classname; \ - } - -#define CLASS_DEFINITION( classname, superclass ) \ - CLASS_DEF_ISA( classname, superclass ) \ - CLASS_DEF_GETCLASS( classname ) - - - - - -class cWorld; -class cReferenceManager; -class cClientHandle; - - - - -// tolua_begin -class cEntity -{ -public: - enum - { - ENTITY_STATUS_HURT = 2, - ENTITY_STATUS_DEAD = 3, - ENTITY_STATUS_WOLF_TAMING = 6, - ENTITY_STATUS_WOLF_TAMED = 7, - ENTITY_STATUS_WOLF_SHAKING = 8, - ENTITY_STATUS_EATING_ACCEPTED = 9, - ENTITY_STATUS_SHEEP_EATING = 10, - } ; - - cEntity(const double & a_X, const double & a_Y, const double & a_Z); - virtual ~cEntity(); - - virtual void Initialize(cWorld * a_World); - - enum eEntityType - { - eEntityType_Entity, - eEntityType_Player, - eEntityType_Pickup - }; - - virtual unsigned int GetEntityType() { return m_EntityType; } - virtual bool IsA( const char* a_EntityType ); - virtual const char* GetClass(); - // tolua_end - - cWorld * GetWorld(void) const { return m_World; } //tolua_export - - const Vector3d & GetPosition(void) const {return m_Pos; } //tolua_export - const double & GetPosX (void) const {return m_Pos.x; } //tolua_export - const double & GetPosY (void) const {return m_Pos.y; } //tolua_export - const double & GetPosZ (void) const {return m_Pos.z; } //tolua_export - const Vector3f & GetRot (void) const {return m_Rot; } //tolua_export - float GetRotation(void) const {return m_Rot.x; } //tolua_export - float GetPitch (void) const {return m_Rot.y; } //tolua_export - float GetRoll (void) const {return m_Rot.z; } //tolua_export - Vector3f GetLookVector(); //tolua_export - - int GetChunkX(void) const {return m_ChunkX; } //tolua_export - int GetChunkY(void) const {return m_ChunkY; } //tolua_export - int GetChunkZ(void) const {return m_ChunkZ; } //tolua_export - - void SetPosX( const double & a_PosX ); //tolua_export - void SetPosY( const double & a_PosY ); //tolua_export - void SetPosZ( const double & a_PosZ ); //tolua_export - void SetPosition( const double & a_PosX, const double & a_PosY, const double & a_PosZ );//tolua_export - void SetPosition( const Vector3d & a_Pos ); //tolua_export - void SetRot( const Vector3f & a_Rot ); //tolua_export - void SetRotation( float a_Rotation ); //tolua_export - void SetPitch( float a_Pitch ); //tolua_export - void SetRoll( float a_Roll ); //tolua_export - - inline int GetUniqueID(void) const { return m_UniqueID; } //tolua_export - inline bool IsDestroyed(void) const { return m_bDestroyed; } //tolua_export - - void Destroy(); //tolua_export - void RemoveFromChunk(void); // for internal use in cChunk - - virtual void Tick(float a_Dt) = 0; //tolua_export - - /** Descendants override this function to send a command to the specified client to spawn the entity on the client. - To spawn on all eligible clients, use cChunkMap::BroadcastSpawnEntity() - Needs to have a default implementation due to Lua bindings. - */ - virtual void SpawnOn(cClientHandle & a_Client) {ASSERT(!"SpawnOn() unimplemented!"); } // tolua_export - - void WrapRotation(); - - // Metadata flags; descendants may override the defaults: - virtual bool IsOnFire (void) const {return (m_BurnPeriod > 0); } - virtual bool IsCrouched (void) const {return false; } - virtual bool IsRiding (void) const {return false; } - virtual bool IsSprinting(void) const {return false; } - virtual bool IsRclking (void) const {return false; } - -protected: - virtual void Destroyed() {} // Called after the entity has been destroyed - - void SetWorld( cWorld* a_World ) { m_World = a_World; } - void MoveToCorrectChunk(bool a_bIgnoreOldChunk = false); - - friend class cReferenceManager; - void AddReference( cEntity*& a_EntityPtr ); - void ReferencedBy( cEntity*& a_EntityPtr ); - void Dereference( cEntity*& a_EntityPtr ); - - static cCriticalSection m_CSCount; - static int m_EntityCount; - - int m_UniqueID; - - cReferenceManager* m_Referencers; - cReferenceManager* m_References; - - int m_ChunkX, m_ChunkY, m_ChunkZ; - Vector3d m_Pos; - bool m_bDirtyPosition; - - Vector3f m_Rot; - bool m_bDirtyOrientation; - - bool m_bDestroyed; - bool m_bRemovedFromChunk; - - eEntityType m_EntityType; - - cWorld* m_World; - - float m_FireDamageInterval; - float m_BurnPeriod; -}; //tolua_export - -typedef std::list cEntityList; - - - - diff --git a/source/cFileFormatUpdater.cpp b/source/cFileFormatUpdater.cpp deleted file mode 100644 index 3991ede34..000000000 --- a/source/cFileFormatUpdater.cpp +++ /dev/null @@ -1,143 +0,0 @@ - -#include "Globals.h" - -#include "cFileFormatUpdater.h" -#include "Vector3d.h" -#include "Vector3f.h" - -#include "cItem.h" -#include - - - - - -void cFileFormatUpdater::UpdateFileFormat() -{ - UpdatePlayersOfWorld("world"); -} - - - - - -// Convert player .bin files to JSON -void cFileFormatUpdater::UpdatePlayersOfWorld( const char* a_WorldName ) -{ - AString PlayerDir = AString( a_WorldName ) + "/player/"; - - AStringList AllFiles = GetDirectoryContents( PlayerDir.c_str() ); - for (AStringList::iterator itr = AllFiles.begin(); itr != AllFiles.end(); ++itr ) - { - AString & FileName = *itr; - if (FileName.rfind(".bin") != AString::npos) // Get only the files ending in .bin - { - PlayerBINtoJSON( (PlayerDir + FileName).c_str() ); - } - } -} - - - - - -#define READ(File, Var) \ - if (File.Read(&Var, sizeof(Var)) != sizeof(Var)) \ - { \ - LOGERROR("ERROR READING \"%s\" FROM FILE \"%s\"", #Var, a_FileName); \ - return; \ - } - -// Converts player binary files to human readable JSON -void cFileFormatUpdater::PlayerBINtoJSON( const char* a_FileName ) -{ - Vector3d PlayerPos; - Vector3f PlayerRot; - short PlayerHealth = 0; - - const unsigned int NumInventorySlots = 45; // At this time the player inventory has/had 45 slots - cItem IventoryItems[ NumInventorySlots ]; - - cFile f; - if (!f.Open(a_FileName, cFile::fmRead)) - { - return; - } - - // First read player position, rotation and health - READ(f, PlayerPos.x); - READ(f, PlayerPos.y); - READ(f, PlayerPos.z); - READ(f, PlayerRot.x); - READ(f, PlayerRot.y); - READ(f, PlayerRot.z); - READ(f, PlayerHealth); - - - for(unsigned int i = 0; i < NumInventorySlots; i++) - { - cItem & Item = IventoryItems[i]; - READ(f, Item.m_ItemID); - READ(f, Item.m_ItemCount); - READ(f, Item.m_ItemHealth); - } - f.Close(); - - // Loaded all the data, now create the JSON data - Json::Value JSON_PlayerPosition; - JSON_PlayerPosition.append( Json::Value( PlayerPos.x ) ); - JSON_PlayerPosition.append( Json::Value( PlayerPos.y ) ); - JSON_PlayerPosition.append( Json::Value( PlayerPos.z ) ); - - Json::Value JSON_PlayerRotation; - JSON_PlayerRotation.append( Json::Value( PlayerRot.x ) ); - JSON_PlayerRotation.append( Json::Value( PlayerRot.y ) ); - JSON_PlayerRotation.append( Json::Value( PlayerRot.z ) ); - - Json::Value JSON_Inventory; - for(unsigned int i = 0; i < NumInventorySlots; i++) - { - Json::Value JSON_Item; - IventoryItems[i].GetJson( JSON_Item ); - JSON_Inventory.append( JSON_Item ); - } - - Json::Value root; - root["position"] = JSON_PlayerPosition; - root["rotation"] = JSON_PlayerRotation; - root["inventory"] = JSON_Inventory; - root["health"] = PlayerHealth; - - Json::StyledWriter writer; - std::string JsonData = writer.write( root ); - - // Get correct filename - std::string FileName = a_FileName; - std::string FileNameWithoutExt = FileName.substr(0, FileName.find_last_of(".") ); - std::string FileNameJson = FileNameWithoutExt + ".json"; - - // Write to file - if (!f.Open(FileNameJson.c_str(), cFile::fmWrite)) - { - return; - } - if (f.Write(JsonData.c_str(), JsonData.size()) != JsonData.size()) - { - LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", FileNameJson.c_str()); - return; - } - f.Close(); - - // Delete old format file, only do this when conversion has succeeded - if (std::remove(a_FileName) != 0) - { - LOGERROR("COULD NOT DELETE old format file \"%s\"", a_FileName); - return; - } - - LOGINFO("Successfully converted binary to Json \"%s\"", FileNameJson.c_str() ); -} - - - - diff --git a/source/cFileFormatUpdater.h b/source/cFileFormatUpdater.h deleted file mode 100644 index 91e2761fb..000000000 --- a/source/cFileFormatUpdater.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -class cFileFormatUpdater -{ -public: - static void UpdateFileFormat(); -private: - static void UpdatePlayersOfWorld( const char* a_WorldName ); - - static void PlayerBINtoJSON( const char* a_FileName ); -}; \ No newline at end of file diff --git a/source/cFireSimulator.cpp b/source/cFireSimulator.cpp deleted file mode 100644 index da447a01f..000000000 --- a/source/cFireSimulator.cpp +++ /dev/null @@ -1,133 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cFireSimulator.h" -#include "cWorld.h" -#include "Vector3i.h" -#include "BlockID.h" -#include "Defines.h" - -cFireSimulator::cFireSimulator( cWorld* a_World ) - : cSimulator(a_World) - , m_Blocks(new BlockList) - , m_Buffer(new BlockList) - , m_BurningBlocks(new BlockList) -{ - -} - -cFireSimulator::~cFireSimulator() -{ - delete m_Buffer; - delete m_Blocks; - delete m_BurningBlocks; -} - -void cFireSimulator::Simulate( float a_Dt ) -{ - m_Buffer->clear(); - std::swap( m_Blocks, m_Buffer ); - - for( BlockList::iterator itr = m_Buffer->begin(); itr != m_Buffer->end(); ++itr ) - { - Vector3i Pos = *itr; - - char BlockID = m_World->GetBlock(Pos.x, Pos.y, Pos.z); - - if(!IsAllowedBlock(BlockID)) //Check wheather the block is still burning - continue; - - if(BurnBlockAround(Pos.x, Pos.y, Pos.z)) //Burn single block and if there was one -> next time again - _AddBlock(Pos.x, Pos.y, Pos.z); - else - if(!IsForeverBurnable(m_World->GetBlock(Pos.x, Pos.y - 1, Pos.z)) && !FiresForever(BlockID)) - m_World->SetBlock(Pos.x, Pos.y, Pos.z, E_BLOCK_AIR, 0); - - } - -} - - -bool cFireSimulator::IsAllowedBlock( char a_BlockID ) -{ - return a_BlockID == E_BLOCK_FIRE - || IsBlockLava(a_BlockID); -} - -void cFireSimulator::AddBlock(int a_X, int a_Y, int a_Z) -{ - char BlockID = m_World->GetBlock(a_X, a_Y, a_Z); - if(!IsAllowedBlock(BlockID)) //This should save very much time because it doesnīt have to iterate through all blocks - return; - - //check for duplicates - for( BlockList::iterator itr = m_Blocks->begin(); itr != m_Blocks->end(); ++itr ) - { - Vector3i Pos = *itr; - if( Pos.x == a_X && Pos.y == a_Y && Pos.z == a_Z ) - return; - } - - _AddBlock(a_X, a_Y, a_Z); - -} - -void cFireSimulator::_AddBlock(int a_X, int a_Y, int a_Z) -{ - m_Blocks->push_back( Vector3i(a_X, a_Y, a_Z) ); - -} - -bool cFireSimulator::IsForeverBurnable( char a_BlockID ) -{ - return a_BlockID == E_BLOCK_BLOODSTONE; -} - -bool cFireSimulator::IsBurnable( char a_BlockID ) -{ - return a_BlockID == E_BLOCK_PLANKS - || a_BlockID == E_BLOCK_LEAVES - || a_BlockID == E_BLOCK_LOG - || a_BlockID == E_BLOCK_WOOL - || a_BlockID == E_BLOCK_BOOKCASE - || a_BlockID == E_BLOCK_FENCE - || a_BlockID == E_BLOCK_TNT - || a_BlockID == E_BLOCK_VINES; -} - -bool cFireSimulator::FiresForever( char a_BlockID ) -{ - return a_BlockID != E_BLOCK_FIRE; -} - -bool cFireSimulator::BurnBlockAround(int a_X, int a_Y, int a_Z) -{ - return BurnBlock(a_X + 1, a_Y, a_Z) - || BurnBlock(a_X - 1, a_Y, a_Z) - || BurnBlock(a_X, a_Y + 1, a_Z) - || BurnBlock(a_X, a_Y - 1, a_Z) - || BurnBlock(a_X, a_Y, a_Z + 1) - || BurnBlock(a_X, a_Y, a_Z - 1); -} - -bool cFireSimulator::BurnBlock(int a_X, int a_Y, int a_Z) -{ - char BlockID = m_World->GetBlock(a_X, a_Y, a_Z); - if(IsBurnable(BlockID)) - { - m_World->SetBlock(a_X, a_Y, a_Z, E_BLOCK_FIRE, 0); - return true; - } - if(IsForeverBurnable(BlockID)) - { - char BlockAbove = m_World->GetBlock(a_X, a_Y + 1, a_Z); - if(BlockAbove == E_BLOCK_AIR) - { - m_World->SetBlock(a_X, a_Y + 1, a_Z, E_BLOCK_FIRE, 0); //Doesnīt notify the simulator so it wonīt go off - return true; - } - return false; - } - - return false; -} \ No newline at end of file diff --git a/source/cFireSimulator.h b/source/cFireSimulator.h deleted file mode 100644 index 5c6a3aeaa..000000000 --- a/source/cFireSimulator.h +++ /dev/null @@ -1,47 +0,0 @@ - -#pragma once - -#include "cSimulator.h" -#include "cBlockEntity.h" - - - - - -class Vector3i; -class cWorld; - - - - - -class cFireSimulator : public cSimulator -{ -public: - cFireSimulator( cWorld* a_World ); - ~cFireSimulator(); - - virtual void Simulate( float a_Dt ); - - virtual bool IsAllowedBlock( char a_BlockID ); - - virtual bool IsBurnable( char a_BlockID ); - virtual bool IsForeverBurnable( char a_BlockID ); - virtual bool FiresForever( char a_BlockID ); - -protected: - virtual void AddBlock(int a_X, int a_Y, int a_Z); - virtual void _AddBlock(int a_X, int a_Y, int a_Z); - virtual bool BurnBlockAround(int a_X, int a_Y, int a_Z); - virtual bool BurnBlock(int a_X, int a_Y, int a_Z); - - typedef std::list BlockList; - BlockList *m_Blocks; - BlockList *m_Buffer; - - BlockList *m_BurningBlocks; -}; - - - - diff --git a/source/cFluidSimulator.cpp b/source/cFluidSimulator.cpp deleted file mode 100644 index 937a40c68..000000000 --- a/source/cFluidSimulator.cpp +++ /dev/null @@ -1,692 +0,0 @@ -#include "Globals.h" - -#include -#include - -#include "cFluidSimulator.h" -#include "cWorld.h" -#include "Vector3i.h" -#include "BlockID.h" -#include "Defines.h" -#include "cItem.h" -#include "blocks/Block.h" - - - - - -//#define DEBUG_FLUID -#ifdef DEBUG_FLUID -#define LOG_FLUID(...) LOGWARN( __VA_ARGS__ ) -#else -#define LOG_FLUID(...) -#endif - - - - - -class cFluidSimulator::FluidData -{ -public: - FluidData( cWorld* a_World, cFluidSimulator *a_Simulator ) - : m_ActiveFluid( new std::set < Vector3i >() ) - , m_Simulator (a_Simulator) - , m_Buffer( new std::set< Vector3i >() ) - , m_World( a_World ) - {} - - ~FluidData() - { - delete m_Buffer; - delete m_ActiveFluid; - } - - void UpdateWave(Vector3i a_LeftCorner, Vector3i a_CurBlock) - { - Vector3i LevelPoints [] = { - Vector3i( a_CurBlock.x-1, a_CurBlock.y, a_CurBlock.z ), - Vector3i( a_CurBlock.x+1, a_CurBlock.y, a_CurBlock.z ), - Vector3i( a_CurBlock.x, a_CurBlock.y, a_CurBlock.z-1 ), - Vector3i( a_CurBlock.x, a_CurBlock.y, a_CurBlock.z+1 ), - }; - - for(int i=0; i<4; i++) - { - Vector3i cur = LevelPoints[i]; - switch(m_Relief[cur.x][cur.z]) - { - case E_HOLE: - { - m_StartSide[cur.x][cur.z] = m_StartSide[a_CurBlock.x][a_CurBlock.z]; - m_CurResult|=m_StartSide[cur.x][cur.z]; - m_NearestHole = m_WayLength[a_CurBlock.x][a_CurBlock.z] + 1; - LOG_FLUID("Hole found: %d \t curResult: %d", int(m_StartSide[cur.x][cur.z]), int(m_CurResult) ); - LOG_FLUID("Coordinates: (%d, %d)", cur.x, cur.z); - }break; - - case E_BLOCK: - {}break; - - case E_PLAIN: - { - if (m_WayLength[cur.x][cur.z] > m_WayLength[a_CurBlock.x][a_CurBlock.z] + 1) - { - m_WayLength[cur.x][cur.z] = m_WayLength[a_CurBlock.x][a_CurBlock.z] + 1; - m_StartSide[cur.x][cur.z] = m_StartSide[a_CurBlock.x][a_CurBlock.z]; - m_WaveQueue.push(cur); - } - else if(m_WayLength[cur.x][cur.z] == m_WayLength[a_CurBlock.x][a_CurBlock.z] + 1) - { - m_StartSide[cur.x][cur.z] |= m_StartSide[a_CurBlock.x][a_CurBlock.z]; - } - LOG_FLUID("Plain step: (%d, %d) from %d", cur.x, cur.z, m_StartSide[cur.x][cur.z]); - } - } - } - } - - std::vector< Vector3i > GetLowestPoints( int a_X, int a_Y, int a_Z ) - { - - std::vector< Vector3i > Points; //result - - Vector3i CornerGlobal(a_X - AREA_WIDTH/2, a_Y, a_Z - AREA_WIDTH/2); - - //TODO: rewrite without relief, get blocks directly in algorithm - for(int x=0; xGetBlock( CornerGlobal.x + x, CornerGlobal.y, CornerGlobal.z + z ); - char DownBlock = m_World->GetBlock( CornerGlobal.x + x, CornerGlobal.y-1, CornerGlobal.z + z ); - - if(m_Simulator->IsSolidBlock(UpperBlock)||(m_Simulator->IsStationaryBlock(UpperBlock))) - { - m_Relief[x][z] = E_BLOCK; - } - else if(m_Simulator->IsSolidBlock(DownBlock)) - { - m_Relief[x][z] = E_PLAIN; - } - else - { - m_Relief[x][z] = E_HOLE; - } - m_WayLength[x][z] = 255; - m_StartSide[x][z] = E_SIDE_NONE; - } - LOG_FLUID("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", m_Relief[x][0], m_Relief[x][1], m_Relief[x][2], m_Relief[x][3], m_Relief[x][4], m_Relief[x][5], m_Relief[x][6], m_Relief[x][7], m_Relief[x][8], m_Relief[x][9], m_Relief[x][10]); - } - - m_NearestHole = 5; - m_CurResult = 0; - while(!m_WaveQueue.empty()) m_WaveQueue.pop(); - - int left = AREA_WIDTH/2 - 1; - int right = AREA_WIDTH/2 + 1; - int center = AREA_WIDTH/2; - - Vector3i r(right, 0, center); //right block - Vector3i l(left, 0, center); //left block - Vector3i f(center, 0, right); //front block - Vector3i b(center, 0, left); //back block - Vector3i c(center, 0, center); //center block - - m_WayLength[c.x][c.z] = 0; - - Vector3i Nearest[] = {r, l, f, b}; - unsigned char Sides[] = {E_SIDE_RIGHT, E_SIDE_LEFT, E_SIDE_FRONT, E_SIDE_BACK}; - - for(int i=0; i<4; i++) - { - Vector3i cur = Nearest[i]; - switch(m_Relief[cur.x][cur.z]) - { - case E_HOLE: - { - m_StartSide[cur.x][cur.z] = Sides[i]; - m_CurResult |= m_StartSide[cur.x][cur.z]; - m_NearestHole = 1; - LOG_FLUID("Hole found: %d \t curResult: %d", int(Sides[i]), int(m_CurResult) ); - }break; - - case E_BLOCK: - {}break; - - case E_PLAIN: - { - m_WaveQueue.push(cur); - m_StartSide[cur.x][cur.z] = Sides[i]; - m_WayLength[cur.x][cur.z] = 1; - LOG_FLUID("Plain found: %d", int(Sides[i])); - } - } - } - - Vector3i curBlock; - - bool bContinue = !m_WaveQueue.empty(); - - if(!m_WaveQueue.empty()) - { - curBlock = m_WaveQueue.front(); - bContinue = (m_WayLength[curBlock.x][curBlock.z] < m_NearestHole); - } - - while(bContinue) - { - LOG_FLUID("while iteration" ); - curBlock = m_WaveQueue.front(); - UpdateWave(CornerGlobal, curBlock); - m_WaveQueue.pop(); - - bContinue = ( (!m_WaveQueue.empty()) && (m_WayLength[m_WaveQueue.front().x][m_WaveQueue.front().z] < m_NearestHole) ); - } - - - - if(m_CurResult & E_SIDE_LEFT) Points.push_back(Vector3i( a_X-1, a_Y, a_Z )); - if(m_CurResult & E_SIDE_RIGHT) Points.push_back(Vector3i( a_X+1, a_Y, a_Z )); - if(m_CurResult & E_SIDE_FRONT) Points.push_back(Vector3i( a_X, a_Y, a_Z+1 )); - if(m_CurResult & E_SIDE_BACK) Points.push_back(Vector3i( a_X, a_Y, a_Z-1 )); - - if(Points.empty()) - { - Vector3i LevelPoints [] = { - Vector3i( a_X-1, a_Y, a_Z ), - Vector3i( a_X+1, a_Y, a_Z ), - Vector3i( a_X, a_Y, a_Z-1 ), - Vector3i( a_X, a_Y, a_Z+1 ), - }; - for( int i = 0; i < 4; ++i ) - { - char Block = m_World->GetBlock( LevelPoints[i].x, a_Y, LevelPoints[i].z ); - if( m_Simulator->IsPassableForFluid(Block) ) - Points.push_back( LevelPoints[i] ); - } - } - - return Points; - } - - std::set< Vector3i >* m_ActiveFluid; - std::set< Vector3i >* m_Buffer; - cWorld* m_World; - cFluidSimulator *m_Simulator; - - const static int AREA_WIDTH = 11; - - const static unsigned char E_SIDE_RIGHT = 0x10; - const static unsigned char E_SIDE_LEFT = 0x20; - const static unsigned char E_SIDE_FRONT = 0x40; - const static unsigned char E_SIDE_BACK = 0x80; - const static unsigned char E_SIDE_NONE = 0x00; - - enum eRelief {E_HOLE = 0, E_PLAIN = 1, E_BLOCK = 2}; - - eRelief m_Relief[AREA_WIDTH][AREA_WIDTH]; - unsigned char m_WayLength[AREA_WIDTH][AREA_WIDTH]; - unsigned char m_StartSide[AREA_WIDTH][AREA_WIDTH]; - - std::queue m_WaveQueue; - - int m_NearestHole; - unsigned char m_CurResult; -}; - - - - - -cFluidSimulator::cFluidSimulator( cWorld* a_World ) - : cSimulator(a_World) - , m_Data(0) -{ - m_Data = new FluidData(a_World, this); -} - - - - - -cFluidSimulator::~cFluidSimulator() -{ - delete m_Data; -} - - - - - -void cFluidSimulator::AddBlock( int a_X, int a_Y, int a_Z ) -{ - char BlockType = m_World->GetBlock(a_X, a_Y, a_Z); - if (!IsAllowedBlock(BlockType)) //This should save very much time because it doesnīt have to iterate through all blocks - { - return; - } - - std::set< Vector3i > & ActiveFluid = *m_Data->m_ActiveFluid; - ActiveFluid.insert( Vector3i( a_X, a_Y, a_Z ) ); -} - - - - - -char cFluidSimulator::GetHighestLevelAround( int a_X, int a_Y, int a_Z ) -{ - char Max = m_MaxHeight + m_FlowReduction; - -#define __HIGHLEVEL_CHECK__( x, y, z ) \ - if( IsAllowedBlock( m_World->GetBlock( x, y, z ) ) ) \ - { \ - char Meta; \ - if( (Meta = m_World->GetBlockMeta( x, y, z ) ) < Max ) Max = Meta; \ - else if( Meta == m_MaxHeight + m_FlowReduction ) Max = 0; \ - if( Max == 0 ) return 0; \ - } - - __HIGHLEVEL_CHECK__( a_X-1, a_Y, a_Z ); - __HIGHLEVEL_CHECK__( a_X+1, a_Y, a_Z ); - __HIGHLEVEL_CHECK__( a_X, a_Y, a_Z-1 ); - __HIGHLEVEL_CHECK__( a_X, a_Y, a_Z+1 ); - - return Max; -} - - - - - -void cFluidSimulator::Simulate( float a_Dt ) -{ - m_Timer += a_Dt; - - if (m_Data->m_ActiveFluid->empty()) //Nothing to do if there is no active fluid ;) saves very little time ;D - { - return; - } - - std::swap( m_Data->m_ActiveFluid, m_Data->m_Buffer ); // Swap so blocks can be added to empty ActiveFluid array - m_Data->m_ActiveFluid->clear(); - - std::set< Vector3i > & FluidBlocks = *m_Data->m_Buffer; - for( std::set< Vector3i >::iterator itr = FluidBlocks.begin(); itr != FluidBlocks.end(); ++itr ) - { - const Vector3i & pos = *itr; - - if(UniqueSituation(pos)) - { - continue; - } - - char BlockID = m_World->GetBlock( pos.x, pos.y, pos.z ); - if( IsAllowedBlock( BlockID ) ) // only care about own fluid - { - bool bIsFed = false; - char Meta = m_World->GetBlockMeta( pos.x, pos.y, pos.z ); - char Feed = Meta; - if( BlockID == m_StationaryFluidBlock) Meta = 0; - if( Meta == 8 ) // Falling fluid - { - if( IsAllowedBlock( m_World->GetBlock(pos.x, pos.y+1, pos.z) ) ) // Block above is fluid - { - bIsFed = true; - Meta = 0; // Make it a full block - } - } - else if( Meta < m_FlowReduction ) // It's a full block, so it's always fed - { - bIsFed = true; - } - else - { - if( (Feed = GetHighestLevelAround( pos.x, pos.y, pos.z )) < Meta ) - bIsFed = true; - } - - - if( bIsFed ) - { - char DownID = m_World->GetBlock( pos.x, pos.y-1, pos.z ); - bool bWashedAwayItem = CanWashAway( DownID ); - if( (IsPassableForFluid(DownID) || bWashedAwayItem) && !IsStationaryBlock(DownID) ) // free for fluid - { - if( bWashedAwayItem ) - { - cBlockHandler * Handler = BlockHandler(DownID); - if(Handler->DropOnUnsuitable()) - { - Handler->DropBlock(m_World, pos.x, pos.y - 1, pos.z); - } - } - if (pos.y > 0) - { - m_World->FastSetBlock( pos.x, pos.y-1, pos.z, m_FluidBlock, 8 ); // falling - AddBlock( pos.x, pos.y-1, pos.z ); - ApplyUniqueToNearest(pos - Vector3i(0, 1, 0)); - } - } - if(IsSolidBlock(DownID)||( BlockID == m_StationaryFluidBlock)) // Not falling - { - if( Feed + m_FlowReduction < Meta ) - { - m_World->FastSetBlock( pos.x, pos.y, pos.z, m_FluidBlock, Feed + m_FlowReduction ); - AddBlock( pos.x, pos.y, pos.z ); - ApplyUniqueToNearest(pos); - } - else if(( Meta < m_MaxHeight )||( BlockID == m_StationaryFluidBlock)) // max is the lowest, so it cannot spread - { - std::vector< Vector3i > Points = m_Data->GetLowestPoints( pos.x, pos.y, pos.z ); - for( std::vector< Vector3i >::iterator itr = Points.begin(); itr != Points.end(); ++itr ) - { - Vector3i & p = *itr; - char BlockID = m_World->GetBlock( p.x, p.y, p.z ); - bool bWashedAwayItem = CanWashAway( BlockID ); - - if (!IsPassableForFluid(BlockID)) continue; - - if (!IsAllowedBlock(BlockID)) - { - if (bWashedAwayItem) - { - cBlockHandler * Handler = BlockHandler(DownID); - if(Handler->DropOnUnsuitable()) - { - Handler->DropBlock(m_World, p.x, p.y, p.z); - } - } - - if( p.y == pos.y ) - { - m_World->FastSetBlock(p.x, p.y, p.z, m_FluidBlock, Meta + m_FlowReduction); - } - else - { - m_World->FastSetBlock(p.x, p.y, p.z, m_FluidBlock, 8); - } - AddBlock( p.x, p.y, p.z ); - ApplyUniqueToNearest(p); - } - else // it's fluid - { - char PointMeta = m_World->GetBlockMeta( p.x, p.y, p.z ); - if( PointMeta > Meta + m_FlowReduction ) - { - AddBlock( p.x, p.y, p.z ); - ApplyUniqueToNearest(p); - } - } - } - } - } - } - else// not fed - { - m_World->FastSetBlock( pos.x, pos.y, pos.z, E_BLOCK_AIR, 0 ); - WakeUp( pos.x, pos.y, pos.z ); - } - } - } -} - - - - - -bool cFluidSimulator::IsPassableForFluid(char a_BlockID) -{ - return a_BlockID == E_BLOCK_AIR - || a_BlockID == E_BLOCK_FIRE - || IsAllowedBlock(a_BlockID) - || CanWashAway(a_BlockID); -} - - - - - -bool cFluidSimulator::IsStationaryBlock (char a_BlockID) -{ - return a_BlockID == m_StationaryFluidBlock; -} - - - - - -bool cFluidSimulator::CanWashAway( char a_BlockID ) -{ - switch( a_BlockID ) - { - case E_BLOCK_YELLOW_FLOWER: - case E_BLOCK_RED_ROSE: - case E_BLOCK_RED_MUSHROOM: - case E_BLOCK_BROWN_MUSHROOM: - case E_BLOCK_CACTUS: - return true; - default: - return false; - }; -} - - - - - -bool cFluidSimulator::IsSolidBlock( char a_BlockID ) -{ - return !(a_BlockID == E_BLOCK_AIR - || a_BlockID == E_BLOCK_FIRE - || IsBlockLava(a_BlockID) - || IsBlockWater(a_BlockID) - || CanWashAway(a_BlockID)); -} - - - - - -//TODO Not working very well yet :s -Direction cFluidSimulator::GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a_Over) -{ - char BlockID = m_World->GetBlock(a_X, a_Y, a_Z); - if(!IsAllowedBlock(BlockID)) //No Fluid -> No Flowing direction :D - return NONE; - - - /* - Disabled because of causing problems and beeing useless atm - char BlockBelow = m_World->GetBlock(a_X, a_Y - 1, a_Z); //If there is nothing or fluid below it -> dominating flow is down :D - if(BlockBelow == E_BLOCK_AIR || IsAllowedBlock(BlockBelow)) - return Y_MINUS; - */ - - char LowestPoint = m_World->GetBlockMeta(a_X, a_Y, a_Z); //Current Block Meta so only lower points will be counted - int X = 0, Y = 0, Z = 0; //Lowest Pos will be stored here - - if(IsAllowedBlock(m_World->GetBlock(a_X, a_Y + 1, a_Z)) && a_Over) //check for upper block to flow because this also affects the flowing direction - { - return GetFlowingDirection(a_X, a_Y + 1, a_Z, false); - } - - std::vector< Vector3i * > Points; - - Points.reserve(4); //Already allocate 4 places :D - - //add blocks around the checking pos - Points.push_back(new Vector3i(a_X - 1, a_Y, a_Z)); - Points.push_back(new Vector3i(a_X + 1, a_Y, a_Z)); - Points.push_back(new Vector3i(a_X, a_Y, a_Z + 1)); - Points.push_back(new Vector3i(a_X, a_Y, a_Z - 1)); - - for(std::vector::iterator it = Points.begin(); it < Points.end(); it++) - { - Vector3i *Pos = (*it); - char BlockID = m_World->GetBlock(Pos->x, Pos->y, Pos->z); - if(IsAllowedBlock(BlockID)) - { - char Meta = m_World->GetBlockMeta(Pos->x, Pos->y, Pos->z); - - if(Meta > LowestPoint) - { - LowestPoint = Meta; - X = Pos->x; - Y = Pos->y; - Z = Pos->z; - } - }else if(BlockID == E_BLOCK_AIR) - { - LowestPoint = 9; //This always dominates - X = Pos->x; - Y = Pos->y; - Z = Pos->z; - - } - delete Pos; - } - - if(LowestPoint == m_World->GetBlockMeta(a_X, a_Y, a_Z)) - return NONE; - - if(a_X - X > 0) - { - return X_MINUS; - } - - if(a_X - X < 0) - { - return X_PLUS; - } - - if(a_Z - Z > 0) - { - return Z_MINUS; - } - - if(a_Z - Z < 0) - { - return Z_PLUS; - } - - return NONE; -} - - - - - -bool cFluidSimulator::UniqueSituation(Vector3i a_Pos) -{ - bool result = false; - - char BlockId = m_World->GetBlock( a_Pos.x, a_Pos.y, a_Pos.z ); - char Meta = m_World->GetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z ); - - if(IsBlockWater(BlockId)) - { - - char UpperBlock = m_World->GetBlock( a_Pos.x, a_Pos.y + 1, a_Pos.z ); - if(IsBlockLava(UpperBlock)) - { - m_World->SetBlock(a_Pos.x, a_Pos.y, a_Pos.z, E_BLOCK_STONE, 0); - } - - - if(BlockId != E_BLOCK_STATIONARY_WATER) - { - char DownBlockId = m_World->GetBlock( a_Pos.x, a_Pos.y-1, a_Pos.z ); - if(IsSolidBlock(DownBlockId)) - { - Vector3i LevelPoints [] = { - Vector3i( a_Pos.x-1, a_Pos.y, a_Pos.z ), - Vector3i( a_Pos.x+1, a_Pos.y, a_Pos.z ), - Vector3i( a_Pos.x, a_Pos.y, a_Pos.z-1 ), - Vector3i( a_Pos.x, a_Pos.y, a_Pos.z+1 ), - }; - int SourceBlocksCount = 0; - for(int i=0; i<4; i++) - { - if (m_World->GetBlock(LevelPoints[i].x, LevelPoints[i].y, LevelPoints[i].z)==E_BLOCK_STATIONARY_WATER) - { - SourceBlocksCount++; - } - } - if(SourceBlocksCount>=2) - { - m_World->SetBlock(a_Pos.x, a_Pos.y, a_Pos.z, E_BLOCK_STATIONARY_WATER, 0); - } - } - - } - } - - if(IsBlockLava(BlockId)) - { - bool bWater = false; - - char UpperBlock = m_World->GetBlock( a_Pos.x, a_Pos.y + 1, a_Pos.z ); - if (IsBlockWater(UpperBlock)) - { - bWater = true; - } - else - { - Vector3i LevelPoints [] = { - Vector3i( a_Pos.x-1, a_Pos.y, a_Pos.z ), - Vector3i( a_Pos.x+1, a_Pos.y, a_Pos.z ), - Vector3i( a_Pos.x, a_Pos.y, a_Pos.z-1 ), - Vector3i( a_Pos.x, a_Pos.y, a_Pos.z+1 ), - }; - - for(int i=0; i<4; i++) - { - if (IsBlockWater(m_World->GetBlock(LevelPoints[i].x, LevelPoints[i].y, LevelPoints[i].z))) - { - bWater = true; - } - } - } - - - if(bWater) - { - if(BlockId == E_BLOCK_STATIONARY_LAVA) - { - m_World->SetBlock(a_Pos.x, a_Pos.y, a_Pos.z, E_BLOCK_OBSIDIAN, 0); - } - else if (MetaSetBlock(a_Pos.x, a_Pos.y, a_Pos.z, E_BLOCK_COBBLESTONE, 0); - } - } - } - - return result; -} - - - - - -void cFluidSimulator::ApplyUniqueToNearest(Vector3i a_Pos) -{ - Vector3i NearPoints [] = { - Vector3i( a_Pos.x-1, a_Pos.y, a_Pos.z ), - Vector3i( a_Pos.x+1, a_Pos.y, a_Pos.z ), - Vector3i( a_Pos.x, a_Pos.y, a_Pos.z-1 ), - Vector3i( a_Pos.x, a_Pos.y, a_Pos.z+1 ), - Vector3i( a_Pos.x, a_Pos.y-1, a_Pos.z ) - }; - - for(int i=0; i<5; i++) - { - UniqueSituation(NearPoints[i]); - } -} - - - - diff --git a/source/cFluidSimulator.h b/source/cFluidSimulator.h deleted file mode 100644 index 4557d3ef1..000000000 --- a/source/cFluidSimulator.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "cSimulator.h" -#include "Vector3i.h" - - -//TODO This definitly needs a better naming :D but how? -enum Direction -{ - X_PLUS, - X_MINUS, - Y_PLUS, - Y_MINUS, - Z_PLUS, - Z_MINUS, - NONE -}; - -class Vector3i; -class cWorld; -class cFluidSimulator : public cSimulator -{ -public: - cFluidSimulator( cWorld* a_World ); - ~cFluidSimulator(); - - virtual void Simulate( float a_Dt ); - - //Gets the flowing direction. if a_Over is true also the block over the current block affects the direction (standard) - Direction GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a_Over = true); - - virtual bool IsAllowedBlock( char a_BlockID ) = 0; - virtual bool IsStationaryBlock( char a_BlockID); - virtual bool IsPassableForFluid( char a_BlockID ); - bool CanWashAway( char a_BlockID ); - bool IsSolidBlock(char a_BlockID); -protected: - virtual void AddBlock( int a_X, int a_Y, int a_Z); - char GetHighestLevelAround( int a_X, int a_Y, int a_Z ); - - bool UniqueSituation(Vector3i a_Pos); //Applys special for this fluid rules like generation of water betwin sources, returns false if it is necessary to apply general rules - void ApplyUniqueToNearest(Vector3i a_Pos); - - float m_Timer; - - class FluidData; - FluidData* m_Data; - - //Customize - char m_FluidBlock; - char m_StationaryFluidBlock; - char m_MaxHeight; - char m_FlowReduction; - -}; \ No newline at end of file diff --git a/source/cFurnaceEntity.cpp b/source/cFurnaceEntity.cpp deleted file mode 100644 index 07df83452..000000000 --- a/source/cFurnaceEntity.cpp +++ /dev/null @@ -1,417 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cFurnaceEntity.h" -#include "BlockID.h" -#include "cItem.h" -#include "UI/Window.h" -#include "cPlayer.h" -#include "cWorld.h" -#include "cClientHandle.h" -#include "cFurnaceRecipe.h" -#include "cServer.h" -#include "cPickup.h" -#include "cRoot.h" -#include - - - - - - -enum -{ - PROGRESSBAR_SMELTING = 0, - PROGRESSBAR_FUEL = 1, -} ; - - - - - -cFurnaceEntity::cFurnaceEntity(int a_X, int a_Y, int a_Z, cWorld * a_World) - : cBlockEntity( E_BLOCK_FURNACE, a_X, a_Y, a_Z, a_World ) - , m_Items( new cItem[3] ) - , m_CookingItem( 0 ) - , m_CookTime( 0 ) - , m_TimeCooked( 0 ) - , m_BurnTime( 0 ) - , m_TimeBurned( 0 ) -{ - SetBlockEntity(this); // cBlockEntityWindowOwner -} - - - - - -cFurnaceEntity::~cFurnaceEntity() -{ - // Tell window its owner is destroyed - if( GetWindow() ) - { - GetWindow()->OwnerDestroyed(); - } - - // Clean up items - if( m_Items ) - { - delete [] m_Items; - } -} - - - - - -void cFurnaceEntity::Destroy() -{ - // Drop items - cItems Pickups; - for( int i = 0; i < 3; i++) - { - if( !m_Items[i].IsEmpty() ) - { - Pickups.push_back(m_Items[i]); - m_Items[i].Empty(); - } - } - m_World->SpawnItemPickups(Pickups, m_PosX, m_PosY, m_PosZ); -} - - - - - -void cFurnaceEntity::UsedBy(cPlayer * a_Player) -{ - if (GetWindow() == NULL) - { - OpenWindow(new cFurnaceWindow(m_PosX, m_PosY, m_PosZ, this)); - } - if (GetWindow() != NULL) - { - if (a_Player->GetWindow() != GetWindow()) - { - a_Player->OpenWindow(GetWindow()); - GetWindow()->SendWholeWindow(*a_Player->GetClientHandle()); - } - } -} - - - - - -bool cFurnaceEntity::Tick( float a_Dt ) -{ - /* - // DEBUG: - Int16 BurnTime = (Int16)(GetTimeToBurn() / 50.0); - Int16 CookTime = (Int16)(GetTimeCooked() / 50.0); - LOGD("Furnace: BurnTime %d, CookTime %d", BurnTime, CookTime); - */ - - if (m_BurnTime <= 0) - { - if (m_TimeCooked > 0) - { - // We have just finished smelting, reset the progress bar: - BroadcastProgress(PROGRESSBAR_SMELTING, 0); - m_TimeCooked = 0; - } - // There is no fuel and no flame, no need to tick at all - return false; - } - - // DEBUG: LOGD("Furnace: Left: %0.1f Burned: %0.1f Burn time: %0.1f", m_CookTime - m_TimeCooked, m_TimeBurned, m_BurnTime ); - - short SmeltingProgress = 0; - if ((m_CookingItem != NULL) && ((m_TimeBurned < m_BurnTime) || (m_TimeCooked + a_Dt >= m_CookTime))) - { - if (m_CookingItem->IsEqual(m_Items[2]) || m_Items[2].IsEmpty()) - { - m_TimeCooked += a_Dt; - if ( m_TimeCooked >= m_CookTime ) - { - m_Items[0].m_ItemCount--; - if( m_Items[0].IsEmpty() ) m_Items[0].Empty(); - - m_Items[2].m_ItemHealth = m_CookingItem->m_ItemHealth; - m_Items[2].m_ItemID = m_CookingItem->m_ItemID; - m_Items[2].m_ItemCount += m_CookingItem->m_ItemCount; - delete m_CookingItem; - m_CookingItem = NULL; - - cWindow * Window = GetWindow(); - if (Window != NULL) - { - Window->BroadcastWholeWindow(); - } - - m_TimeCooked -= m_CookTime; - StartCooking(); - } - SmeltingProgress = (short)( m_TimeCooked * (180.f / m_CookTime)); - if (SmeltingProgress > 180) SmeltingProgress = 180; - if (SmeltingProgress < 0) SmeltingProgress = 0; - } - } - BroadcastProgress(PROGRESSBAR_SMELTING, SmeltingProgress); - - m_TimeBurned += a_Dt; - - cWindow * Window = GetWindow(); - if (m_TimeBurned >= m_BurnTime) - { - m_TimeBurned -= m_BurnTime; - m_BurnTime = 0; - if (StartCooking() && (Window != NULL)) - { - Window->BroadcastWholeWindow(); - } - } - short Value = 0; - if (m_BurnTime > 0.f) - { - Value = 250 - (short)( m_TimeBurned * (250.f / m_BurnTime)); - if (Value > 250) Value = 250; - if (Value < 0) Value = 0; - } - BroadcastProgress(PROGRESSBAR_FUEL, Value); - - return ((m_CookingItem != NULL) || (m_TimeBurned < m_BurnTime)) && (m_BurnTime > 0.0); // Keep on ticking, if there's more to cook, or if it's cooking -} - - - - - -bool cFurnaceEntity::StartCooking(void) -{ - cFurnaceRecipe* FR = cRoot::Get()->GetFurnaceRecipe(); - float BurnTime = FR->GetBurnTime( m_Items[1] ); - if( (m_TimeBurned < m_BurnTime) || BurnTime > 0.f ) // burnable material - { - const cFurnaceRecipe::Recipe* R = FR->GetRecipeFrom( m_Items[0] ); - if (R != NULL) // cook able ingredient - { - if (m_Items[2].IsEqual(*R->Out) || m_Items[2].IsEmpty()) - { - // good to go - - if( m_TimeBurned >= m_BurnTime ) // burn new material - { - m_Items[1].m_ItemCount--; - if( m_Items[1].m_ItemCount <= 0 ) m_Items[1].Empty(); - m_TimeBurned = 0; - m_BurnTime = BurnTime; - } - - if( !m_CookingItem ) // Only cook new item if not already cooking - { - m_CookingItem = new cItem( *R->Out ); // Resulting item - m_TimeCooked = 0.f; - m_CookTime = R->CookTime; - } - return true; - } - } - } - return false; -} - - - - - -bool cFurnaceEntity::ContinueCooking(void) -{ - cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe(); - float BurnTime = FR->GetBurnTime( m_Items[1] ); - if( (m_TimeBurned < m_BurnTime) || BurnTime > 0.f ) // burnable material - { - const cFurnaceRecipe::Recipe * R = FR->GetRecipeFrom( m_Items[0] ); - if (R != NULL) // cook able ingredient - { - if (m_Items[2].IsEqual(*R->Out) || m_Items[2].IsEmpty()) - { - // good to go - if (m_CookingItem == NULL) // Only cook new item if not already cooking - { - m_CookingItem = new cItem( *R->Out ); // Resulting item - } - return true; - } - } - } - return false; -} - - - - - -void cFurnaceEntity::ResetCookTimer() -{ - delete m_CookingItem; - m_CookingItem = NULL; - m_TimeCooked = 0.f; - m_CookTime = 0.f; -} - - - - - -void cFurnaceEntity::SetSlot(int a_Slot, const cItem & a_Item) -{ - if ((a_Slot < 0) || (a_Slot >= 3)) - { - ASSERT(!"Furnace: slot number out of range"); - return; - } - m_Items[a_Slot] = a_Item; -} - - - - - -#define READ(File, Var) \ - if (File.Read(&Var, sizeof(Var)) != sizeof(Var)) \ - { \ - LOGERROR("ERROR READING cFurnaceEntity %s FROM FILE (line %d)", #Var, __LINE__); \ - return false; \ - } - -bool cFurnaceEntity::LoadFromFile(cFile & f) -{ - READ(f, m_PosX); - READ(f, m_PosY); - READ(f, m_PosZ); - - unsigned int NumSlots = 0; - READ(f, NumSlots); - m_Items = new cItem[ NumSlots ]; - for(unsigned int i = 0; i < NumSlots; i++) - { - cItem & Item = m_Items[i]; - READ(f, Item.m_ItemID); - READ(f, Item.m_ItemCount); - READ(f, Item.m_ItemHealth); - } - cItem CookingItem; - READ(f, CookingItem.m_ItemID); - READ(f, CookingItem.m_ItemCount); - READ(f, CookingItem.m_ItemHealth); - if (!CookingItem.IsEmpty()) - { - m_CookingItem = new cItem(CookingItem); - } - - READ(f, m_CookTime); - READ(f, m_TimeCooked); - READ(f, m_BurnTime); - READ(f, m_TimeBurned); - - return true; -} - - - - - -bool cFurnaceEntity::LoadFromJson( const Json::Value& a_Value ) -{ - m_PosX = a_Value.get("x", 0).asInt(); - m_PosY = a_Value.get("y", 0).asInt(); - m_PosZ = a_Value.get("z", 0).asInt(); - - Json::Value AllSlots = a_Value.get("Slots", 0); - int SlotIdx = 0; - for( Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr ) - { - m_Items[ SlotIdx ].FromJson( *itr ); - SlotIdx++; - } - - // Get currently cooking item - Json::Value JsonItem = a_Value.get("Cooking", Json::nullValue ); - if( !JsonItem.empty() ) - { - cItem Item; - Item.FromJson( JsonItem ); - if( !Item.IsEmpty() ) - { - m_CookingItem = new cItem( Item ); - } - } - - m_CookTime = (float)a_Value.get("CookTime", 0).asDouble(); - m_TimeCooked = (float)a_Value.get("TimeCooked", 0).asDouble(); - m_BurnTime = (float)a_Value.get("BurnTime", 0).asDouble(); - m_TimeBurned = (float)a_Value.get("TimeBurned", 0).asDouble(); - - return true; -} - - - - - -void cFurnaceEntity::SaveToJson( Json::Value& a_Value ) -{ - a_Value["x"] = m_PosX; - a_Value["y"] = m_PosY; - a_Value["z"] = m_PosZ; - - Json::Value AllSlots; - for(unsigned int i = 0; i < 3; i++) - { - Json::Value Slot; - m_Items[ i ].GetJson( Slot ); - AllSlots.append( Slot ); - } - a_Value["Slots"] = AllSlots; - - // Currently cooking item - if( m_CookingItem ) - { - Json::Value JsonItem; - m_CookingItem->GetJson( JsonItem ); - a_Value["Cooking"] = JsonItem; - } - - a_Value["CookTime"] = m_CookTime; - a_Value["TimeCooked"] = m_TimeCooked; - a_Value["BurnTime"] = m_BurnTime; - a_Value["TimeBurned"] = m_TimeBurned; -} - - - - - -void cFurnaceEntity::SendTo(cClientHandle & a_Client) -{ - // Nothing needs to be sent - UNUSED(a_Client); -} - - - - - -void cFurnaceEntity::BroadcastProgress(int a_ProgressbarID, short a_Value) -{ - cWindow * Window = GetWindow(); - if (Window != NULL) - { - Window->BroadcastInventoryProgress(a_ProgressbarID, a_Value); - } -} - - - - diff --git a/source/cFurnaceEntity.h b/source/cFurnaceEntity.h deleted file mode 100644 index 60aec47af..000000000 --- a/source/cFurnaceEntity.h +++ /dev/null @@ -1,78 +0,0 @@ - -#pragma once - -#include "cBlockEntity.h" -#include "UI/WindowOwner.h" -#include "cItem.h" - - - - - -namespace Json -{ - class Value; -} - -class cClientHandle; -class cServer; - - - - - -class cFurnaceEntity : - public cBlockEntity, - public cBlockEntityWindowOwner -{ -public: - cFurnaceEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); - virtual ~cFurnaceEntity(); - virtual void Destroy(); - - bool LoadFromFile(cFile & a_File); // deprecated format - - bool LoadFromJson(const Json::Value& a_Value ); - virtual void SaveToJson(Json::Value& a_Value ) override; - - virtual void SendTo(cClientHandle & a_Client) override; - - // Returns true if there's any change, forcing the chunk to go dirty. - bool Tick( float a_Dt ); - - virtual void UsedBy( cPlayer * a_Player ) override; - - bool StartCooking(); - - /// Restarts cooking. Used after the furnace is loaded from storage to set up the internal variables so that cooking continues, if it was active. Returns true if cooking. - bool ContinueCooking(void); - - void ResetCookTimer(); - - const cItem * GetSlot(int i) const { return &(m_Items[i]); } - - void SetSlot(int a_Slot, const cItem & a_Item); - - float GetTimeCooked(void) const {return m_TimeCooked; } - float GetTimeToBurn(void) const {return m_BurnTime - m_TimeBurned; } - - void SetBurnTimes(float a_BurnTime, float a_TimeBurned) {m_BurnTime = a_BurnTime; m_TimeBurned = 0; } - void SetCookTimes(float a_CookTime, float a_TimeCooked) {m_CookTime = a_CookTime; m_TimeCooked = a_TimeCooked; } - -private: - - cItem * m_Items; - cItem * m_CookingItem; - - // All timers are in 1 ms - float m_CookTime; // Amount of time needed to fully cook current item - float m_TimeCooked; // Amount of time that the current item has been cooking - float m_BurnTime; // Amount of time that the current fuel can burn (in total); zero if no fuel burning - float m_TimeBurned; // Amount of time that the current fuel has been burning - - void BroadcastProgress(int a_ProgressbarID, short a_Value); -}; - - - - diff --git a/source/cFurnaceRecipe.cpp b/source/cFurnaceRecipe.cpp deleted file mode 100644 index 3f6416d92..000000000 --- a/source/cFurnaceRecipe.cpp +++ /dev/null @@ -1,241 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cFurnaceRecipe.h" -#include "cItem.h" - -#include -#include - - - - - -typedef std::list< cFurnaceRecipe::Recipe > RecipeList; -typedef std::list< cFurnaceRecipe::Fuel > FuelList; - - - - - -struct cFurnaceRecipe::sFurnaceRecipeState -{ - RecipeList Recipes; - FuelList Fuel; -}; - - - - - -cFurnaceRecipe::cFurnaceRecipe() - : m_pState( new sFurnaceRecipeState ) -{ - ReloadRecipes(); -} - - - - - -cFurnaceRecipe::~cFurnaceRecipe() -{ - ClearRecipes(); - delete m_pState; -} - - - - - -void cFurnaceRecipe::ReloadRecipes() -{ - ClearRecipes(); - LOG("-- Loading furnace recipes --"); - - std::ifstream f; - char a_File[] = "furnace.txt"; - f.open(a_File, std::ios::in); - std::string input; - - if( !f.good() ) - { - f.close(); - LOG("Could not open file for recipes: %s", a_File); - return; - } - - bool bSyntaxError = false; - while( f.good() ) - { - char c; - - ////////////////////////////////////////////////////////////////////////// - // comments - f >> c; - f.unget(); - if( c == '#' ) - { - while( f.good() && c != '\n' ) - { - f.get( c ); - } - continue; - } - - - ////////////////////////////////////////////////////////////////////////// - // Line breaks - f.get( c ); - while( f.good() && ( c == '\n' || c == '\r' ) ) { f.get( c ); } - if( f.eof() ) break; - f.unget(); - - ////////////////////////////////////////////////////////////////////////// - // Check for fuel - f >> c; - if( c == '!' ) // It's fuel :) - { - // Read item - int IItemID = 0, IItemCount = 0, IItemHealth = 0; - f >> IItemID; - f >> c; if( c != ':' ) { bSyntaxError = true; break; } - f >> IItemCount; - - // Optional health - f >> c; - if( c != ':' ) - f.unget(); - else - { - f >> IItemHealth; - } - - // Burn time - float BurnTime; - f >> c; if( c != '=' ) { bSyntaxError = true; break; } - f >> BurnTime; - - // Add to fuel list - Fuel F; - F.In = new cItem( (ENUM_ITEM_ID) IItemID, (char)IItemCount, (short)IItemHealth ); - F.BurnTime = BurnTime; - m_pState->Fuel.push_back( F ); - continue; - } - f.unget(); - - ////////////////////////////////////////////////////////////////////////// - // Read items - int IItemID = 0, IItemCount = 0, IItemHealth = 0; - f >> IItemID; - f >> c; if( c != ':' ) { bSyntaxError = true; break; } - f >> IItemCount; - - // Optional health - f >> c; - if( c != ':' ) - f.unget(); - else - { - f >> IItemHealth; - } - - float CookTime; - f >> c; if( c != '@' ) { bSyntaxError = true; break; } - f >> CookTime; - - int OItemID = 0, OItemCount = 0, OItemHealth = 0; - f >> c; if( c != '=' ) { bSyntaxError = true; break; } - f >> OItemID; - f >> c; if( c != ':' ) { bSyntaxError = true; break; } - f >> OItemCount; - - // Optional health - f >> c; - if( c != ':' ) - f.unget(); - else - { - f >> OItemHealth; - } - - // Add to recipe list - Recipe R; - R.In = new cItem( (ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth ); - R.Out = new cItem( (ENUM_ITEM_ID)OItemID, (char)OItemCount, (short)OItemHealth ); - R.CookTime = CookTime; - m_pState->Recipes.push_back( R ); - } - if( bSyntaxError ) - { - LOGERROR("ERROR: FurnaceRecipe, syntax error" ); - } - LOG("Got %i furnace recipes, and %i fuels.", m_pState->Recipes.size(), m_pState->Fuel.size() ); - - LOG("-- Done loading furnace recipes --"); -} - - - - - -void cFurnaceRecipe::ClearRecipes() -{ - for( RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr ) - { - Recipe R = *itr; - delete R.In; - delete R.Out; - } - m_pState->Recipes.clear(); - - for( FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr ) - { - Fuel F = *itr; - delete F.In; - } - m_pState->Fuel.clear(); -} - -const cFurnaceRecipe::Recipe* cFurnaceRecipe::GetRecipeFrom( const cItem & a_Ingredient ) const -{ - const Recipe* BestRecipe = 0; - for( RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr ) - { - const Recipe & R = *itr; - if( (R.In->m_ItemID == a_Ingredient.m_ItemID) && (R.In->m_ItemCount <= a_Ingredient.m_ItemCount ) ) - { - if( BestRecipe && (BestRecipe->In->m_ItemCount > R.In->m_ItemCount) ) - { - continue; - } - else - { - BestRecipe = &R; - } - } - } - return BestRecipe; -} - -float cFurnaceRecipe::GetBurnTime( const cItem & a_Fuel ) const -{ - float BestFuel = 0.f; - for( FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr ) - { - const Fuel & F = *itr; - if( (F.In->m_ItemID == a_Fuel.m_ItemID) && (F.In->m_ItemCount <= a_Fuel.m_ItemCount ) ) - { - if( BestFuel > 0.f && (BestFuel > F.BurnTime ) ) - { - continue; - } - else - { - BestFuel = F.BurnTime; - } - } - } - return BestFuel; -} \ No newline at end of file diff --git a/source/cFurnaceRecipe.h b/source/cFurnaceRecipe.h deleted file mode 100644 index ccb008604..000000000 --- a/source/cFurnaceRecipe.h +++ /dev/null @@ -1,46 +0,0 @@ - -#pragma once - - - - - -class cItem; - - - - - -class cFurnaceRecipe -{ -public: - cFurnaceRecipe(); - ~cFurnaceRecipe(); - - void ReloadRecipes(); - - struct Fuel - { - cItem* In; - float BurnTime; - }; - - struct Recipe - { - cItem* In; - cItem* Out; - float CookTime; - }; - const Recipe* GetRecipeFrom( const cItem & a_Ingredient ) const; - float GetBurnTime( const cItem & a_Fuel ) const; - -private: - void ClearRecipes(); - - struct sFurnaceRecipeState; - sFurnaceRecipeState* m_pState; -}; - - - - diff --git a/source/cGroup.cpp b/source/cGroup.cpp deleted file mode 100644 index 04549b0eb..000000000 --- a/source/cGroup.cpp +++ /dev/null @@ -1,37 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cGroup.h" - -void cGroup::AddCommand( std::string a_Command ) -{ - m_Commands[ a_Command ] = true; -} - -void cGroup::AddPermission( std::string a_Permission ) -{ - m_Permissions[ a_Permission ] = true; -} - -bool cGroup::HasCommand( std::string a_Command ) -{ - if( m_Commands.find("*") != m_Commands.end() ) return true; - - CommandMap::iterator itr = m_Commands.find( a_Command ); - if( itr != m_Commands.end() ) - { - if( itr->second ) return true; - } - - for( GroupList::iterator itr = m_Inherits.begin(); itr != m_Inherits.end(); ++itr ) - { - if( (*itr)->HasCommand( a_Command ) ) return true; - } - return false; -} - -void cGroup::InheritFrom( cGroup* a_Group ) -{ - m_Inherits.remove( a_Group ); - m_Inherits.push_back( a_Group ); -} \ No newline at end of file diff --git a/source/cGroup.h b/source/cGroup.h deleted file mode 100644 index 447802e88..000000000 --- a/source/cGroup.h +++ /dev/null @@ -1,40 +0,0 @@ - -#pragma once - - - - - -class cGroup //tolua_export -{ //tolua_export -public: //tolua_export - cGroup() {} - ~cGroup() {} - - void SetName( std::string a_Name ) { m_Name = a_Name; } //tolua_export - const std::string & GetName() const { return m_Name; } //tolua_export - void SetColor( std::string a_Color ) { m_Color = a_Color; } //tolua_export - void AddCommand( std::string a_Command ); //tolua_export - void AddPermission( std::string a_Permission ); //tolua_export - void InheritFrom( cGroup* a_Group ); //tolua_export - - bool HasCommand( std::string a_Command ); //tolua_export - - typedef std::map< std::string, bool > PermissionMap; - const PermissionMap & GetPermissions() const { return m_Permissions; } - - typedef std::map< std::string, bool > CommandMap; - const CommandMap & GetCommands() const { return m_Commands; } - - const AString & GetColor() const { return m_Color; } //tolua_export - - typedef std::list< cGroup* > GroupList; - const GroupList & GetInherits() const { return m_Inherits; } -private: - std::string m_Name; - std::string m_Color; - - PermissionMap m_Permissions; - CommandMap m_Commands; - GroupList m_Inherits; -};//tolua_export \ No newline at end of file diff --git a/source/cGroupManager.cpp b/source/cGroupManager.cpp deleted file mode 100644 index 2458308c9..000000000 --- a/source/cGroupManager.cpp +++ /dev/null @@ -1,108 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cGroupManager.h" -#include "cGroup.h" -#include "../iniFile/iniFile.h" -#include "cChatColor.h" -#include "cRoot.h" - - - - - -typedef std::map< std::string, cGroup* > GroupMap; -struct cGroupManager::sGroupManagerState -{ - GroupMap Groups; -}; - -cGroupManager* cGroupManager::GetGroupManager() -{ - LOGWARN("WARNING: Using deprecated function cGroupManager::GetGroupManager() use cRoot::Get()->GetGroupManager() instead!"); - return cRoot::Get()->GetGroupManager(); -} - -cGroupManager::~cGroupManager() -{ - for( GroupMap::iterator itr = m_pState->Groups.begin(); itr != m_pState->Groups.end(); ++itr ) - { - delete itr->second; - } - m_pState->Groups.clear(); - - delete m_pState; -} - -cGroupManager::cGroupManager() - : m_pState( new sGroupManagerState ) -{ - LOG("-- Loading Groups --"); - cIniFile IniFile("groups.ini"); - if( IniFile.ReadFile() ) - { - unsigned int NumKeys = IniFile.GetNumKeys(); - for( unsigned int i = 0; i < NumKeys; i++ ) - { - std::string KeyName = IniFile.GetKeyName( i ); - cGroup* Group = GetGroup( KeyName.c_str() ); - - LOG("Loading group: %s", KeyName.c_str() ); - - Group->SetName( KeyName ); - char Color = IniFile.GetValue( KeyName, "Color", "-" )[0]; - if( Color != '-' ) - Group->SetColor( cChatColor::MakeColor(Color) ); - else - Group->SetColor( cChatColor::White ); - - std::string Commands = IniFile.GetValue( KeyName, "Commands", "" ); - if( Commands.size() > 0 ) - { - AStringVector Split = StringSplit( Commands, "," ); - for( unsigned int i = 0; i < Split.size(); i++) - { - Group->AddCommand( Split[i] ); - //LOG("%s", Split[i].c_str() ); - } - } - - std::string Permissions = IniFile.GetValue( KeyName, "Permissions", "" ); - if( Permissions.size() > 0 ) - { - AStringVector Split = StringSplit( Permissions, "," ); - for( unsigned int i = 0; i < Split.size(); i++) - { - Group->AddPermission( Split[i] ); - LOGINFO("Permission: %s", Split[i].c_str() ); - } - } - - std::string Groups = IniFile.GetValue( KeyName, "Inherits", "" ); - if( Groups.size() > 0 ) - { - AStringVector Split = StringSplit( Groups, "," ); - for( unsigned int i = 0; i < Split.size(); i++) - { - Group->InheritFrom( GetGroup( Split[i].c_str() ) ); - } - } - } - } - LOG("-- Done Loading Groups --"); -} - -cGroup* cGroupManager::GetGroup( const char* a_Name ) -{ - GroupMap::iterator itr = m_pState->Groups.find( a_Name ); - if( itr != m_pState->Groups.end() ) - { - return itr->second; - } - - cGroup* Group = new cGroup(); - m_pState->Groups[a_Name] = Group; - - return Group; - -} \ No newline at end of file diff --git a/source/cGroupManager.h b/source/cGroupManager.h deleted file mode 100644 index 214d6991d..000000000 --- a/source/cGroupManager.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -class cGroup; -class cGroupManager -{ -public: - static cGroupManager * GetGroupManager(); //tolua_export - - cGroup* GetGroup( const char* a_Name ); -private: - friend class cRoot; - cGroupManager(); - ~cGroupManager(); - - struct sGroupManagerState; - sGroupManagerState* m_pState; -}; \ No newline at end of file diff --git a/source/cHeartBeat.cpp b/source/cHeartBeat.cpp deleted file mode 100644 index 1cff0abb2..000000000 --- a/source/cHeartBeat.cpp +++ /dev/null @@ -1,143 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cHeartBeat.h" -#include "cMCLogger.h" -#include "md5/md5.h" - -#include "cRoot.h" -#include "cServer.h" - - - - - -cHeartBeat::cHeartBeat() -{ - m_State = 0; - Authenticate(); -} - - - - - -cHeartBeat::~cHeartBeat() -{ -} - - - - - -void cHeartBeat::ReceivedData( char a_Data[256], int a_Size ) -{ - if( a_Size < 0 ) // Disconnected - return; - - char MySalt[] = "1234567890"; - - if( a_Size == 0 ) - { - Authenticate(); - return; - } - - bool bLoop = false; - do - { - switch (m_State) - { - case 1: - { - m_ServerID = std::string( a_Data, a_Size ); - LOGINFO("Got server ID %s", m_ServerID.c_str() ); - std::string Hash = md5( m_ServerID + std::string( MySalt ) ); - CloseSocket(); - if( Connect( "mc-server.org", 80 ) ) - { - SendMessage( (std::string("GET http://master.mc-server.org/?hash=") + Hash + std::string("&server=") + m_ServerID + "\n").c_str() ); - m_State = 2; - } - } - break; - case 2: - { - std::string ReturnedString = std::string( a_Data, a_Size ); - if( ReturnedString.compare("VALIDATED") == 0 ) - { - LOGINFO("Successfully validated server on master server list"); - } - else - { - LOGINFO("Could not validate server! Will try again later."); - cSleep::MilliSleep( 10*1000 ); - Authenticate(); - return; - } - m_State = 3; - } // Don't break, but fall through and update server info - case 3: - { - cSleep::MilliSleep( 10*1000 ); - SendUpdate(); - m_State = 4; - } - break; - case 4: - { - if( a_Data[0] == '0' ) - { - LOGINFO("Successfully updated server info!"); - cSleep::MilliSleep( 10*1000 ); - SendUpdate(); - } - else - { - LOGINFO("Failed to update server info, reauthenticating"); - Authenticate(); - } - } - default: - break; - }; - } while( bLoop ); -} - - - - - -void cHeartBeat::SendUpdate() -{ - CloseSocket(); - if( Connect( "mc-server.org", 80 ) ) - { - int Port = cRoot::Get()->GetServer()->GetPort(); - AString Msg; - AString sPort; - Printf(sPort, "%i", Port); - AString sChecksum = md5( m_ServerID + sPort ); - Printf(Msg, "GET http://master.mc-server.org/?update=%s&checksum=%s&port=%d\n", m_ServerID.c_str(), sChecksum.c_str(), Port); - SendMessage(Msg.c_str()); - } -} - - - - - -void cHeartBeat::Authenticate() -{ - CloseSocket(); - if (Connect( "mc-server.org", 80)) - { - m_State = 1; - int RetVal = SendMessage( "GET http://master.mc-server.org/\r\n\r\n"); - LOGINFO("Returned %i", RetVal); - } -} - - - - diff --git a/source/cHeartBeat.h b/source/cHeartBeat.h deleted file mode 100644 index 79465507c..000000000 --- a/source/cHeartBeat.h +++ /dev/null @@ -1,28 +0,0 @@ - -#pragma once - -#include "OSSupport/TCPLink.h" - - - - - -class cHeartBeat : public cTCPLink -{ -public: - cHeartBeat(); - ~cHeartBeat(); -private: - virtual void ReceivedData( char a_Data[256], int a_Size ); - - void Authenticate(); - int m_State; - - void SendUpdate(); - - std::string m_ServerID; -}; - - - - diff --git a/source/cInventory.cpp b/source/cInventory.cpp deleted file mode 100644 index 41d4c14fa..000000000 --- a/source/cInventory.cpp +++ /dev/null @@ -1,427 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cInventory.h" -#include "cPlayer.h" -#include "cClientHandle.h" -#include "UI/Window.h" -#include "cItem.h" -#include "cRoot.h" - -#include - -#include "items/Item.h" - - - - - -cInventory::~cInventory() -{ - /* - // TODO - cWindow wnd = GetWindow(); - if (wnd != NULL) - { - wnd->Close(*m_Owner); - } - CloseWindow(); - */ -} - - - - - -cInventory::cInventory(cPlayer & a_Owner) : - m_Owner(a_Owner) -{ - for (unsigned int i = 0; i < c_NumSlots; i++) - { - m_Slots[i].Empty(); - } - - m_CraftSlots = m_Slots + c_CraftOffset; - m_ArmorSlots = m_Slots + c_ArmorOffset; - m_MainSlots = m_Slots + c_MainOffset; - m_HotSlots = m_Slots + c_HotOffset; - - SetEquippedSlot(0); -} - - - - - -bool cInventory::AddItem( cItem & a_Item ) -{ - cItem BackupSlots[c_NumSlots]; - memcpy( BackupSlots, m_Slots, c_NumSlots * sizeof( cItem ) ); - - bool ChangedSlots[c_NumSlots]; - memset( ChangedSlots, false, c_NumSlots * sizeof( bool ) ); - - if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_HotOffset, c_HotSlots, ChangedSlots, 0 ); - if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_MainOffset, c_MainSlots, ChangedSlots, 0 ); - if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_HotOffset, c_HotSlots, ChangedSlots, 2 ); - if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_MainOffset, c_MainSlots, ChangedSlots, 2 ); - - if( a_Item.m_ItemCount > 0 ) // Could not add all items - { - // retore backup - memcpy( m_Slots, BackupSlots, c_NumSlots * sizeof( cItem ) ); - return false; - } - - for(unsigned int i = 0; i < c_NumSlots; i++) - { - if( ChangedSlots[i] ) - { - LOG("Item was added to %i ID:%i Count:%i", i, m_Slots[i].m_ItemID, m_Slots[i].m_ItemCount ); - SendSlot(i); - } - } - - return (a_Item.m_ItemCount == 0); -} - - - - - -// TODO: Right now if you dont have enough items, the items you did have are removed, and the function returns false anyway -bool cInventory::RemoveItem( cItem & a_Item ) -{ - // First check equipped slot - if ((m_EquippedSlot >= 0) && (m_EquippedSlot < 9)) - { - if (m_HotSlots[m_EquippedSlot].m_ItemID == a_Item.m_ItemID) - { - cItem & Item = m_HotSlots[m_EquippedSlot]; - if(Item.m_ItemCount > a_Item.m_ItemCount) - { - Item.m_ItemCount -= a_Item.m_ItemCount; - SendSlot( m_EquippedSlot + c_HotOffset ); - return true; - } - else if(Item.m_ItemCount > 0 ) - { - a_Item.m_ItemCount -= Item.m_ItemCount; - Item.Empty(); - SendSlot( m_EquippedSlot + c_HotOffset ); - } - } - } - - // Then check other slotz - if (a_Item.m_ItemCount > 0) - { - for(int i = 0; i < 36; i++) - { - cItem & Item = m_MainSlots[i]; - if( Item.m_ItemID == a_Item.m_ItemID ) - { - if(Item.m_ItemCount > a_Item.m_ItemCount) - { - Item.m_ItemCount -= a_Item.m_ItemCount; - SendSlot( i + c_MainOffset ); - return true; - } - else if(Item.m_ItemCount > 0 ) - { - a_Item.m_ItemCount -= Item.m_ItemCount; - Item.Empty(); - SendSlot( i + c_MainOffset ); - } - } - } - } - - return (a_Item.m_ItemCount == 0); -} - - - - - -void cInventory::Clear() -{ - for(unsigned int i = 0; i < c_NumSlots; i++) - m_Slots[i].Empty(); -} - - - - - -cItem * cInventory::GetSlotsForType( int a_Type ) -{ - switch( a_Type ) - { - case -1: - return m_MainSlots; - case -2: - return m_CraftSlots; - case -3: - return m_ArmorSlots; - } - return 0; -} - - - - - -/* -int cInventory::GetSlotCountForType( int a_Type ) -{ - switch (a_Type) - { - case -1: - return 36; - case -2: - case -3: - return 4; - } - return 0; -} -*/ - - - - - -cItem* cInventory::GetSlot( int a_SlotNum ) -{ - if( a_SlotNum < 0 || a_SlotNum >= (short)c_NumSlots ) return 0; - return &m_Slots[a_SlotNum]; -} - - - - - -cItem* cInventory::GetFromHotBar( int a_SlotNum ) -{ - if ((a_SlotNum < 0) || (a_SlotNum >= 9)) - { - return NULL; - } - return &m_HotSlots[a_SlotNum]; -} - - - - - -void cInventory::SetEquippedSlot(int a_SlotNum) -{ - if ((a_SlotNum < 0) || (a_SlotNum >= 9)) - { - m_EquippedSlot = 0; - } - else - { - m_EquippedSlot = (short)a_SlotNum; - } - m_EquippedItem = GetFromHotBar(m_EquippedSlot); -} - - - - - -cItem & cInventory::GetEquippedItem(void) -{ - cItem* Item = GetFromHotBar( m_EquippedSlot ); - if( Item ) - { - *m_EquippedItem = *Item; - return *Item; - } - else - { - m_EquippedItem->Empty(); - } - return *m_EquippedItem; -} - - - - - -const cItem & cInventory::GetEquippedItem(void) const -{ - return *m_EquippedItem; -} - - - - - -void cInventory::SendWholeInventory(cClientHandle & a_Client) -{ - a_Client.SendWholeInventory(*this); -} - - - - - -void cInventory::SendSlot(int a_SlotNum) -{ - cItem * Item = GetSlot(a_SlotNum); - if (Item != NULL) - { - if (Item->IsEmpty()) - { - // Sanitize items that are not completely empty (ie. count == 0, but type != empty) - Item->Empty(); - } - m_Owner.GetClientHandle()->SendInventorySlot(0, a_SlotNum, *Item); - } -} - - - - - -int cInventory::HowManyCanFit(short a_ItemType, short a_ItemDamage, int a_BeginSlot, int a_EndSlot) -{ - int res = 0; - for (int i = a_BeginSlot; i <= a_EndSlot; i++) - { - if ( - m_Slots[i].IsEmpty() || - ((m_Slots[i].m_ItemID == a_ItemType) && (m_Slots[i].m_ItemHealth == a_ItemDamage)) - ) - { - int MaxCount = ItemHandler(a_ItemType)->GetMaxStackSize(); - ASSERT(m_Slots[i].m_ItemCount <= MaxCount); - res += MaxCount - m_Slots[i].m_ItemCount; - } - } // for i - m_Slots[] - return res; -} - - - - - -int cInventory::MoveItem(short a_ItemType, short a_ItemDamage, int a_Count, int a_BeginSlot, int a_EndSlot) -{ - int res = 0; - for (int i = a_BeginSlot; i <= a_EndSlot; i++) - { - if ( - m_Slots[i].IsEmpty() || - ((m_Slots[i].m_ItemID == a_ItemType) && (m_Slots[i].m_ItemHealth == a_ItemDamage)) - ) - { - int MaxCount = ItemHandler(a_ItemType)->GetMaxStackSize(); - ASSERT(m_Slots[i].m_ItemCount <= MaxCount); - int NumToMove = std::min(a_Count, MaxCount - m_Slots[i].m_ItemCount); - m_Slots[i].m_ItemCount += NumToMove; - m_Slots[i].m_ItemHealth = a_ItemDamage; - m_Slots[i].m_ItemID = a_ItemType; - SendSlot(i); - res += NumToMove; - a_Count -= NumToMove; - if (a_Count <= 0) - { - // No more items to distribute - return res; - } - } - } // for i - m_Slots[] - // No more space to distribute to - return res; -} - - - - - -bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode /* = 0 */ ) -{ - // Fill already present stacks - if( a_Mode < 2 ) - { - for(int i = 0; i < a_Size; i++) - { - if( m_Slots[i + a_Offset].m_ItemID == a_Item.m_ItemID && m_Slots[i + a_Offset].m_ItemCount < 64 && m_Slots[i + a_Offset].m_ItemHealth == a_Item.m_ItemHealth ) - { - int NumFree = 64 - m_Slots[i + a_Offset].m_ItemCount; - if( NumFree >= a_Item.m_ItemCount ) - { - - //printf("1. Adding %i items ( free: %i )\n", a_Item.m_ItemCount, NumFree ); - m_Slots[i + a_Offset].m_ItemCount += a_Item.m_ItemCount; - a_Item.m_ItemCount = 0; - a_bChangedSlots[i + a_Offset] = true; - break; - } - else - { - //printf("2. Adding %i items\n", NumFree ); - m_Slots[i + a_Offset].m_ItemCount += (char)NumFree; - a_Item.m_ItemCount -= (char)NumFree; - a_bChangedSlots[i + a_Offset] = true; - } - } - } - } - - if( a_Mode > 0 ) - { - // If we got more left, find first empty slot - for(int i = 0; i < a_Size && a_Item.m_ItemCount > 0; i++) - { - if( m_Slots[i + a_Offset].m_ItemID == -1 ) - { - m_Slots[i + a_Offset] = a_Item; - a_Item.m_ItemCount = 0; - a_bChangedSlots[i + a_Offset] = true; - } - } - } - - return true; -} - - - - - -void cInventory::SaveToJson(Json::Value & a_Value) -{ - for(unsigned int i = 0; i < c_NumSlots; i++) - { - Json::Value JSON_Item; - m_Slots[i].GetJson( JSON_Item ); - a_Value.append( JSON_Item ); - } -} - - - - - -bool cInventory::LoadFromJson(Json::Value & a_Value) -{ - int SlotIdx = 0; - - // TODO: Limit the number of slots written to the actual number of slots, - // otherwise an invalid json will crash the server! - - for( Json::Value::iterator itr = a_Value.begin(); itr != a_Value.end(); ++itr ) - { - m_Slots[SlotIdx].FromJson( *itr ); - SlotIdx++; - } - return true; -} - - - - diff --git a/source/cInventory.h b/source/cInventory.h deleted file mode 100644 index bce485852..000000000 --- a/source/cInventory.h +++ /dev/null @@ -1,88 +0,0 @@ - -#pragma once - -#include "cItem.h" - - - - - -namespace Json -{ - class Value; -}; - -class cClientHandle; -class cPlayer; - - - - - -class cInventory //tolua_export -{ //tolua_export -public: - cInventory(cPlayer & a_Owner); - ~cInventory(); - - void Clear(); //tolua_export - - cItem* GetSlotsForType( int a_Type ); - int GetSlotCountForType( int a_Type ); - - bool AddItem( cItem & a_Item ); //tolua_export - bool RemoveItem( cItem & a_Item ); //tolua_export - - void SaveToJson(Json::Value & a_Value); - bool LoadFromJson(Json::Value & a_Value); - - void SendWholeInventory(cClientHandle & a_Client); - - cItem * GetSlot(int a_SlotNum ); //tolua_export - cItem * GetSlots(void) { return m_Slots; } - const cItem * GetSlots(void) const { return m_Slots; } - cItem * GetFromHotBar(int a_HotBarSlotNum); //tolua_export - - cItem & GetEquippedItem(void); //tolua_export - const cItem & GetEquippedItem(void) const; - void SetEquippedSlot(int a_SlotNum); //tolua_export - short GetEquippedSlot(void) { return m_EquippedSlot; } //tolua_export - - void SendSlot( int a_SlotNum ); //tolua_export - - /// Returns how many items of the specified type would fit into the slot range specified - int HowManyCanFit(short a_ItemType, short a_ItemDamage, int a_BeginSlot, int a_EndSlot); - - /// Moves items, fitting them into the slot range specified, up to a_Count items. Returns the number of items moved - int MoveItem(short a_ItemType, short a_ItemDamage, int a_Count, int a_BeginSlot, int a_EndSlot); - - static const unsigned int c_NumSlots = 45; - static const unsigned int c_MainSlots = 27; - static const unsigned int c_HotSlots = 9; - static const unsigned int c_CraftSlots = 4; - static const unsigned int c_ArmorSlots = 4; - - static const unsigned int c_CraftOffset = 0; - static const unsigned int c_ArmorOffset = 5; - static const unsigned int c_MainOffset = 9; - static const unsigned int c_HotOffset = 36; - -protected: - bool AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode = 0 ); - - cItem m_Slots[c_NumSlots]; - - cItem * m_MainSlots; - cItem * m_CraftSlots; - cItem * m_ArmorSlots; - cItem * m_HotSlots; - - cItem * m_EquippedItem; - short m_EquippedSlot; - - cPlayer & m_Owner; -}; //tolua_export - - - - diff --git a/source/cItem.cpp b/source/cItem.cpp deleted file mode 100644 index fd71a0b54..000000000 --- a/source/cItem.cpp +++ /dev/null @@ -1,58 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cItem.h" -#include - - - - - -void cItem::GetJson( Json::Value & a_OutValue ) const -{ - a_OutValue["ID"] = m_ItemID; - if( m_ItemID > 0 ) - { - a_OutValue["Count"] = m_ItemCount; - a_OutValue["Health"] = m_ItemHealth; - } -} - -void cItem::FromJson( const Json::Value & a_Value ) -{ - m_ItemID = (ENUM_ITEM_ID)a_Value.get("ID", -1 ).asInt(); - if( m_ItemID > 0 ) - { - m_ItemCount = (char)a_Value.get("Count", -1 ).asInt(); - m_ItemHealth = (short)a_Value.get("Health", -1 ).asInt(); - } -} - - - - - -bool cItem::IsEnchantable(short item) -{ - if ((item >= 256) && (item <= 259)) - return true; - if ((item >= 267) && (item <= 279)) - return true; - if ((item >= 283) && (item <= 286)) - return true; - if ((item >= 290) && (item <= 294)) - return true; - if ((item >= 298) && (item <= 317)) - return true; - if ((item >= 290) && (item <= 294)) - return true; - - if ((item == 346) || (item == 359) || (item == 261)) - return true; - - return false; -} - - - - diff --git a/source/cItem.h b/source/cItem.h deleted file mode 100644 index aa4b76bbc..000000000 --- a/source/cItem.h +++ /dev/null @@ -1,146 +0,0 @@ -#pragma once - -#include "Defines.h" -#include "BlockID.h" - -namespace Json -{ - class Value; -} - - - - - -// tolua_begin -class cItem -{ -public: - cItem(short a_ItemType = E_ITEM_EMPTY, char a_ItemCount = 0, short a_ItemHealth = 0) - : m_ItemType (a_ItemType) - , m_ItemCount (a_ItemCount) - , m_ItemHealth(a_ItemHealth) - { - if (!IsValidItem( m_ItemID ) ) m_ItemID = E_ITEM_EMPTY; - } - - void Empty() - { - m_ItemID = E_ITEM_EMPTY; - m_ItemCount = 0; - m_ItemHealth = 0; - } - - void Clear(void) - { - m_ItemID = E_ITEM_EMPTY; - m_ItemCount = 0; - m_ItemHealth = 0; - } - - bool IsEmpty(void) const - { - return (m_ItemID <= 0 || m_ItemCount <= 0); - } - - // tolua_end - OBSOLETE - // tolua_begin - bool Equals(const cItem & a_Item) const // obsolete, use IsEqual() instead - { - return IsEqual(a_Item); - } - - bool IsEqual(const cItem & a_Item) const - { - return (IsSameType(a_Item) && (m_ItemHealth == a_Item.m_ItemHealth)); - } - - bool IsSameType(const cItem & a_Item) const - { - return (m_ItemID == a_Item.m_ItemID) || (IsEmpty() && a_Item.IsEmpty()); - } - - // TODO Sorry for writing the functions in the header. But somehow it doesnīt worked when I put them into the cpp File :s - - inline int GetMaxDuration(void) const - { - switch(m_ItemID) - { - case 256: return 251; - case 257: return 251; - case 258: return 251; - case 259: return 65; //Lighter / Flint and Steel - case 267: return 251; - case 268: return 60; - case 269: return 60; - case 270: return 60; - case 271: return 60; - case 272: return 132; - case 273: return 132; - case 274: return 132; - case 275: return 132; - case 276: return 1563; - case 277: return 1563; - case 278: return 1563; - case 279: return 1563; - case 283: return 32; - case 284: return 32; - case 285: return 32; - case 286: return 32; - case 290: return 60; - case 291: return 132; - case 292: return 251; - case 293: return 1563; - case 294: return 32; - case 359: return 251; - default: return 0; - } - } - - // Damages a weapon / tool. Returns true when destroyed - inline bool DamageItem() - { - if (HasDuration()) - { - m_ItemHealth++; - if(m_ItemHealth >= GetMaxDuration()) - return true; - } - return false; - } - - inline bool HasDuration() { return GetMaxDuration() > 0; } - - // tolua_end - void GetJson( Json::Value & a_OutValue ) const; - void FromJson( const Json::Value & a_Value ); - // tolua_begin - - static bool IsEnchantable(short a_ItemType); - - // tolua_end - union - { - // tolua_begin - short m_ItemID; // OBSOLETE, use m_ItemType instead - short m_ItemType; - // tolua_end - } ; - char m_ItemCount; // tolua_export - union - { - // tolua_begin - short m_ItemHealth; // OBSOLETE, use m_ItemDamage instead - short m_ItemDamage; - // tolua_end - } ; - // tolua_begin -}; -// tolua_end - -typedef std::vector cItems; - - - - diff --git a/source/cLadder.h b/source/cLadder.h deleted file mode 100644 index b3dc0fb96..000000000 --- a/source/cLadder.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -class cLadder //tolua_export -{ //tolua_export -public: - - static char DirectionToMetaData( char a_Direction ) //tolua_export - { //tolua_export - switch( a_Direction ) - { - case 0x2: - return 0x2; - case 0x3: - return 0x3; - case 0x4: - return 0x4; - case 0x5: - return 0x5; - default: - break; - }; - return 0x2; - } //tolua_export - - static char MetaDataToDirection( char a_MetaData ) //tolua_export - { //tolua_export - switch( a_MetaData ) - { - case 0x2: - return 0x2; - case 0x3: - return 0x3; - case 0x4: - return 0x4; - case 0x5: - return 0x5; - default: - break; - }; - return 0x2; - } //tolua_export - -}; //tolua_export \ No newline at end of file diff --git a/source/cLavaSimulator.cpp b/source/cLavaSimulator.cpp deleted file mode 100644 index 6e3c2a3fa..000000000 --- a/source/cLavaSimulator.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "Globals.h" -#include "cLavaSimulator.h" -#include "Defines.h" -#include "cWorld.h" - - -cLavaSimulator::cLavaSimulator(cWorld *a_World) - : cFluidSimulator(a_World) -{ - m_FluidBlock = E_BLOCK_LAVA; - m_StationaryFluidBlock = E_BLOCK_STATIONARY_LAVA; - m_MaxHeight = 6; - m_FlowReduction = 2; -} - - -bool cLavaSimulator::IsAllowedBlock(char a_BlockID) -{ - return IsBlockLava(a_BlockID); -} \ No newline at end of file diff --git a/source/cLavaSimulator.h b/source/cLavaSimulator.h deleted file mode 100644 index 44f9935dd..000000000 --- a/source/cLavaSimulator.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "cFluidSimulator.h" - -class cLavaSimulator : public cFluidSimulator -{ -public: - cLavaSimulator( cWorld* a_World ); - - virtual bool IsAllowedBlock( char a_BlockID ); - - -}; \ No newline at end of file diff --git a/source/cLog.cpp b/source/cLog.cpp deleted file mode 100644 index 142aabe28..000000000 --- a/source/cLog.cpp +++ /dev/null @@ -1,168 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cLog.h" - -#include -#include -#include "OSSupport/MakeDir.h" -#include "OSSupport/IsThread.h" - -#if defined(ANDROID_NDK) - #include - #include "ToJava.h" -#endif - - - - -cLog* cLog::s_Log = NULL; - -cLog::cLog(const AString & a_FileName ) - : m_File(NULL) -{ - s_Log = this; - - // create logs directory - cMakeDir::MakeDir("logs"); - - OpenLog( (FILE_IO_PREFIX + std::string("logs/") + a_FileName).c_str() ); -} - - - - - -cLog::~cLog() -{ - CloseLog(); - s_Log = NULL; -} - - - - - -cLog* cLog::GetInstance() -{ - if(s_Log) - return s_Log; - - new cLog("log.txt"); - return s_Log; -} - - - - - -void cLog::CloseLog() -{ - if( m_File ) - fclose (m_File); - m_File = 0; -} - - - - - -void cLog::OpenLog( const char* a_FileName ) -{ - if(m_File) fclose (m_File); - #ifdef _WIN32 - fopen_s( &m_File, a_FileName, "a+" ); - #else - m_File = fopen(a_FileName, "a+" ); - #endif -} - - - - - -void cLog::ClearLog() -{ - #ifdef _WIN32 - if( fopen_s( &m_File, "log.txt", "w" ) == 0) - fclose (m_File); - #else - m_File = fopen("log.txt", "w" ); - if( m_File ) - fclose (m_File); - #endif - m_File = 0; -} - - - - - -void cLog::Log(const char * a_Format, va_list argList) -{ - AString Message; - AppendVPrintf(Message, a_Format, argList); - - time_t rawtime; - time ( &rawtime ); - - struct tm* timeinfo; -#ifdef _WIN32 - struct tm timeinforeal; - timeinfo = &timeinforeal; - localtime_s(timeinfo, &rawtime ); -#else - timeinfo = localtime( &rawtime ); -#endif - - AString Line; - #ifdef _DEBUG - Printf(Line, "[%04x|%02d:%02d:%02d] %s\n", cIsThread::GetCurrentID(), timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, Message.c_str()); - #else - Printf(Line, "[%02d:%02d:%02d] %s\n", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, Message.c_str()); - #endif - if (m_File) - { - fputs(Line.c_str(), m_File); - fflush(m_File); - } - - // Print to console: -#if defined(ANDROID_NDK) - //__android_log_vprint(ANDROID_LOG_ERROR,"MCServer", a_Format, argList); - __android_log_print(ANDROID_LOG_ERROR, "MCServer", "%s", Line.c_str() ); - //CallJavaFunction_Void_String(g_JavaThread, "AddToLog", Line ); -#else - printf("%s", Line.c_str()); -#endif - - #if defined (_WIN32) && defined(_DEBUG) - // In a Windows Debug build, output the log to debug console as well: - OutputDebugString(Line.c_str()); - #endif // _WIN32 -} - - - - - -void cLog::Log(const char* a_Format, ...) -{ - va_list argList; - va_start(argList, a_Format); - Log( a_Format, argList ); - va_end(argList); -} - - - - - -void cLog::SimpleLog(const char* a_String) -{ - Log("%s", a_String ); -} - - - - diff --git a/source/cLog.h b/source/cLog.h deleted file mode 100644 index b405b52a4..000000000 --- a/source/cLog.h +++ /dev/null @@ -1,30 +0,0 @@ - -#pragma once - - - - - -class cLog -{ // tolua_export -private: - FILE * m_File; - static cLog * s_Log; - -public: - cLog(const AString & a_FileName); - ~cLog(); - void Log(const char* a_Format, va_list argList ); - void Log(const char* a_Format, ...); - //tolua_begin - void SimpleLog(const char* a_String); - void OpenLog( const char* a_FileName ); - void CloseLog(); - void ClearLog(); - static cLog* GetInstance(); -}; -//tolua_end - - - - diff --git a/source/cLuaChunk.cpp b/source/cLuaChunk.cpp deleted file mode 100644 index a5f831ba7..000000000 --- a/source/cLuaChunk.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "Globals.h" - -#include "cLuaChunk.h" - diff --git a/source/cLuaChunk.h b/source/cLuaChunk.h deleted file mode 100644 index dc6f2a0f2..000000000 --- a/source/cLuaChunk.h +++ /dev/null @@ -1,139 +0,0 @@ -#pragma once - -#include "ChunkDef.h" - -class cLuaChunk //tolua_export -{ //tolua_export -public: - cLuaChunk( cChunkDef::BlockTypes & a_BlockTypes - , cChunkDef::BlockNibbles & a_BlockNibbles - , cChunkDef::HeightMap & a_HeightMap - , cChunkDef::BiomeMap & a_BiomeMap - ) - : m_BiomeMap( a_BiomeMap ) - , m_BlockTypes( a_BlockTypes ) - , m_BlockMeta( a_BlockNibbles ) - , m_HeightMap( a_HeightMap ) - , m_bUseDefaultBiomes( false ) - , m_bUseDefaultComposition( false ) - , m_bUseDefaultStructures( false ) - , m_bUseDefaultFinish( false ) - { - memset( m_BlockTypes, 0, sizeof( cChunkDef::BlockTypes ) ); - memset( m_BlockMeta, 0, sizeof( cChunkDef::BlockNibbles ) ); - memset( m_BiomeMap, 0, sizeof( cChunkDef::BiomeMap ) ); - memset( m_HeightMap, 0, sizeof( cChunkDef::HeightMap ) ); - } - ~cLuaChunk() - {} - - //tolua_begin - - // Block functions - void FillBlocks( char a_BlockID, unsigned char a_BlockMeta ) - { - const NIBBLETYPE CompressedMeta = a_BlockMeta | a_BlockMeta << 4; - memset( m_BlockTypes, a_BlockID, sizeof( cChunkDef::BlockTypes ) ); - memset( m_BlockMeta, CompressedMeta, sizeof( cChunkDef::BlockNibbles ) ); - } - - void SetBlock( int a_X, int a_Y, int a_Z, char a_BlockID, unsigned char a_BlockMeta ) - { - cChunkDef::SetBlock( m_BlockTypes, a_X, a_Y, a_Z, a_BlockID ); - cChunkDef::SetNibble( m_BlockMeta, a_X, a_Y, a_Z, a_BlockMeta ); - } - - char GetBlock( int a_X, int a_Y, int a_Z ) - { - return cChunkDef::GetBlock( m_BlockTypes, a_X, a_Y, a_Z ); - } - - char GetBlockMeta( int a_X, int a_Y, int a_Z ) - { - return cChunkDef::GetNibble( m_BlockMeta, a_X, a_Y, a_Z ); - } - - - - - - // Biome functinos - void SetBiome( int a_X, int a_Z, int a_BiomeID ) - { - cChunkDef::SetBiome( m_BiomeMap, a_X, a_Z, (EMCSBiome)a_BiomeID ); - } - - int GetBiome( int a_X, int a_Z ) - { - return cChunkDef::GetBiome( m_BiomeMap, a_X, a_Z ); - } - - - - - - // Height functions - void SetHeight( int a_X, int a_Z, int a_Height ) - { - cChunkDef::SetHeight( m_HeightMap, a_X, a_Z, a_Height ); - } - - int GetHeight( int a_X, int a_Z ) - { - return cChunkDef::GetHeight( m_HeightMap, a_X, a_Z ); - } - - - - - - // Functions to explicitly tell the server to use default behavior for certain parts of generating terrain - void SetUseDefaultBiomes( bool a_bUseDefaultBiomes ) - { - m_bUseDefaultBiomes = a_bUseDefaultBiomes; - } - bool IsUsingDefaultBiomes() - { - return m_bUseDefaultBiomes; - } - - void SetUseDefaultComposition( bool a_bUseDefaultComposition ) - { - m_bUseDefaultComposition = a_bUseDefaultComposition; - } - bool IsUsingDefaultComposition() - { - return m_bUseDefaultComposition; - } - - void SetUseDefaultStructures( bool a_bUseDefaultStructures ) - { - m_bUseDefaultStructures = a_bUseDefaultStructures; - } - bool IsUsingDefaultStructures() - { - return m_bUseDefaultStructures; - } - - void SetUseDefaultFinish( bool a_bUseDefaultFinish ) - { - m_bUseDefaultFinish = a_bUseDefaultFinish; - } - bool IsUsingDefaultFinish() - { - return m_bUseDefaultFinish; - } - - //tolua_end - -private: - bool m_bUseDefaultBiomes; - bool m_bUseDefaultComposition; - bool m_bUseDefaultStructures; - bool m_bUseDefaultFinish; - - cChunkDef::BiomeMap & m_BiomeMap; - cChunkDef::BlockTypes & m_BlockTypes; - cChunkDef::BlockNibbles & m_BlockMeta; - cChunkDef::HeightMap & m_HeightMap; -}; //tolua_export \ No newline at end of file diff --git a/source/cLuaCommandBinder.cpp b/source/cLuaCommandBinder.cpp deleted file mode 100644 index 62d57b640..000000000 --- a/source/cLuaCommandBinder.cpp +++ /dev/null @@ -1,122 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cLuaCommandBinder.h" -#include "cPlayer.h" -#include "cPlugin.h" -#include "cPlugin_Lua.h" - -#include "tolua++.h" - - - - - -extern bool report_errors(lua_State* lua, int status); - -cLuaCommandBinder::cLuaCommandBinder() -{ -} - -cLuaCommandBinder::~cLuaCommandBinder() -{ -} - -void cLuaCommandBinder::ClearBindings() -{ - m_BoundCommands.clear(); -} - -void cLuaCommandBinder::RemoveBindingsForPlugin( cPlugin* a_Plugin ) -{ - for( CommandMap::iterator itr = m_BoundCommands.begin(); itr != m_BoundCommands.end(); ) - { - if( itr->second.Plugin == a_Plugin ) - { - LOGINFO("Unbinding %s ", itr->first.c_str( ) ); - luaL_unref( itr->second.LuaState, LUA_REGISTRYINDEX, itr->second.Reference ); // unreference - CommandMap::iterator eraseme = itr; - ++itr; - m_BoundCommands.erase( eraseme ); - continue; - } - ++itr; - } -} - -bool cLuaCommandBinder::BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, lua_State * a_LuaState, int a_FunctionReference ) -{ - if( !a_Plugin->CanBindCommands() ) - { - LOGERROR("ERROR: Trying to bind command \"%s\" to a plugin that is not initialized.", a_Command.c_str() ); - return false; - } - if( m_BoundCommands.find( a_Command ) != m_BoundCommands.end() ) - { - LOGERROR("ERROR: Trying to bind command \"%s\" that has already been bound.", a_Command.c_str() ); - return false; - } - LOGINFO("Binding %s (%s)", a_Command.c_str(), a_Permission.c_str() ); - m_BoundCommands[ a_Command ] = BoundFunction( a_Plugin, a_LuaState, a_FunctionReference, a_Permission ); - return true; -} - -bool cLuaCommandBinder::HandleCommand( const std::string & a_Command, cPlayer* a_Player ) -{ - AStringVector Split = StringSplit(a_Command, " "); - if (Split.size() == 0) - { - return false; - } - - CommandMap::iterator FoundCommand = m_BoundCommands.find( Split[0] ); - if( FoundCommand != m_BoundCommands.end() ) - { - const BoundFunction & func = FoundCommand->second; - if( func.Permission.size() > 0 ) - { - if( !a_Player->HasPermission( func.Permission.c_str() ) ) - { - return false; - } - } - - // For enabling 'self' in the function, it's kind of a hack I'm not sure this is the way to go - lua_pushvalue(func.LuaState, LUA_GLOBALSINDEX); - lua_pushstring(func.LuaState, "self"); - tolua_pushusertype( func.LuaState, func.Plugin, "cPlugin" ); - lua_rawset(func.LuaState, -3); - lua_pop(func.LuaState, 1); - - LOGINFO("1. Stack size: %i", lua_gettop(func.LuaState) ); - lua_rawgeti( func.LuaState, LUA_REGISTRYINDEX, func.Reference); // same as lua_getref() - - // Push the split - LOGINFO("2. Stack size: %i", lua_gettop(func.LuaState) ); - lua_createtable(func.LuaState, Split.size(), 0); - int newTable = lua_gettop(func.LuaState); - int index = 1; - std::vector::const_iterator iter = Split.begin(); - while(iter != Split.end()) { - tolua_pushstring( func.LuaState, (*iter).c_str() ); - lua_rawseti(func.LuaState, newTable, index); - ++iter; - ++index; - } - LOGINFO("3. Stack size: %i", lua_gettop(func.LuaState) ); - // Push player - tolua_pushusertype( func.LuaState, a_Player, "cPlayer" ); - LOGINFO("Calling bound function! :D"); - int s = lua_pcall(func.LuaState, 2, 1, 0); - if( report_errors( func.LuaState, s ) ) - { - LOGINFO("error. Stack size: %i", lua_gettop(func.LuaState) ); - return false; - } - bool RetVal = (tolua_toboolean(func.LuaState, -1, 0) > 0); - lua_pop(func.LuaState, 1); // Pop return value - LOGINFO("ok. Stack size: %i", lua_gettop(func.LuaState) ); - return RetVal; - } - return false; -} diff --git a/source/cLuaCommandBinder.h b/source/cLuaCommandBinder.h deleted file mode 100644 index 768d097f8..000000000 --- a/source/cLuaCommandBinder.h +++ /dev/null @@ -1,42 +0,0 @@ - -#pragma once - -struct lua_State; -class cPlugin; -class cPlayer; - - - - - -class cLuaCommandBinder -{ -public: - cLuaCommandBinder(); - ~cLuaCommandBinder(); - - bool HandleCommand( const std::string & a_Command, cPlayer* a_Player ); - - bool BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, lua_State * a_LuaState, int a_FunctionReference ); - - void ClearBindings(); - void RemoveBindingsForPlugin( cPlugin* a_Plugin ); -private: - struct BoundFunction - { - BoundFunction() : Plugin( 0 ), LuaState( 0 ), Reference( 0 ) {} - BoundFunction( cPlugin* a_Plugin, lua_State * a_LuaState, int a_Reference, const std::string & a_Permission ) : Plugin( a_Plugin ), LuaState( a_LuaState ), Reference( a_Reference ), Permission( a_Permission ) {} - cPlugin* Plugin; - lua_State* LuaState; - int Reference; - std::string Permission; - }; - - typedef std::map< std::string, BoundFunction > CommandMap; - CommandMap m_BoundCommands; -}; - - - - - diff --git a/source/cMCLogger.cpp b/source/cMCLogger.cpp deleted file mode 100644 index 4e0823d14..000000000 --- a/source/cMCLogger.cpp +++ /dev/null @@ -1,191 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include -#include "cLog.h" - - - - - -cMCLogger* cMCLogger::s_MCLogger = 0; - -cMCLogger* cMCLogger::GetInstance() -{ - return s_MCLogger; -} - - - - - -cMCLogger::cMCLogger() -{ - AString FileName; - Printf(FileName, "LOG_%d.txt", (int)time(0) ); - m_Log = new cLog(FileName); - m_Log->Log("--- Started Log ---"); - - s_MCLogger = this; -} - - - - - -cMCLogger::cMCLogger( char* a_File ) -{ - m_Log = new cLog( a_File ); -} - - - - - -cMCLogger::~cMCLogger() -{ - m_Log->Log("--- Stopped Log ---"); - delete m_Log; - if (this == s_MCLogger) - s_MCLogger = NULL; -} - - - - - -void cMCLogger::LogSimple(const char* a_Text, int a_LogType /* = 0 */ ) -{ - switch( a_LogType ) - { - case 0: - LOG("%s", a_Text); - break; - case 1: - LOGINFO("%s", a_Text); - break; - case 2: - LOGWARN("%s", a_Text); - break; - case 3: - LOGERROR("%s", a_Text); - break; - default: - LOG("(#%d#: %s", a_LogType, a_Text); - break; - } -} - - - - - -void cMCLogger::Log(const char* a_Format, va_list a_ArgList) -{ - cCSLock Lock(m_CriticalSection); - SetColor( 0x7 ); // 0x7 is default grey color - m_Log->Log( a_Format, a_ArgList ); - SetColor(0x07); // revert color back -} - - - - - -void cMCLogger::Info(const char* a_Format, va_list a_ArgList) -{ - cCSLock Lock(m_CriticalSection); -// for( int i = 0; i < 16; i++) -// { -// for( int j = 0; j < 16; j++ ) -// { -// SetConsoleTextAttribute( hConsole, i | (j<<4) ); -// printf("0x%x", (i|j<<4)); -// } -// printf("\n"); -// } - - SetColor( 0xe ); // 0xe is yellow - m_Log->Log( a_Format, a_ArgList ); - SetColor(0x07); // revert color back -} - - - - - -void cMCLogger::Warn(const char* a_Format, va_list a_ArgList) -{ - cCSLock Lock(m_CriticalSection); - SetColor( 0xc ); // 0xc is red - m_Log->Log( a_Format, a_ArgList ); - SetColor(0x07); // revert color back -} - - - - - -void cMCLogger::Error(const char* a_Format, va_list a_ArgList) -{ - cCSLock Lock(m_CriticalSection); - SetColor( 0xc0 ); // 0xc0 is red bg and black text - m_Log->Log( a_Format, a_ArgList ); - SetColor(0x07); // revert color back -} - - - - - -void cMCLogger::SetColor( unsigned char a_Color ) -{ -#ifdef _WIN32 - HANDLE hConsole = GetStdHandle( STD_OUTPUT_HANDLE ); - SetConsoleTextAttribute( hConsole, a_Color ); -#else - (void)a_Color; -#endif -} - - - - - -////////////////////////////////////////////////////////////////////////// -// Global functions -void LOG(const char* a_Format, ...) -{ - va_list argList; - va_start(argList, a_Format); - cMCLogger::GetInstance()->Log( a_Format, argList ); - va_end(argList); -} - -void LOGINFO(const char* a_Format, ...) -{ - va_list argList; - va_start(argList, a_Format); - cMCLogger::GetInstance()->Info( a_Format, argList ); - va_end(argList); -} - -void LOGWARN(const char* a_Format, ...) -{ - va_list argList; - va_start(argList, a_Format); - cMCLogger::GetInstance()->Warn( a_Format, argList ); - va_end(argList); -} - -void LOGERROR(const char* a_Format, ...) -{ - va_list argList; - va_start(argList, a_Format); - cMCLogger::GetInstance()->Error( a_Format, argList ); - va_end(argList); -} - - - - diff --git a/source/cMCLogger.h b/source/cMCLogger.h deleted file mode 100644 index 04ba732bf..000000000 --- a/source/cMCLogger.h +++ /dev/null @@ -1,60 +0,0 @@ - -#pragma once - - - - -class cLog; - - - - - -class cMCLogger //tolua_export -{ //tolua_export -public: //tolua_export - cMCLogger(); - cMCLogger( char* a_File ); //tolua_export - ~cMCLogger(); //tolua_export - - void Log(const char* a_Format, va_list a_ArgList); - void Info(const char* a_Format, va_list a_ArgList); - void Warn(const char* a_Format, va_list a_ArgList); - void Error(const char* a_Format, va_list a_ArgList); - - void LogSimple(const char* a_Text, int a_LogType = 0 ); //tolua_export - - static cMCLogger* GetInstance(); -private: - void SetColor( unsigned char a_Color ); - - cCriticalSection m_CriticalSection; - cLog* m_Log; - static cMCLogger* s_MCLogger; -}; //tolua_export - -extern void LOG(const char* a_Format, ...); -extern void LOGINFO(const char* a_Format, ...); -extern void LOGWARN(const char* a_Format, ...); -extern void LOGERROR(const char* a_Format, ...); - - - - - -// In debug builds, translate LOGD to LOG, otherwise leave it out altogether: -#ifdef _DEBUG - #define LOGD LOG -#else - #define LOGD(...) -#endif // _DEBUG - - - - - -#define LOGWARNING LOGWARN - - - - diff --git a/source/cMonsterConfig.cpp b/source/cMonsterConfig.cpp deleted file mode 100644 index c399902c3..000000000 --- a/source/cMonsterConfig.cpp +++ /dev/null @@ -1,114 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cMonsterConfig.h" -#include "Mobs/Monster.h" -#include "../iniFile/iniFile.h" -//#include - - - - - -struct cMonsterConfig::sAttributesStruct -{ - AString m_name; - float m_SightDistance; - float m_AttackDamage; - float m_AttackRange; - float m_AttackRate; - int m_MaxHealth; -}; - - - - - -struct cMonsterConfig::sMonsterConfigState -{ - AString MonsterTypes; - std::list< sAttributesStruct > AttributesList; -}; - - - - - -cMonsterConfig::cMonsterConfig(void) - : m_pState( new sMonsterConfigState ) -{ - Initialize(); -} - - - - - -cMonsterConfig::~cMonsterConfig() { - delete m_pState; -} - - - - - -void cMonsterConfig::Initialize() { - - sAttributesStruct Attributes; - cIniFile SettingsIniFile("settings.ini"); - cIniFile MonstersIniFile("monsters.ini"); - - if (!SettingsIniFile.ReadFile() || !MonstersIniFile.ReadFile()) - { - LOGWARNING("cMonsterConfig: Must have both settings.ini and monsters.ini to configure attributes\n\tusing default attributes \n"); - return; - } - - m_pState->MonsterTypes = SettingsIniFile.GetValue("Monsters","Types",""); - - if ( m_pState->MonsterTypes.empty() ) - { - LOGWARNING("cMonsterConfig: No Monster types listed in config file, using default attributes \n"); - return; - } - - AStringVector SplitList = StringSplit(m_pState->MonsterTypes,","); - for (unsigned int i = 0; i < SplitList.size(); ++i) - { - if (!SplitList[i].empty()) - { - Attributes.m_name = SplitList[i].c_str(); - Attributes.m_AttackDamage = (float)MonstersIniFile.GetValueF(SplitList[i].c_str(), "AttackDamage", 0); - Attributes.m_AttackRange = (float)MonstersIniFile.GetValueF(SplitList[i].c_str(), "AttackRange", 0); - Attributes.m_SightDistance = (float)MonstersIniFile.GetValueF(SplitList[i].c_str(), "SightDistance", 0); - Attributes.m_AttackRate = (float)MonstersIniFile.GetValueF(SplitList[i].c_str(), "AttackRate", 0); - Attributes.m_MaxHealth = MonstersIniFile.GetValueI(SplitList[i].c_str(), "MaxHealth", 0); - m_pState->AttributesList.push_front(Attributes); - } - } // for i - SplitList[] -} - - - - - -void cMonsterConfig::AssignAttributes(cMonster *m, const char* n) -{ - std::list::const_iterator itr; - for (itr = m_pState->AttributesList.begin(); itr != m_pState->AttributesList.end(); ++itr) - { - if(itr->m_name.compare(n) == 0) - { - m->SetAttackDamage (itr->m_AttackDamage); - m->SetAttackRange (itr->m_AttackRange); - m->SetSightDistance(itr->m_SightDistance); - m->SetAttackRate ((int)itr->m_AttackRate); - m->SetMaxHealth ((short)itr->m_MaxHealth); - } - } // for itr - m_pState->AttributesList[] -} - - - - - diff --git a/source/cMonsterConfig.h b/source/cMonsterConfig.h deleted file mode 100644 index d1a514b17..000000000 --- a/source/cMonsterConfig.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -class cMonster; -class cMonsterConfig -{ -public: - cMonsterConfig(void); - ~cMonsterConfig(); - - void AssignAttributes(cMonster *m, const char* n); - -private: - struct sAttributesStruct; - struct sMonsterConfigState; - sMonsterConfigState* m_pState; - void Initialize(); -}; \ No newline at end of file diff --git a/source/cNoise.cpp b/source/cNoise.cpp deleted file mode 100644 index b4e7a5752..000000000 --- a/source/cNoise.cpp +++ /dev/null @@ -1,377 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cNoise.h" -#include - -#if NOISE_USE_SSE -#include //_mm_mul_epi32 -#endif - -#define FAST_FLOOR( x ) ( (x) < 0 ? ((int)x)-1 : ((int)x) ) - - - - - -cNoise::cNoise( unsigned int a_Seed ) - : m_Seed( a_Seed ) -{ -} - - -cNoise::~cNoise() -{ -} - -#if NOISE_USE_SSE -/**************** - * SSE Random value generator - **/ -__m128 cNoise::SSE_IntNoise2D( int a_X1, int a_Y1, int a_X2, int a_Y2, int a_X3, int a_Y3, int a_X4, int a_Y4 ) const -{ - const __m128i X4 = _mm_set_epi32(a_X4, a_X3, a_X2, a_X1); - const __m128i Y4 = _mm_set_epi32(a_Y4, a_Y3, a_Y2, a_Y1); - - const __m128 One4 = _mm_set_ps1( 1.f ); - const __m128i YScale4 = _mm_set1_epi32( 57 ); - - const __m128i i15731 = _mm_set1_epi32( 15731 ); - const __m128i i789221 = _mm_set1_epi32( 789221 ); - const __m128i i1376312589 = _mm_set1_epi32(1376312589); - const __m128i MaskValue4 = _mm_set1_epi32(0x7fffffff); - const __m128 f1073741824 = _mm_set_ps1( 1073741824.0f ); - - const __m128i Seed4 = _mm_mullo_epi32( _mm_mullo_epi32( _mm_set1_epi32( m_Seed ), YScale4 ), YScale4 ); - - const __m128i ScaledY4 = _mm_mullo_epi32( Y4, YScale4 ); - const __m128i n4 = _mm_add_epi32( _mm_add_epi32( X4, ScaledY4 ), Seed4 ); - const __m128i nn4 = _mm_slli_epi32( n4, 13 ); - const __m128i nnn4 = _mm_xor_si128( nn4, n4 ); - - const __m128i StepA4 = _mm_mullo_epi32( nnn4, nnn4 ); - const __m128i StepAA4 = _mm_add_epi32( _mm_mullo_epi32( StepA4, i15731 ), i789221 ); - const __m128i StepB4 = _mm_add_epi32( _mm_mullo_epi32( nnn4, StepAA4 ), i1376312589 ); - const __m128i StepC4 = _mm_and_si128( StepB4, MaskValue4 ); - const __m128 StepD4 = _mm_div_ps( _mm_cvtepi32_ps( StepC4 ), f1073741824 ); - const __m128 Result4 = _mm_sub_ps( One4, StepD4 ); - - return Result4; -} -#endif - - -/*************** - * Interpolated (and 1 smoothed) noise in 1-dimension - **/ -float cNoise::LinearNoise1D( float a_X ) const -{ - int BaseX = FAST_FLOOR( a_X ); - float FracX = (a_X) - BaseX; - return LinearInterpolate( IntNoise( BaseX ), IntNoise( BaseX+1 ), FracX); -} - -float cNoise::CosineNoise1D( float a_X ) const -{ - int BaseX = FAST_FLOOR( a_X ); - float FracX = (a_X) - BaseX; - return CosineInterpolate( IntNoise( BaseX ), IntNoise( BaseX+1 ), FracX); -} - -float cNoise::CubicNoise1D( float a_X ) const -{ - int BaseX = FAST_FLOOR( a_X ); - float FracX = (a_X) - BaseX; - return CubicInterpolate( IntNoise( BaseX-1 ), IntNoise( BaseX ), IntNoise( BaseX+1 ), IntNoise( BaseX+2 ), FracX); -} - -float cNoise::SmoothNoise1D( int a_X ) const -{ - return IntNoise(a_X)/2 + IntNoise(a_X-1)/4 + IntNoise(a_X+1)/4; -} - -/****************** - * Interpolated (and 1 smoothed) noise in 2-dimensions - **/ -float cNoise::LinearNoise2D( float a_X, float a_Y ) const -{ - const int BaseX = FAST_FLOOR( a_X ); - const int BaseY = FAST_FLOOR( a_Y ); - - const float tl = IntNoise2D( BaseX, BaseY ); - const float tr = IntNoise2D( BaseX+1, BaseY ); - const float bl = IntNoise2D( BaseX, BaseY+1 ); - const float br = IntNoise2D( BaseX+1, BaseY+1 ); - - const float FracX = (a_X) - BaseX; - const float interp1 = LinearInterpolate( tl, tr, FracX ); - const float interp2 = LinearInterpolate( bl, br, FracX ); - - const float FracY = (a_Y) - BaseY; - return LinearInterpolate( interp1, interp2, FracY ); -} - -float cNoise::CosineNoise2D( float a_X, float a_Y ) const -{ - const int BaseX = FAST_FLOOR( a_X ); - const int BaseY = FAST_FLOOR( a_Y ); - - const float tl = IntNoise2D( BaseX, BaseY ); - const float tr = IntNoise2D( BaseX+1, BaseY ); - const float bl = IntNoise2D( BaseX, BaseY+1 ); - const float br = IntNoise2D( BaseX+1, BaseY+1 ); - - const float FracX = (a_X) - BaseX; - const float interp1 = CosineInterpolate( tl, tr, FracX ); - const float interp2 = CosineInterpolate( bl, br, FracX ); - - const float FracY = (a_Y) - BaseY; - return CosineInterpolate( interp1, interp2, FracY ); -} - - - - - -float cNoise::CubicNoise2D( float a_X, float a_Y ) const -{ - const int BaseX = FAST_FLOOR( a_X ); - const int BaseY = FAST_FLOOR( a_Y ); - - const float points[4][4] = - { - IntNoise2D( BaseX-1, BaseY-1 ), IntNoise2D( BaseX, BaseY-1 ), IntNoise2D( BaseX+1, BaseY-1 ), IntNoise2D( BaseX+2, BaseY-1 ), - IntNoise2D( BaseX-1, BaseY ), IntNoise2D( BaseX, BaseY ), IntNoise2D( BaseX+1, BaseY ), IntNoise2D( BaseX+2, BaseY ), - IntNoise2D( BaseX-1, BaseY+1 ), IntNoise2D( BaseX, BaseY+1 ), IntNoise2D( BaseX+1, BaseY+1 ), IntNoise2D( BaseX+2, BaseY+1 ), - IntNoise2D( BaseX-1, BaseY+2 ), IntNoise2D( BaseX, BaseY+2 ), IntNoise2D( BaseX+1, BaseY+2 ), IntNoise2D( BaseX+2, BaseY+2 ), - }; - - const float FracX = (a_X) - BaseX; - const float interp1 = CubicInterpolate( points[0][0], points[0][1], points[0][2], points[0][3], FracX ); - const float interp2 = CubicInterpolate( points[1][0], points[1][1], points[1][2], points[1][3], FracX ); - const float interp3 = CubicInterpolate( points[2][0], points[2][1], points[2][2], points[2][3], FracX ); - const float interp4 = CubicInterpolate( points[3][0], points[3][1], points[3][2], points[3][3], FracX ); - - - const float FracY = (a_Y) - BaseY; - return CubicInterpolate( interp1, interp2, interp3, interp4, FracY ); -} - - - - - -#if NOISE_USE_SSE -float cNoise::SSE_CubicNoise2D( float a_X, float a_Y ) const -{ - const int BaseX = FAST_FLOOR( a_X ); - const int BaseY = FAST_FLOOR( a_Y ); - - __m128 points4[4] = { - SSE_IntNoise2D( BaseX-1, BaseY-1, BaseX-1, BaseY, BaseX-1, BaseY+1, BaseX-1, BaseY+2 ), - SSE_IntNoise2D( BaseX, BaseY-1, BaseX, BaseY, BaseX, BaseY+1, BaseX, BaseY+2 ), - SSE_IntNoise2D( BaseX+1, BaseY-1, BaseX+1, BaseY, BaseX+1, BaseY+1, BaseX+1, BaseY+2 ), - SSE_IntNoise2D( BaseX+2, BaseY-1, BaseX+2, BaseY, BaseX+2, BaseY+1, BaseX+2, BaseY+2 ), - }; - - const float FracX = (a_X) - BaseX; - union { __m128 p4; float p[4]; } - AllInterp = { CubicInterpolate4( points4[0], points4[1], points4[2], points4[3], FracX ) }; - - const float FracY = (a_Y) - BaseY; - return CubicInterpolate( AllInterp.p[0], AllInterp.p[1], AllInterp.p[2], AllInterp.p[3], FracY ); -} -#endif - - - - - -/****************** - * Interpolated (and 1 smoothed) noise in 3-dimensions - **/ -float cNoise::CosineNoise3D( float a_X, float a_Y, float a_Z ) const -{ - const int BaseX = FAST_FLOOR( a_X ); - const int BaseY = FAST_FLOOR( a_Y ); - const int BaseZ = FAST_FLOOR( a_Z ); - - const float ftl = IntNoise3D( BaseX, BaseY, BaseZ ); - const float ftr = IntNoise3D( BaseX+1, BaseY, BaseZ ); - const float fbl = IntNoise3D( BaseX, BaseY+1, BaseZ ); - const float fbr = IntNoise3D( BaseX+1, BaseY+1, BaseZ ); - - const float btl = IntNoise3D( BaseX, BaseY, BaseZ+1 ); - const float btr = IntNoise3D( BaseX+1, BaseY, BaseZ+1 ); - const float bbl = IntNoise3D( BaseX, BaseY+1, BaseZ+1 ); - const float bbr = IntNoise3D( BaseX+1, BaseY+1, BaseZ+1 ); - - const float FracX = (a_X) - BaseX; - const float finterp1 = CosineInterpolate( ftl, ftr, FracX ); - const float finterp2 = CosineInterpolate( fbl, fbr, FracX ); - const float binterp1 = CosineInterpolate( btl, btr, FracX ); - const float binterp2 = CosineInterpolate( bbl, bbr, FracX ); - - const float FracY = (a_Y) - BaseY; - const float interp1 = CosineInterpolate( finterp1, finterp2, FracY ); - const float interp2 = CosineInterpolate( binterp1, binterp2, FracY ); - - const float FracZ = (a_Z) - BaseZ; - return CosineInterpolate( interp1, interp2, FracZ ); -} - - - - - -float cNoise::CubicNoise3D( float a_X, float a_Y, float a_Z ) const -{ - const int BaseX = FAST_FLOOR( a_X ); - const int BaseY = FAST_FLOOR( a_Y ); - const int BaseZ = FAST_FLOOR( a_Z ); - - const float points1[4][4] = { - IntNoise3D( BaseX-1, BaseY-1, BaseZ-1 ), IntNoise3D( BaseX, BaseY-1, BaseZ-1 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ-1 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ-1 ), - IntNoise3D( BaseX-1, BaseY, BaseZ-1 ), IntNoise3D( BaseX, BaseY, BaseZ-1 ), IntNoise3D( BaseX+1, BaseY, BaseZ-1 ), IntNoise3D( BaseX+2, BaseY, BaseZ-1 ), - IntNoise3D( BaseX-1, BaseY+1, BaseZ-1 ), IntNoise3D( BaseX, BaseY+1, BaseZ-1 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ-1 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ-1 ), - IntNoise3D( BaseX-1, BaseY+2, BaseZ-1 ), IntNoise3D( BaseX, BaseY+2, BaseZ-1 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ-1 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ-1 ), - }; - - const float FracX = (a_X) - BaseX; - const float x1interp1 = CubicInterpolate( points1[0][0], points1[0][1], points1[0][2], points1[0][3], FracX ); - const float x1interp2 = CubicInterpolate( points1[1][0], points1[1][1], points1[1][2], points1[1][3], FracX ); - const float x1interp3 = CubicInterpolate( points1[2][0], points1[2][1], points1[2][2], points1[2][3], FracX ); - const float x1interp4 = CubicInterpolate( points1[3][0], points1[3][1], points1[3][2], points1[3][3], FracX ); - - const float points2[4][4] = { - IntNoise3D( BaseX-1, BaseY-1, BaseZ ), IntNoise3D( BaseX, BaseY-1, BaseZ ), IntNoise3D( BaseX+1, BaseY-1, BaseZ ), IntNoise3D( BaseX+2, BaseY-1, BaseZ ), - IntNoise3D( BaseX-1, BaseY, BaseZ ), IntNoise3D( BaseX, BaseY, BaseZ ), IntNoise3D( BaseX+1, BaseY, BaseZ ), IntNoise3D( BaseX+2, BaseY, BaseZ ), - IntNoise3D( BaseX-1, BaseY+1, BaseZ ), IntNoise3D( BaseX, BaseY+1, BaseZ ), IntNoise3D( BaseX+1, BaseY+1, BaseZ ), IntNoise3D( BaseX+2, BaseY+1, BaseZ ), - IntNoise3D( BaseX-1, BaseY+2, BaseZ ), IntNoise3D( BaseX, BaseY+2, BaseZ ), IntNoise3D( BaseX+1, BaseY+2, BaseZ ), IntNoise3D( BaseX+2, BaseY+2, BaseZ ), - }; - - const float x2interp1 = CubicInterpolate( points2[0][0], points2[0][1], points2[0][2], points2[0][3], FracX ); - const float x2interp2 = CubicInterpolate( points2[1][0], points2[1][1], points2[1][2], points2[1][3], FracX ); - const float x2interp3 = CubicInterpolate( points2[2][0], points2[2][1], points2[2][2], points2[2][3], FracX ); - const float x2interp4 = CubicInterpolate( points2[3][0], points2[3][1], points2[3][2], points2[3][3], FracX ); - - const float points3[4][4] = { - IntNoise3D( BaseX-1, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ+1 ), - IntNoise3D( BaseX-1, BaseY, BaseZ+1 ), IntNoise3D( BaseX, BaseY, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY, BaseZ+1 ), - IntNoise3D( BaseX-1, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ+1 ), - IntNoise3D( BaseX-1, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ+1 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ+1 ), - }; - - const float x3interp1 = CubicInterpolate( points3[0][0], points3[0][1], points3[0][2], points3[0][3], FracX ); - const float x3interp2 = CubicInterpolate( points3[1][0], points3[1][1], points3[1][2], points3[1][3], FracX ); - const float x3interp3 = CubicInterpolate( points3[2][0], points3[2][1], points3[2][2], points3[2][3], FracX ); - const float x3interp4 = CubicInterpolate( points3[3][0], points3[3][1], points3[3][2], points3[3][3], FracX ); - - const float points4[4][4] = { - IntNoise3D( BaseX-1, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY-1, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY-1, BaseZ+2 ), - IntNoise3D( BaseX-1, BaseY, BaseZ+2 ), IntNoise3D( BaseX, BaseY, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY, BaseZ+2 ), - IntNoise3D( BaseX-1, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY+1, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY+1, BaseZ+2 ), - IntNoise3D( BaseX-1, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX+1, BaseY+2, BaseZ+2 ), IntNoise3D( BaseX+2, BaseY+2, BaseZ+2 ), - }; - - const float x4interp1 = CubicInterpolate( points4[0][0], points4[0][1], points4[0][2], points4[0][3], FracX ); - const float x4interp2 = CubicInterpolate( points4[1][0], points4[1][1], points4[1][2], points4[1][3], FracX ); - const float x4interp3 = CubicInterpolate( points4[2][0], points4[2][1], points4[2][2], points4[2][3], FracX ); - const float x4interp4 = CubicInterpolate( points4[3][0], points4[3][1], points4[3][2], points4[3][3], FracX ); - - const float FracY = (a_Y) - BaseY; - const float yinterp1 = CubicInterpolate( x1interp1, x1interp2, x1interp3, x1interp4, FracY ); - const float yinterp2 = CubicInterpolate( x2interp1, x2interp2, x2interp3, x2interp4, FracY ); - const float yinterp3 = CubicInterpolate( x3interp1, x3interp2, x3interp3, x3interp4, FracY ); - const float yinterp4 = CubicInterpolate( x4interp1, x4interp2, x4interp3, x4interp4, FracY ); - - const float FracZ = (a_Z) - BaseZ; - return CubicInterpolate( yinterp1, yinterp2, yinterp3, yinterp4, FracZ ); -} - - - - - -/****************** - * Private - **/ - -#if NOISE_USE_SSE -__m128 cNoise::CubicInterpolate4( const __m128 & a_A, const __m128 & a_B, const __m128 & a_C, const __m128 & a_D, float a_Pct ) const -{ - const __m128 P = _mm_sub_ps( _mm_sub_ps( a_D, a_C ), _mm_sub_ps( a_A, a_B ) ); - const __m128 Q = _mm_sub_ps( _mm_sub_ps( a_A, a_B ), P ); - const __m128 R = _mm_sub_ps( a_C, a_A ); - - const __m128 Pct = _mm_set_ps1( a_Pct ); - const __m128 Pct2 = _mm_mul_ps( Pct, Pct ); - const __m128 Pct3 = _mm_mul_ps( Pct2, Pct ); - - return _mm_add_ps( _mm_add_ps( _mm_add_ps( _mm_mul_ps(P, Pct3), _mm_mul_ps( Q, Pct2 ) ), _mm_mul_ps( R, Pct ) ), a_B ); -} -#endif - - - - - -void IntArrayLinearInterpolate2D( - int * a_Array, - int a_SizeX, int a_SizeY, // Dimensions of the array - int a_AnchorStepX, int a_AnchorStepY // Distances between the anchor points in each direction -) -{ - // First interpolate columns where the anchor points are: - int LastYCell = a_SizeY - a_AnchorStepY; - for (int y = 0; y < LastYCell; y += a_AnchorStepY) - { - int Idx = a_SizeX * y; - for (int x = 0; x < a_SizeX; x += a_AnchorStepX) - { - int StartValue = a_Array[Idx]; - int EndValue = a_Array[Idx + a_SizeX * a_AnchorStepY]; - int Diff = EndValue - StartValue; - for (int CellY = 1; CellY < a_AnchorStepY; CellY++) - { - a_Array[Idx + a_SizeX * CellY] = StartValue + CellY * Diff / a_AnchorStepY; - } // for CellY - Idx += a_AnchorStepX; - } // for x - } // for y - - // Now interpolate in rows, each row has values in the anchor columns - int LastXCell = a_SizeX - a_AnchorStepX; - for (int y = 0; y < a_SizeY; y++) - { - int Idx = a_SizeX * y; - for (int x = 0; x < LastXCell; x += a_AnchorStepX) - { - int StartValue = a_Array[Idx]; - int EndValue = a_Array[Idx + a_AnchorStepX]; - int Diff = EndValue - StartValue; - for (int CellX = 1; CellX < a_AnchorStepX; CellX++) - { - a_Array[Idx + CellX] = StartValue + CellX * Diff / a_AnchorStepX; - } // for CellY - Idx += a_AnchorStepX; - } - } -} - - - - - - - - - - -#if NOISE_USE_INLINE -# include "cNoise.inc" -#endif - - - - diff --git a/source/cNoise.h b/source/cNoise.h deleted file mode 100644 index 6d0a69483..000000000 --- a/source/cNoise.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -// Some settings -#define NOISE_USE_INLINE 1 -#define NOISE_USE_SSE 0 - -// Do not touch -#if NOISE_USE_INLINE - #ifdef _MSC_VER - #define __NOISE_INLINE__ __forceinline - #else - #define __NOISE_INLINE__ inline - #endif // _MSC_VER -#else - #define __NOISE_INLINE__ -#endif - -#if NOISE_USE_SSE -# include -#endif - - - - - -class cNoise -{ -public: - cNoise( unsigned int a_Seed ); - ~cNoise(); - -#if NOISE_USE_SSE - __m128 SSE_IntNoise2D( int a_X1, int a_Y1, int a_X2, int a_Y2, int a_X3, int a_Y3, int a_X4, int a_Y4 ) const; -#endif - - __NOISE_INLINE__ float IntNoise( int a_X ) const; - __NOISE_INLINE__ float IntNoise2D( int a_X, int a_Y ) const; - __NOISE_INLINE__ float IntNoise3D( int a_X, int a_Y, int a_Z ) const; - - // Note: These functions have a mod8-irregular chance - each of the mod8 remainders has different chance of occurrence. Divide by 8 to rectify. - __NOISE_INLINE__ int IntNoise1DInt( int a_X ) const; - __NOISE_INLINE__ int IntNoise2DInt( int a_X, int a_Y ) const; - __NOISE_INLINE__ int IntNoise3DInt( int a_X, int a_Y, int a_Z ) const; - - float LinearNoise1D( float a_X ) const; - float CosineNoise1D( float a_X ) const; - float CubicNoise1D( float a_X ) const; - float SmoothNoise1D( int a_X ) const; - - float LinearNoise2D( float a_X, float a_Y ) const; - float CosineNoise2D( float a_X, float a_Y ) const; - float CubicNoise2D( float a_X, float a_Y ) const; - float SSE_CubicNoise2D( float a_X, float a_Y ) const; - - float CosineNoise3D( float a_X, float a_Y, float a_Z ) const; - float CubicNoise3D( float a_X, float a_Y, float a_Z ) const; - - void SetSeed( unsigned int a_Seed ) { m_Seed = a_Seed; } - - __NOISE_INLINE__ static float CubicInterpolate( float a_A, float a_B, float a_C, float a_D, float a_Pct ); - __NOISE_INLINE__ static float CosineInterpolate( float a_A, float a_B, float a_Pct ); - __NOISE_INLINE__ static float LinearInterpolate( float a_A, float a_B, float a_Pct ); - -private: - -#if NOISE_USE_SSE - __m128 CubicInterpolate4( const __m128 & a_A, const __m128 & a_B, const __m128 & a_C, const __m128 & a_D, float a_Pct ) const; -#endif - - unsigned int m_Seed; -}; - - - - - -/// Linearly interpolates values in the array between the anchor points -extern void IntArrayLinearInterpolate2D( - int * a_Array, - int a_SizeX, int a_SizeY, // Dimensions of the array - int a_AnchorStepX, int a_AnchorStepY // Distances between the anchor points in each direction -); - - - - - -#if NOISE_USE_INLINE -# include "cNoise.inc" -#endif - - - - diff --git a/source/cNoise.inc b/source/cNoise.inc deleted file mode 100644 index 60fd2f394..000000000 --- a/source/cNoise.inc +++ /dev/null @@ -1,124 +0,0 @@ - -#ifndef __C_NOISE_INC__ -#define __C_NOISE_INC__ - -#include - - - - - -/**************** - * Random value generator - **/ - -float cNoise::IntNoise( int a_X ) const -{ - int x = ((a_X*m_Seed)<<13) ^ a_X; - return ( 1.0f - ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f); - // returns a float number in the range of [-1, 1] -} - - - - - -float cNoise::IntNoise2D( int a_X, int a_Y ) const -{ - int n = a_X + a_Y * 57 + m_Seed*57*57; - n = (n<<13) ^ n; - return ( 1.0f - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f); - // returns a float number in the range of [-1, 1] -} - - - - - -float cNoise::IntNoise3D( int a_X, int a_Y, int a_Z ) const -{ - int n = a_X + a_Y * 57 + a_Z * 57*57 + m_Seed*57*57*57; - n = (n<<13) ^ n; - return ( 1.0f - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f); - // returns a float number in the range of [-1, 1] -} - - - - - -int cNoise::IntNoise1DInt( int a_X ) const -{ - int x = ((a_X*m_Seed)<<13) ^ a_X; - return ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff); -} - - - - - -int cNoise::IntNoise2DInt( int a_X, int a_Y ) const -{ - int n = a_X + a_Y * 57 + m_Seed*57*57; - n = (n<<13) ^ n; - return ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); -} - - - - - -int cNoise::IntNoise3DInt( int a_X, int a_Y, int a_Z ) const -{ - int n = a_X + a_Y * 57 + a_Z * 57*57 + m_Seed*57*57*57; - n = (n<<13) ^ n; - return ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); -} - - - - - -/**************** - * Interpolation functions - **/ - -float cNoise::CubicInterpolate( float a_A, float a_B, float a_C, float a_D, float a_Pct ) -{ - float P = (a_D - a_C) - (a_A - a_B); - float Q = (a_A - a_B) - P; - float R = a_C - a_A; - float S = a_B; - - return ((P * a_Pct + Q) * a_Pct + R) * a_Pct + S; -} - - - - - -float cNoise::CosineInterpolate( float a_A, float a_B, float a_Pct ) -{ - const float ft = a_Pct * 3.1415927f; - const float f = (1.f - cosf(ft)) * 0.5f; - return a_A*(1-f) + a_B*f; -} - - - - - -float cNoise::LinearInterpolate( float a_A, float a_B, float a_Pct ) -{ - return a_A*(1.f-a_Pct) + a_B*a_Pct; -} - - - - - -#endif - - - - diff --git a/source/cNoteEntity.cpp b/source/cNoteEntity.cpp deleted file mode 100644 index f007433fc..000000000 --- a/source/cNoteEntity.cpp +++ /dev/null @@ -1,159 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cNoteEntity.h" -#include "cWorld.h" -#include - - -cNoteEntity::cNoteEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) - : cBlockEntity(E_BLOCK_NOTE_BLOCK, a_BlockX, a_BlockY, a_BlockZ, a_World) - , m_Pitch( 0 ) -{ -} - - - - - -cNoteEntity::~cNoteEntity() -{ -} - - - - - -void cNoteEntity::UsedBy( cPlayer * a_Player ) -{ - IncrementPitch(); - MakeSound(); -} - - - - - -void cNoteEntity::MakeSound( void ) -{ - char instrument; - AString sampleName; - - switch (m_World->GetBlock(m_PosX, m_PosY - 1, m_PosZ)) - { - case E_BLOCK_PLANKS: - case E_BLOCK_LOG: - case E_BLOCK_NOTE_BLOCK: - { - // TODO: add other wood-based blocks if needed - instrument = E_INST_DOUBLE_BASS; - sampleName = "note.db"; - break; - } - - case E_BLOCK_SAND: - case E_BLOCK_GRAVEL: - case E_BLOCK_SOULSAND: - { - instrument = E_INST_SNARE_DRUM; - sampleName = "note.snare"; - break; - } - - case E_BLOCK_GLASS: - case E_BLOCK_GLASS_PANE: - case E_BLOCK_GLOWSTONE: - { - instrument = E_INST_CLICKS; - sampleName = "note.hat"; - break; - } - - case E_BLOCK_STONE: - case E_BLOCK_STONE_BRICKS: - case E_BLOCK_COBBLESTONE: - case E_BLOCK_OBSIDIAN: - case E_BLOCK_NETHERRACK: - case E_BLOCK_BRICK: - case E_BLOCK_NETHER_BRICK: - { - // TODO: add other stone-based blocks if needed - instrument = E_INST_BASS_DRUM; - sampleName = "note.bassattack"; - break; - } - - default: - { - instrument = E_INST_HARP_PIANO; - sampleName = "note.harp"; - break; - } - } - - m_World->BroadcastBlockAction(m_PosX, m_PosY, m_PosZ, instrument, m_Pitch, E_BLOCK_NOTE_BLOCK); - - // TODO: instead of calculating the power function over and over, make a precalculated table - there's only 24 pitches after all - float calcPitch = pow(2.0f, ((float)m_Pitch - 12.0f) / 12.0f); - m_World->BroadcastSoundEffect(sampleName, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 3.0f, calcPitch); -} - - - - - -char cNoteEntity::GetPitch( void ) -{ - return m_Pitch; -} - - - - - -void cNoteEntity::SetPitch( char a_Pitch ) -{ - m_Pitch = a_Pitch % 25; -} - - - - - -void cNoteEntity::IncrementPitch( void ) -{ - SetPitch( m_Pitch + 1 ); -} - - - - - -bool cNoteEntity::LoadFromJson( const Json::Value & a_Value ) -{ - - m_PosX = a_Value.get("x", 0).asInt(); - m_PosY = a_Value.get("y", 0).asInt(); - m_PosZ = a_Value.get("z", 0).asInt(); - - m_Pitch = (char)a_Value.get("p", 0).asInt(); - - return true; -} - - - - - -void cNoteEntity::SaveToJson( Json::Value & a_Value ) -{ - a_Value["x"] = m_PosX; - a_Value["y"] = m_PosY; - a_Value["z"] = m_PosZ; - - a_Value["p"] = m_Pitch; -} - - - - diff --git a/source/cNoteEntity.h b/source/cNoteEntity.h deleted file mode 100644 index fad1f8f06..000000000 --- a/source/cNoteEntity.h +++ /dev/null @@ -1,52 +0,0 @@ - -#pragma once - -#include "cBlockEntity.h" - - -namespace Json -{ - class Value; -} - - - - - -enum ENUM_NOTE_INSTRUMENTS -{ - E_INST_HARP_PIANO = 0, - E_INST_DOUBLE_BASS = 1, - E_INST_SNARE_DRUM = 2, - E_INST_CLICKS = 3, - E_INST_BASS_DRUM = 4 -}; - - - - - -class cNoteEntity : - public cBlockEntity -{ -public: - cNoteEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); - virtual ~cNoteEntity(); - - bool LoadFromJson( const Json::Value& a_Value ); - virtual void SaveToJson( Json::Value& a_Value ) override; - - char GetPitch( void ); - void SetPitch( char a_Pitch ); - void IncrementPitch( void ); - void MakeSound( void ); - virtual void UsedBy( cPlayer * a_Player ) override; - virtual void SendTo(cClientHandle & a_Client) override { }; - -private: - unsigned char m_Pitch; -}; - - - - diff --git a/source/cPawn.cpp b/source/cPawn.cpp deleted file mode 100644 index cecc19522..000000000 --- a/source/cPawn.cpp +++ /dev/null @@ -1,230 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cPawn.h" -#include "cRoot.h" -#include "cServer.h" -#include "cWorld.h" -#include "cPlayer.h" -#include "cPluginManager.h" -#include "Vector3d.h" -#include "BlockID.h" -#include "Defines.h" - - - - - -CLASS_DEFINITION( cPawn, cEntity ) - - - - - -cPawn::cPawn() - : cEntity( 0, 0, 0 ) - , m_LastPosX( 0.0 ) - , m_LastPosY( 0.0 ) - , m_LastPosZ( 0.0 ) - , m_TimeLastTeleportPacket( 0.f ) - , m_bBurnable(true) - , m_MetaData(NORMAL) -{ - SetMaxHealth(20); -} - - - - - -cPawn::~cPawn() -{ - -} - - - - - -void cPawn::Heal( int a_Health ) -{ - (void)a_Health; -} - - - - - -void cPawn::TakeDamage(int a_Damage, cEntity * a_Instigator) -{ - TakeDamageInfo TDI; - TDI.Damage = a_Damage; - TDI.Instigator = a_Instigator; - cRoot::Get()->GetPluginManager()->CallHook( cPluginManager::E_PLUGIN_TAKE_DAMAGE, 2, this, &TDI); - - if (TDI.Damage == 0) - { - return; - } - if (m_Health <= 0) - { - // Can't take damage if already dead - return; - } - - m_Health -= (short)TDI.Damage; - if (m_Health < 0) - { - m_Health = 0; - } - - m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_HURT); - - if (m_Health <= 0) - { - KilledBy(TDI.Instigator); - } -} - - - - - -void cPawn::KilledBy(cEntity * a_Killer) -{ - m_Health = 0; - - if( cRoot::Get()->GetPluginManager()->CallHook( cPluginManager::E_PLUGIN_KILLED, 2, this, a_Killer ) ) - { - return; // Give plugins a chance to 'unkill' the pawn. - } - - m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_DEAD); -} - - - - - -void cPawn::TeleportToEntity(cEntity * a_Entity) -{ - TeleportTo(a_Entity->GetPosX(), a_Entity->GetPosY(), a_Entity->GetPosZ()); -} - - - - - -void cPawn::TeleportTo( const double & a_PosX, const double & a_PosY, const double & a_PosZ ) -{ - SetPosition( a_PosX, a_PosY, a_PosZ ); - - GetWorld()->BroadcastTeleportEntity(*this); -} - - - - - -void cPawn::Tick(float a_Dt) -{ - CheckMetaDataBurn(); // Check to see if pawn should burn based on block they are on - - if (GetMetaData() == BURNING) - { - InStateBurning(a_Dt); - } -} - - - - - - -void cPawn::SetMetaData(MetaData a_MetaData) -{ - //Broadcast new status to clients in the chunk - m_MetaData = a_MetaData; - m_World->BroadcastMetadata(*this); -} - - - - - -//----Change Entity MetaData -void cPawn::CheckMetaDataBurn() -{ - char Block = GetWorld()->GetBlock((int) m_Pos.x, (int) m_Pos.y, (int) m_Pos.z); - char BlockAbove = GetWorld()->GetBlock((int) m_Pos.x, (int) m_Pos.y + 1, (int) m_Pos.z); - char BlockBelow = GetWorld()->GetBlock((int) m_Pos.x, (int) m_Pos.y - 1, (int) m_Pos.z); - - if ( - (GetMetaData() == BURNING) && - (IsBlockWater(Block) || IsBlockWater(BlockAbove) || IsBlockWater(BlockBelow)) - ) - { - SetMetaData(NORMAL); - } - else if ( - m_bBurnable && - (GetMetaData() != BURNING) && - ( - IsBlockLava(Block) || (Block == E_BLOCK_FIRE) || - IsBlockLava(BlockAbove) || (BlockAbove == E_BLOCK_FIRE) || - IsBlockLava(BlockBelow) || (BlockBelow == E_BLOCK_FIRE) - ) - ) - { - SetMetaData(BURNING); - } -} - - - - - -//What to do if On fire -void cPawn::InStateBurning(float a_Dt) -{ - m_FireDamageInterval += a_Dt; - char Block = GetWorld()->GetBlock( (int)m_Pos.x, (int)m_Pos.y, (int)m_Pos.z ); - char BlockAbove = GetWorld()->GetBlock( (int)m_Pos.x, (int)m_Pos.y + 1, (int)m_Pos.z ); - if (m_FireDamageInterval > 800) - { - - m_FireDamageInterval = 0; - TakeDamage(1, this); - - m_BurnPeriod++; - if (IsBlockLava(Block) || Block == E_BLOCK_FIRE - || IsBlockLava(BlockAbove) || BlockAbove == E_BLOCK_FIRE) - { - m_BurnPeriod = 0; - TakeDamage(6, this); - } - else - { - TakeDamage(1, this); - } - - if (m_BurnPeriod > 7) - { - SetMetaData(NORMAL); - m_BurnPeriod = 0; - } - } -} - - - - - -void cPawn::SetMaxHealth(short a_MaxHealth) -{ - this->m_MaxHealth = a_MaxHealth; - - //Reset health - m_Health = a_MaxHealth; -} - diff --git a/source/cPawn.h b/source/cPawn.h deleted file mode 100644 index b48ce94ab..000000000 --- a/source/cPawn.h +++ /dev/null @@ -1,66 +0,0 @@ - -#pragma once - -#include "cEntity.h" - - - - - -struct TakeDamageInfo //tolua_export -{ //tolua_export - int Damage; //tolua_export - cEntity* Instigator; //tolua_export -}; //tolua_export - - - - - -class cPawn : public cEntity //tolua_export -{ //tolua_export -public: - CLASS_PROTOTYPE() - - cPawn(); - virtual ~cPawn(); - - virtual void TeleportToEntity( cEntity* a_Entity ); //tolua_export - virtual void TeleportTo( const double & a_PosX, const double & a_PosY, const double & a_PosZ ); //tolua_export - - virtual void Tick(float a_Dt) override; - - void Heal( int a_Health ); //tolua_export - virtual void TakeDamage( int a_Damage, cEntity* a_Instigator ); //tolua_export - virtual void KilledBy( cEntity* a_Killer ); //tolua_export - int GetHealth() { return m_Health; } //tolua_export - - enum MetaData {NORMAL, BURNING, CROUCHED, RIDING, SPRINTING, EATING, BLOCKING}; - - virtual void SetMetaData(MetaData a_MetaData); - virtual MetaData GetMetaData(void) const { return m_MetaData; } - - virtual void InStateBurning(float a_Dt); - - virtual void CheckMetaDataBurn(); - - virtual void SetMaxHealth(short a_MaxHealth); - virtual short GetMaxHealth() { return m_MaxHealth; } - -protected: - - short m_Health; - short m_MaxHealth; - - - bool m_bBurnable; - - MetaData m_MetaData; - - double m_LastPosX, m_LastPosY, m_LastPosZ; - float m_TimeLastTeleportPacket; -}; //tolua_export - - - - diff --git a/source/cPickup.cpp b/source/cPickup.cpp deleted file mode 100644 index a9545e57d..000000000 --- a/source/cPickup.cpp +++ /dev/null @@ -1,275 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#ifndef _WIN32 -#include -#endif - -#include "cPickup.h" -#include "cClientHandle.h" -#include "cInventory.h" -#include "cWorld.h" -#include "cWaterSimulator.h" -#include "cServer.h" -#include "cPlayer.h" -#include "cPluginManager.h" -#include "cItem.h" -#include "cRoot.h" -#include "cTracer.h" - -#include "Vector3d.h" -#include "Vector3f.h" - - - - - -CLASS_DEFINITION( cPickup, cEntity ) - - - - - -cPickup::cPickup(int a_X, int a_Y, int a_Z, const cItem & a_Item, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */) - : cEntity( ((double)(a_X))/32, ((double)(a_Y))/32, ((double)(a_Z))/32 ) - , m_Speed( a_SpeedX, a_SpeedY, a_SpeedZ ) - , m_bOnGround( false ) - , m_bReplicated( false ) - , m_Timer( 0.f ) - , m_Item( new cItem( a_Item ) ) - , m_bCollected( false ) -{ - // LOGD("New pickup: ID(%i) Amount(%i) Health(%i)", m_Item.m_ItemID, m_Item.m_ItemCount, m_Item.m_ItemHealth ); - - m_EntityType = eEntityType_Pickup; -} - - - - - -cPickup::~cPickup() -{ - delete m_Item; -} - - - - - -void cPickup::Initialize(cWorld * a_World) -{ - super::Initialize(a_World); - a_World->BroadcastSpawn(*this); -} - - - - - -void cPickup::SpawnOn(cClientHandle & a_Client) -{ - a_Client.SendPickupSpawn(*this); -} - - - - - -void cPickup::Tick(float a_Dt) -{ - m_Timer += a_Dt; - a_Dt = a_Dt / 1000.f; - if(m_bCollected) - { - if(m_Timer > 500.f) // 0.5 second - { - Destroy(); - return; - } - } - - if( m_Timer > 1000*60*5 ) // 5 minutes - { - Destroy(); - return; - } - - if( m_Pos.y < 0 ) // Out of this world! - { - Destroy(); - return; - } - - if (!m_bCollected) - { - HandlePhysics(a_Dt); - } - - if (!m_bReplicated || m_bDirtyPosition) - { - MoveToCorrectChunk(); - m_bReplicated = true; - m_bDirtyPosition = false; - GetWorld()->BroadcastTeleportEntity(*this); - } -} - - - - - -void cPickup::HandlePhysics(float a_Dt) -{ - m_ResultingSpeed.Set(0.f, 0.f, 0.f); - cWorld * World = GetWorld(); - - if( m_bOnGround ) // check if it's still on the ground - { - int BlockX = (m_Pos.x)<0 ? (int)m_Pos.x-1 : (int)m_Pos.x; - int BlockZ = (m_Pos.z)<0 ? (int)m_Pos.z-1 : (int)m_Pos.z; - char BlockBelow = World->GetBlock( BlockX, (int)m_Pos.y -1, BlockZ ); - //Not only air, falls through water ;) - if( BlockBelow == E_BLOCK_AIR || IsBlockWater(BlockBelow)) - { - m_bOnGround = false; - } - char Block = World->GetBlock( BlockX, (int)m_Pos.y - (int)m_bOnGround, BlockZ ); - char BlockIn = World->GetBlock( BlockX, (int)m_Pos.y, BlockZ ); - - if( IsBlockLava(Block) || Block == E_BLOCK_FIRE - || IsBlockLava(BlockIn) || BlockIn == E_BLOCK_FIRE) - { - m_bCollected = true; - m_Timer = 0; - return; - } - - if( BlockIn != E_BLOCK_AIR && !IsBlockWater(BlockIn) ) // If in ground itself, push it out - { - m_bOnGround = true; - m_Pos.y += 0.2; - m_bReplicated = false; - } - m_Speed.x *= 0.7f/(1+a_Dt); - if( fabs(m_Speed.x) < 0.05 ) m_Speed.x = 0; - m_Speed.z *= 0.7f/(1+a_Dt); - if( fabs(m_Speed.z) < 0.05 ) m_Speed.z = 0; - } - - - //get flowing direction - Direction WaterDir = World->GetWaterSimulator()->GetFlowingDirection((int) m_Pos.x - 1, (int) m_Pos.y, (int) m_Pos.z - 1); - - - m_WaterSpeed *= 0.9f; //Keep old speed but lower it - - switch(WaterDir) - { - case X_PLUS: - m_WaterSpeed.x = 1.f; - m_bOnGround = false; - break; - case X_MINUS: - m_WaterSpeed.x = -1.f; - m_bOnGround = false; - break; - case Z_PLUS: - m_WaterSpeed.z = 1.f; - m_bOnGround = false; - break; - case Z_MINUS: - m_WaterSpeed.z = -1.f; - m_bOnGround = false; - break; - - default: - break; - } - - m_ResultingSpeed += m_WaterSpeed; - - - if( !m_bOnGround ) - { - - float Gravity = -9.81f*a_Dt; - m_Speed.y += Gravity; - - // Set to hit position - m_ResultingSpeed += m_Speed; - - cTracer Tracer( GetWorld() ); - int Ret = Tracer.Trace( m_Pos, m_Speed, 2 ); - if( Ret ) // Oh noez! we hit something - { - - - if( (Tracer.RealHit - Vector3f(m_Pos)).SqrLength() <= ( m_ResultingSpeed * a_Dt ).SqrLength() ) - { - m_bReplicated = false; // It's only interesting to replicate when we actually hit something... - if( Ret == 1 ) - { - - if( Tracer.HitNormal.x != 0.f ) m_Speed.x = 0.f; - if( Tracer.HitNormal.y != 0.f ) m_Speed.y = 0.f; - if( Tracer.HitNormal.z != 0.f ) m_Speed.z = 0.f; - - if( Tracer.HitNormal.y > 0 ) // means on ground - { - m_bOnGround = true; - } - } - m_Pos = Tracer.RealHit; - m_Pos += Tracer.HitNormal * 0.2f; - - } - else - m_Pos += m_ResultingSpeed*a_Dt; - } - else - { // We didn't hit anything, so move =] - m_Pos += m_ResultingSpeed * a_Dt; - } - } - //Usable for debugging - //SetPosition(m_Pos.x, m_Pos.y, m_Pos.z); -} - - - - - -bool cPickup::CollectedBy( cPlayer* a_Dest ) -{ - if (m_bCollected) - { - return false; // It's already collected! - } - - // 800 is to long - if (m_Timer < 500.f) - { - return false; // Not old enough - } - - if (cRoot::Get()->GetPluginManager()->CallHookCollectPickup(a_Dest, *this)) - { - return false; - } - - if (a_Dest->GetInventory().AddItem(*m_Item)) - { - m_World->BroadcastCollectPickup(*this, *a_Dest); - - m_bCollected = true; - m_Timer = 0; - return true; - } - - return false; -} - - - - diff --git a/source/cPickup.h b/source/cPickup.h deleted file mode 100644 index 8e8f514b8..000000000 --- a/source/cPickup.h +++ /dev/null @@ -1,63 +0,0 @@ - -#pragma once - -#include "cEntity.h" - - - - -class cPlayer; -class cItem; - - - - - -// tolua_begin -class cPickup : - public cEntity -{ - // tolua_end - typedef cEntity super; - // tolua_begin - -public: - // tolua_end - CLASS_PROTOTYPE(); - - cPickup(int a_X, int a_Y, int a_Z, const cItem & a_Item, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); //tolua_export - ~cPickup(); //tolua_export - - virtual void Initialize(cWorld * a_World) override; - - cItem * GetItem(void) {return m_Item; } //tolua_export - const cItem * GetItem(void) const {return m_Item; } - - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - - virtual bool CollectedBy( cPlayer* a_Dest ); //tolua_export - - void Tick(float a_Dt); - void HandlePhysics(float a_Dt); - - const Vector3f & GetSpeed(void) const {return m_Speed; } - -private: - - Vector3f m_Speed; - Vector3f m_ResultingSpeed; //Can be used to modify the resulting speed for the current tick ;) - - Vector3f m_WaterSpeed; - bool m_bOnGround; - bool m_bReplicated; - - float m_Timer; - - cItem* m_Item; - - bool m_bCollected; -}; //tolua_export - - - - diff --git a/source/cPiston.cpp b/source/cPiston.cpp deleted file mode 100644 index 4d6ccef0e..000000000 --- a/source/cPiston.cpp +++ /dev/null @@ -1,152 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cPiston.h" -#include "cRedstone.h" -#include "ChunkDef.h" -#include "cPickup.h" -#include "cItem.h" -#include "cRoot.h" -#include "cClientHandle.h" -#include "cWorld.h" -#include "BlockID.h" -#include "cServer.h" -#include "blocks/Block.h" - -extern bool g_BlockPistonBreakable[]; - -#define AddDir( x, y, z, dir, amount ) switch(dir) { case 0: (y)-=(amount); break; case 1: (y)+=(amount); break;\ - case 2: (z)-=(amount); break; case 3: (z)+=(amount); break;\ - case 4: (x)-=(amount); break; case 5: (x)+=(amount); break; } - - - - - -cPiston::cPiston( cWorld* a_World ) - :m_World ( a_World ) -{ - -} - -unsigned short cPiston::FirstPassthroughBlock( int pistonX, int pistonY, int pistonZ, char pistonmeta ) { - unsigned short ret; - pistonmeta &= 7; - if(pistonmeta >= 6) { // just in case, it shouldn't happen but if it would, it'd case inf loop - printf("cPiston::FirstPassthroughBlock - piston has invalid meta data!\n"); - return 9001; - } - char currBlock; - for( ret = 0; ret < 24; ret++ ) { // push up to 24 blocks - AddDir( pistonX, pistonY, pistonZ, pistonmeta, 1 ) - currBlock = m_World->GetBlock( pistonX, pistonY, pistonZ ); - if(currBlock == E_BLOCK_BEDROCK || currBlock == E_BLOCK_OBSIDIAN || currBlock == E_BLOCK_PISTON_EXTENSION ) {return 9001;} - if(g_BlockPistonBreakable[currBlock]) {return ret;} - } - return 9001; -} - - - - - -void cPiston::ExtendPiston( int pistx, int pisty, int pistz ) -{ - char pistonBlock = m_World->GetBlock( pistx, pisty, pistz ); - char pistonMeta = m_World->GetBlockMeta( pistx, pisty, pistz ); - char isSticky = (char)(pistonBlock == E_BLOCK_STICKY_PISTON) * 8; - bool recalc = false; - if ( (pistonMeta & 0x8) == 0x0 ) // only extend if piston is not already extended - { - unsigned short dist = FirstPassthroughBlock(pistx, pisty, pistz, pistonMeta); - if (dist > 9000) return; // too many blocks - - AddDir( pistx, pisty, pistz, pistonMeta & 7, dist+1 ) - BLOCKTYPE currBlock = m_World->GetBlock (pistx, pisty, pistz); - NIBBLETYPE currMeta = m_World->GetBlockMeta(pistx, pisty, pistz); - if (currBlock != E_BLOCK_AIR) - { - cBlockHandler * Handler = BlockHandler(currBlock); - if(Handler->DropOnUnsuitable()) - { - Handler->DropBlock(m_World, pistx, pisty, pistz); - } - recalc = true; - } - int oldx = pistx, oldy = pisty, oldz = pistz; - char currBlockMeta; - for (int i = dist + 1; i>0; i--) - { - AddDir(pistx, pisty, pistz, pistonMeta & 7, -1) - currBlock = m_World->GetBlock(pistx, pisty, pistz); - currBlockMeta = m_World->GetBlockMeta(pistx, pisty, pistz); - m_World->SetBlock( oldx, oldy, oldz, currBlock, currBlockMeta); - oldx = pistx; - oldy = pisty; - oldz = pistz; - } - m_World->BroadcastBlockAction(pistx, pisty, pistz, 0, pistonMeta, E_BLOCK_PISTON); - m_World->FastSetBlock( pistx, pisty, pistz, pistonBlock, pistonMeta | 0x8 ); - - int extx = pistx; - int exty = pisty; - int extz = pistz; - - AddDir(extx, exty, extz, pistonMeta & 7, 1) - - m_World->SetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, isSticky + pistonMeta & 7); - - if (recalc) - { - cRedstone Redstone(m_World); - Redstone.ChangeRedstone(extx, exty, extz, false); // recalculate redstone around current device - Redstone.ChangeRedstone(pistx, pisty, pistz, false); // recalculate redstone around current device - } - } -} - - - - - -void cPiston::RetractPiston( int pistx, int pisty, int pistz ) -{ - char pistonBlock = m_World->GetBlock( pistx, pisty, pistz ); - char pistonMeta = m_World->GetBlockMeta( pistx, pisty, pistz ); - if (pistonMeta <= 6) // only retract if piston is not already retracted - { - return; - } - m_World->BroadcastBlockAction(pistx, pisty, pistz, 1, pistonMeta & ~(8), E_BLOCK_PISTON); - m_World->FastSetBlock(pistx, pisty, pistz, pistonBlock, pistonMeta & ~(8)); - - AddDir(pistx, pisty, pistz, pistonMeta & 7, 1) - if (m_World->GetBlock(pistx, pisty, pistz) == E_BLOCK_PISTON_EXTENSION) - { - if (pistonBlock == E_BLOCK_STICKY_PISTON) - { - int tempx = pistx, tempy = pisty, tempz = pistz; - AddDir( tempx, tempy, tempz, pistonMeta & 7, 1 ) - char tempblock = m_World->GetBlock( tempx, tempy, tempz ); - if ( - (tempblock == E_BLOCK_OBSIDIAN) || - (tempblock == E_BLOCK_BEDROCK) || - (tempblock == E_BLOCK_PISTON_EXTENSION) - ) - { - // These cannot be moved by the sticky piston, bail out - return; - } - m_World->SetBlock( pistx, pisty, pistz, tempblock, m_World->GetBlockMeta( tempx, tempy, tempz ) ); - m_World->SetBlock( tempx, tempy, tempz, E_BLOCK_AIR, 0 ); - } - else - { - m_World->SetBlock( pistx, pisty, pistz, E_BLOCK_AIR, 0 ); - } - } -} - - - - diff --git a/source/cPiston.h b/source/cPiston.h deleted file mode 100644 index d52b92b3d..000000000 --- a/source/cPiston.h +++ /dev/null @@ -1,60 +0,0 @@ - -#pragma once - - - - - -// fwd: "cWorld.h" -class cWorld; - - - - - -class cPiston -{ -public: - - cPiston( cWorld* a_World ); - - static char RotationPitchToMetaData( float a_Rotation, float a_Pitch ) - { - LOGD("pre:a_Rotation %f \n",a_Rotation); - LOGD("a_Pitch %f \n",a_Pitch); - - if (a_Pitch >= 50.f ){ - return 0x1; - } else if ( a_Pitch <= -50.f ) { - return 0x0; - } else { - - a_Rotation += 90 + 45; // So its not aligned with axis - std::printf("a_Rotation %f \n",a_Rotation); - - if( a_Rotation > 360.f ) a_Rotation -= 360.f; - if( a_Rotation >= 0.f && a_Rotation < 90.f ) - { LOG("1111\n");return 0x4;} - else if( a_Rotation >= 180 && a_Rotation < 270 ) - { LOG("2222\n");return 0x5;} - else if( a_Rotation >= 90 && a_Rotation < 180 ) - { LOG("3333\n");return 0x2;} - else - { LOG("4444\n");return 0x3;} - } - } - - void ExtendPiston( int, int, int ); - void RetractPiston( int, int, int ); - - cWorld* m_World; - -private: - void ChainMove( int, int, int, char, unsigned short * ); - unsigned short FirstPassthroughBlock( int, int, int, char ); - -}; - - - - diff --git a/source/cPlayer.cpp b/source/cPlayer.cpp deleted file mode 100644 index 1de9bf36a..000000000 --- a/source/cPlayer.cpp +++ /dev/null @@ -1,1019 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cPlayer.h" -#include "cServer.h" -#include "cClientHandle.h" -#include "UI/Window.h" -#include "UI/WindowOwner.h" -#include "cWorld.h" -#include "cPickup.h" -#include "cPluginManager.h" -#include "cBlockEntity.h" -#include "cGroupManager.h" -#include "cGroup.h" -#include "cChatColor.h" -#include "cItem.h" -#include "cTracer.h" -#include "cRoot.h" -#include "OSSupport/MakeDir.h" -#include "OSSupport/Timer.h" -#include "MersenneTwister.h" - -#include "Vector3d.h" -#include "Vector3f.h" - -#include "../iniFile/iniFile.h" -#include - -#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x)) - - - - - -CLASS_DEFINITION( cPlayer, cPawn ); - - - - - -cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) - : m_GameMode(eGameMode_NotSet) - , m_IP("") - , m_LastBlockActionTime( 0 ) - , m_LastBlockActionCnt( 0 ) - , m_bVisible( true ) - , m_LastGroundHeight( 0 ) - , m_bTouchGround( false ) - , m_Stance( 0.0 ) - , m_Inventory(*this) - , m_CurrentWindow(NULL) - , m_InventoryWindow(NULL) - , m_TimeLastPickupCheck( 0.f ) - , m_Color('-') - , m_ClientHandle( a_Client ) - , m_FoodExhaustionLevel(0.f) - , m_FoodTickTimer(0) -{ - LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d", - a_PlayerName.c_str(), a_Client->GetIPString().c_str(), - this, GetUniqueID() - ); - m_EntityType = eEntityType_Player; - - m_InventoryWindow = new cInventoryWindow(*this); - m_CurrentWindow = m_InventoryWindow; - m_InventoryWindow->OpenedByPlayer(*this); - - SetMaxHealth(20); - m_MaxFoodLevel = 20; - m_MaxFoodSaturationLevel = 20.f; - - m_FoodLevel = m_MaxFoodLevel; - m_FoodSaturationLevel = 5.f; - - cTimer t1; - m_LastPlayerListTime = t1.GetNowTime(); - - m_TimeLastTeleportPacket = cWorld::GetTime(); - m_TimeLastPickupCheck = cWorld::GetTime(); - - m_PlayerName = a_PlayerName; - m_bDirtyPosition = true; // So chunks are streamed to player at spawn - - if (!LoadFromDisk()) - { - m_Inventory.Clear(); - m_Pos.x = cRoot::Get()->GetDefaultWorld()->GetSpawnX(); - m_Pos.y = cRoot::Get()->GetDefaultWorld()->GetSpawnY(); - m_Pos.z = cRoot::Get()->GetDefaultWorld()->GetSpawnZ(); - - LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}", - a_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z - ); - } - m_LastGroundHeight = (float)(m_Pos.y); - m_Stance = m_Pos.y + 1.62; -} - - - - - -cPlayer::~cPlayer(void) -{ - LOG("Deleting cPlayer \"%s\" at %p, ID %d", m_PlayerName.c_str(), this, GetUniqueID()); - - SaveToDisk(); - - m_World->RemovePlayer( this ); - - m_ClientHandle = NULL; - - delete m_InventoryWindow; - - LOG("Player %p deleted", this); -} - - - - - -void cPlayer::Initialize( cWorld* a_World ) -{ - cPawn::Initialize( a_World ); - GetWorld()->AddPlayer( this ); -} - - - - - -void cPlayer::Destroyed() -{ - CloseWindow(-1); - m_ClientHandle = NULL; -} - - - - - -void cPlayer::SpawnOn(cClientHandle & a_Client) -{ - /* - LOGD("cPlayer::SpawnOn(%s) for \"%s\" at pos {%.2f, %.2f, %.2f}", - a_Client.GetUsername().c_str(), m_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z - ); - */ - - if (m_bVisible) - { - a_Client.SendPlayerSpawn(*this); - } -} - - - - - -void cPlayer::Tick(float a_Dt) -{ - if (!m_ClientHandle->IsPlaying()) - { - // We're not yet in the game, ignore everything - return; - } - - cPawn::Tick(a_Dt); - - if (m_bDirtyOrientation && !m_bDirtyPosition) - { - m_World->BroadcastEntLook(*this, m_ClientHandle); - m_World->BroadcastEntHeadLook(*this, m_ClientHandle); - m_bDirtyOrientation = false; - } - else if (m_bDirtyPosition) - { - cRoot::Get()->GetPluginManager()->CallHook( cPluginManager::E_PLUGIN_PLAYER_MOVE, 1, this ); - - float DiffX = (float)(GetPosX() - m_LastPosX ); - float DiffY = (float)(GetPosY() - m_LastPosY ); - float DiffZ = (float)(GetPosZ() - m_LastPosZ ); - float SqrDist = DiffX * DiffX + DiffY * DiffY + DiffZ * DiffZ; - if ( - (SqrDist > 4 * 4) || // 4 blocks is max Relative Move - (cWorld::GetTime() - m_TimeLastTeleportPacket > 2 ) // Send an absolute position every 2 seconds - ) - { - // LOG("Teleported %f", sqrtf(SqrDist) ); - m_World->BroadcastTeleportEntity(*this, m_ClientHandle); - m_TimeLastTeleportPacket = cWorld::GetTime(); - } - else - { - // Relative move sucks balls! It's always wrong wtf! - if (m_bDirtyOrientation) - { - m_World->BroadcastEntRelMoveLook(*this, (char)(DiffX * 32), (char)(DiffY * 32), (char)(DiffZ * 32), m_ClientHandle); - m_bDirtyOrientation = false; - } - else - { - m_World->BroadcastEntRelMove(*this, (char)(DiffX * 32), (char)(DiffY * 32), (char)(DiffZ * 32), m_ClientHandle); - } - } - m_LastPosX = GetPosX(); - m_LastPosY = GetPosY(); - m_LastPosZ = GetPosZ(); - m_bDirtyPosition = false; - m_ClientHandle->StreamChunks(); - } - - if (m_Health > 0) // make sure player is alive - { - m_World->CollectPickupsByPlayer(this); - - //Handle Health: - m_FoodTickTimer++; - if(m_FoodTickTimer >= 80) - { - m_FoodTickTimer = 0; - - if(m_FoodLevel >= 17) - { - Heal(1); - }else if(m_FoodLevel == 0) - { - TakeDamage(1, NULL); - } - } - - // TODO: Increase Exhaustion level http://www.minecraftwiki.net/wiki/Hunger#Exhaustion_level_increase - if (m_FoodExhaustionLevel >= 4.f) - { - m_FoodExhaustionLevel -= 4.f; - - if (m_FoodSaturationLevel >= 1.f) - { - m_FoodSaturationLevel--; - } - else - { - m_FoodLevel = MAX(m_FoodLevel -1, 0); - } - - SendHealth(); - } - } - - cTimer t1; - // Send Player List (Once per m_LastPlayerListTime/1000 ms) - if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime()) - { - m_World->SendPlayerList(this); - m_LastPlayerListTime = t1.GetNowTime(); - } -} - - - - - -void cPlayer::SetTouchGround(bool a_bTouchGround) -{ - m_bTouchGround = a_bTouchGround; - - if (!m_bTouchGround) - { - cWorld* World = GetWorld(); - char BlockID = World->GetBlock( float2int(m_Pos.x), float2int(m_Pos.y), float2int(m_Pos.z) ); - if( BlockID != E_BLOCK_AIR ) - { - // LOGD("TouchGround set to true by server"); - m_bTouchGround = true; - } - if( BlockID == E_BLOCK_WATER || BlockID == E_BLOCK_STATIONARY_WATER || BlockID == E_BLOCK_LADDER || BlockID == E_BLOCK_TORCH ) - { - // LOGD("Water / Ladder / Torch"); - m_LastGroundHeight = (float)m_Pos.y; - } - } - - if (m_bTouchGround) - { - float Dist = (float)(m_LastGroundHeight - m_Pos.y); - int Damage = (int)(Dist - 4.f); - if (Damage > 0) - { - TakeDamage(Damage, 0); - } - - m_LastGroundHeight = (float)m_Pos.y; - } -} - - - - - -void cPlayer::Heal( int a_Health ) -{ - if( m_Health < GetMaxHealth() ) - { - m_Health = (short) MIN(a_Health + m_Health, GetMaxHealth()); - - - SendHealth(); - } -} - - - - - -bool cPlayer::Feed(short a_Food, float a_Saturation) -{ - if (m_FoodLevel >= GetMaxFoodLevel()) - { - return false; - } - - m_FoodLevel = MIN(a_Food + m_FoodLevel, GetMaxFoodLevel()); - m_FoodSaturationLevel = MIN(m_FoodSaturationLevel + a_Saturation, GetMaxFoodSaturationLevel()); - - SendHealth(); - return true; -} - - - - - -void cPlayer::SendHealth() -{ - if (m_ClientHandle != NULL) - { - m_ClientHandle->SendHealth(); - } -} - - - - - -void cPlayer::TakeDamage( int a_Damage, cEntity* a_Instigator ) -{ - if (m_GameMode != eGameMode_Creative) - { - cPawn::TakeDamage( a_Damage, a_Instigator ); - - AddFoodExhaustion(0.3f); - - SendHealth(); - } -} - - - - - -void cPlayer::KilledBy(cEntity * a_Killer) -{ - cPawn::KilledBy(a_Killer); - - if (m_Health > 0) - { - return; // not dead yet =] - } - - m_bVisible = false; // So new clients don't see the player - - // Puke out all the items - cItem * Items = m_Inventory.GetSlots(); - cItems Pickups; - for (unsigned int i = 1; i < m_Inventory.c_NumSlots; ++i) - { - if( !Items[i].IsEmpty() ) - { - Pickups.push_back(Items[i]); - } - Items[i].Empty(); - } - m_World->SpawnItemPickups(Pickups, m_Pos.x, m_Pos.y, m_Pos.z, 10); - SaveToDisk(); // Save it, yeah the world is a tough place ! -} - - - - - -void cPlayer::Respawn() -{ - m_Health = GetMaxHealth(); - - m_ClientHandle->SendRespawn(); - - // Set non Burning - SetMetaData(NORMAL); - - TeleportTo(GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ()); - - SetVisible(true); -} - - - - - -double cPlayer::GetEyeHeight() -{ - return m_Stance; -} - -Vector3d cPlayer::GetEyePosition() -{ - return Vector3d( m_Pos.x, m_Stance, m_Pos.z ); -} - - - - - -void cPlayer::OpenWindow( cWindow* a_Window ) -{ - CloseWindow(m_CurrentWindow ? (char)m_CurrentWindow->GetWindowType() : 0); - a_Window->OpenedByPlayer(*this); - m_CurrentWindow = a_Window; -} - - - - - -void cPlayer::CloseWindow(char a_WindowType) -{ - if (m_CurrentWindow == m_InventoryWindow) - { - // The inventory window must not be closed and must not be even sent a close packet - return; - } - - if (m_CurrentWindow != NULL) - { - // TODO: This code should be in cChestWindow instead - if ((a_WindowType == 1) && (m_CurrentWindow->GetWindowType() == cWindow::Chest)) - { - int x, y, z; - m_CurrentWindow->GetOwner()->GetBlockPos(x, y, z); - m_World->BroadcastBlockAction(x, y, z, 1, 0, E_BLOCK_CHEST); - } - - m_CurrentWindow->ClosedByPlayer(*this); - } - m_CurrentWindow = m_InventoryWindow; -} - - - - - -void cPlayer::SetLastBlockActionTime() -{ - if (m_World != NULL) - { - m_LastBlockActionTime = m_World->GetTime(); - } -} - - - - - -void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt ) -{ - m_LastBlockActionCnt = a_LastBlockActionCnt; -} - - - - - -void cPlayer::SetGameMode(eGameMode a_GameMode) -{ - if ((a_GameMode >= 2) || (a_GameMode < 0)) - { - LOGWARNING("%s: Setting invalid gamemode: %d", GetName().c_str(), a_GameMode); - return; - } - - if (m_GameMode == a_GameMode) - { - // Gamemode already set - return; - } - - m_GameMode = a_GameMode; - m_ClientHandle->SendGameMode(a_GameMode); -} - - - - - -void cPlayer::LoginSetGameMode( eGameMode a_GameMode ) -{ - m_GameMode = a_GameMode; -} - - - - - -void cPlayer::SetIP(const AString & a_IP) -{ - m_IP = a_IP; -} - - - - - -void cPlayer::SendMessage(const AString & a_Message) -{ - m_ClientHandle->SendChat(a_Message); -} - - - - - -void cPlayer::TeleportTo(const double & a_PosX, const double & a_PosY, const double & a_PosZ) -{ - SetPosition( a_PosX, a_PosY, a_PosZ ); - - m_World->BroadcastTeleportEntity(*this, GetClientHandle()); - m_ClientHandle->SendPlayerMoveLook(); -} - - - - - -void cPlayer::MoveTo( const Vector3d & a_NewPos ) -{ - // TODO: should do some checks to see if player is not moving through terrain - // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too - - SetPosition( a_NewPos ); - SetStance(a_NewPos.y + 1.62); -} - - - - - -void cPlayer::SetVisible(bool a_bVisible) -{ - if (a_bVisible && !m_bVisible) // Make visible - { - m_bVisible = true; - m_World->BroadcastSpawn(*this); - } - if (!a_bVisible && m_bVisible) - { - m_bVisible = false; - m_World->BroadcastDestroyEntity(*this, m_ClientHandle); // Destroy on all clients - } -} - - - - - -void cPlayer::AddToGroup( const char* a_GroupName ) -{ - cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName ); - m_Groups.push_back( Group ); - LOGD("Added %s to group %s", m_PlayerName.c_str(), a_GroupName ); - ResolveGroups(); - ResolvePermissions(); -} - - - - - -bool cPlayer::CanUseCommand( const char* a_Command ) -{ - for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr ) - { - if( (*itr)->HasCommand( a_Command ) ) return true; - } - return false; -} - - - - - -bool cPlayer::HasPermission( const char* a_Permission ) -{ - AStringVector Split = StringSplit( a_Permission, "." ); - PermissionMap Possibilities = m_ResolvedPermissions; - // Now search the namespaces - while( Possibilities.begin() != Possibilities.end() ) - { - PermissionMap::iterator itr = Possibilities.begin(); - if( itr->second ) - { - AStringVector OtherSplit = StringSplit( itr->first, "." ); - if( OtherSplit.size() <= Split.size() ) - { - unsigned int i; - for( i = 0; i < OtherSplit.size(); ++i ) - { - if( OtherSplit[i].compare( Split[i] ) != 0 ) - { - if( OtherSplit[i].compare("*") == 0 ) return true; // WildCard man!! WildCard! - break; - } - } - if( i == Split.size() ) return true; - } - } - Possibilities.erase( itr ); - } - - // Nothing that matched :( - return false; -} - - - - - -bool cPlayer::IsInGroup( const char* a_Group ) -{ - for( GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr ) - { - if( strcmp( a_Group, (*itr)->GetName().c_str() ) == 0 ) - return true; - } - return false; -} - - - - - -void cPlayer::ResolvePermissions() -{ - m_ResolvedPermissions.clear(); // Start with an empty map yo~ - - // Copy all player specific permissions into the resolved permissions map - for( PermissionMap::iterator itr = m_Permissions.begin(); itr != m_Permissions.end(); ++itr ) - { - m_ResolvedPermissions[ itr->first ] = itr->second; - } - - for( GroupList::iterator GroupItr = m_ResolvedGroups.begin(); GroupItr != m_ResolvedGroups.end(); ++GroupItr ) - { - const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions(); - for( cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr ) - { - m_ResolvedPermissions[ itr->first ] = itr->second; - } - } -} - - - - - -void cPlayer::ResolveGroups() -{ - // Clear resolved groups first - m_ResolvedGroups.clear(); - - // Get a complete resolved list of all groups the player is in - std::map< cGroup*, bool > AllGroups; // Use a map, because it's faster than iterating through a list to find duplicates - GroupList ToIterate; - for( GroupList::iterator GroupItr = m_Groups.begin(); GroupItr != m_Groups.end(); ++GroupItr ) - { - ToIterate.push_back( *GroupItr ); - } - while( ToIterate.begin() != ToIterate.end() ) - { - cGroup* CurrentGroup = *ToIterate.begin(); - if( AllGroups.find( CurrentGroup ) != AllGroups.end() ) - { - LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!", - m_PlayerName.c_str(), CurrentGroup->GetName().c_str() - ); - } - else - { - AllGroups[ CurrentGroup ] = true; - m_ResolvedGroups.push_back( CurrentGroup ); // Add group to resolved list - const cGroup::GroupList & Inherits = CurrentGroup->GetInherits(); - for( cGroup::GroupList::const_iterator itr = Inherits.begin(); itr != Inherits.end(); ++itr ) - { - if( AllGroups.find( *itr ) != AllGroups.end() ) - { - LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", m_PlayerName.c_str(), (*itr)->GetName().c_str() ); - continue; - } - ToIterate.push_back( *itr ); - } - } - ToIterate.erase( ToIterate.begin() ); - } -} - - - - - -AString cPlayer::GetColor(void) const -{ - if ( m_Color != '-' ) - { - return cChatColor::MakeColor( m_Color ); - } - - if ( m_Groups.size() < 1 ) - { - return cChatColor::White; - } - - return (*m_Groups.begin())->GetColor(); -} - - - - - -void cPlayer::TossItem( - bool a_bDraggingItem, - char a_Amount /* = 1 */, - short a_CreateType /* = 0 */, - short a_CreateHealth /* = 0 */ -) -{ - cItems Drops; - if (a_CreateType) - { - // Just create item without touching the inventory (used in creative mode) - Drops.push_back(cItem(a_CreateType, a_Amount, a_CreateHealth)); - } - else - { - // Drop an item from the inventory: - if (a_bDraggingItem) - { - cItem & Item = GetDraggingItem(); - if (!Item.IsEmpty()) - { - Drops.push_back(Item); - if (Item.m_ItemCount > a_Amount) - { - Item.m_ItemCount -= (char)a_Amount; - } - else - { - Item.Empty(); - } - } - } - else - { - // Else drop equipped item - cItem DroppedItem = GetInventory().GetEquippedItem(); - if (!DroppedItem.IsEmpty()) - { - DroppedItem.m_ItemCount = 1; - if (GetInventory().RemoveItem(DroppedItem)) - { - DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again - Drops.push_back(DroppedItem); - } - } - } - } - float vX = 0, vY = 0, vZ = 0; - EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY); - vY = -vY * 2 + 1.f; - m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 2, vY * 2, vZ * 2); -} - - - - - -bool cPlayer::MoveToWorld( const char* a_WorldName ) -{ - cWorld * World = cRoot::Get()->GetWorld( a_WorldName ); - if ( World ) - { - /* Remove all links to the old world */ - m_World->RemovePlayer( this ); - m_ClientHandle->RemoveFromAllChunks(); - m_World->RemoveEntityFromChunk(this, m_ChunkX, m_ChunkY, m_ChunkZ); - - /* Add player to all the necessary parts of the new world */ - SetWorld( World ); - GetWorld()->AddPlayer( this ); - MoveToCorrectChunk(true); - GetClientHandle()->StreamChunks(); - - return true; - } - - return false; -} - - - - - -void cPlayer::LoadPermissionsFromDisk() -{ - m_Groups.clear(); - m_Permissions.clear(); - - cIniFile IniFile("users.ini"); - if( IniFile.ReadFile() ) - { - std::string Groups = IniFile.GetValue(m_PlayerName, "Groups", ""); - if( Groups.size() > 0 ) - { - AStringVector Split = StringSplit( Groups, "," ); - for( unsigned int i = 0; i < Split.size(); i++ ) - { - AddToGroup( Split[i].c_str() ); - } - } - else - { - AddToGroup("Default"); - } - - m_Color = IniFile.GetValue(m_PlayerName, "Color", "-")[0]; - } - else - { - LOGWARN("WARNING: Failed to read ini file users.ini"); - AddToGroup("Default"); - } - ResolvePermissions(); -} - - - - -bool cPlayer::LoadFromDisk() -{ - LoadPermissionsFromDisk(); - - // Log player permissions, cause it's what the cool kids do - LOGINFO("Player %s has permissions:", m_PlayerName.c_str() ); - for( PermissionMap::iterator itr = m_ResolvedPermissions.begin(); itr != m_ResolvedPermissions.end(); ++itr ) - { - if( itr->second ) LOGINFO("%s", itr->first.c_str() ); - } - - AString SourceFile; - Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); - - cFile f; - if (!f.Open(SourceFile, cFile::fmRead)) - { - return false; - } - - AString buffer; - if (f.ReadRestOfFile(buffer) != f.GetSize()) - { - LOGERROR("ERROR READING FROM FILE \"%s\"", SourceFile.c_str()); - return false; - } - f.Close(); - - Json::Value root; - Json::Reader reader; - if (!reader.parse(buffer, root, false)) - { - LOGERROR("ERROR WHILE PARSING JSON FROM FILE %s", SourceFile.c_str()); - } - - Json::Value & JSON_PlayerPosition = root["position"]; - if (JSON_PlayerPosition.size() == 3) - { - m_Pos.x = JSON_PlayerPosition[(unsigned int)0].asDouble(); - m_Pos.y = JSON_PlayerPosition[(unsigned int)1].asDouble(); - m_Pos.z = JSON_PlayerPosition[(unsigned int)2].asDouble(); - } - - Json::Value & JSON_PlayerRotation = root["rotation"]; - if (JSON_PlayerRotation.size() == 3) - { - m_Rot.x = (float)JSON_PlayerRotation[(unsigned int)0].asDouble(); - m_Rot.y = (float)JSON_PlayerRotation[(unsigned int)1].asDouble(); - m_Rot.z = (float)JSON_PlayerRotation[(unsigned int)2].asDouble(); - } - - m_Health = (short)root.get("health", 0 ).asInt(); - m_FoodLevel = (short)root.get("food", m_MaxFoodLevel ).asInt(); - m_FoodSaturationLevel = (float)root.get("foodSaturation", m_MaxFoodSaturationLevel ).asDouble(); - - m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt(); - - m_Inventory.LoadFromJson(root["inventory"]); - - m_LoadedWorldName = root.get("world", "world").asString(); - - LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"", - m_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z, m_LoadedWorldName.c_str() - ); - - return true; -} - - - - - -bool cPlayer::SaveToDisk() -{ - cMakeDir::MakeDir("players"); - - // create the JSON data - Json::Value JSON_PlayerPosition; - JSON_PlayerPosition.append( Json::Value( m_Pos.x ) ); - JSON_PlayerPosition.append( Json::Value( m_Pos.y ) ); - JSON_PlayerPosition.append( Json::Value( m_Pos.z ) ); - - Json::Value JSON_PlayerRotation; - JSON_PlayerRotation.append( Json::Value( m_Rot.x ) ); - JSON_PlayerRotation.append( Json::Value( m_Rot.y ) ); - JSON_PlayerRotation.append( Json::Value( m_Rot.z ) ); - - Json::Value JSON_Inventory; - m_Inventory.SaveToJson( JSON_Inventory ); - - Json::Value root; - root["position"] = JSON_PlayerPosition; - root["rotation"] = JSON_PlayerRotation; - root["inventory"] = JSON_Inventory; - root["health"] = m_Health; - root["food"] = m_FoodLevel; - root["foodSaturation"] = m_FoodSaturationLevel; - root["world"] = GetWorld()->GetName(); - - if (m_GameMode == GetWorld()->GetGameMode()) - { - root["gamemode"] = (int) eGameMode_NotSet; - } - else - { - root["gamemode"] = (int) m_GameMode; - } - - Json::StyledWriter writer; - std::string JsonData = writer.write( root ); - - AString SourceFile; - Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); - - cFile f; - if (!f.Open(SourceFile, cFile::fmWrite)) - { - LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", m_PlayerName.c_str(), SourceFile.c_str()); - return false; - } - if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size()) - { - LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str()); - return false; - } - return true; -} - - - - - -cPlayer::StringList cPlayer::GetResolvedPermissions() -{ - StringList Permissions; - - const PermissionMap& ResolvedPermissions = m_ResolvedPermissions; - for( PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr ) - { - if( itr->second ) Permissions.push_back( itr->first ); - } - - return Permissions; -} - - - - - -void cPlayer::UseEquippedItem() -{ - if(GetGameMode() != 1) //No damage in creative - { - if (GetInventory().GetEquippedItem().DamageItem()) - { - LOG("Player %s Broke ID: %i", GetClientHandle()->GetUsername().c_str(), GetInventory().GetEquippedItem().m_ItemID); - GetInventory().RemoveItem( GetInventory().GetEquippedItem()); - } - } -} - - - - diff --git a/source/cPlayer.h b/source/cPlayer.h deleted file mode 100644 index d5b63941c..000000000 --- a/source/cPlayer.h +++ /dev/null @@ -1,171 +0,0 @@ - -#pragma once - -#include "cPawn.h" -#include "cInventory.h" -#include "Defines.h" - - - - - -class cGroup; -class cWindow; -class cClientHandle; - - - - - -class cPlayer : public cPawn //tolua_export -{ //tolua_export -public: - typedef cPawn super; - - CLASS_PROTOTYPE() - - cPlayer(cClientHandle * a_Client, const AString & a_PlayerName); - virtual ~cPlayer(); - - virtual void Initialize( cWorld* a_World ); //tolua_export - - virtual void SpawnOn(cClientHandle & a_Client) override; - - virtual void Tick(float a_Dt) override; - - void SetTouchGround( bool a_bTouchGround ); - inline void SetStance( const double a_Stance ) { m_Stance = a_Stance; } - double GetEyeHeight(); //tolua_export - Vector3d GetEyePosition(); //tolua_export - OBSOLETE - inline bool GetFlying() { return m_bTouchGround; } //tolua_export - inline bool IsOnGround(void) const {return m_bTouchGround; } // tolua_export - inline const double GetStance(void) const { return m_Pos.y + 1.62; } //tolua_export // TODO: Proper stance when crouching etc. - inline cInventory & GetInventory(void) { return m_Inventory; } //tolua_export - inline const cInventory & GetInventory(void) const { return m_Inventory; } - - inline const cItem & GetEquippedItem(void) const {return GetInventory().GetEquippedItem(); } - - virtual void TeleportTo( const double & a_PosX, const double & a_PosY, const double & a_PosZ ); //tolua_export - - eGameMode GetGameMode(void) const { return m_GameMode; } //tolua_export - std::string GetIP() { return m_IP; } //tolua_export - float GetLastBlockActionTime() { return m_LastBlockActionTime; } //tolua_export - int GetLastBlockActionCnt() { return m_LastBlockActionCnt; } //tolua_export - void SetLastBlockActionCnt( int ); //tolua_export - void SetLastBlockActionTime(); //tolua_export - void SetGameMode( eGameMode a_GameMode ); //tolua_export - void LoginSetGameMode( eGameMode a_GameMode ); - void SetIP(const AString & a_IP); - - // Tries to move to a new position, with collision checks and stuff - virtual void MoveTo( const Vector3d & a_NewPos ); //tolua_export - - cWindow* GetWindow() { return m_CurrentWindow; } - void OpenWindow( cWindow* a_Window ); - void CloseWindow(char a_WindowType); - - cClientHandle * GetClientHandle(void) const { return m_ClientHandle; } //tolua_export - - void SendMessage(const AString & a_Message); //tolua_export - - const AString & GetName(void) const { return m_PlayerName; } //tolua_export - void SetName(const AString & a_Name) { m_PlayerName = a_Name; } //tolua_export - - typedef std::list< cGroup* > GroupList; - typedef std::list< std::string > StringList; - void AddToGroup( const char* a_GroupName ); //tolua_export - bool CanUseCommand( const char* a_Command ); //tolua_export - bool HasPermission( const char* a_Permission ); //tolua_export - const GroupList & GetGroups() { return m_Groups; } // >> EXPORTED IN MANUALBINDINGS << - StringList GetResolvedPermissions(); // >> EXPORTED IN MANUALBINDINGS << - bool IsInGroup( const char* a_Group ); //tolua_export - - AString GetColor(void) const; //tolua_export - - void TossItem(bool a_bDraggingItem, char a_Amount = 1, short a_CreateType = 0, short a_CreateHealth = 0); //tolua_export - - void Heal( int a_Health ); //tolua_export - - /// Returns true if any food has been consumed, false if player "full" - bool Feed(short a_Food, float a_Saturation); - - short GetMaxFoodLevel() { return m_MaxFoodLevel; } - short GetFoodLevel() { return m_FoodLevel; } - - float GetMaxFoodSaturationLevel() { return m_MaxFoodSaturationLevel; } - float GetFoodSaturationLevel() { return m_FoodSaturationLevel; } - - void AddFoodExhaustion(float a_Exhaustion) { m_FoodExhaustionLevel += a_Exhaustion; } - - void TakeDamage( int a_Damage, cEntity* a_Instigator ); //tolua_export - void KilledBy( cEntity* a_Killer ); //tolua_export - void Respawn(); //tolua_export - - void SetVisible( bool a_bVisible ); //tolua_export - bool IsVisible() { return m_bVisible; } //tolua_export - - bool MoveToWorld( const char* a_WorldName ); //tolua_export - - bool SaveToDisk(); - bool LoadFromDisk(); - void LoadPermissionsFromDisk(); //tolua_export - - const AString & GetLoadedWorldName() { return m_LoadedWorldName; } - - void UseEquippedItem(void); - - void SendHealth(); - - // In UI windows, the item that the player is dragging: - bool IsDraggingItem(void) const { return !m_DraggingItem.IsEmpty(); } - cItem & GetDraggingItem(void) {return m_DraggingItem; } - -protected: - virtual void Destroyed(); - - typedef std::map< std::string, bool > PermissionMap; - PermissionMap m_ResolvedPermissions; - PermissionMap m_Permissions; - - GroupList m_ResolvedGroups; - GroupList m_Groups; - - std::string m_PlayerName; - std::string m_LoadedWorldName; - - bool m_bVisible; - - short m_FoodLevel; - short m_MaxFoodLevel; - float m_FoodSaturationLevel; - float m_MaxFoodSaturationLevel; - float m_FoodExhaustionLevel; - char m_FoodTickTimer; - - float m_LastGroundHeight; - bool m_bTouchGround; - double m_Stance; - cInventory m_Inventory; - cWindow * m_CurrentWindow; - cWindow * m_InventoryWindow; - - float m_TimeLastPickupCheck; - - void ResolvePermissions(); - - void ResolveGroups(); - char m_Color; - - float m_LastBlockActionTime; - int m_LastBlockActionCnt; - eGameMode m_GameMode; - std::string m_IP; - - cItem m_DraggingItem; - - long long m_LastPlayerListTime; - static const unsigned short PLAYER_LIST_TIME_MS = 1000; // 1000 = once per second - - cClientHandle* m_ClientHandle; -}; //tolua_export diff --git a/source/cPlugin.cpp b/source/cPlugin.cpp deleted file mode 100644 index ce66a9655..000000000 --- a/source/cPlugin.cpp +++ /dev/null @@ -1,308 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cPlugin.h" - - - - - -cPlugin::cPlugin() - : m_Version( 0 ) - , m_Language( E_CPP ) - , m_bCanBindCommands( false ) -{ -} - -cPlugin::~cPlugin() -{ -} - -// bool cPlugin::Initialize() -// { -// LOG("cPlugin::Initialize()"); -// return false; -// } - - - - - -void cPlugin::Tick(float a_Dt) -{ - UNUSED(a_Dt); -} - - - - - -bool cPlugin::OnBlockDig(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) -{ - UNUSED(a_Player); - UNUSED(a_BlockX); - UNUSED(a_BlockY); - UNUSED(a_BlockZ); - UNUSED(a_BlockFace); - UNUSED(a_Status); - UNUSED(a_OldBlock); - UNUSED(a_OldMeta); - return false; -} - - - - - -bool cPlugin::OnBlockPlace(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) -{ - UNUSED(a_Player); - UNUSED(a_BlockX); - UNUSED(a_BlockY); - UNUSED(a_BlockZ); - UNUSED(a_BlockFace); - UNUSED(a_HeldItem); - return false; -} - - - - - -bool cPlugin::OnBlockToPickup(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups) -{ - UNUSED(a_BlockType); - UNUSED(a_BlockMeta); - UNUSED(a_Player); - UNUSED(a_EquippedItem); - UNUSED(a_Pickups); - return false; -} - - - - - -bool cPlugin::OnChat(cPlayer * a_Player, const AString & a_Message) -{ - UNUSED(a_Player); - UNUSED(a_Message); - return false; -} - - - - - -void cPlugin::OnChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ) -{ - UNUSED(a_World); - UNUSED(a_ChunkX); - UNUSED(a_ChunkZ); -} - - - - - -bool cPlugin::OnChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cLuaChunk * a_pLuaChunk) -{ - UNUSED(a_World); - UNUSED(a_ChunkX); - UNUSED(a_ChunkZ); - UNUSED(a_pLuaChunk); - return false; -} - - - - - -bool cPlugin::OnCollectPickup(cPlayer * a_Player, cPickup * a_Pickup) -{ - UNUSED(a_Player); - UNUSED(a_Pickup); - return false; -} - - - - - -bool cPlugin::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - UNUSED(a_Player); - UNUSED(a_Grid); - UNUSED(a_Recipe); - return false; -} - - - - - -bool cPlugin::OnDisconnect(cPlayer * a_Player, const AString & a_Reason) -{ - UNUSED(a_Reason); - UNUSED(a_Player); - return false; -} - - - - - -bool cPlugin::OnKilled(cPawn * a_Killed, cEntity * a_Killer) -{ - UNUSED(a_Killed); - UNUSED(a_Killer); - return false; -} - - - - - -bool cPlugin::OnLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) -{ - UNUSED(a_Client); - UNUSED(a_ProtocolVersion); - UNUSED(a_Username); - return false; -} - - - - - -bool cPlugin::OnPlayerJoin(cPlayer * a_Player) -{ - UNUSED(a_Player); - return false; -} - - - - - -void cPlugin::OnPlayerMove(cPlayer * a_Player) -{ - UNUSED(a_Player); -} - - - - - -void cPlugin::OnPlayerSpawn(cPlayer * a_Player) -{ - UNUSED(a_Player); -} - - - - - -bool cPlugin::OnPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - UNUSED(a_Player); - UNUSED(a_Grid); - UNUSED(a_Recipe); - return false; -} - - - - - -bool cPlugin::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - UNUSED(a_Player); - UNUSED(a_Grid); - UNUSED(a_Recipe); - return false; -} - - - - - -void cPlugin::OnTakeDamage(cPawn * a_Pawn, TakeDamageInfo * a_TakeDamageInfo) -{ - UNUSED(a_Pawn); - UNUSED(a_TakeDamageInfo); -} - - - - - -bool cPlugin::OnUpdatedSign(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) -{ - UNUSED(a_World); - UNUSED(a_BlockX); - UNUSED(a_BlockY); - UNUSED(a_BlockZ); - UNUSED(a_Line1); - UNUSED(a_Line2); - UNUSED(a_Line3); - UNUSED(a_Line4); - UNUSED(a_Player); - return false; -} - - - - - -bool cPlugin::OnUpdatingSign(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) -{ - UNUSED(a_World); - UNUSED(a_BlockX); - UNUSED(a_BlockY); - UNUSED(a_BlockZ); - UNUSED(a_Line1); - UNUSED(a_Line2); - UNUSED(a_Line3); - UNUSED(a_Line4); - UNUSED(a_Player); - return false; -} - - - - - -bool cPlugin::OnWeatherChanged(cWorld * a_World) -{ - UNUSED(a_World); - return false; -} - - - - - -bool cPlugin::OnHandshake(cClientHandle * a_Client, const AString & a_Username) -{ - UNUSED(a_Client); - UNUSED(a_Username); - return false; -} - - - - - -void cPlugin::AddCommand(const AString & a_Command, const AString & a_Description, const AString & a_Permission) -{ - CommandStruct Command; - Command.Command = a_Command; - Command.Description = a_Description; - Command.Permission = a_Permission; - m_Commands.push_back( Command ); -} - - - - diff --git a/source/cPlugin.h b/source/cPlugin.h deleted file mode 100644 index c9367051a..000000000 --- a/source/cPlugin.h +++ /dev/null @@ -1,111 +0,0 @@ - -#pragma once - -#include "cItem.h" - -class cClientHandle; -class cPlayer; -class cPickup; -class cItem; -class cEntity; -class cPawn; -class cWorld; -class cLuaChunk; -struct TakeDamageInfo; - -// fwd: cPlayer.h -class cPlayer; - -// fwd: CraftingRecipes.h -class cCraftingGrid; -class cCraftingRecipe; - - - - - -// tolua_begin -class cPlugin -{ -public: - cPlugin(); - virtual ~cPlugin(); - - virtual void OnDisable() {} - virtual bool Initialize() = 0; - - // Called each tick - virtual void Tick(float a_Dt); - - /** - * On all these functions, return true if you want to override default behavior - * You can also return false, so default behavior is used. - **/ - virtual bool OnBlockDig (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta); - virtual bool OnBlockPlace (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem); - virtual bool OnBlockToPickup (BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups); - virtual bool OnChat (cPlayer * a_Player, const AString & a_Message); - virtual void OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ); - virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cLuaChunk * a_pLuaChunk); - virtual bool OnCollectPickup (cPlayer * a_Player, cPickup * a_Pickup); - virtual bool OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); - virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason); - virtual bool OnKilled (cPawn * a_Killed, cEntity* a_Killer ); - virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username); - virtual bool OnPlayerJoin (cPlayer* a_Player ); - virtual void OnPlayerMove (cPlayer* a_Player ); - virtual void OnPlayerSpawn (cPlayer* a_Player ); - virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); - virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); - virtual void OnTakeDamage (cPawn * a_Pawn, TakeDamageInfo * a_TakeDamageInfo ); - virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player); - virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player); - virtual bool OnWeatherChanged (cWorld * a_World); - virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username); - - // Accessors - const AString & GetName() const { return m_Name; } - virtual void SetName( const AString & a_Name ) { m_Name = a_Name; } - - int GetVersion() const { return m_Version; } - void SetVersion( int a_Version ) { m_Version = a_Version; } - - struct CommandStruct - { - std::string Command; - std::string Description; - std::string Permission; - }; - - void AddCommand(const AString & a_Command, const AString & a_Description, const AString & a_Permission); - // tolua_end - - typedef bool (FuncCommandHandler)( std::string & a_Command, std::vector< std::string > & a_Split ); - void BindCommand( FuncCommandHandler* a_Function, std::string & a_Command ); // >> EXPORTED IN MANUALBINDINGS << - const std::vector< CommandStruct > & GetCommands() const { return m_Commands; } // >> EXPORTED IN MANUALBINDINGS << - - - /* This should not be exposed to scripting languages */ - enum PluginLanguage - { - E_CPP, - E_LUA, - E_SQUIRREL, - }; - PluginLanguage GetLanguage() { return m_Language; } - void SetLanguage( PluginLanguage a_Language ) { m_Language = a_Language; } - - bool CanBindCommands() { return m_bCanBindCommands; } -private: - friend class cPluginManager; - bool m_bCanBindCommands; // Only changed by cPluginManager - - PluginLanguage m_Language; - std::vector< CommandStruct > m_Commands; - std::string m_Name; - int m_Version; -}; //tolua_export - - - - diff --git a/source/cPluginManager.cpp b/source/cPluginManager.cpp deleted file mode 100644 index 8f52839c8..000000000 --- a/source/cPluginManager.cpp +++ /dev/null @@ -1,883 +0,0 @@ -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cPluginManager.h" -#include "cPlugin.h" -#include "cPlugin_Lua.h" -#include "cPlugin_NewLua.h" -#include "cWebAdmin.h" -#include "cItem.h" -#include "cRoot.h" -#include "cLuaCommandBinder.h" - -#ifdef USE_SQUIRREL - #include "cPlugin_Squirrel.h" - #include "cSquirrelCommandBinder.h" -#endif - -#include "../iniFile/iniFile.h" -#include "tolua++.h" -#include "cPlayer.h" - -#ifdef USE_SQUIRREL - #include "squirrelbindings/SquirrelBindings.h" - #include "squirrelbindings/SquirrelFunctions.h" - #pragma warning(disable:4100;disable:4127;disable:4510;disable:4610;disable:4244;disable:4512) // Getting A LOT of these warnings from SqPlus - - #pragma warning(default:4100;default:4127;default:4510;default:4610;default:4244;default:4512) -#endif - - - - - -cPluginManager* cPluginManager::GetPluginManager() -{ - LOGWARN("WARNING: Using deprecated function cPluginManager::GetPluginManager() use cRoot::Get()->GetPluginManager() instead!"); - return cRoot::Get()->GetPluginManager(); -} - - - - - -cPluginManager::cPluginManager() - : m_LuaCommandBinder( new cLuaCommandBinder() ) -#ifdef USE_SQUIRREL - , m_SquirrelCommandBinder( new cSquirrelCommandBinder() ) -#endif - , m_bReloadPlugins(false) -{ -} - - - - - -cPluginManager::~cPluginManager() -{ - UnloadPluginsNow(); - - delete m_LuaCommandBinder; -#ifdef USE_SQUIRREL - delete m_SquirrelCommandBinder; -#endif -} - - - - - -void cPluginManager::ReloadPlugins() -{ - m_bReloadPlugins = true; -} - - - - - -void cPluginManager::ReloadPluginsNow() -{ - LOG("Loading plugins"); - m_bReloadPlugins = false; - UnloadPluginsNow(); - - #ifdef USE_SQUIRREL - CloseSquirrelVM(); - OpenSquirrelVM(); - #endif // USE_SQUIRREL - - cIniFile IniFile("settings.ini"); - if (!IniFile.ReadFile() ) - { - LOGWARNING("cPluginManager: Can't find settings.ini, so can't load any plugins."); - } - - unsigned int KeyNum = IniFile.FindKey("Plugins"); - unsigned int NumPlugins = IniFile.GetNumValues( KeyNum ); - if( NumPlugins > 0 ) - { - for(unsigned int i = 0; i < NumPlugins; i++) - { - AString ValueName = IniFile.GetValueName(KeyNum, i ); - if( ValueName.compare("Plugin") == 0 ) // It's a Lua plugin - { - AString PluginFile = IniFile.GetValue(KeyNum, i ); - if( !PluginFile.empty() ) - { - // allow for comma separated plugin list - // degrades and works fine for the plugin - // per line - AStringVector split = StringSplit( PluginFile, "," ); - for (unsigned int j = 0; j < split.size(); j++) - { - cPlugin_Lua* Plugin = new cPlugin_Lua( (split[j] + AString(".lua") ).c_str() ); - if( !AddLuaPlugin( Plugin ) ) - { - delete Plugin; - } - } - } - } - else if( ValueName.compare("NewPlugin") == 0 ) // New plugin style - { - AString PluginFile = IniFile.GetValue(KeyNum, i ); - if( !PluginFile.empty() ) - { - cPlugin_NewLua* Plugin = new cPlugin_NewLua( PluginFile.c_str() ); - if( !AddPlugin( Plugin ) ) - { - delete Plugin; - } - } - } - - #ifdef USE_SQUIRREL - else if( ValueName.compare("Squirrel") == 0 ) // Squirrel plugin - { - AString PluginFile = IniFile.GetValue(KeyNum, i ); - if( !PluginFile.empty() ) - { - LOGINFO("Loading Squirrel plugin: %s", PluginFile.c_str() ); - - cPlugin_Squirrel *Plugin = new cPlugin_Squirrel(PluginFile.c_str()); - - if( !AddPlugin( Plugin ) ) - { - delete Plugin; - } - } - } - #endif // USE_SQUIRREL - } - } - - if( GetNumPlugins() == 0 ) - { - LOG("No plugins loaded"); - } - else - { - LOG("Loaded %i plugin(s)", GetNumPlugins() ); - } -} - - - - - -void cPluginManager::Tick(float a_Dt) -{ - if( m_bReloadPlugins ) - { - ReloadPluginsNow(); - } - - HookMap::iterator Plugins = m_Hooks.find( E_PLUGIN_TICK ); - if( Plugins != m_Hooks.end() ) - { - for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr ) - { - (*itr)->Tick( a_Dt ); - } - } -} - - - - - -bool cPluginManager::CallHook(PluginHook a_Hook, unsigned int a_NumArgs, ...) -{ - HookMap::iterator Plugins = m_Hooks.find( a_Hook ); - - if (Plugins == m_Hooks.end()) - { - return false; - } - - switch( a_Hook ) - { - case HOOK_PLAYER_JOIN: - { - if( a_NumArgs != 1 ) break; - va_list argptr; - va_start( argptr, a_NumArgs); - cPlayer* Player = va_arg(argptr, cPlayer* ); - va_end (argptr); - for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr ) - { - if( (*itr)->OnPlayerJoin( Player ) ) - return true; - } - break; - } - - case HOOK_PLAYER_MOVE: - { - if( a_NumArgs != 1 ) break; - va_list argptr; - va_start( argptr, a_NumArgs); - cPlayer* Player = va_arg(argptr, cPlayer* ); - va_end (argptr); - for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr ) - { - (*itr)->OnPlayerMove( Player ); - } - break; - } - - case HOOK_TAKE_DAMAGE: - { - if( a_NumArgs != 2 ) break; - va_list argptr; - va_start( argptr, a_NumArgs); - cPawn* Pawn = va_arg(argptr, cPawn* ); - TakeDamageInfo* TDI = va_arg(argptr, TakeDamageInfo* ); - va_end (argptr); - for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr ) - { - (*itr)->OnTakeDamage( Pawn, TDI ); - } - break; - } - - case HOOK_KILLED: - { - if( a_NumArgs != 2 ) break; - va_list argptr; - va_start( argptr, a_NumArgs); - cPawn* Killed = va_arg(argptr, cPawn* ); - cEntity* Killer = va_arg(argptr, cEntity* ); - va_end (argptr); - for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr ) - { - if( (*itr)->OnKilled( Killed, Killer ) ) - return true; - } - break; - } - - case HOOK_CHUNK_GENERATED: - { - if (a_NumArgs != 3) - { - break; - } - va_list argptr; - va_start( argptr, a_NumArgs); - cWorld * World = va_arg(argptr, cWorld *); - int ChunkX = va_arg(argptr, int); - int ChunkZ = va_arg(argptr, int); - va_end (argptr); - for( PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr ) - { - (*itr)->OnChunkGenerated(World, ChunkX, ChunkZ); - } - break; - } - - case HOOK_PLAYER_SPAWN: - { - if (a_NumArgs != 1) - { - break; - } - va_list argptr; - va_start( argptr, a_NumArgs); - cPlayer * Player = va_arg(argptr, cPlayer *); - va_end (argptr); - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - (*itr)->OnPlayerSpawn(Player); - } - break; - } - - default: - { - LOGWARNING("cPluginManager: Calling Unknown hook: %i", a_Hook ); - ASSERT(!"Calling an unknown hook"); - break; - } - } // switch (a_Hook) - return false; -} - - - - - -bool cPluginManager::CallHookLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_LOGIN); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnLogin(a_Client, a_ProtocolVersion, a_Username)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookBlockDig(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_BLOCK_DIG); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnBlockDig(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status, a_OldBlock, a_OldMeta)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookBlockPlace(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_BLOCK_PLACE); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnBlockPlace(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_HeldItem)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookChat(cPlayer * a_Player, const AString & a_Message) -{ - #ifdef USE_SQUIRREL - if (m_SquirrelCommandBinder->HandleCommand(a_Message, a_Player)) - { - return true; - } - #endif // USE_SQUIRREL - - if (m_LuaCommandBinder->HandleCommand(a_Message, a_Player)) - { - return true; - } - - //Check if it was a standard command (starts with a slash) - if (a_Message[0] == '/') - { - a_Player->SendMessage("Unknown Command"); - LOGINFO("Player \"%s\" issued an unknown command: \"%s\"", a_Player->GetName().c_str(), a_Message.c_str()); - return true; // Cancel sending - } - - HookMap::iterator Plugins = m_Hooks.find(HOOK_CHAT); - if (Plugins == m_Hooks.end()) - { - return false; - } - - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnChat(a_Player, a_Message)) - { - return true; - } - } - - return false; -} - - - - - -bool cPluginManager::CallHookChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cLuaChunk * a_LuaChunk) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_CHUNK_GENERATING); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnChunkGenerating(a_World, a_ChunkX, a_ChunkZ, a_LuaChunk)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookCollectPickup(cPlayer * a_Player, cPickup & a_Pickup) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_COLLECT_PICKUP); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnCollectPickup(a_Player, &a_Pickup)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_PRE_CRAFTING); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPreCrafting(a_Player, a_Grid, a_Recipe)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_CRAFTING_NO_RECIPE); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnCraftingNoRecipe(a_Player, a_Grid, a_Recipe)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookDisconnect(cPlayer * a_Player, const AString & a_Reason) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_DISCONNECT); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnDisconnect(a_Player, a_Reason)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_POST_CRAFTING); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnPostCrafting(a_Player, a_Grid, a_Recipe)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookBlockToPickup( - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, - const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups -) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_POST_CRAFTING); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnBlockToPickup(a_BlockType, a_BlockMeta, a_Player, a_EquippedItem, a_Pickups)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookWeatherChanged(cWorld * a_World) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_WEATHER_CHANGED); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnWeatherChanged(a_World)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookUpdatingSign(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_UPDATING_SIGN); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnUpdatingSign(a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookUpdatedSign(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_UPDATED_SIGN); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnUpdatedSign(a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player)) - { - return true; - } - } - return false; -} - - - - - -bool cPluginManager::CallHookHandshake(cClientHandle * a_ClientHandle, const AString & a_Username) -{ - HookMap::iterator Plugins = m_Hooks.find(HOOK_HANDSHAKE); - if (Plugins == m_Hooks.end()) - { - return false; - } - for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) - { - if ((*itr)->OnHandshake(a_ClientHandle, a_Username)) - { - return true; - } - } - return false; -} - - - - - -cPlugin* cPluginManager::GetPlugin( const AString & a_Plugin ) const -{ - for( PluginList::const_iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr ) - { - if ((*itr)->GetName().compare(a_Plugin) == 0) - { - return *itr; - } - } - return 0; -} - - - - - -const cPluginManager::PluginList & cPluginManager::GetAllPlugins() const -{ - return m_Plugins; -} - - - - - -void cPluginManager::UnloadPluginsNow() -{ - m_Hooks.clear(); - - while( m_LuaPlugins.size() > 0 ) - { - cPlugin_Lua* LuaPlugin = *m_LuaPlugins.begin(); - if( LuaPlugin ) - { - delete LuaPlugin; - } - m_LuaPlugins.remove( LuaPlugin ); - } - - while( m_Plugins.size() > 0 ) - { - RemovePlugin( *m_Plugins.begin(), true ); - } - - //SquirrelVM::Shutdown(); // This breaks shit -} - - - - - -void cPluginManager::RemoveHooks( cPlugin* a_Plugin ) -{ - m_Hooks[ E_PLUGIN_TICK].remove ( a_Plugin ); - m_Hooks[ E_PLUGIN_CHAT].remove ( a_Plugin ); - m_Hooks[ E_PLUGIN_COLLECT_ITEM].remove ( a_Plugin ); - m_Hooks[ E_PLUGIN_BLOCK_DIG].remove ( a_Plugin ); - m_Hooks[ E_PLUGIN_BLOCK_PLACE].remove ( a_Plugin ); - m_Hooks[ E_PLUGIN_DISCONNECT].remove ( a_Plugin ); - m_Hooks[ E_PLUGIN_HANDSHAKE].remove ( a_Plugin ); - m_Hooks[ E_PLUGIN_LOGIN].remove ( a_Plugin ); - m_Hooks[ E_PLUGIN_PLAYER_SPAWN].remove ( a_Plugin ); - m_Hooks[ E_PLUGIN_PLAYER_JOIN].remove ( a_Plugin ); - m_Hooks[ E_PLUGIN_PLAYER_MOVE].remove ( a_Plugin ); - m_Hooks[ E_PLUGIN_TAKE_DAMAGE].remove ( a_Plugin ); - m_Hooks[ E_PLUGIN_KILLED].remove ( a_Plugin ); - m_Hooks[ E_PLUGIN_CHUNK_GENERATED ].remove ( a_Plugin ); - m_Hooks[ E_PLUGIN_CHUNK_GENERATING ].remove( a_Plugin ); - m_Hooks[ E_PLUGIN_BLOCK_TO_DROPS ].remove ( a_Plugin ); -} - - - - - -void cPluginManager::RemovePlugin( cPlugin* a_Plugin, bool a_bDelete /* = false */ ) -{ - if( a_bDelete ) - { - m_LuaCommandBinder->RemoveBindingsForPlugin( a_Plugin ); -#ifdef USE_SQUIRREL - m_SquirrelCommandBinder->RemoveBindingsForPlugin( a_Plugin ); -#endif - m_Plugins.remove( a_Plugin ); - RemoveHooks( a_Plugin ); - a_Plugin->OnDisable(); - - delete a_Plugin; - } - else - { - for( LuaPluginList::iterator itr = m_LuaPlugins.begin(); itr != m_LuaPlugins.end(); ++itr ) - { - (*itr)->RemovePlugin( a_Plugin ); - } - } -} - - - - - -bool cPluginManager::AddPlugin( cPlugin* a_Plugin ) -{ - a_Plugin->m_bCanBindCommands = true; - if( a_Plugin->Initialize() ) - { - m_Plugins.remove( a_Plugin ); - m_Plugins.push_back( a_Plugin ); - return true; - } - - a_Plugin->m_bCanBindCommands = false; - RemoveHooks( a_Plugin ); // Undo any damage the Initialize() might have done - return false; -} - - - - - -bool cPluginManager::AddPlugin( lua_State* a_LuaState, cPlugin* a_Plugin ) -{ - a_Plugin->SetLanguage( cPlugin::E_LUA ); - cPlugin_Lua* LuaPlugin = GetLuaPlugin( a_LuaState ); - if( LuaPlugin == NULL ) - { - lua_Debug ar; - lua_getstack(a_LuaState, 1, &ar); - lua_getinfo(a_LuaState, "nSl", &ar); - LOGERROR("ERROR: Trying to add an 'old style' plugin from within a 'new style' plugin.\nIn file: %s at line: %i", ar.source, ar.currentline); - } - a_Plugin->m_bCanBindCommands = true; - if( LuaPlugin && a_Plugin->Initialize() ) - { - m_Plugins.remove( a_Plugin ); - m_Plugins.push_back( a_Plugin ); - LuaPlugin->AddPlugin( a_Plugin ); - return true; - } - - a_Plugin->m_bCanBindCommands = false; - return false; -} - - - - - -bool cPluginManager::AddLuaPlugin( cPlugin_Lua* a_Plugin ) -{ - m_LuaPlugins.push_back( a_Plugin ); // It HAS to be in here before calling Initialize, so it can be found by AddPlugin() - if(a_Plugin->Initialize() ) - { - return true; - } - LOG(">>>>>>> Could not initialize a plugin! "); - m_LuaPlugins.remove( a_Plugin ); - return false; -} - - - - - -void cPluginManager::RemoveLuaPlugin( std::string a_FileName ) -{ - for( LuaPluginList::iterator itr = m_LuaPlugins.begin(); itr != m_LuaPlugins.end(); ++itr ) - { - if( (*itr)->GetFileName() == a_FileName ) - { - cPlugin_Lua* Plugin = *itr; - m_LuaPlugins.remove( Plugin ); - delete Plugin; - return; - } - } -} - - - - - -cPlugin_Lua* cPluginManager::GetLuaPlugin( lua_State* a_State ) -{ - for( LuaPluginList::iterator itr = m_LuaPlugins.begin(); itr != m_LuaPlugins.end(); ++itr ) - { - if( (*itr)->GetLuaState() == a_State ) - { - return *itr; - } - } - return NULL; -} - - - - - -void cPluginManager::AddHook( cPlugin* a_Plugin, PluginHook a_Hook ) -{ - if( !a_Plugin ) - { - LOGWARN("Called cPluginManager::AddHook while a_Plugin is NULL"); - return; - } - PluginList & Plugins = m_Hooks[ a_Hook ]; - Plugins.remove( a_Plugin ); - Plugins.push_back( a_Plugin ); -} - - - - - -unsigned int cPluginManager::GetNumPlugins() const -{ - return m_Plugins.size(); -} - - - - - -bool cPluginManager::HasPlugin( cPlugin* a_Plugin ) const -{ - for( PluginList::const_iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr ) - { - if( *itr == a_Plugin ) - return true; - } - return false; -} \ No newline at end of file diff --git a/source/cPluginManager.h b/source/cPluginManager.h deleted file mode 100644 index ea02bff19..000000000 --- a/source/cPluginManager.h +++ /dev/null @@ -1,149 +0,0 @@ - -#pragma once - -#include "cItem.h" - -struct lua_State; -class cLuaCommandBinder; -class cSquirrelCommandBinder; -class cPlugin; -class cPlugin_Lua; - -// fwd: cWorld.h -class cWorld; - -// fwd: cLuaChunk.h -class cLuaChunk; - -// fwd: cPlayer.h -class cPlayer; - -// fwd: CraftingRecipes.h -class cCraftingGrid; -class cCraftingRecipe; - -// fwd: cPickup.h -class cPickup; - - - - - -class cPluginManager //tolua_export -{ //tolua_export -public: //tolua_export - - // Called each tick - virtual void Tick(float a_Dt); - - // tolua_begin - enum PluginHook - { - HOOK_TICK, - HOOK_CHAT, - HOOK_COLLECT_PICKUP, - HOOK_COLLECT_ITEM = HOOK_COLLECT_PICKUP, // OBSOLETE, use HOOK_COLLECT_PICKUP instead - HOOK_BLOCK_DIG, - HOOK_BLOCK_PLACE, - HOOK_DISCONNECT, - HOOK_HANDSHAKE, - HOOK_LOGIN, - HOOK_PLAYER_SPAWN, - HOOK_PLAYER_JOIN, - HOOK_PLAYER_MOVE, - HOOK_TAKE_DAMAGE, - HOOK_KILLED, - HOOK_CHUNK_GENERATED, - HOOK_CHUNK_GENERATING, - HOOK_BLOCK_TO_DROPS, - HOOK_PRE_CRAFTING, /// cPlayer, cCraftingGrid, cCraftingRecipe - HOOK_CRAFTING_NO_RECIPE, /// cPlayer, cCraftingGrid, cCraftingRecipe - HOOK_POST_CRAFTING, /// cPlayer, cCraftingGrid, cCraftingRecipe - HOOK_BLOCK_TO_PICKUP, /// BlockType, BlockMeta, cPlayer, cItem, cItems - HOOK_WEATHER_CHANGED, /// cWorld - HOOK_UPDATING_SIGN, /// cWorld, int, int, int, string, string, string, string - HOOK_UPDATED_SIGN, /// cWorld, int, int, int, string, string, string, string - - // E_PLUGIN_ names are obsolete, but are kept for compatibility reasons - E_PLUGIN_TICK = HOOK_TICK, - E_PLUGIN_CHAT = HOOK_CHAT, - E_PLUGIN_COLLECT_ITEM = HOOK_COLLECT_ITEM, - E_PLUGIN_BLOCK_DIG = HOOK_BLOCK_DIG, - E_PLUGIN_BLOCK_PLACE = HOOK_BLOCK_PLACE, - E_PLUGIN_DISCONNECT = HOOK_DISCONNECT, - E_PLUGIN_HANDSHAKE = HOOK_HANDSHAKE, - E_PLUGIN_LOGIN = HOOK_LOGIN, - E_PLUGIN_PLAYER_SPAWN = HOOK_PLAYER_SPAWN, - E_PLUGIN_PLAYER_JOIN = HOOK_PLAYER_JOIN, - E_PLUGIN_PLAYER_MOVE = HOOK_PLAYER_MOVE, - E_PLUGIN_TAKE_DAMAGE = HOOK_TAKE_DAMAGE, - E_PLUGIN_KILLED = HOOK_KILLED, - E_PLUGIN_CHUNK_GENERATED = HOOK_CHUNK_GENERATED, - E_PLUGIN_CHUNK_GENERATING = HOOK_CHUNK_GENERATING, - E_PLUGIN_BLOCK_TO_DROPS = HOOK_BLOCK_TO_DROPS, - }; - // tolua_end - - static cPluginManager * GetPluginManager(); //tolua_export - - typedef std::list< cPlugin * > PluginList; - cPlugin * GetPlugin( const AString & a_Plugin ) const; //tolua_export - const PluginList & GetAllPlugins() const; // >> EXPORTED IN MANUALBINDINGS << - - void ReloadPlugins(); //tolua_export - bool AddPlugin( cPlugin* a_Plugin ); - bool AddPlugin( lua_State* a_LuaState, cPlugin* a_Plugin ); //tolua_export - bool AddLuaPlugin( cPlugin_Lua* a_Plugin ); - void AddHook( cPlugin* a_Plugin, PluginHook a_Hook ); //tolua_export - - unsigned int GetNumPlugins() const; //tolua_export - - // If the hook returns true, no further hook is called and the functions return false - bool CallHook( PluginHook a_Hook, unsigned int a_NumArgs, ... ); - - bool CallHookBlockDig (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE OldBlock, NIBBLETYPE OldMeta); - bool CallHookBlockPlace (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem); - bool CallHookBlockToPickup (BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups); - bool CallHookChat (cPlayer * a_Player, const AString & a_Message); - bool CallHookChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cLuaChunk * a_Chunk); - bool CallHookCollectPickup (cPlayer * a_Player, cPickup & a_Pickup); - bool CallHookCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); - bool CallHookDisconnect (cPlayer * a_Player, const AString & a_Reason); - bool CallHookLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username); - bool CallHookPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); - bool CallHookPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); - bool CallHookUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player); - bool CallHookUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player); - bool CallHookWeatherChanged (cWorld * a_World); - bool CallHookHandshake (cClientHandle * a_ClientHandle, const AString & a_Username); - - void RemoveHooks( cPlugin* a_Plugin ); - void RemovePlugin( cPlugin* a_Plugin, bool a_bDelete = false ); //tolua_export - void RemoveLuaPlugin( std::string a_FileName ); //tolua_export - cPlugin_Lua* GetLuaPlugin( lua_State* a_State ); //tolua_export - - cLuaCommandBinder* GetLuaCommandBinder() const { return m_LuaCommandBinder; } - - cSquirrelCommandBinder* GetSquirrelCommandBinder() { return m_SquirrelCommandBinder; } - - bool HasPlugin( cPlugin* a_Plugin ) const; -private: - friend class cRoot; - cPluginManager(); - ~cPluginManager(); - - typedef std::list< cPlugin_Lua* > LuaPluginList; - typedef std::map< cPluginManager::PluginHook, cPluginManager::PluginList > HookMap; - - LuaPluginList m_LuaPlugins; - PluginList m_Plugins; - HookMap m_Hooks; - - void ReloadPluginsNow(); - void UnloadPluginsNow(); - - cLuaCommandBinder* m_LuaCommandBinder; - cSquirrelCommandBinder* m_SquirrelCommandBinder; - - bool m_bReloadPlugins; -}; //tolua_export diff --git a/source/cPlugin_Lua.cpp b/source/cPlugin_Lua.cpp deleted file mode 100644 index a4cf972f4..000000000 --- a/source/cPlugin_Lua.cpp +++ /dev/null @@ -1,98 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#define LUA_USE_POSIX -#include "cPlugin_Lua.h" -#include "cPluginManager.h" -#include "cRoot.h" - -extern "C" -{ - #include "lualib.h" -} - -#include "tolua++.h" -#include "Bindings.h" -#include "ManualBindings.h" - -bool report_errors(lua_State* lua, int status) -{ - if ( status!=0 ) - { - std::string s = lua_tostring(lua, -1); - LOGERROR("-- %s", s.c_str() ); - lua_pop(lua, 1); - return true; - } - return false; -} - -cPlugin_Lua::~cPlugin_Lua() -{ - UnloadPlugins(); - if( m_LuaState ) - { - lua_close( m_LuaState ); - m_LuaState = 0; - } -} - -cPlugin_Lua::cPlugin_Lua(const char* a_Plugin) -: m_LuaState( 0 ) -{ - m_FileName.assign( a_Plugin ); -} - -bool cPlugin_Lua::Initialize() -{ - if( !m_LuaState ) - { - m_LuaState = lua_open(); - luaL_openlibs( m_LuaState ); - tolua_AllToLua_open(m_LuaState); - ManualBindings::Bind( m_LuaState ); - } - - int s = luaL_loadfile(m_LuaState, (std::string("Plugins/") + m_FileName ).c_str() ); - if( report_errors( m_LuaState, s ) ) - { - LOGERROR("Can't load plugin %s", m_FileName.c_str() ); - lua_close( m_LuaState ); - m_LuaState = 0; - return false; - } - - s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0); - if( report_errors( m_LuaState, s ) ) - { - LOGERROR("Error in plugin %s", m_FileName.c_str() ); - lua_close( m_LuaState ); - m_LuaState = 0; - return false; - } - return true; -} - -void cPlugin_Lua::AddPlugin( cPlugin* a_Plugin ) -{ - m_Plugins.push_back( a_Plugin ); -} - -void cPlugin_Lua::RemovePlugin( cPlugin* a_Plugin ) -{ - m_Plugins.remove( a_Plugin ); - cRoot::Get()->GetPluginManager()->RemovePlugin( a_Plugin, true ); -} - -void cPlugin_Lua::UnloadPlugins() -{ - while( m_Plugins.begin() != m_Plugins.end() ) - { - RemovePlugin( *m_Plugins.begin() ); - } -} - -lua_State* cPlugin_Lua::GetLuaState() -{ - return m_LuaState; -} \ No newline at end of file diff --git a/source/cPlugin_Lua.h b/source/cPlugin_Lua.h deleted file mode 100644 index 73787de0c..000000000 --- a/source/cPlugin_Lua.h +++ /dev/null @@ -1,38 +0,0 @@ - -#pragma once - -class cPickup; -class cPlayer; -class cPlugin; - - - - - -class cPlugin_Lua //tolua_export -{ //tolua_export -public: - cPlugin_Lua(const char* a_Plugin); - ~cPlugin_Lua(); - - virtual bool Initialize(); - - std::string GetFileName() { return m_FileName; } //tolua_export - typedef struct lua_State lua_State; - lua_State* GetLuaState(); - - void AddPlugin( cPlugin* a_Plugin ); - void RemovePlugin( cPlugin* a_Plugin ); -private: - void UnloadPlugins(); - - std::string m_FileName; - lua_State* m_LuaState; - - typedef std::list< cPlugin* > PluginList; - PluginList m_Plugins; -}; //tolua_export - - - - diff --git a/source/cPlugin_NewLua.cpp b/source/cPlugin_NewLua.cpp deleted file mode 100644 index bb7878f60..000000000 --- a/source/cPlugin_NewLua.cpp +++ /dev/null @@ -1,794 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#define LUA_USE_POSIX -#include "cPlugin_NewLua.h" -#include "cMCLogger.h" -#include "LuaItems.h" - -extern "C" -{ -#include "lualib.h" -} - -#include "tolua++.h" -#include "Bindings.h" -#include "ManualBindings.h" - -#ifdef _WIN32 -// #include "wdirent.h" -#else -#include -#endif - - - - - -extern bool report_errors(lua_State* lua, int status); - - - - - -cPlugin_NewLua::cPlugin_NewLua( const char* a_PluginName ) - : m_LuaState( 0 ) - , cWebPlugin() -{ - m_Directory = a_PluginName; -} - - - - - -cPlugin_NewLua::~cPlugin_NewLua() -{ - cCSLock Lock( m_CriticalSection ); - - if( m_LuaState ) - { - lua_close( m_LuaState ); - m_LuaState = 0; - } -} - - - - - -bool cPlugin_NewLua::Initialize() -{ - cCSLock Lock( m_CriticalSection ); - if( !m_LuaState ) - { - m_LuaState = lua_open(); - luaL_openlibs( m_LuaState ); - tolua_AllToLua_open(m_LuaState); - ManualBindings::Bind( m_LuaState ); - } - - std::string PluginPath = FILE_IO_PREFIX + GetLocalDirectory() + "/"; - - // Load all files for this plugin, and execute them - AStringList Files = GetDirectoryContents(PluginPath.c_str()); - for (AStringList::const_iterator itr = Files.begin(); itr != Files.end(); ++itr) - { - if (itr->rfind(".lua") == AString::npos) - { - continue; - } - AString Path = PluginPath + *itr; - int s = luaL_loadfile(m_LuaState, Path.c_str() ); - if( report_errors( m_LuaState, s ) ) - { - LOGERROR("Can't load plugin %s because of an error in file %s", m_Directory.c_str(), Path.c_str() ); - lua_close( m_LuaState ); - m_LuaState = 0; - return false; - } - - s = lua_pcall(m_LuaState, 0, LUA_MULTRET, 0); - if( report_errors( m_LuaState, s ) ) - { - LOGERROR("Error in plugin %s in file %s", m_Directory.c_str(), Path.c_str() ); - lua_close( m_LuaState ); - m_LuaState = 0; - return false; - } - } // for itr - Files[] - - // Call intialize function - if( !PushFunction("Initialize") ) - { - lua_close( m_LuaState ); - m_LuaState = 0; - return false; - } - - tolua_pushusertype(m_LuaState, this, "cPlugin_NewLua"); - - if( !CallFunction(1, 1, "Initialize") ) - { - lua_close( m_LuaState ); - m_LuaState = 0; - return false; - } - - if( !lua_isboolean( m_LuaState, -1 ) ) - { - LOGWARN("Error in plugin %s Initialize() must return a boolean value!", m_Directory.c_str() ); - lua_close( m_LuaState ); - m_LuaState = 0; - return false; - } - - bool bSuccess = (tolua_toboolean( m_LuaState, -1, 0) > 0); - return bSuccess; -} - - - - - -AString cPlugin_NewLua::GetLocalDirectory(void) const -{ - return std::string("Plugins/") + m_Directory; -} - - - - - -void cPlugin_NewLua::OnDisable() -{ - cCSLock Lock( m_CriticalSection ); - if( !PushFunction("OnDisable", false) ) // false = don't log error if not found - return; - - CallFunction(0, 0, "OnDisable"); -} - - - - - -void cPlugin_NewLua::Tick(float a_Dt) -{ - cCSLock Lock( m_CriticalSection ); - if( !PushFunction("Tick") ) - return; - - tolua_pushnumber( m_LuaState, a_Dt ); - - CallFunction(1, 0, "Tick"); -} - - - - - -bool cPlugin_NewLua::OnCollectPickup(cPlayer * a_Player, cPickup * a_Pickup) -{ - cCSLock Lock(m_CriticalSection); - if (!PushFunction("OnCollectPickup")) - { - return false; - } - - tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); - tolua_pushusertype(m_LuaState, a_Pickup, "cPickup"); - - if (!CallFunction(2, 1, "OnCollectPickup")) - { - return false; - } - - return (tolua_toboolean(m_LuaState, -1, 0) > 0); -} - - - - - -bool cPlugin_NewLua::OnDisconnect(cPlayer * a_Player, const AString & a_Reason) -{ - cCSLock Lock(m_CriticalSection); - if (!PushFunction("OnDisconnect")) - { - return false; - } - - tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); - tolua_pushstring (m_LuaState, a_Reason.c_str()); - - if (!CallFunction(2, 1, "OnDisconnect")) - { - return false; - } - - return (tolua_toboolean( m_LuaState, -1, 0) > 0); -} - - - - - -bool cPlugin_NewLua::OnBlockPlace(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) -{ - cCSLock Lock(m_CriticalSection); - if (!PushFunction("OnBlockPlace")) - { - return false; - } - - tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); - tolua_pushnumber (m_LuaState, a_BlockX); - tolua_pushnumber (m_LuaState, a_BlockY); - tolua_pushnumber (m_LuaState, a_BlockZ); - tolua_pushnumber (m_LuaState, a_BlockFace); - tolua_pushusertype(m_LuaState, (void *)&a_HeldItem, "cItem"); - - if (!CallFunction(6, 1, "OnBlockPlace")) - { - return false; - } - - return (tolua_toboolean( m_LuaState, -1, 0) > 0); -} - - - - - -bool cPlugin_NewLua::OnBlockDig(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) -{ - cCSLock Lock(m_CriticalSection); - if (!PushFunction("OnBlockDig")) - { - return false; - } - - tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); - tolua_pushnumber (m_LuaState, a_BlockX); - tolua_pushnumber (m_LuaState, a_BlockY); - tolua_pushnumber (m_LuaState, a_BlockZ); - tolua_pushnumber (m_LuaState, a_BlockFace); - tolua_pushnumber (m_LuaState, a_Status); - tolua_pushnumber (m_LuaState, a_OldBlock); - tolua_pushnumber (m_LuaState, a_OldMeta); - - if (!CallFunction(8, 1, "OnBlockDig")) - { - return false; - } - - return (tolua_toboolean( m_LuaState, -1, 0) > 0); -} - - - - - -bool cPlugin_NewLua::OnChat(cPlayer * a_Player, const AString & a_Message) -{ - cCSLock Lock(m_CriticalSection); - if (!PushFunction("OnChat")) - { - return false; - } - - tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); - tolua_pushstring (m_LuaState, a_Message.c_str()); - - if (!CallFunction(2, 1, "OnChat")) - { - return false; - } - - return (tolua_toboolean( m_LuaState, -1, 0) > 0); -} - - - - - -bool cPlugin_NewLua::OnLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) -{ - cCSLock Lock( m_CriticalSection ); - if( !PushFunction("OnLogin") ) - return false; - - tolua_pushusertype (m_LuaState, a_Client, "cClientHandle"); - tolua_pushnumber (m_LuaState, a_ProtocolVersion); - tolua_pushcppstring(m_LuaState, a_Username); - - if (!CallFunction(3, 1, "OnLogin")) - return false; - - bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); - return bRetVal; -} - - - - - -void cPlugin_NewLua::OnPlayerSpawn( cPlayer* a_Player ) -{ - cCSLock Lock( m_CriticalSection ); - if( !PushFunction("OnPlayerSpawn") ) - return; - - tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); - - CallFunction(1, 0, "OnPlayerSpawn"); -} - - - - - -bool cPlugin_NewLua::OnPlayerJoin( cPlayer* a_Player ) -{ - cCSLock Lock( m_CriticalSection ); - if( !PushFunction("OnPlayerJoin") ) - return false; - - tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); - - if( !CallFunction(1, 1, "OnPlayerJoin") ) - return false; - - bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); - return bRetVal; -} - - - - - -void cPlugin_NewLua::OnPlayerMove( cPlayer* a_Player ) -{ - cCSLock Lock( m_CriticalSection ); - if( !PushFunction("OnPlayerMove") ) - return; - - tolua_pushusertype(m_LuaState, a_Player, "cPlayer"); - - CallFunction(1, 0, "OnPlayerMove"); -} - - - - - -void cPlugin_NewLua::OnTakeDamage( cPawn* a_Pawn, TakeDamageInfo* a_TakeDamageInfo ) -{ - cCSLock Lock( m_CriticalSection ); - if( !PushFunction("OnTakeDamage") ) - return; - - tolua_pushusertype(m_LuaState, a_Pawn, "cPawn"); - tolua_pushusertype(m_LuaState, a_TakeDamageInfo, "TakeDamageInfo"); - - CallFunction(2, 0, "OnTakeDamage"); -} - - - - - -bool cPlugin_NewLua::OnKilled( cPawn* a_Killed, cEntity* a_Killer ) -{ - cCSLock Lock( m_CriticalSection ); - if( !PushFunction("OnKilled") ) - return false; - - tolua_pushusertype(m_LuaState, a_Killed, "cPawn"); - tolua_pushusertype(m_LuaState, a_Killer, "cEntity"); - - if( !CallFunction(2, 1, "OnKilled") ) - return false; - - bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); - return bRetVal; -} - - - - - -void cPlugin_NewLua::OnChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CriticalSection); - if (!PushFunction("OnChunkGenerated")) - { - return; - } - - tolua_pushusertype(m_LuaState, a_World, "cWorld"); - tolua_pushnumber (m_LuaState, a_ChunkX); - tolua_pushnumber (m_LuaState, a_ChunkZ); - - CallFunction(3, 0, "OnChunkGenerated"); -} - - - - - -bool cPlugin_NewLua::OnChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cLuaChunk * a_pLuaChunk) -{ - cCSLock Lock(m_CriticalSection); - if (!PushFunction("OnChunkGenerating")) - return false; - - tolua_pushusertype(m_LuaState, a_World, "cWorld"); - tolua_pushnumber (m_LuaState, a_ChunkX); - tolua_pushnumber (m_LuaState, a_ChunkZ); - tolua_pushusertype(m_LuaState, a_pLuaChunk, "cLuaChunk"); - - if( !CallFunction(3, 1, "OnChunkGenerating") ) - return false; - - bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); - return bRetVal; -} - - - - - -bool cPlugin_NewLua::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - cCSLock Lock(m_CriticalSection); - if (!PushFunction("OnPreCrafting")) - return false; - - tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer"); - tolua_pushusertype(m_LuaState, (void *)a_Grid, "cCraftingGrid"); - tolua_pushusertype(m_LuaState, (void *)a_Recipe, "cCraftingRecipe"); - - if (!CallFunction(3, 1, "OnPreCrafting")) - { - return false; - } - - bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); - return bRetVal; -} - - - - - -bool cPlugin_NewLua::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - cCSLock Lock(m_CriticalSection); - if (!PushFunction("OnCraftingNoRecipe")) - return false; - - tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer"); - tolua_pushusertype(m_LuaState, (void *)a_Grid, "cCraftingGrid"); - tolua_pushusertype(m_LuaState, (void *)a_Recipe, "cCraftingRecipe"); - - if (!CallFunction(3, 1, "OnCraftingNoRecipe")) - { - return false; - } - - bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); - return bRetVal; -} - - - - - -bool cPlugin_NewLua::OnPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - cCSLock Lock(m_CriticalSection); - if (!PushFunction("OnPostCrafting")) - return false; - - tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer"); - tolua_pushusertype(m_LuaState, (void *)a_Grid, "cCraftingGrid"); - tolua_pushusertype(m_LuaState, (void *)a_Recipe, "cCraftingRecipe"); - - if (!CallFunction(3, 1, "OnPostCrafting")) - { - return false; - } - - bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); - return bRetVal; -} - - - - - -bool cPlugin_NewLua::OnBlockToPickup( - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, - const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups -) -{ - cLuaItems Pickups(a_Pickups); - cCSLock Lock(m_CriticalSection); - if (!PushFunction("OnBlockToPickup")) - return false; - - tolua_pushnumber (m_LuaState, a_BlockType); - tolua_pushnumber (m_LuaState, a_BlockMeta); - tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer"); - tolua_pushusertype(m_LuaState, (void *)&a_EquippedItem, "cItem"); - tolua_pushusertype(m_LuaState, (void *)&Pickups, "cLuaItems"); - - if (!CallFunction(5, 1, "OnBlockToPickup")) - { - return false; - } - - bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); - return bRetVal; -} - - - - - -bool cPlugin_NewLua::OnWeatherChanged(cWorld * a_World) -{ - cCSLock Lock(m_CriticalSection); - if (!PushFunction("OnWeatherChanged")) - { - return false; - } - - tolua_pushusertype(m_LuaState, (void *)a_World, "cWorld"); - - if (!CallFunction(1, 1, "OnWeatherChanged")) - { - return false; - } - - bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); - return bRetVal; -} - - - - - -bool cPlugin_NewLua::OnUpdatingSign( - cWorld * a_World, - int a_BlockX, int a_BlockY, int a_BlockZ, - AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, - cPlayer * a_Player -) -{ - cCSLock Lock(m_CriticalSection); - if (!PushFunction("OnUpdatingSign")) - { - return false; - } - - tolua_pushusertype(m_LuaState, (void *)a_World, "cWorld"); - tolua_pushnumber (m_LuaState, a_BlockX); - tolua_pushnumber (m_LuaState, a_BlockY); - tolua_pushnumber (m_LuaState, a_BlockZ); - tolua_pushstring (m_LuaState, a_Line1.c_str()); - tolua_pushstring (m_LuaState, a_Line2.c_str()); - tolua_pushstring (m_LuaState, a_Line3.c_str()); - tolua_pushstring (m_LuaState, a_Line4.c_str()); - tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer"); - - if (!CallFunction(9, 5, "OnUpdatingSign")) - { - return false; - } - - bool bRetVal = (tolua_toboolean( m_LuaState, -5, 0) > 0); - if (lua_isstring(m_LuaState, -4)) - { - a_Line1 = tolua_tostring(m_LuaState, -4, ""); - } - if (lua_isstring(m_LuaState, -3)) - { - a_Line2 = tolua_tostring(m_LuaState, -3, ""); - } - if (lua_isstring(m_LuaState, -2)) - { - a_Line3 = tolua_tostring(m_LuaState, -2, ""); - } - if (lua_isstring(m_LuaState, -1)) - { - a_Line4 = tolua_tostring(m_LuaState, -1, ""); - } - return bRetVal; -} - - - - - -bool cPlugin_NewLua::OnUpdatedSign( - cWorld * a_World, - int a_BlockX, int a_BlockY, int a_BlockZ, - const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, - cPlayer * a_Player -) -{ - cCSLock Lock(m_CriticalSection); - if (!PushFunction("OnUpdatedSign")) - { - return false; - } - - tolua_pushusertype(m_LuaState, (void *)a_World, "cWorld"); - tolua_pushnumber (m_LuaState, a_BlockX); - tolua_pushnumber (m_LuaState, a_BlockY); - tolua_pushnumber (m_LuaState, a_BlockZ); - tolua_pushstring (m_LuaState, a_Line1.c_str()); - tolua_pushstring (m_LuaState, a_Line2.c_str()); - tolua_pushstring (m_LuaState, a_Line3.c_str()); - tolua_pushstring (m_LuaState, a_Line4.c_str()); - tolua_pushusertype(m_LuaState, (void *)a_Player, "cPlayer"); - - if (!CallFunction(9, 1, "OnUpdatedSign")) - { - return false; - } - - bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); - return bRetVal; -} - - - - - -bool cPlugin_NewLua::OnHandshake(cClientHandle * a_Client, const AString & a_Username) -{ - cCSLock Lock(m_CriticalSection); - if (!PushFunction("OnHandshake")) - { - return false; - } - - tolua_pushusertype(m_LuaState, a_Client, "cClientHandle"); - tolua_pushstring (m_LuaState, a_Username.c_str()); - - if (!CallFunction(2, 1, "OnHandshake")) - { - return false; - } - - bool bRetVal = (tolua_toboolean( m_LuaState, -1, 0) > 0); - return bRetVal; -} - - - - - -cPlugin_NewLua * cPlugin_NewLua::CreateWebPlugin(lua_State * a_LuaState) -{ - LOGWARN("WARNING: Using deprecated function CreateWebPlugin()! A Lua plugin is a WebPlugin by itself now. (plugin \"%s\" in folder \"%s\")", - cPlugin::GetName().c_str(), m_Directory.c_str() - ); - return this; -} - - - - - -AString cPlugin_NewLua::HandleWebRequest( HTTPRequest * a_Request ) -{ - cCSLock Lock( m_CriticalSection ); - std::string RetVal = ""; - - std::pair< std::string, std::string > TabName = GetTabNameForRequest(a_Request); - std::string SafeTabName = TabName.second; - if( SafeTabName.empty() ) - return ""; - - sWebPluginTab* Tab = 0; - for( TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr ) - { - if( (*itr)->SafeTitle.compare( SafeTabName ) == 0 ) // This is the one! Rawr - { - Tab = *itr; - break; - } - } - - if( Tab ) - { - //LOGINFO("1. Stack size: %i", lua_gettop(m_LuaState) ); - lua_rawgeti( m_LuaState, LUA_REGISTRYINDEX, Tab->UserData); // same as lua_getref() - - //LOGINFO("2. Stack size: %i", lua_gettop(m_LuaState) ); - // Push HTTPRequest - tolua_pushusertype( m_LuaState, a_Request, "HTTPRequest" ); - //LOGINFO("Calling bound function! :D"); - int s = lua_pcall( m_LuaState, 1, 1, 0); - - if ( s != 0 ) - { - std::string err = lua_tostring(m_LuaState, -1); - LOGERROR("-- %s", err.c_str() ); - lua_pop(m_LuaState, 1); - LOGINFO("error. Stack size: %i", lua_gettop(m_LuaState) ); - return err; // Show the error message in the web page, looks cool - } - - if( !lua_isstring( m_LuaState, -1 ) ) - { - LOGWARN("WARNING: WebPlugin tab '%s' did not return a string!", Tab->Title.c_str() ); - lua_pop(m_LuaState, 1); // Pop return value - return std::string("WARNING: WebPlugin tab '") + Tab->Title + std::string("' did not return a string!"); - } - - RetVal += tolua_tostring(m_LuaState, -1, 0); - lua_pop(m_LuaState, 1); // Pop return value - //LOGINFO("ok. Stack size: %i", lua_gettop(m_LuaState) ); - } - - return RetVal; -} - - - - - -bool cPlugin_NewLua::AddWebTab( const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference ) -{ - cCSLock Lock( m_CriticalSection ); - if( a_LuaState != m_LuaState ) - { - LOGERROR("Only allowed to add a tab to a WebPlugin of your own Plugin!"); - return false; - } - sWebPluginTab* Tab = new sWebPluginTab(); - Tab->Title = a_Title; - Tab->SafeTitle = SafeString( a_Title ); - - Tab->UserData = a_FunctionReference; - - GetTabs().push_back( Tab ); - return true; -} - - - - - -// Helper functions -bool cPlugin_NewLua::PushFunction( const char* a_FunctionName, bool a_bLogError /* = true */ ) -{ - lua_getglobal(m_LuaState, a_FunctionName); - if(!lua_isfunction(m_LuaState,-1)) - { - if( a_bLogError ) - { - LOGWARN("Error in plugin %s: Could not find function %s()", m_Directory.c_str(), a_FunctionName ); - } - lua_pop(m_LuaState,1); - return false; - } - return true; -} - -bool cPlugin_NewLua::CallFunction( int a_NumArgs, int a_NumResults, const char* a_FunctionName ) -{ - int s = lua_pcall(m_LuaState, a_NumArgs, a_NumResults, 0); - if( report_errors( m_LuaState, s ) ) - { - LOGWARN("Error in plugin %s calling function %s()", m_Directory.c_str(), a_FunctionName ); - return false; - } - return true; -} \ No newline at end of file diff --git a/source/cPlugin_NewLua.h b/source/cPlugin_NewLua.h deleted file mode 100644 index 082b2a82b..000000000 --- a/source/cPlugin_NewLua.h +++ /dev/null @@ -1,76 +0,0 @@ - -#pragma once - -#include "cPlugin.h" -#include "cWebPlugin.h" - - - - - -typedef struct lua_State lua_State; - - - - - -class cPlugin_NewLua : public cPlugin, public cWebPlugin //tolua_export -{ //tolua_export -public: //tolua_export - cPlugin_NewLua( const char* a_PluginName ); - ~cPlugin_NewLua(); - - virtual void OnDisable(); //tolua_export - virtual bool Initialize(); //tolua_export - - virtual void Tick(float a_Dt); //tolua_export - - virtual bool OnBlockDig (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) override; - virtual bool OnBlockPlace (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) override; - virtual bool OnBlockToPickup (BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups); - virtual bool OnChat (cPlayer * a_Player, const AString & a_Message) override; - virtual void OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override; - virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cLuaChunk * a_pLuaChunk ) override; - virtual bool OnCollectPickup (cPlayer * a_Player, cPickup * a_Pickup) override; - virtual bool OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; - virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override; - virtual bool OnKilled (cPawn * a_Killed, cEntity* a_Killer ) override; - virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) override; - virtual bool OnPlayerJoin (cPlayer * a_Player ) override; - virtual void OnPlayerMove (cPlayer * a_Player ) override; - virtual void OnPlayerSpawn (cPlayer * a_Player ) override; - virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; - virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; - virtual void OnTakeDamage (cPawn * a_Pawn, TakeDamageInfo * a_TakeDamageInfo ) override; - virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) override; - virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) override; - virtual bool OnWeatherChanged (cWorld * a_World) override; - virtual bool OnHandshake (cClientHandle * a_Client, const AString & a_Username) override; - - const AString & GetDirectory(void) const {return m_Directory; } - AString GetLocalDirectory(void) const; //tolua_export - - virtual void SetName( const AString & a_Name ) override { cPlugin::SetName(a_Name); } - - // cWebPlugin override - virtual const AString & GetName(void) const {return cPlugin::GetName(); } - - // cWebPlugin and WebAdmin stuff - virtual AString HandleWebRequest( HTTPRequest * a_Request ) override; - bool AddWebTab( const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference ); // >> EXPORTED IN MANUALBINDINGS << - - lua_State* GetLuaState() { return m_LuaState; } - - OBSOLETE cPlugin_NewLua * CreateWebPlugin(lua_State * a_LuaState); //tolua_export - - cCriticalSection & GetCriticalSection() { return m_CriticalSection; } - -private: - bool PushFunction( const char* a_FunctionName, bool a_bLogError = true ); - bool CallFunction( int a_NumArgs, int a_NumResults, const char* a_FunctionName ); // a_FunctionName is only used for error messages, nothing else - - cCriticalSection m_CriticalSection; - - std::string m_Directory; - lua_State * m_LuaState; -};//tolua_export \ No newline at end of file diff --git a/source/cPlugin_Squirrel.cpp b/source/cPlugin_Squirrel.cpp deleted file mode 100644 index 8b13718a7..000000000 --- a/source/cPlugin_Squirrel.cpp +++ /dev/null @@ -1,387 +0,0 @@ -#include "Globals.h" -#include "cPlugin_Squirrel.h" -#include "squirrelbindings/SquirrelFunctions.h" -#include "squirrelbindings/SquirrelBindings.h" -#include "squirrelbindings/cSquirrelBaseClass.h" - - -cPlugin_Squirrel::cPlugin_Squirrel( const char* a_PluginName ) -{ - SetLanguage( cPlugin::E_SQUIRREL ); - m_PluginName = a_PluginName; -} - -cPlugin_Squirrel::~cPlugin_Squirrel() -{ - delete m_Plugin; -} - -bool cPlugin_Squirrel::Initialize() -{ - cCSLock Lock(m_CriticalSection); - - std::string PluginPath = std::string("Plugins/") + m_PluginName + ".nut"; - - Sqrat::Script script; - script.CompileFile(PluginPath); - if(script.IsNull()) - { - LOGERROR("Unable to run script \"%s\"", m_PluginName); - } - - try { - script.Run(); - - Sqrat::Function construct(Sqrat::RootTable(), m_PluginName); - - if(construct.IsNull()) - { - LOGERROR("Constructor for Plugin \"%s\" not found.", m_PluginName); - return false; - } - - Sqrat::Object obj = construct.Evaluate(); - - ((cSquirrelBaseClass *) obj.GetInstanceUP())->setInstance(this); - - m_Plugin = new SquirrelObject(obj); - - - Sqrat::Object PluginName = obj.GetSlot("name"); - if(!PluginName.IsNull()) - { - this->SetName(PluginName.Cast()); - } - - Sqrat::Function init = m_Plugin->GetFunction("Initialize"); - if(init.IsNull()) - { - LOGERROR("Can not initialize plugin \"%s\"", m_PluginName); - return false; - } - - return init.Evaluate(); - - } catch(Sqrat::Exception &e) - { - LOGERROR("Initialisation of \"%s\" failed: %s", m_PluginName, e.Message().c_str()); - return false; - } -} - - - - - -void cPlugin_Squirrel::OnDisable() -{ - cCSLock Lock(m_CriticalSection); - - if(!m_Plugin->HasFunction("OnDisable")) return; - - m_Plugin->GetFunction("OnDisable").Execute(); -} - - - - - -void cPlugin_Squirrel::Tick(float a_Dt) -{ - cCSLock Lock( m_CriticalSection ); - - if(!m_Plugin->HasFunction("OnTick")) return; - - m_Plugin->GetFunction("OnTick").Execute(a_Dt); -} - - - - - -bool cPlugin_Squirrel::OnCollectPickup(cPlayer * a_Player, cPickup * a_Pickup) -{ - cCSLock Lock(m_CriticalSection); - - if (!m_Plugin->HasFunction("OnCollectPickup")) - { - return false; - } - - return m_Plugin->GetFunction("OnCollectPickup").Evaluate(a_Player, a_Pickup); -} - - - - - -bool cPlugin_Squirrel::OnDisconnect(cPlayer* a_Player, const AString & a_Reason) -{ - cCSLock Lock( m_CriticalSection ); - - if (!m_Plugin->HasFunction("OnDisconnect")) return false; - - return m_Plugin->GetFunction("OnDisconnect").Evaluate(a_Player, a_Reason); -} - - - - - -bool cPlugin_Squirrel::OnBlockPlace(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) -{ - cCSLock Lock(m_CriticalSection); - - if (!m_Plugin->HasFunction("OnBlockPlace")) return false; - - return m_Plugin->GetFunction("OnBlockPlace").Evaluate(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_HeldItem); -} - - - - - -bool cPlugin_Squirrel::OnBlockDig(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) -{ - cCSLock Lock(m_CriticalSection); - - if (!m_Plugin->HasFunction("OnBlockDig")) return false; - - return m_Plugin->GetFunction("OnBlockDig").Evaluate(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status, a_OldBlock, a_OldMeta); -} - - - - - -bool cPlugin_Squirrel::OnChat(cPlayer * a_Player, const AString & a_Message) -{ - cCSLock Lock(m_CriticalSection); - - if (!m_Plugin->HasFunction("OnChat")) return false; - - return m_Plugin->GetFunction("OnChat").Evaluate(a_Player, a_Message); - -} - - - - - -bool cPlugin_Squirrel::OnLogin(cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) -{ - cCSLock Lock( m_CriticalSection ); - - if (!m_Plugin->HasFunction("OnLogin")) - { - return false; - } - - return m_Plugin->GetFunction("OnLogin").Evaluate(a_Client, a_ProtocolVersion, a_Username); -} - - - - - -void cPlugin_Squirrel::OnPlayerSpawn( cPlayer* a_Player ) -{ - cCSLock Lock( m_CriticalSection ); - - if(!m_Plugin->HasFunction("OnPlayerSpawn")) return; - - return m_Plugin->GetFunction("OnPlayerSpawn").Execute(a_Player); - -} - - - - - -bool cPlugin_Squirrel::OnPlayerJoin( cPlayer* a_Player ) -{ - cCSLock Lock( m_CriticalSection ); - - if(!m_Plugin->HasFunction("OnPlayerJoin")) return false; - - return m_Plugin->GetFunction("OnPlayerJoin").Evaluate(a_Player); -} - - - - - -void cPlugin_Squirrel::OnPlayerMove( cPlayer* a_Player ) -{ - cCSLock Lock( m_CriticalSection ); - - if(!m_Plugin->HasFunction("OnPlayerMove")) return; - - return m_Plugin->GetFunction("OnPlayerMove").Execute(a_Player); - -} - - - - - -void cPlugin_Squirrel::OnTakeDamage( cPawn* a_Pawn, TakeDamageInfo* a_TakeDamageInfo ) -{ - cCSLock Lock( m_CriticalSection ); - - if(!m_Plugin->HasFunction("OnTakeDamage")) return; - - return m_Plugin->GetFunction("OnTakeDamage")(a_Pawn, a_TakeDamageInfo); -} - - - - - -bool cPlugin_Squirrel::OnKilled( cPawn* a_Killed, cEntity* a_Killer ) -{ - cCSLock Lock( m_CriticalSection ); - if(!m_Plugin->HasFunction("OnKilled")) return false; - return m_Plugin->GetFunction("OnKilled").Evaluate(a_Killed, a_Killer); -} - - - - - -void cPlugin_Squirrel::OnChunkGenerated(cWorld * a_World, int a_ChunkX, int a_ChunkZ) -{ - cCSLock Lock(m_CriticalSection); - if(!m_Plugin->HasFunction("OnChunkGenerated")) return; - return m_Plugin->GetFunction("OnChunkGenerated")(a_World, a_ChunkX, a_ChunkZ); -} - - - - - -bool cPlugin_Squirrel::OnChunkGenerating(cWorld * a_World, int a_ChunkX, int a_ChunkZ, cLuaChunk * a_pLuaChunk) -{ - cCSLock Lock(m_CriticalSection); - if(!m_Plugin->HasFunction("OnChunkGenerating")) return false; - return m_Plugin->GetFunction("OnChunkGenerating").Evaluate(a_World, a_ChunkX, a_ChunkZ, a_pLuaChunk); -} - - - - - -bool cPlugin_Squirrel::OnPreCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - cCSLock Lock(m_CriticalSection); - if(!m_Plugin->HasFunction("OnPreCrafting")) return false; - return m_Plugin->GetFunction("OnPreCrafting").Evaluate((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe); - -} - - - - - -bool cPlugin_Squirrel::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - cCSLock Lock(m_CriticalSection); - if(!m_Plugin->HasFunction("OnCraftingNoRecipe")) return false; - return m_Plugin->GetFunction("OnCraftingNoRecipe").Evaluate((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe); -} - - - - - -bool cPlugin_Squirrel::OnPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) -{ - cCSLock Lock(m_CriticalSection); - - if(!m_Plugin->HasFunction("OnPostCrafting")) return false; - return m_Plugin->GetFunction("OnPostCrafting").Evaluate((cPlayer *) a_Player, (cCraftingGrid *) a_Grid, a_Recipe); -} - - - - - -bool cPlugin_Squirrel::OnBlockToPickup( - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, - const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups -) -{ - cCSLock Lock(m_CriticalSection); - - if(!m_Plugin->HasFunction("OnBlockToPickup")) return false; - return m_Plugin->GetFunction("OnBlockToPickup").Evaluate(a_BlockType, a_BlockMeta, (cPlayer *) a_Player, a_EquippedItem, a_Pickups); - -} - - - - - -bool cPlugin_Squirrel::OnWeatherChanged(cWorld * a_World) -{ - cCSLock Lock(m_CriticalSection); - - if(!m_Plugin->HasFunction("OnWeatherChanged")) return false; - return m_Plugin->GetFunction("OnWeatherChanged").Evaluate(a_World); -} - - - - - -bool cPlugin_Squirrel::OnUpdatingSign( - cWorld * a_World, - int a_BlockX, int a_BlockY, int a_BlockZ, - AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, - cPlayer * a_Player -) -{ - cCSLock Lock(m_CriticalSection); - - if(!m_Plugin->HasFunction("OnUpdatingSign")) return false; - return m_Plugin->GetFunction("OnUpdatingSign") - .Evaluate( - a_World, - a_BlockX, - a_BlockY, - a_BlockZ, - a_Line1, - a_Line2, - a_Line3, - a_Line4, - a_Player - ); -} - - - - - -bool cPlugin_Squirrel::OnUpdatedSign( - cWorld * a_World, - int a_BlockX, int a_BlockY, int a_BlockZ, - const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, - cPlayer * a_Player -) -{ - cCSLock Lock(m_CriticalSection); - - if(!m_Plugin->HasFunction("OnUpdatedSign")) return false; - return m_Plugin->GetFunction("OnUpdatedSign") - .Evaluate( - a_World, - a_BlockX, - a_BlockY, - a_BlockZ, - a_Line1, - a_Line2, - a_Line3, - a_Line4, - a_Player - ); -} diff --git a/source/cPlugin_Squirrel.h b/source/cPlugin_Squirrel.h deleted file mode 100644 index 9cec0fee8..000000000 --- a/source/cPlugin_Squirrel.h +++ /dev/null @@ -1,53 +0,0 @@ - -#pragma once - -#include "cPlugin.h" -#include -#include "squirrelbindings/SquirrelObject.h" - - - - - -class cPlugin_Squirrel : - public cPlugin -{ -public: - cPlugin_Squirrel(const char* a_PluginName); - ~cPlugin_Squirrel(); - - void OnDisable(); - bool Initialize(); - - void Tick(float a_Dt); - - virtual bool OnBlockDig (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta) override; - virtual bool OnBlockPlace (cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, const cItem & a_HeldItem) override; - virtual bool OnBlockToPickup (BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, const cPlayer * a_Player, const cItem & a_EquippedItem, cItems & a_Pickups); - virtual bool OnChat (cPlayer * a_Player, const AString & a_Message) override; - virtual void OnChunkGenerated (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override; - virtual bool OnChunkGenerating (cWorld * a_World, int a_ChunkX, int a_ChunkZ, cLuaChunk * a_pLuaChunk ) override; - virtual bool OnCollectPickup (cPlayer * a_Player, cPickup * a_Pickup) override; - virtual bool OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; - virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override; - virtual bool OnKilled (cPawn* a_Killed, cEntity* a_Killer ) override; - virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) override; - virtual bool OnPlayerJoin (cPlayer* a_Player ) override; - virtual void OnPlayerMove (cPlayer* a_Player ) override; - virtual void OnPlayerSpawn (cPlayer* a_Player ) override; - virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; - virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; - virtual void OnTakeDamage (cPawn* a_Pawn, TakeDamageInfo* a_TakeDamageInfo ) override; - virtual bool OnUpdatedSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) override; - virtual bool OnUpdatingSign (cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player) override; - virtual bool OnWeatherChanged (cWorld * a_World) override; - -protected: - const char * m_PluginName; - cCriticalSection m_CriticalSection; - SquirrelObject *m_Plugin; -}; - - - - diff --git a/source/cRedstone.cpp b/source/cRedstone.cpp deleted file mode 100644 index 701835d5a..000000000 --- a/source/cRedstone.cpp +++ /dev/null @@ -1,374 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cRedstone.h" -#include "cPiston.h" -#include "cRoot.h" -#include "cWorld.h" -#include "BlockID.h" -#include - - - - - -bool cRedstone::s_UseRedstone = false; - - - - - -cRedstone::cRedstone( cWorld* a_World ) - :m_World ( a_World ) - ,m_Metadata ( 0 ) - ,m_Direction ( 0 ) -{ - -} - - - - - -void cRedstone::ChangeRedstone( int fillx, int filly, int fillz, bool added ) -{ - s_UseRedstone = false; - if( !s_UseRedstone ) return; - - char before; - //int tempX; - //int tempY; - //int tempZ; - //int state; - //m_Direction - // 0 = x+ - // 1 = x- - // 2 = z+ - // 3 = z- - // 4 = y+ - // 5 = v- - - - if (added) { - m_Metadata = 15; - } else { - m_Metadata = 0; - } - before = m_Metadata; - - //printf("etb1\n"); - CalculateRedstone( fillx, filly, fillz ); //calculate all item centers. - //printf("etb2\n"); - - int Block = (int)m_World->GetBlock( fillx, filly, fillz ); - - switch (Block)//these blocks won't trigger the normal calculaton because they are affected by the redstone around them. So we check each possible channel around them instead. - { - case E_BLOCK_REDSTONE_TORCH_ON: - case E_BLOCK_REDSTONE_TORCH_OFF: - case E_BLOCK_AIR: - case E_BLOCK_PISTON_EXTENSION: - case E_BLOCK_PISTON: - case E_BLOCK_STICKY_PISTON: - { - m_Metadata = 0; - m_Direction = 0; - CalculateRedstone( fillx+1, filly, fillz ); - m_Metadata = 0; - m_Direction = 1; - CalculateRedstone( fillx-1, filly, fillz ); - m_Metadata = 0; - m_Direction = 2; - CalculateRedstone( fillx, filly, fillz+1 ); - m_Metadata = 0; - m_Direction = 3; - CalculateRedstone( fillx, filly, fillz-1 ); - m_Metadata = 0; - CalculateRedstone( fillx, filly-1, fillz ); - break; - } - case E_BLOCK_REDSTONE_WIRE: //special case for redstone wire. - { - m_Direction = 0; - CalculateRedstone( fillx+1, filly, fillz ); - m_Direction = 1; - CalculateRedstone( fillx-1, filly, fillz ); - m_Direction = 2; - CalculateRedstone( fillx, filly, fillz+1 ); - m_Direction = 3; - CalculateRedstone( fillx, filly, fillz-1 ); - m_Metadata = 0; - CalculateRedstone( fillx, filly-1, fillz ); - - m_Direction = 4; - CalculateRedstone( fillx+1, filly+1, fillz ); - CalculateRedstone( fillx-1, filly+1, fillz ); - CalculateRedstone( fillx, filly+1, fillz+1 ); - CalculateRedstone( fillx, filly+1, fillz-1 ); - - m_Direction = 5; - CalculateRedstone( fillx+1, filly-1, fillz ); - CalculateRedstone( fillx-1, filly-1, fillz ); - CalculateRedstone( fillx, filly-1, fillz+1 ); - CalculateRedstone( fillx, filly-1, fillz-1 ); - break; - } - } - - //printf("done here\n"); -} - - - - - -void cRedstone::CalculateRedstone( int fillx, int filly, int fillz) -{ - - if ( ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_BLOCK_STICKY_PISTON ) || ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_BLOCK_PISTON ) ) { - char pistonMeta = m_World->GetBlockMeta( fillx, filly, fillz ); - if (m_Metadata > 0) { - if (pistonMeta < 6) { // only extend if piston is not already extended - cPiston Piston(m_World); - Piston.ExtendPiston(fillx, filly, fillz); - } - } else { - if (pistonMeta > 6) { // only retract if piston is not already retracted - cPiston Piston(m_World); - Piston.RetractPiston(fillx, filly, fillz); - } - } - - } else if ( ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_ITEM_LEVER ) || ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_ITEM_STONE_BUTTON ) ) { - - if ( (int)m_World->GetBlockMeta( fillx, filly, fillz) > 6 ) { //button powered - m_Metadata = 15; //change meta to 15 only if redstone power device in on possition is found. - if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) != m_Metadata ) ) { - CalculateRedstone(fillx-1,filly,fillz); - } - if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) != m_Metadata ) ) { - CalculateRedstone(fillx+1,filly,fillz); - } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) != m_Metadata ) ) { - CalculateRedstone(fillx,filly,fillz-1); - } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) != m_Metadata ) ) { - CalculateRedstone(fillx,filly,fillz+1); - } - } else { - if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) != m_Metadata ) ) { - CalculateRedstone(fillx-1,filly,fillz); - } - if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) != m_Metadata ) ) { - CalculateRedstone(fillx+1,filly,fillz); - } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) != m_Metadata ) ) { - CalculateRedstone(fillx,filly,fillz-1); - } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) != m_Metadata ) ) { - CalculateRedstone(fillx,filly,fillz+1); - } - } - - } else if ( ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_BLOCK_STONE_PRESSURE_PLATE ) || ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_BLOCK_STONE_BUTTON ) ) { - - if ( (int)m_World->GetBlockMeta( fillx, filly, fillz) == 1 ) { //plate powered - m_Metadata = 15; //change meta to 15 only if redstone power device in on possition is found. - if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) != m_Metadata ) ) { - CalculateRedstone(fillx-1,filly,fillz); - } - if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) != m_Metadata ) ) { - CalculateRedstone(fillx+1,filly,fillz); - } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) != m_Metadata ) ) { - CalculateRedstone(fillx,filly,fillz-1); - } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) != m_Metadata ) ) { - CalculateRedstone(fillx,filly,fillz+1); - } - - } else { - - if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) != m_Metadata ) ) { - CalculateRedstone(fillx-1,filly,fillz); - } - if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) != m_Metadata ) ) { - CalculateRedstone(fillx+1,filly,fillz); - } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) != m_Metadata ) ) { - CalculateRedstone(fillx,filly,fillz-1); - } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) != m_Metadata ) ) { - CalculateRedstone(fillx,filly,fillz+1); - } - } - - } else if ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_BLOCK_REDSTONE_TORCH_ON ) { //If torch is on - //printf("found torch on\n"); - m_Metadata = 15; //change meta to 15 only if redstone torch in on possition is found. - if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) != m_Metadata ) ) { - CalculateRedstone(fillx-1,filly,fillz); - } - if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) != m_Metadata ) ) { - CalculateRedstone(fillx+1,filly,fillz); - } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) != m_Metadata ) ) { - CalculateRedstone(fillx,filly,fillz-1); - } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) != m_Metadata ) ) { - CalculateRedstone(fillx,filly,fillz+1); - } - - } else if ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_BLOCK_REDSTONE_TORCH_OFF ) { //if the torch is off - //printf("found torch off\n"); - // no need to change meta here. - if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) != m_Metadata ) ) { - CalculateRedstone(fillx-1,filly,fillz); - } - if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) != m_Metadata ) ) { - CalculateRedstone(fillx+1,filly,fillz); - } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) != m_Metadata ) ) { - CalculateRedstone(fillx,filly,fillz-1); - } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) != m_Metadata ) ) { - CalculateRedstone(fillx,filly,fillz+1); - } - - } else if ( (int)m_World->GetBlock( fillx, filly, fillz ) == E_BLOCK_REDSTONE_WIRE ) { //simple fill algorithm for redstone wire. - - if ( (int)m_World->GetBlockMeta( fillx, filly, fillz) != m_Metadata ) { - m_World->FastSetBlock( fillx, filly, fillz, (char)E_BLOCK_REDSTONE_WIRE, m_Metadata ); - m_Direction = 0; - CalculateRedstone( fillx+1, filly, fillz ); - m_Direction = 1; - CalculateRedstone( fillx-1, filly, fillz ); - m_Direction = 2; - CalculateRedstone( fillx, filly, fillz+1 ); - m_Direction = 3; - CalculateRedstone( fillx, filly, fillz-1 ); - CalculateRedstone( fillx, filly-1, fillz ); //check one block below //similar to same plane - - m_Direction = 4; - CalculateRedstone( fillx+1, filly+1, fillz ); - CalculateRedstone( fillx-1, filly+1, fillz ); - CalculateRedstone( fillx, filly+1, fillz+1 ); - CalculateRedstone( fillx, filly+1, fillz-1 ); - - m_Direction = 5; - CalculateRedstone( fillx+1, filly-1, fillz ); - CalculateRedstone( fillx-1, filly-1, fillz ); - CalculateRedstone( fillx, filly-1, fillz+1 ); - CalculateRedstone( fillx, filly-1, fillz-1 ); - } - - } else { //default, check item for torch attached to it. If meta > 0 then turn that torch off, otherwise turn it on. This change needs to be passed to the next world tick. - //check for torch to east with meta 1 //turn off - //check for torch to west with meta 2 //turn off - //check for torch to south with meta 3 //turn off - //check for torch to north with meta 4 //turn off - //check for torch above with meta 5 //turn off - if ( (int)m_World->GetBlock( fillx, filly, fillz ) != E_BLOCK_AIR ) { - if (m_Direction < 4) { //redstone wire can only power blocks on the same plane or directly below - if ( (int)m_Metadata > 0 ) { //wire powered - - //printf("bird: %i dog: %i \n",(int)m_World->GetBlock( fillx, filly+1, fillz ),(int)m_World->GetBlockMeta( fillx, filly+1, fillz)); - if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_TORCH_ON) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) == 1 ) ) { //east - m_World->m_RSList.push_back(fillx+1); - m_World->m_RSList.push_back(filly); - m_World->m_RSList.push_back(fillz); - m_World->m_RSList.push_back(00000); - } - if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_TORCH_ON) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) == 2 ) ) { //west - m_World->m_RSList.push_back(fillx-1); - m_World->m_RSList.push_back(filly); - m_World->m_RSList.push_back(fillz); - m_World->m_RSList.push_back(00000); - } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_TORCH_ON) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) == 3 ) ) { //south - m_World->m_RSList.push_back(fillx); - m_World->m_RSList.push_back(filly); - m_World->m_RSList.push_back(fillz+1); - m_World->m_RSList.push_back(00000); - } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_TORCH_ON) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) == 4 ) ) { //north - m_World->m_RSList.push_back(fillx); - m_World->m_RSList.push_back(filly); - m_World->m_RSList.push_back(fillz-1); - m_World->m_RSList.push_back(00000); - } - if ( ( (int)m_World->GetBlock( fillx, filly+1, fillz ) == E_BLOCK_REDSTONE_TORCH_ON) && ( (int)m_World->GetBlockMeta( fillx, filly+1, fillz) == 5 ) ) { //top - m_World->m_RSList.push_back(fillx); - m_World->m_RSList.push_back(filly+1); - m_World->m_RSList.push_back(fillz); - m_World->m_RSList.push_back(00000); - } - - } else { //wire not powered - - bool BlockPowered = IsBlockPowered( fillx, filly, fillz ); //chck this block for other wire turned on or torch turned on below: - if (BlockPowered == false) { //if block is not bowered by something else then I need to check for off torches and turn them on. - - //printf("echo: %i cruiser: %i \n",(int)m_World->GetBlock( fillx, filly+1, fillz ),(int)m_World->GetBlockMeta( fillx, filly+1, fillz)); - if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_TORCH_OFF) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) == 1 ) ) { //east - m_World->m_RSList.push_back(fillx+1); - m_World->m_RSList.push_back(filly); - m_World->m_RSList.push_back(fillz); - m_World->m_RSList.push_back(11111); - } - if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_TORCH_OFF) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) == 2 ) ) { //west - m_World->m_RSList.push_back(fillx-1); - m_World->m_RSList.push_back(filly); - m_World->m_RSList.push_back(fillz); - m_World->m_RSList.push_back(11111); - } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_TORCH_OFF) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) == 3 ) ) { //south - m_World->m_RSList.push_back(fillx); - m_World->m_RSList.push_back(filly); - m_World->m_RSList.push_back(fillz+1); - m_World->m_RSList.push_back(11111);; - } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_TORCH_OFF) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) == 4 ) ) { //north - m_World->m_RSList.push_back(fillx); - m_World->m_RSList.push_back(filly); - m_World->m_RSList.push_back(fillz-1); - m_World->m_RSList.push_back(11111); - } - if ( ( (int)m_World->GetBlock( fillx, filly+1, fillz ) == E_BLOCK_REDSTONE_TORCH_OFF) && ( (int)m_World->GetBlockMeta( fillx, filly+1, fillz) == 5 ) ) { //top - m_World->m_RSList.push_back(fillx); - m_World->m_RSList.push_back(filly+1); - m_World->m_RSList.push_back(fillz); - m_World->m_RSList.push_back(11111); - } - - } - - } - - } - - } - - } - -} - - - - - -bool cRedstone::IsBlockPowered( int fillx, int filly, int fillz ) -{ - - if ( (int)m_World->GetBlock( fillx, filly-1, fillz ) == E_BLOCK_REDSTONE_TORCH_ON) { return true; } - if ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_TORCH_ON) { return true; } - if ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_TORCH_ON) { return true; } - if ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_TORCH_ON) { return true; } - if ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_TORCH_ON) { return true; } - if ( ( (int)m_World->GetBlock( fillx+1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx+1, filly, fillz) > 0 ) ) { return true; } - if ( ( (int)m_World->GetBlock( fillx-1, filly, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx-1, filly, fillz) > 0 ) ) { return true; } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz+1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz+1) > 0 ) ) { return true; } - if ( ( (int)m_World->GetBlock( fillx, filly, fillz-1 ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly, fillz-1) > 0 ) ) { return true; } - if ( ( (int)m_World->GetBlock( fillx, filly+1, fillz ) == E_BLOCK_REDSTONE_WIRE) && ( (int)m_World->GetBlockMeta( fillx, filly+1, fillz) > 0 ) ) { return true; } - return false; -} \ No newline at end of file diff --git a/source/cRedstone.h b/source/cRedstone.h deleted file mode 100644 index a5377e8a7..000000000 --- a/source/cRedstone.h +++ /dev/null @@ -1,122 +0,0 @@ -#pragma once - -#include "Vector3i.h" - -class cWorld; -class cRedstone -{ -public: - - cRedstone( cWorld* a_World ); - - static char RepeaterRotationToMetaData( float a_Rotation ) - { - a_Rotation += 90 + 45; // So its not aligned with axis - if( a_Rotation > 360.f ) a_Rotation -= 360.f; - if( a_Rotation >= 0.f && a_Rotation < 90.f ) - return 0x1; - else if( a_Rotation >= 180 && a_Rotation < 270 ) - return 0x3; - else if( a_Rotation >= 90 && a_Rotation < 180 ) - return 0x2; - else - return 0x0; - } - - static bool IsRepeaterPointingTo( const Vector3i & a_RepeaterPos, char a_MetaData, const Vector3i & a_BlockPos ) - { - switch( a_MetaData & 0x3 ) - { - case 0x0: - if( (a_RepeaterPos - a_BlockPos).Equals( Vector3i( 0, 0, 1 ) ) ) - { - return true; - } - break; - case 0x1: - if( (a_RepeaterPos - a_BlockPos).Equals( Vector3i(-1, 0, 0 ) ) ) - { - return true; - } - break; - case 0x2: - if( (a_RepeaterPos - a_BlockPos).Equals( Vector3i( 0, 0,-1 ) ) ) - { - return true; - } - break; - case 0x3: - if( (a_RepeaterPos - a_BlockPos).Equals( Vector3i( 1, 0, 0 ) ) ) - { - return true; - } - break; - default: - break; - } - return false; - } - - static bool IsRepeaterPointingAway( const Vector3i & a_RepeaterPos, char a_MetaData, const Vector3i & a_BlockPos ) - { - switch( a_MetaData & 0x3 ) - { - case 0x0: - if( (a_RepeaterPos - a_BlockPos).Equals( Vector3i( 0, 0,-1 ) ) ) - { - return true; - } - break; - case 0x1: - if( (a_RepeaterPos - a_BlockPos).Equals( Vector3i( 1, 0, 0 ) ) ) - { - return true; - } - break; - case 0x2: - if( (a_RepeaterPos - a_BlockPos).Equals( Vector3i( 0, 0, 1 ) ) ) - { - return true; - } - break; - case 0x3: - if( (a_RepeaterPos - a_BlockPos).Equals( Vector3i(-1, 0, 0 ) ) ) - { - return true; - } - break; - default: - break; - } - return false; - } - - static Vector3i GetRepeaterDirection( char a_MetaData ) - { - switch( a_MetaData & 0x3 ) - { - case 0x0: - return Vector3i( 0, 0,-1 ); - case 0x1: - return Vector3i( 1, 0, 0 ); - case 0x2: - return Vector3i( 0, 0, 1 ); - case 0x3: - return Vector3i(-1, 0, 0 ); - default: - break; - } - return Vector3i(); - } - - void CalculateRedstone( int, int, int ); - void ChangeRedstone( int, int, int, bool ); - bool IsBlockPowered( int, int, int ); - - cWorld* m_World; - - char m_Metadata; - char m_Direction; - - static bool s_UseRedstone; -}; diff --git a/source/cRedstoneSimulator.cpp b/source/cRedstoneSimulator.cpp deleted file mode 100644 index 8d46b1347..000000000 --- a/source/cRedstoneSimulator.cpp +++ /dev/null @@ -1,701 +0,0 @@ -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cRedstoneSimulator.h" -#include "cPiston.h" -#include "cWorld.h" -#include "BlockID.h" -#include "cTorch.h" - -#include "cRedstone.h" - - - -cRedstoneSimulator::cRedstoneSimulator( cWorld* a_World ) - : super(a_World) -{ -} - - - - - -cRedstoneSimulator::~cRedstoneSimulator() -{ -} - - - - - -void cRedstoneSimulator::WakeUp( int a_X, int a_Y, int a_Z ) -{ - if( cRedstone::s_UseRedstone ) return; // Using the other/broken simulator - - cCSLock Lock( m_CS ); - m_Blocks.push_back( Vector3i( a_X, a_Y, a_Z ) ); -} - - - - - -void cRedstoneSimulator::Simulate( float a_Dt ) -{ - if( cRedstone::s_UseRedstone ) return; // Using the other/broken simulator - - // Toggle torches on/off - while( !m_RefreshTorchesAround.empty() ) - { - Vector3i pos = m_RefreshTorchesAround.front(); - m_RefreshTorchesAround.pop_front(); - - RefreshTorchesAround( pos ); - } - - // Set repeaters to correct values, and decrement ticks - for( RepeaterList::iterator itr = m_SetRepeaters.begin(); itr != m_SetRepeaters.end(); ) - { - (*itr).Ticks--; - if( (*itr).Ticks <= 0 ) - { - char Block = m_World->GetBlock( (*itr).Position ); - if( (*itr).bPowerOn == true && Block == E_BLOCK_REDSTONE_REPEATER_OFF ) - { - m_World->FastSetBlock( (*itr).Position.x, (*itr).Position.y, (*itr).Position.z, E_BLOCK_REDSTONE_REPEATER_ON, m_World->GetBlockMeta( (*itr).Position ) ); - m_Blocks.push_back( (*itr).Position ); - } - else if( (*itr).bPowerOn == false && Block == E_BLOCK_REDSTONE_REPEATER_ON ) - { - m_World->FastSetBlock( (*itr).Position.x, (*itr).Position.y, (*itr).Position.z, E_BLOCK_REDSTONE_REPEATER_OFF, m_World->GetBlockMeta( (*itr).Position ) ); - m_Blocks.push_back( (*itr).Position ); - } - - if( (*itr).bPowerOffNextTime ) - { - (*itr).bPowerOn = false; - (*itr).bPowerOffNextTime = false; - (*itr).Ticks = 10; // TODO: Look up actual ticks from block metadata - ++itr; - } - else - { - itr = m_SetRepeaters.erase( itr ); - } - } - else - { - ++itr; - } - } - - // Handle changed blocks - { - cCSLock Lock( m_CS ); - std::swap( m_Blocks, m_BlocksBuffer ); - } - for( BlockList::iterator itr = m_BlocksBuffer.begin(); itr != m_BlocksBuffer.end(); ++itr ) - { - HandleChange( *itr ); - } - m_BlocksBuffer.clear(); -} - - - - - -void cRedstoneSimulator::RefreshTorchesAround( const Vector3i & a_BlockPos ) -{ - static Vector3i Surroundings [] = { - Vector3i(-1, 0, 0), - Vector3i( 1, 0, 0), - Vector3i( 0, 0,-1), - Vector3i( 0, 0, 1), - Vector3i( 0, 1, 0), // Also toggle torch on top - }; - char TargetBlockID = E_BLOCK_REDSTONE_TORCH_ON; - char TargetRepeaterID = E_BLOCK_REDSTONE_REPEATER_OFF; - if( IsPowered( a_BlockPos, true ) ) - { - TargetBlockID = E_BLOCK_REDSTONE_TORCH_OFF; - TargetRepeaterID = E_BLOCK_REDSTONE_REPEATER_ON; - //if( m_World->GetBlock( a_BlockPos ) == E_BLOCK_DIRT ) - //{ - // m_World->FastSetBlock( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_STONE, 0 ); - //} - } - else - { - //if( m_World->GetBlock( a_BlockPos ) == E_BLOCK_STONE ) - //{ - // m_World->FastSetBlock( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_DIRT, 0 ); - //} - } - - for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) - { - Vector3i TorchPos = a_BlockPos + Surroundings[i]; - const char Block = m_World->GetBlock( TorchPos ); - switch( Block ) - { - case E_BLOCK_REDSTONE_TORCH_ON: - case E_BLOCK_REDSTONE_TORCH_OFF: - if( Block != TargetBlockID ) - { - char TorchMeta = m_World->GetBlockMeta( TorchPos ); - if( cTorch::IsAttachedTo( TorchPos, TorchMeta, a_BlockPos ) ) - { - m_World->FastSetBlock( TorchPos.x, TorchPos.y, TorchPos.z, TargetBlockID, m_World->GetBlockMeta( TorchPos ) ); - m_Blocks.push_back( TorchPos ); - } - } - break; - case E_BLOCK_REDSTONE_REPEATER_ON: - case E_BLOCK_REDSTONE_REPEATER_OFF: - if( Block != TargetRepeaterID && cRedstone::IsRepeaterPointingAway( TorchPos, m_World->GetBlockMeta( TorchPos ), a_BlockPos ) ) - { - SetRepeater( TorchPos, 10, TargetRepeaterID == E_BLOCK_REDSTONE_REPEATER_ON ); - } - break; - default: - break; - }; - } -} - - - - - -void cRedstoneSimulator::HandleChange( const Vector3i & a_BlockPos ) -{ - std::deque< Vector3i > SpreadStack; - - Vector3i Surroundings[] = { - Vector3i( 1, 0, 0 ), - Vector3i( 1, 1, 0 ), - Vector3i( 1,-1, 0 ), - Vector3i(-1, 0, 0 ), - Vector3i(-1, 1, 0 ), - Vector3i(-1,-1, 0 ), - Vector3i( 0, 0, 1 ), - Vector3i( 0, 1, 1 ), - Vector3i( 0,-1, 1 ), - Vector3i( 0, 0,-1 ), - Vector3i( 0, 1,-1 ), - Vector3i( 0,-1,-1 ), - Vector3i( 0,-1, 0 ), - }; - - char Block = m_World->GetBlock( a_BlockPos ); - - // First check whether torch should be on or off - if( Block == E_BLOCK_REDSTONE_TORCH_ON || Block == E_BLOCK_REDSTONE_TORCH_OFF ) - { - static Vector3i Surroundings [] = { - Vector3i(-1, 0, 0), - Vector3i( 1, 0, 0), - Vector3i( 0, 0,-1), - Vector3i( 0, 0, 1), - Vector3i( 0,-1, 0), - }; - for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) - { - Vector3i pos = a_BlockPos + Surroundings[i]; - char OtherBlock = m_World->GetBlock( pos ); - if( (OtherBlock != E_BLOCK_AIR) && (OtherBlock != E_BLOCK_REDSTONE_TORCH_ON) && (OtherBlock != E_BLOCK_REDSTONE_TORCH_OFF) ) - { - RefreshTorchesAround( pos ); - } - } - Block = m_World->GetBlock( a_BlockPos ); // Make sure we got the updated block - } - else if( Block == E_BLOCK_REDSTONE_REPEATER_ON || Block == E_BLOCK_REDSTONE_REPEATER_OFF ) // Check if repeater is powered by a 'powered block' (not wires/torch) - { - Vector3i Direction = cRedstone::GetRepeaterDirection( m_World->GetBlockMeta( a_BlockPos ) ); - Vector3i pos = a_BlockPos - Direction; // NOTE: It's minus Direction - char OtherBlock = m_World->GetBlock( pos ); - if( (OtherBlock != E_BLOCK_AIR) && (OtherBlock != E_BLOCK_REDSTONE_TORCH_ON) && (OtherBlock != E_BLOCK_REDSTONE_TORCH_OFF) && (OtherBlock != E_BLOCK_REDSTONE_WIRE) ) - { - RefreshTorchesAround( pos ); - } - else - { - SetRepeater( a_BlockPos, 10, IsPowered( a_BlockPos, false ) ); - } - Block = m_World->GetBlock( a_BlockPos ); - } - - BlockList Sources; - // If torch is still on, use it as a source - if( Block == E_BLOCK_REDSTONE_TORCH_ON ) - { - Sources.push_back( a_BlockPos ); - } - else if( Block == E_BLOCK_REDSTONE_REPEATER_ON ) - { - static Vector3i Surroundings [] = { // It only spreads right in front, and one block up - Vector3i( 0, 0, 0), - Vector3i( 0, 1, 0), - }; - Vector3i Direction = cRedstone::GetRepeaterDirection( m_World->GetBlockMeta( a_BlockPos ) ); - for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) - { - Vector3i pos = a_BlockPos + Direction + Surroundings[i]; - if( PowerBlock( pos, a_BlockPos, 0xf ) ) - { - SpreadStack.push_back( pos ); - } - } - } - - // Power all blocks legally connected to the sources - if( Block != E_BLOCK_REDSTONE_REPEATER_ON ) - { - BlockList NewSources = RemoveCurrent( a_BlockPos ); - Sources.insert( Sources.end(), NewSources.begin(), NewSources.end() ); - while( !Sources.empty() ) - { - Vector3i SourcePos = Sources.back(); - Sources.pop_back(); - - char Block = m_World->GetBlock( SourcePos ); - switch( Block ) - { - case E_BLOCK_REDSTONE_TORCH_OFF: - case E_BLOCK_REDSTONE_TORCH_ON: - { - static Vector3i Surroundings [] = { - Vector3i(-1, 0, 0), - Vector3i( 1, 0, 0), - Vector3i( 0, 0,-1), - Vector3i( 0, 0, 1), - }; - for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) - { - Vector3i OtherPos = SourcePos + Surroundings[i]; - if( PowerBlock( OtherPos, a_BlockPos, 0xf ) ) - { - SpreadStack.push_back( OtherPos ); // Changed, so add to stack - } - } - } - break; - case E_BLOCK_REDSTONE_REPEATER_OFF: - case E_BLOCK_REDSTONE_REPEATER_ON: - { - static Vector3i Surroundings [] = { - Vector3i( 0, 0, 0), - Vector3i( 0, 1, 0), - }; - Vector3i Direction = cRedstone::GetRepeaterDirection( m_World->GetBlockMeta( SourcePos ) ); - for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) - { - Vector3i pos = SourcePos + Direction + Surroundings[i]; - if( PowerBlock( pos, a_BlockPos, 0xf ) ) - { - SpreadStack.push_back( pos ); - } - } - } - break; - }; - - - } - } - - - // Do a floodfill - while( !SpreadStack.empty() ) - { - Vector3i pos = SpreadStack.back(); - SpreadStack.pop_back(); - - char Meta = m_World->GetBlockMeta( pos ); - - for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) - { - Vector3i OtherPos = pos + Surroundings[i]; - if( PowerBlock( OtherPos, pos, Meta-1 ) ) - { - SpreadStack.push_back( OtherPos ); // Changed, so add to stack - } - } - } - - // Only after a redstone area has been completely simulated the redstone entities can react - while( !m_RefreshPistons.empty() ) - { - Vector3i pos = m_RefreshPistons.back(); - m_RefreshPistons.pop_back(); - - char Block = m_World->GetBlock( pos ); - switch( Block ) - { - case E_BLOCK_PISTON: - case E_BLOCK_STICKY_PISTON: - if( IsPowered( pos ) ) - { - cPiston Piston( m_World ); - Piston.ExtendPiston( pos.x, pos.y, pos.z ); - } - else - { - cPiston Piston( m_World ); - Piston.RetractPiston( pos.x, pos.y, pos.z ); - } - break; - default: - break; - }; - } -} - - - - - -bool cRedstoneSimulator::PowerBlock( const Vector3i & a_BlockPos, const Vector3i & a_FromBlock, char a_Power ) -{ - char Block = m_World->GetBlock( a_BlockPos ); - switch( Block ) - { - case E_BLOCK_REDSTONE_WIRE: - { - if( m_World->GetBlockMeta( a_BlockPos ) < a_Power ) - { - m_World->SetBlockMeta( a_BlockPos, a_Power ); - return true; - } - } - break; - case E_BLOCK_PISTON: - case E_BLOCK_STICKY_PISTON: - { - m_RefreshPistons.push_back( a_BlockPos ); - } - break; - case E_BLOCK_REDSTONE_REPEATER_OFF: - case E_BLOCK_REDSTONE_REPEATER_ON: - { - if( cRedstone::IsRepeaterPointingAway( a_BlockPos, m_World->GetBlockMeta( a_BlockPos ), a_FromBlock ) ) - { - SetRepeater( a_BlockPos, 10, true ); - } - } - break; - default: - { - if( Block != E_BLOCK_AIR && Block != E_BLOCK_REDSTONE_TORCH_ON && Block != E_BLOCK_REDSTONE_TORCH_OFF ) - { - if( IsPowered( a_BlockPos, true ) ) - { - m_RefreshTorchesAround.push_back( a_BlockPos ); - } - } - } - break; - }; - - return false; -} - - - - - -int cRedstoneSimulator::UnPowerBlock( const Vector3i & a_BlockPos, const Vector3i & a_FromBlock ) -{ - char Block = m_World->GetBlock( a_BlockPos ); - switch( Block ) - { - case E_BLOCK_REDSTONE_WIRE: - { - if( m_World->GetBlockMeta( a_BlockPos ) > 0 ) - { - m_World->SetBlockMeta( a_BlockPos, 0 ); - return 1; - } - } - break; - case E_BLOCK_PISTON: - case E_BLOCK_STICKY_PISTON: - { - m_RefreshPistons.push_back( a_BlockPos ); - } - break; - case E_BLOCK_REDSTONE_TORCH_ON: - { - return 2; - } - break; - case E_BLOCK_REDSTONE_REPEATER_ON: - { - if( cRedstone::IsRepeaterPointingTo( a_BlockPos, m_World->GetBlockMeta( a_BlockPos ), a_FromBlock ) - || cRedstone::IsRepeaterPointingTo( a_BlockPos, m_World->GetBlockMeta( a_BlockPos ), a_FromBlock - Vector3i(0, 1, 0) ) ) // Check if repeater is next/below wire - { - return 2; - } - else if( cRedstone::IsRepeaterPointingAway( a_BlockPos, m_World->GetBlockMeta( a_BlockPos ), a_FromBlock ) ) - { - SetRepeater( a_BlockPos, 10, false ); - } - } - case E_BLOCK_REDSTONE_REPEATER_OFF: - if( cRedstone::IsRepeaterPointingAway( a_BlockPos, m_World->GetBlockMeta( a_BlockPos ), a_FromBlock ) ) - { - SetRepeater( a_BlockPos, 10, false ); - } - break; - default: - if( Block != E_BLOCK_AIR && Block != E_BLOCK_REDSTONE_TORCH_ON && Block != E_BLOCK_REDSTONE_TORCH_OFF ) - { - if( !IsPowered( a_BlockPos, true ) ) - { - m_RefreshTorchesAround.push_back( a_BlockPos ); - } - } - break; - }; - - return 0; -} - - - - - -// Removes current from all powered redstone wires until it reaches an energy source. -// Also returns all energy sources it encountered -cRedstoneSimulator::BlockList cRedstoneSimulator::RemoveCurrent( const Vector3i & a_BlockPos ) -{ - - - std::deque< Vector3i > SpreadStack; - std::deque< Vector3i > FoundSources; - - Vector3i Surroundings[] = { - Vector3i( 1, 0, 0 ), - Vector3i( 1, 1, 0 ), - Vector3i( 1,-1, 0 ), - Vector3i(-1, 0, 0 ), - Vector3i(-1, 1, 0 ), - Vector3i(-1,-1, 0 ), - Vector3i( 0, 0, 1 ), - Vector3i( 0, 1, 1 ), - Vector3i( 0,-1, 1 ), - Vector3i( 0, 0,-1 ), - Vector3i( 0, 1,-1 ), - Vector3i( 0,-1,-1 ), - Vector3i( 0,-1, 0 ), - }; - - char Block = m_World->GetBlock( a_BlockPos ); - if( Block == E_BLOCK_REDSTONE_REPEATER_ON || Block == E_BLOCK_REDSTONE_REPEATER_OFF ) - { - static Vector3i Surroundings [] = { // Repeaters only spread right in front and 1 block up - Vector3i( 0, 0, 0), - Vector3i( 0, 1, 0), - }; - Vector3i Direction = cRedstone::GetRepeaterDirection( m_World->GetBlockMeta( a_BlockPos ) ); - for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) - { - Vector3i pos = a_BlockPos + Direction + Surroundings[i]; - int RetVal = UnPowerBlock( pos, a_BlockPos ); - if( RetVal == 1 ) - { - SpreadStack.push_back( pos ); // Changed, so add to stack - } - else if( RetVal == 2 ) - { - FoundSources.push_back( pos ); - } - } - } - else if( Block == E_BLOCK_REDSTONE_TORCH_OFF || Block == E_BLOCK_REDSTONE_TORCH_ON ) - { - static Vector3i Surroundings [] = { // Torches only spread on the same level - Vector3i(-1, 0, 0), - Vector3i( 1, 0, 0), - Vector3i( 0, 0,-1), - Vector3i( 0, 0, 1), - }; - - for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) - { - Vector3i pos = Vector3i( a_BlockPos ) + Surroundings[i]; - int RetVal = UnPowerBlock( pos, a_BlockPos ); - if( RetVal == 1 ) - { - SpreadStack.push_back( pos ); // Changed, so add to stack - } - else if( RetVal == 2 ) - { - FoundSources.push_back( pos ); - } - } - } - else - { - SpreadStack.push_back( a_BlockPos ); - } - - - while( !SpreadStack.empty() ) - { - Vector3i pos = SpreadStack.back(); - SpreadStack.pop_back(); - - for( unsigned int i = 0; i < ARRAYCOUNT( Surroundings ); ++i ) - { - Vector3i OtherPos = pos + Surroundings[i]; - int RetVal = UnPowerBlock( OtherPos, pos ); - if( RetVal == 1 ) - { - SpreadStack.push_back( OtherPos ); // Changed, so add to stack - } - else if( RetVal == 2 ) - { - FoundSources.push_back( OtherPos ); - } - } - } - - return FoundSources; -} - - - - - -bool cRedstoneSimulator::IsPowering( const Vector3i & a_PowerPos, const Vector3i & a_BlockPos, eRedstoneDirection a_WireDirection, bool a_bOnlyByWire ) -{ - char PowerBlock = m_World->GetBlock( a_PowerPos ); - if( !a_bOnlyByWire && PowerBlock == E_BLOCK_REDSTONE_TORCH_ON ) return true; - if( PowerBlock == E_BLOCK_REDSTONE_REPEATER_ON ) // A repeater pointing towards block is regarded as wire - { - if( cRedstone::IsRepeaterPointingTo( a_PowerPos, m_World->GetBlockMeta( a_PowerPos ), a_BlockPos ) ) - { - return true; - } - } - if( PowerBlock == E_BLOCK_REDSTONE_WIRE ) - { - if( m_World->GetBlockMeta( a_PowerPos ) > 0 ) - { - if( GetDirection( a_PowerPos ) == a_WireDirection ) - { - return true; - } - } - } - - return false; -} - - - - - -bool cRedstoneSimulator::IsPowered( const Vector3i & a_BlockPos, bool a_bOnlyByWire /* = false */ ) -{ - char Block = m_World->GetBlock( a_BlockPos ); - if( Block == E_BLOCK_REDSTONE_REPEATER_OFF || Block == E_BLOCK_REDSTONE_REPEATER_ON ) - { - Vector3i Behind = a_BlockPos - cRedstone::GetRepeaterDirection( m_World->GetBlockMeta( a_BlockPos ) ); - char BehindBlock = m_World->GetBlock( Behind ); - if( BehindBlock == E_BLOCK_REDSTONE_TORCH_ON ) - { - return true; - } - if( BehindBlock == E_BLOCK_REDSTONE_WIRE ) - { - if( m_World->GetBlockMeta( Behind ) > 0 ) - { - return true; - } - } - if( BehindBlock == E_BLOCK_REDSTONE_REPEATER_ON ) - { - if( cRedstone::IsRepeaterPointingTo( Behind, m_World->GetBlockMeta( Behind ), a_BlockPos ) ) - { - return true; - } - } - return false; - } - - if( IsPowering( Vector3i( a_BlockPos.x-1, a_BlockPos.y, a_BlockPos.z ), a_BlockPos, REDSTONE_X_POS, a_bOnlyByWire ) ) - return true; - if( IsPowering( Vector3i( a_BlockPos.x+1, a_BlockPos.y, a_BlockPos.z ), a_BlockPos, REDSTONE_X_NEG, a_bOnlyByWire ) ) - return true; - if( IsPowering( Vector3i( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z-1 ), a_BlockPos, REDSTONE_Z_POS, a_bOnlyByWire ) ) - return true; - if( IsPowering( Vector3i( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z+1 ), a_BlockPos, REDSTONE_Z_NEG, a_bOnlyByWire ) ) - return true; - - // Only wires can power the bottom block - char PosY = m_World->GetBlock( a_BlockPos.x, a_BlockPos.y+1, a_BlockPos.z ); - if( PosY == E_BLOCK_REDSTONE_WIRE ) - { - if( m_World->GetBlockMeta( a_BlockPos.x, a_BlockPos.y+1, a_BlockPos.z ) > 0 ) - { - return true; - } - } - - return false; -} - - - - -// Believe me, it works!! TODO: Add repeaters and low/high wires -cRedstoneSimulator::eRedstoneDirection cRedstoneSimulator::GetDirection( int a_X, int a_Y, int a_Z ) -{ - int Dir = REDSTONE_NONE; - - char NegX = m_World->GetBlock( a_X-1, a_Y, a_Z ); - if( NegX == E_BLOCK_REDSTONE_WIRE || NegX == E_BLOCK_REDSTONE_TORCH_ON ) - { - Dir |= (REDSTONE_X_POS); - } - char PosX = m_World->GetBlock( a_X+1, a_Y, a_Z ); - if( PosX == E_BLOCK_REDSTONE_WIRE || PosX == E_BLOCK_REDSTONE_TORCH_ON ) - { - Dir |= (REDSTONE_X_NEG); - } - char NegZ = m_World->GetBlock( a_X, a_Y, a_Z-1 ); - if( NegZ == E_BLOCK_REDSTONE_WIRE || NegZ == E_BLOCK_REDSTONE_TORCH_ON ) - { - if( (Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG ) ) //corner - { - Dir ^= REDSTONE_X_POS; - Dir |= REDSTONE_X_NEG; - } - if( (Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS ) ) //corner - { - Dir ^= REDSTONE_X_NEG; - Dir |= REDSTONE_X_POS; - } - Dir |= REDSTONE_Z_POS; - } - char PosZ = m_World->GetBlock( a_X, a_Y, a_Z+1 ); - if( PosZ == E_BLOCK_REDSTONE_WIRE || PosZ == E_BLOCK_REDSTONE_TORCH_ON ) - { - if( (Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG ) ) //corner - { - Dir ^= REDSTONE_X_POS; - Dir |= REDSTONE_X_NEG; - } - if( (Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS ) ) //corner - { - Dir ^= REDSTONE_X_NEG; - Dir |= REDSTONE_X_POS; - } - Dir |= REDSTONE_Z_NEG; - } - - return (eRedstoneDirection)Dir; -} \ No newline at end of file diff --git a/source/cRedstoneSimulator.h b/source/cRedstoneSimulator.h deleted file mode 100644 index 6ac1eef5d..000000000 --- a/source/cRedstoneSimulator.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once - -#include "cSimulator.h" -#include "Vector3i.h" - -class cRedstoneSimulator : public cSimulator -{ - typedef cSimulator super; -public: - cRedstoneSimulator( cWorld* a_World ); - ~cRedstoneSimulator(); - - virtual void Simulate( float a_Dt ); - virtual bool IsAllowedBlock( char a_BlockID ) { return true; } - - virtual void WakeUp( int a_X, int a_Y, int a_Z ); - - enum eRedstoneDirection - { - REDSTONE_NONE = 0, - REDSTONE_X_POS = 0x1, - REDSTONE_X_NEG = 0x2, - REDSTONE_Z_POS = 0x4, - REDSTONE_Z_NEG = 0x8, - }; - eRedstoneDirection GetDirection( int a_X, int a_Y, int a_Z ); - eRedstoneDirection GetDirection( const Vector3i & a_Pos ) { return GetDirection( a_Pos.x, a_Pos.y, a_Pos.z ); } -private: - struct sRepeaterChange - { - Vector3i Position; - int Ticks; - bool bPowerOn; - bool bPowerOffNextTime; - }; - - typedef std::deque BlockList; - - typedef std::deque< sRepeaterChange > RepeaterList; - RepeaterList m_SetRepeaters; - void SetRepeater( const Vector3i & a_Position, int a_Ticks, bool a_bPowerOn ) - { - for( RepeaterList::iterator itr = m_SetRepeaters.begin(); itr != m_SetRepeaters.end(); ++itr ) - { - sRepeaterChange & Change = *itr; - if( Change.Position.Equals( a_Position ) ) - { - if( Change.bPowerOn && a_bPowerOn == false ) - { - Change.bPowerOffNextTime = true; - } - if( a_bPowerOn == true ) - { - Change.bPowerOffNextTime = false; - } - Change.bPowerOn |= a_bPowerOn; - return; - } - } - - sRepeaterChange RC; - RC.Position = a_Position; - RC.Ticks = a_Ticks; - RC.bPowerOn = a_bPowerOn; - RC.bPowerOffNextTime = false; - m_SetRepeaters.push_back( RC ); - } - - virtual void AddBlock(int a_X, int a_Y, int a_Z) {} - - void HandleChange( const Vector3i & a_BlockPos ); - BlockList RemoveCurrent( const Vector3i & a_BlockPos ); - - bool PowerBlock( const Vector3i & a_BlockPos, const Vector3i & a_FromBlock, char a_Power ); - int UnPowerBlock( const Vector3i & a_BlockPos, const Vector3i & a_FromBlock ); - - bool IsPowered( const Vector3i & a_BlockPos, bool a_bOnlyByWire = false ); - bool IsPowering( const Vector3i & a_PowerPos, const Vector3i & a_BlockPos, eRedstoneDirection a_WireDirection, bool a_bOnlyByWire ); - - BlockList m_Blocks; - BlockList m_BlocksBuffer; - - BlockList m_RefreshPistons; - - BlockList m_RefreshTorchesAround; - - void RefreshTorchesAround( const Vector3i & a_BlockPos ); - - cCriticalSection m_CS; -}; \ No newline at end of file diff --git a/source/cReferenceManager.cpp b/source/cReferenceManager.cpp deleted file mode 100644 index b39d71858..000000000 --- a/source/cReferenceManager.cpp +++ /dev/null @@ -1,43 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cReferenceManager.h" -#include "cEntity.h" - - - - - -cReferenceManager::cReferenceManager( ENUM_REFERENCE_MANAGER_TYPE a_Type ) - : m_Type( a_Type ) -{ -} - -cReferenceManager::~cReferenceManager() -{ - if( m_Type == RFMNGR_REFERENCERS ) - { - for( std::list< cEntity** >::iterator itr = m_References.begin(); itr != m_References.end(); ++itr ) - { - *(*itr) = 0; // Set referenced pointer to 0 - } - } - else - { - for( std::list< cEntity** >::iterator itr = m_References.begin(); itr != m_References.end(); ++itr ) - { - cEntity* Ptr = (*(*itr)); - if( Ptr ) Ptr->Dereference( *(*itr) ); - } - } -} - -void cReferenceManager::AddReference( cEntity*& a_EntityPtr ) -{ - m_References.push_back( &a_EntityPtr ); -} - -void cReferenceManager::Dereference( cEntity*& a_EntityPtr ) -{ - m_References.remove( &a_EntityPtr ); -} \ No newline at end of file diff --git a/source/cReferenceManager.h b/source/cReferenceManager.h deleted file mode 100644 index bcd451f72..000000000 --- a/source/cReferenceManager.h +++ /dev/null @@ -1,34 +0,0 @@ - -#pragma once - - - - - -class cEntity; - - - - - -class cReferenceManager -{ -public: - enum ENUM_REFERENCE_MANAGER_TYPE - { - RFMNGR_REFERENCERS, - RFMNGR_REFERENCES, - }; - cReferenceManager( ENUM_REFERENCE_MANAGER_TYPE a_Type ); - ~cReferenceManager(); - - void AddReference( cEntity*& a_EntityPtr ); - void Dereference( cEntity*& a_EntityPtr ); -private: - ENUM_REFERENCE_MANAGER_TYPE m_Type; - std::list< cEntity** > m_References; -}; - - - - diff --git a/source/cRoot.cpp b/source/cRoot.cpp deleted file mode 100644 index 6cd696da8..000000000 --- a/source/cRoot.cpp +++ /dev/null @@ -1,527 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cRoot.h" -#include "cServer.h" -#include "cWorld.h" -#include "cWebAdmin.h" -#include "cFurnaceRecipe.h" -#include "cGroupManager.h" -#include "CraftingRecipes.h" -#include "cPluginManager.h" -#include "cMonsterConfig.h" -#include "cFileFormatUpdater.h" -#include "cRedstone.h" -#include "cPlayer.h" -#include "blocks/Block.h" -#include "items/Item.h" -#include "cChunk.h" - -#ifdef USE_SQUIRREL - #include "squirrelbindings/SquirrelFunctions.h" - #include "squirrelbindings/SquirrelBindings.h" -#endif - -#include "../iniFile/iniFile.h" - -#include - - - - - -cRoot* cRoot::s_Root = 0; - - - - - -cRoot::cRoot() - : m_Server( NULL ) - , m_MonsterConfig( NULL ) - , m_GroupManager( NULL ) - , m_CraftingRecipes(NULL) - , m_FurnaceRecipe( NULL ) - , m_WebAdmin( NULL ) - , m_PluginManager( NULL ) - , m_Log( NULL ) - , m_bStop( false ) - , m_bRestart( false ) - , m_InputThread( NULL ) - , m_pDefaultWorld( NULL ) -{ - s_Root = this; -} - - - - - -cRoot::~cRoot() -{ - s_Root = 0; -} - - - - - -void cRoot::InputThread(void * a_Params) -{ - cRoot & self = *(cRoot*)a_Params; - - while (!(self.m_bStop || self.m_bRestart)) - { - std::string Command; - std::getline(std::cin, Command); - self.ServerCommand(Command); - } -} - - - - - -void cRoot::Start() -{ - delete m_Log; - m_Log = new cMCLogger(); - - m_bStop = false; - while (!m_bStop) - { - m_bRestart = false; - - LoadGlobalSettings(); - - cFileFormatUpdater::UpdateFileFormat(); - - LOG("Creating new server instance..."); - m_Server = new cServer(); - - LOG("Starting server..."); - cIniFile IniFile("settings.ini"); - IniFile.ReadFile(); - int Port = IniFile.GetValueSetI("Server", "Port", 25565 ); - if(!m_Server->InitServer( Port )) - { - LOG("Failed to start server, shutting down."); - return; - } - IniFile.WriteFile(); - - cIniFile WebIniFile("webadmin.ini"); - if( WebIniFile.ReadFile() ) - { - if( WebIniFile.GetValueB("WebAdmin", "Enabled", false ) == true ) - { - LOG("Creating WebAdmin..."); - m_WebAdmin = new cWebAdmin(8080); - } - } - - LOG("Loading settings..."); - m_GroupManager = new cGroupManager(); - m_CraftingRecipes = new cCraftingRecipes; - m_FurnaceRecipe = new cFurnaceRecipe(); - - LOG("Loading worlds..."); - LoadWorlds(); - - LOG("Loading plugin manager..."); - m_PluginManager = new cPluginManager(); // This should be last - m_PluginManager->ReloadPluginsNow(); - - LOG("Loading MonsterConfig..."); - m_MonsterConfig = new cMonsterConfig; - - // This sets stuff in motion - LOG("Starting Authenticator..."); - m_Authenticator.Start(); - - LOG("Starting worlds..."); - StartWorlds(); - - LOG("Starting server..."); - m_Server->StartListenThread(); - //cHeartBeat* HeartBeat = new cHeartBeat(); - -#if !defined(ANDROID_NDK) - LOG("Starting InputThread..."); - m_InputThread = new cThread( InputThread, this, "cRoot::InputThread" ); - m_InputThread->Start( false ); //we should NOT wait? Otherwise we canīt stop the server from other threads than the input thread -#endif - - LOG("Initialization done, server running now."); - while( !m_bStop && !m_bRestart ) // These are modified by external threads - { - cSleep::MilliSleep( 1000 ); - } - -#if !defined(ANDROID_NDK) - delete m_InputThread; m_InputThread = 0; -#endif - - // Deallocate stuffs - LOG("Shutting down server..."); - m_Server->Shutdown(); // This waits for threads to stop and d/c clients - LOG("Stopping world threads..."); - StopWorlds(); - LOG("Stopping authenticator..."); - m_Authenticator.Stop(); - LOG("Stopping plugin manager..."); - delete m_PluginManager; m_PluginManager = 0; // This should be first - - - #ifdef USE_SQUIRREL - CloseSquirrelVM(); - #endif - LOG("Freeing MonsterConfig..."); - delete m_MonsterConfig; m_MonsterConfig = 0; - LOG("Stopping WebAdmin..."); - delete m_WebAdmin; m_WebAdmin = 0; - LOG("Unloading recipes..."); - delete m_FurnaceRecipe; m_FurnaceRecipe = NULL; - delete m_CraftingRecipes; m_CraftingRecipes = NULL; - LOG("Forgetting groups..."); - delete m_GroupManager; m_GroupManager = 0; - LOG("Unloading worlds..."); - UnloadWorlds(); - - cItemHandler::Deinit(); - cBlockHandler::Deinit(); - - LOG("Destroying server..."); - //delete HeartBeat; HeartBeat = 0; - delete m_Server; m_Server = 0; - LOG("Shutdown done."); - } - - delete m_Log; m_Log = 0; -} - - - - - -void cRoot::LoadGlobalSettings() -{ - cIniFile IniFile("settings.ini"); - if( IniFile.ReadFile() ) - { - cRedstone::s_UseRedstone = IniFile.GetValueB("Redstone", "SimulateRedstone", true ); - } -} - - - - - -void cRoot::LoadWorlds(void) -{ - cIniFile IniFile("settings.ini"); IniFile.ReadFile(); - - // First get the default world - AString DefaultWorldName = IniFile.GetValueSet("Worlds", "DefaultWorld", "world"); - m_pDefaultWorld = new cWorld( DefaultWorldName.c_str() ); - m_WorldsByName[ DefaultWorldName ] = m_pDefaultWorld; - - // Then load the other worlds - unsigned int KeyNum = IniFile.FindKey("Worlds"); - unsigned int NumWorlds = IniFile.GetNumValues( KeyNum ); - if (NumWorlds <= 0) - { - return; - } - - for (unsigned int i = 0; i < NumWorlds; i++) - { - AString ValueName = IniFile.GetValueName(KeyNum, i ); - if (ValueName.compare("World") != 0) - { - continue; - } - AString WorldName = IniFile.GetValue(KeyNum, i ); - if (WorldName.empty()) - { - continue; - } - cWorld* NewWorld = new cWorld( WorldName.c_str() ); - m_WorldsByName[ WorldName ] = NewWorld; - } // for i - Worlds -} - - - - - -void cRoot::StartWorlds(void) -{ - for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) - { - itr->second->InitializeSpawn(); - } -} - - - - - -void cRoot::StopWorlds(void) -{ - for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) - { - itr->second->StopThreads(); - } -} - - - - - -void cRoot::UnloadWorlds(void) -{ - m_pDefaultWorld = NULL; - for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) - { - delete itr->second; - } - m_WorldsByName.clear(); -} - - - - - -cWorld* cRoot::GetDefaultWorld() -{ - return m_pDefaultWorld; -} - - - - - -cWorld* cRoot::GetWorld( const AString & a_WorldName ) -{ - WorldMap::iterator itr = m_WorldsByName.find( a_WorldName ); - if( itr != m_WorldsByName.end() ) - return itr->second; - return 0; -} - - - - - -bool cRoot::ForEachWorld(cWorldListCallback & a_Callback) -{ - for (WorldMap::iterator itr = m_WorldsByName.begin(), itr2 = itr; itr != m_WorldsByName.end(); itr = itr2) - { - ++itr2; - if (a_Callback.Item(itr->second)) - { - return false; - } - } - return true; -} - - - - - -void cRoot::TickWorlds( float a_Dt ) -{ - for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) - { - itr->second->Tick( a_Dt ); - } -} - - - - - -void cRoot::ServerCommand(const AString & a_Cmd) -{ - LOG("Server console command: \"%s\"", a_Cmd.c_str()); - m_Server->ServerCommand(a_Cmd); - if (a_Cmd == "stop") - { - m_bStop = true; - } - else if (a_Cmd == "restart") - { - m_bRestart = true; - } -} - - - - - -void cRoot::KickUser(int a_ClientID, const AString & a_Reason) -{ - m_Server->KickUser(a_ClientID, a_Reason); -} - - - - - -void cRoot::AuthenticateUser(int a_ClientID) -{ - m_Server->AuthenticateUser(a_ClientID); -} - - - - - -int cRoot::GetTotalChunkCount(void) -{ - int res = 0; - for ( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) - { - res += itr->second->GetNumChunks(); - } - return res; -} - - - - - -void cRoot::SaveAllChunks(void) -{ - for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) - { - itr->second->SaveAllChunks(); - } -} - - - - - -bool cRoot::ForEachPlayer(cPlayerListCallback & a_Callback) -{ - for (WorldMap::iterator itr = m_WorldsByName.begin(), itr2 = itr; itr != m_WorldsByName.end(); itr = itr2) - { - ++itr2; - if (!itr->second->ForEachPlayer(a_Callback)) - { - return false; - } - } - return true; -} - - - - - -bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback) -{ - class cCallback : public cPlayerListCallback - { - unsigned int BestRating; - unsigned int NameLength; - const AString PlayerName; - - cPlayerListCallback & m_Callback; - virtual bool Item (cPlayer * a_pPlayer) - { - unsigned int Rating = RateCompareString (PlayerName, a_pPlayer->GetName()); - if (Rating > 0 && Rating >= BestRating) - { - BestMatch = a_pPlayer; - if( Rating > BestRating ) NumMatches = 0; - BestRating = Rating; - ++NumMatches; - } - if (Rating == NameLength) // Perfect match - { - return false; - } - return true; - } - - public: - cCallback (const AString & a_PlayerName, cPlayerListCallback & a_Callback) - : m_Callback( a_Callback ) - , BestMatch( NULL ) - , BestRating( 0 ) - , NumMatches( 0 ) - , NameLength( a_PlayerName.length() ) - , PlayerName( a_PlayerName ) - {} - - cPlayer * BestMatch; - unsigned int NumMatches; - } Callback (a_PlayerName, a_Callback); - ForEachPlayer( Callback ); - - if (Callback.NumMatches == 1) - { - return a_Callback.Item (Callback.BestMatch); - } - return false; -} - - - - - -void cRoot::LogChunkStats(void) -{ - int SumNumValid = 0; - int SumNumDirty = 0; - int SumNumInLighting = 0; - int SumNumInGenerator = 0; - int SumMem = 0; - for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr) - { - cWorld * World = itr->second; - int NumInGenerator = World->GetGeneratorQueueLength(); - int NumInSaveQueue = World->GetStorageSaveQueueLength(); - int NumInLoadQueue = World->GetStorageLoadQueueLength(); - int NumValid = 0; - int NumDirty = 0; - int NumInLighting = 0; - World->GetChunkStats(NumValid, NumDirty, NumInLighting); - LOG("World %s:", World->GetName().c_str()); - LOG(" Num loaded chunks: %d", NumValid); - LOG(" Num dirty chunks: %d", NumDirty); - LOG(" Num chunks in lighting queue: %d", NumInLighting); - LOG(" Num chunks in generator queue: %d", NumInGenerator); - LOG(" Num chunks in storage load queue: %d", NumInLoadQueue); - LOG(" Num chunks in storage save queue: %d", NumInSaveQueue); - int Mem = NumValid * sizeof(cChunk); - LOG(" Memory used by chunks: %d KiB (%d MiB)", (Mem + 1023) / 1024, (Mem + 1024 * 1024 - 1) / (1024 * 1024)); - LOG(" Per-chunk memory size breakdown:"); - LOG(" block types: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockTypes), (sizeof(cChunkDef::BlockTypes) + 1023) / 1024); - LOG(" block metadata: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockNibbles), (sizeof(cChunkDef::BlockNibbles) + 1023) / 1024); - LOG(" block lighting: %6d bytes (%3d KiB)", 2 * sizeof(cChunkDef::BlockNibbles), (2 * sizeof(cChunkDef::BlockNibbles) + 1023) / 1024); - LOG(" heightmap: %6d bytes (%3d KiB)", sizeof(cChunkDef::HeightMap), (sizeof(cChunkDef::HeightMap) + 1023) / 1024); - LOG(" biomemap: %6d bytes (%3d KiB)", sizeof(cChunkDef::BiomeMap), (sizeof(cChunkDef::BiomeMap) + 1023) / 1024); - int Rest = sizeof(cChunk) - sizeof(cChunkDef::BlockTypes) - 3 * sizeof(cChunkDef::BlockNibbles) - sizeof(cChunkDef::HeightMap) - sizeof(cChunkDef::BiomeMap); - LOG(" other: %6d bytes (%3d KiB)", Rest, (Rest + 1023) / 1024); - SumNumValid += NumValid; - SumNumDirty += NumDirty; - SumNumInLighting += NumInLighting; - SumNumInGenerator += NumInGenerator; - SumMem += Mem; - } - LOG("Totals:"); - LOG(" Num loaded chunks: %d", SumNumValid); - LOG(" Num dirty chunks: %d", SumNumDirty); - LOG(" Num chunks in lighting queue: %d", SumNumInLighting); - LOG(" Num chunks in generator queue: %d", SumNumInGenerator); - LOG(" Memory used by chunks: %d KiB (%d MiB)", (SumMem + 1023) / 1024, (SumMem + 1024 * 1024 - 1) / (1024 * 1024)); -} - - - - diff --git a/source/cRoot.h b/source/cRoot.h deleted file mode 100644 index e25a9805b..000000000 --- a/source/cRoot.h +++ /dev/null @@ -1,120 +0,0 @@ - -#pragma once - - - - -#include "cAuthenticator.h" - - - - - -class cThread; -class cMonsterConfig; -class cGroupManager; -class cCraftingRecipes; -class cFurnaceRecipe; -class cWebAdmin; -class cPluginManager; -class cServer; -class cWorld; -class cPlayer; -typedef cItemCallback cPlayerListCallback; -typedef cItemCallback cWorldListCallback; - - - - - -class cRoot //tolua_export -{ //tolua_export -public: - static cRoot* Get() { return s_Root; } //tolua_export - - cRoot(void); - ~cRoot(); - - void Start(void); - - cServer * GetServer(void) { return m_Server; } //tolua_export - cWorld * GetDefaultWorld(void); //tolua_export - cWorld * GetWorld(const AString & a_WorldName); //tolua_export - - /// Calls the callback for each world; returns true if the callback didn't abort (return true) - bool ForEachWorld(cWorldListCallback & a_Callback); // >> Exported in ManualBindings << - - /// Logs chunkstats for each world and totals - void LogChunkStats(void); - - cMonsterConfig * GetMonsterConfig() { return m_MonsterConfig; } - - cGroupManager * GetGroupManager (void) { return m_GroupManager; } // tolua_export - cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export - cFurnaceRecipe * GetFurnaceRecipe (void) { return m_FurnaceRecipe; } // tolua_export - cWebAdmin * GetWebAdmin (void) { return m_WebAdmin; } // tolua_export - cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export - cAuthenticator & GetAuthenticator (void) { return m_Authenticator; } - - void ServerCommand(const AString & a_Cmd); //tolua_export - - void KickUser(int a_ClientID, const AString & a_Reason); // Kicks the user, no matter in what world they are. Used from cAuthenticator - void AuthenticateUser(int a_ClientID); // Called by cAuthenticator to auth the specified user - - void TickWorlds( float a_Dt ); - - /// Returns the number of chunks loaded - int GetTotalChunkCount(void); // tolua_export - - /// Saves all chunks in all worlds - void SaveAllChunks(void); - - /// Calls the callback for each player in all worlds - bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << - - /// Finds a player from a partial or complete player name and calls the callback - case-insensitive - bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << - -private: - void LoadGlobalSettings(); - - /// Loads the worlds from settings.ini, creates the worldmap - void LoadWorlds(void); - - /// Starts each world's life - void StartWorlds(void); - - /// Stops each world's threads, so that it's safe to unload them - void StopWorlds(void); - - /// Unloads all worlds from memory - void UnloadWorlds(void); - - cServer * m_Server; - cMonsterConfig * m_MonsterConfig; - - cGroupManager * m_GroupManager; - cCraftingRecipes * m_CraftingRecipes; - cFurnaceRecipe * m_FurnaceRecipe; - cWebAdmin * m_WebAdmin; - cPluginManager * m_PluginManager; - cAuthenticator m_Authenticator; - - cMCLogger * m_Log; - - bool m_bStop; - bool m_bRestart; - - typedef std::map< AString, cWorld* > WorldMap; - cWorld* m_pDefaultWorld; - WorldMap m_WorldsByName; - - cThread* m_InputThread; - static void InputThread(void* a_Params); - - static cRoot* s_Root; -}; //tolua_export - - - - diff --git a/source/cSandSimulator.cpp b/source/cSandSimulator.cpp deleted file mode 100644 index 6e5c5ecde..000000000 --- a/source/cSandSimulator.cpp +++ /dev/null @@ -1,91 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cSandSimulator.h" -#include "cWorld.h" -#include "Vector3i.h" -#include "BlockID.h" -#include "Defines.h" - - - - - -cSandSimulator::cSandSimulator( cWorld* a_World ) - : cSimulator(a_World) - , m_Blocks(new BlockList) - , m_Buffer(new BlockList) -{ - -} - -cSandSimulator::~cSandSimulator() -{ - delete m_Buffer; - delete m_Blocks; -} - -void cSandSimulator::Simulate( float a_Dt ) -{ - m_Buffer->clear(); - std::swap( m_Blocks, m_Buffer ); - - for( BlockList::iterator itr = m_Buffer->begin(); itr != m_Buffer->end(); ++itr ) - { - Vector3i Pos = *itr; - char BlockID = m_World->GetBlock(Pos.x, Pos.y, Pos.z); - if(!IsAllowedBlock(BlockID)) - continue; - - char BottomBlock = m_World->GetBlock( Pos.x, Pos.y - 1, Pos.z ); - - if( IsPassable(BottomBlock) ) - { - m_World->SetBlock( Pos.x, Pos.y, Pos.z, E_BLOCK_AIR, 0 ); - m_World->SetBlock( Pos.x, Pos.y - 1, Pos.z, BlockID, 0 ); - } - } - -} - - -bool cSandSimulator::IsAllowedBlock( char a_BlockID ) -{ - return a_BlockID == E_BLOCK_SAND - || a_BlockID == E_BLOCK_GRAVEL; -} - -void cSandSimulator::AddBlock(int a_X, int a_Y, int a_Z) -{ - if(!IsAllowedBlock(m_World->GetBlock(a_X, a_Y, a_Z))) //This should save very much time because it doesnīt have to iterate through all blocks - return; - - Vector3i Block(a_X, a_Y, a_Z); - - //check for duplicates - for( BlockList::iterator itr = m_Blocks->begin(); itr != m_Blocks->end(); ++itr ) - { - Vector3i Pos = *itr; - if( Pos.x == a_X && Pos.y == a_Y && Pos.z == a_Z ) - return; - } - - m_Blocks->push_back(Block); - -} - -bool cSandSimulator::IsPassable( char a_BlockID ) -{ - return a_BlockID == E_BLOCK_AIR - || IsBlockWater(a_BlockID) - || IsBlockLava(a_BlockID) - || a_BlockID == E_BLOCK_FIRE; - -} - -void cSandSimulator::WakeUp( int a_X, int a_Y, int a_Z ) -{ - //Nothing else needs to be simulated :D (Bugs not included :s) - AddBlock( a_X, a_Y+1, a_Z ); - AddBlock( a_X, a_Y, a_Z ); -} diff --git a/source/cSandSimulator.h b/source/cSandSimulator.h deleted file mode 100644 index c95d5e476..000000000 --- a/source/cSandSimulator.h +++ /dev/null @@ -1,40 +0,0 @@ - -#pragma once - -#include "cSimulator.h" -#include "cBlockEntity.h" -#include "Vector3i.h" - - - - - -class cWorld; - - - - - -class cSandSimulator : public cSimulator -{ -public: - cSandSimulator( cWorld* a_World ); - ~cSandSimulator(); - - virtual void Simulate( float a_Dt ); - virtual void WakeUp( int a_X, int a_Y, int a_Z ); - - virtual bool IsAllowedBlock( char a_BlockID ); - virtual bool IsPassable( char a_BlockID ); - -protected: - virtual void AddBlock(int a_X, int a_Y, int a_Z); - - typedef std::list BlockList; - BlockList * m_Blocks; - BlockList * m_Buffer; -}; - - - - diff --git a/source/cServer.cpp b/source/cServer.cpp deleted file mode 100644 index e8b58c756..000000000 --- a/source/cServer.cpp +++ /dev/null @@ -1,714 +0,0 @@ - -// ReDucTor is an awesome guy who helped me a lot - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cServer.h" -#include "cClientHandle.h" -#include "OSSupport/Timer.h" -#include "Mobs/Monster.h" -#include "OSSupport/Socket.h" -#include "cRoot.h" -#include "cWorld.h" -#include "ChunkDef.h" -#include "cPluginManager.h" -#include "cGroupManager.h" -#include "cChatColor.h" -#include "cPlayer.h" -#include "cInventory.h" -#include "cItem.h" -#include "cFurnaceRecipe.h" -#include "cTracer.h" -#include "cWebAdmin.h" -#include "Protocol/ProtocolRecognizer.h" - -#include "MersenneTwister.h" - -#include "../iniFile/iniFile.h" -#include "Vector3f.h" - -#include -#include -#include - - - - - -extern "C" { - #include "zlib.h" -} - - - -bool g_bWaterPhysics = false; - -typedef std::list< cClientHandle* > ClientList; - - - - - -struct cServer::sServerState -{ - sServerState() - : pListenThread( 0 ) - , pTickThread( 0 ) - , bStopListenThread( false ) - , bStopTickThread( false ) - {} - cSocket SListenClient; // socket listening for client calls - - cThread* pListenThread; bool bStopListenThread; - cThread* pTickThread; bool bStopTickThread; - - cEvent RestartEvent; - std::string ServerID; -}; - - - - - -cServer * cServer::GetServer() -{ - LOGWARN("WARNING: Using deprecated function cServer::GetServer() use cRoot::Get()->GetServer() instead!"); - return cRoot::Get()->GetServer(); -} - - - - - -void cServer::ServerListenThread( void *a_Args ) -{ - LOG("ServerListenThread"); - cServer* self = (cServer*)a_Args; - sServerState* m_pState = self->m_pState; - while( !m_pState->bStopListenThread ) - { - self->StartListenClient(); - } -} - - - - - -void cServer::ClientDestroying(const cClientHandle * a_Client) -{ - m_SocketThreads.StopReading(a_Client); -} - - - - - -void cServer::NotifyClientWrite(const cClientHandle * a_Client) -{ - m_NotifyWriteThread.NotifyClientWrite(a_Client); -} - - - - - -void cServer::WriteToClient(const cSocket * a_Socket, const AString & a_Data) -{ - m_SocketThreads.Write(a_Socket, a_Data); -} - - - - - -void cServer::QueueClientClose(const cSocket * a_Socket) -{ - m_SocketThreads.QueueClose(a_Socket); -} - - - - - -void cServer::RemoveClient(const cSocket * a_Socket) -{ - m_SocketThreads.RemoveClient(a_Socket); -} - - - - - -bool cServer::InitServer( int a_Port ) -{ - if( m_bIsConnected ) - { - LOGERROR("ERROR: Trying to initialize server while server is already running!"); - return false; - } - - printf("/============================\\\n"); - printf("| Custom Minecraft Server |\n"); - printf("| Created by Kevin Bansberg |\n"); - printf("| A.K.A. FakeTruth |\n"); - printf("| Monsters by Alex Sonek |\n"); - printf("| A.K.A. Duralex |\n"); - printf("| Stuff by Mattes D |\n"); - printf("| A.K.A. _Xoft(o) |\n"); - printf("\\============================/\n"); - printf("More info: WWW.MC-SERVER.ORG\n"); - printf(" WWW.AE-C.NET\n"); - printf(" WWW.RBTHINKTANK.COM\n"); - printf("email: faketruth@gmail.com\n\n"); - - LOG("Starting up server."); - LOGINFO("Compatible clients: %s, protocol versions %s", MCS_CLIENT_VERSIONS, MCS_PROTOCOL_VERSIONS); - - if( cSocket::WSAStartup() != 0 ) // Only does anything on Windows, but whatever - { - LOGERROR("WSAStartup() != 0"); - return false; - } - - m_pState->SListenClient = cSocket::CreateSocket(); - - if( !m_pState->SListenClient.IsValid() ) - { - LOGERROR("m_SListenClient==INVALID_SOCKET (%s)", cSocket::GetErrorString( cSocket::GetLastError() ).c_str() ); - return false; - } - - if( m_pState->SListenClient.SetReuseAddress() == -1 ) - { - LOGERROR("setsockopt == -1"); - return false; - } - - cSocket::SockAddr_In local; - local.Family = cSocket::ADDRESS_FAMILY_INTERNET; - local.Address = cSocket::INTERNET_ADDRESS_ANY; - local.Port = (unsigned short)a_Port; // 25565 - - if( m_pState->SListenClient.Bind( local ) != 0 ) - { - LOGERROR("bind fail (%s)", cSocket::GetErrorString( cSocket::GetLastError() ).c_str() ); - return false; - } - - if( m_pState->SListenClient.Listen( 10 ) != 0) - { - LOGERROR("listen fail (%s)", cSocket::GetErrorString( cSocket::GetLastError() ).c_str() ); - return false; - } - - m_iServerPort = a_Port; - LOG("Port %i has been bound, server is open for connections", m_iServerPort); - m_bIsConnected = true; - - cIniFile IniFile("settings.ini"); - if (IniFile.ReadFile()) - { - g_bWaterPhysics = IniFile.GetValueB("Physics", "Water", false ); - - m_pState->ServerID = "-"; - if (IniFile.GetValueB("Authentication", "Authenticate")) - { - MTRand mtrand1; - unsigned int r1 = (mtrand1.randInt()%1147483647) + 1000000000; - unsigned int r2 = (mtrand1.randInt()%1147483647) + 1000000000; - std::ostringstream sid; - sid << std::hex << r1; - sid << std::hex << r2; - std::string ServerID = sid.str(); - ServerID.resize(16, '0'); - m_pState->ServerID = ServerID; - } - - m_ClientViewDistance = IniFile.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE); - if (m_ClientViewDistance < cClientHandle::MIN_VIEW_DISTANCE) - { - m_ClientViewDistance = cClientHandle::MIN_VIEW_DISTANCE; - LOGINFO("Setting default viewdistance to the minimum of %d", m_ClientViewDistance); - } - if (m_ClientViewDistance > cClientHandle::MAX_VIEW_DISTANCE) - { - m_ClientViewDistance = cClientHandle::MAX_VIEW_DISTANCE; - LOGINFO("Setting default viewdistance to the maximum of %d", m_ClientViewDistance); - } - IniFile.WriteFile(); - } - - m_NotifyWriteThread.Start(this); - - PrepareKeys(); - - return true; -} - - - - - -cServer::cServer() - : m_pState( new sServerState ) - , m_Millisecondsf( 0 ) - , m_Milliseconds( 0 ) - , m_bIsConnected( false ) - , m_iServerPort( 0 ) - , m_bRestarting( false ) -{ -} - - - - - -cServer::~cServer() -{ - // TODO: Shut down the server gracefully - if ( m_pState->SListenClient ) - { - m_pState->SListenClient.CloseSocket(); - } - m_pState->SListenClient = 0; - - m_pState->bStopListenThread = true; - delete m_pState->pListenThread; m_pState->pListenThread = NULL; - m_pState->bStopTickThread = true; - delete m_pState->pTickThread; m_pState->pTickThread = NULL; - - delete m_pState; -} - - - - - -void cServer::PrepareKeys(void) -{ - // TODO: Save and load key for persistence across sessions - // But generating the key takes only a moment, do we even need that? - - LOG("Generating protocol encryption keypair..."); - - time_t CurTime = time(NULL); - CryptoPP::RandomPool rng; - rng.Put((const byte *)&CurTime, sizeof(CurTime)); - m_PrivateKey.GenerateRandomWithKeySize(rng, 1024); - CryptoPP::RSA::PublicKey pk(m_PrivateKey); - m_PublicKey = pk; -} - - - - - -void cServer::BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSClients); - for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) - { - if ((*itr == a_Exclude) || !(*itr)->IsLoggedIn()) - { - continue; - } - (*itr)->SendChat(a_Message); - } -} - - - - - -void cServer::StartListenClient() -{ - cSocket SClient = m_pState->SListenClient.Accept(); - - if (!SClient.IsValid()) - { - return; - } - - const AString & ClientIP = SClient.GetIPString(); - if (ClientIP.empty()) - { - LOGWARN("cServer: A client connected, but didn't present its IP, disconnecting."); - SClient.CloseSocket(); - return; - } - - LOG("Client \"%s\" connected!", ClientIP.c_str()); - - cClientHandle *NewHandle = new cClientHandle(SClient, m_ClientViewDistance); - if (!m_SocketThreads.AddClient(&(NewHandle->GetSocket()), NewHandle)) - { - // For some reason SocketThreads have rejected the handle, clean it up - LOGERROR("Client \"%s\" cannot be handled, server probably unstable", SClient.GetIPString().c_str()); - SClient.CloseSocket(); - delete NewHandle; - return; - } - - cCSLock Lock(m_CSClients); - m_Clients.push_back( NewHandle ); -} - - - - - -bool cServer::Tick(float a_Dt) -{ - //LOG("1. Tick %0.2f", a_Dt); - if( a_Dt > 100.f ) a_Dt = 100.f; // Don't go over 1/10 second - - m_Millisecondsf += a_Dt; - if( m_Millisecondsf > 1.f ) - { - m_Milliseconds += (int)m_Millisecondsf; - m_Millisecondsf = m_Millisecondsf - (int)m_Millisecondsf; - } - - cRoot::Get()->TickWorlds( a_Dt ); // TODO - Maybe give all worlds their own thread? - - cClientHandleList RemoveClients; - { - cCSLock Lock(m_CSClients); - for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();) - { - if ((*itr)->IsDestroyed()) - { - RemoveClients.push_back(*itr); // Remove the client later, when CS is not held, to avoid deadlock ( http://forum.mc-server.org/showthread.php?tid=374 ) - itr = m_Clients.erase(itr); - continue; - } - (*itr)->Tick(a_Dt); - ++itr; - } // for itr - m_Clients[] - } - for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr) - { - delete *itr; - } // for itr - RemoveClients[] - - cRoot::Get()->GetPluginManager()->Tick( a_Dt ); - - if( !m_bRestarting ) - { - return true; - } - else - { - m_bRestarting = false; - m_pState->RestartEvent.Set(); - return false; - } -} - - - - - -void ServerTickThread( void * a_Param ) -{ - LOG("ServerTickThread"); - cServer *CServerObj = (cServer*)a_Param; - - cTimer Timer; - - long long msPerTick = 50; // TODO - Put this in server config file - long long LastTime = Timer.GetNowTime(); - - bool bKeepGoing = true; - while( bKeepGoing ) - { - long long NowTime = Timer.GetNowTime(); - float DeltaTime = (float)(NowTime-LastTime); - bKeepGoing = CServerObj->Tick( DeltaTime ); - long long TickTime = Timer.GetNowTime() - NowTime; - - if( TickTime < msPerTick ) // Stretch tick time until it's at least msPerTick - { - cSleep::MilliSleep( (unsigned int)( msPerTick - TickTime ) ); - } - - LastTime = NowTime; - } - - LOG("TICK THREAD STOPPED"); -} - - - - - -void cServer::StartListenThread() -{ - m_pState->pListenThread = new cThread( ServerListenThread, this, "cServer::ServerListenThread" ); - m_pState->pTickThread = new cThread( ServerTickThread, this, "cServer::ServerTickThread" ); - m_pState->pListenThread->Start( true ); - m_pState->pTickThread->Start( true ); -} - - - - - -bool cServer::Command(cClientHandle & a_Client, const AString & a_Cmd) -{ - return cRoot::Get()->GetPluginManager()->CallHookChat(a_Client.GetPlayer(), a_Cmd); -} - - - - - -void cServer::ServerCommand(const AString & a_Cmd) -{ - AStringVector split = StringSplit(a_Cmd, " "); - if (split.empty()) - { - return; - } - - if (split[0].compare( "help" ) == 0) - { - printf("================== ALL COMMANDS ===================\n"); - printf("help - Shows this message\n"); - printf("save-all - Saves all loaded chunks to disk\n"); - printf("list - Lists all players currently in server\n"); - printf("unload - Unloads all unused chunks\n"); - printf("numchunks - Shows number of chunks currently loaded\n"); - printf("chunkstats - Shows chunks statistics\n"); - printf("say - Sends a chat message to all players\n"); - printf("restart - Kicks all clients, and saves everything\n"); - printf(" and clears memory\n"); - printf("stop - Saves everything and closes server\n"); - printf("===================================================\n"); - return; - } - if ((split[0].compare("stop") == 0) || (split[0].compare("restart") == 0)) - { - return; - } - if (split[0].compare("save-all") == 0) - { - cRoot::Get()->SaveAllChunks(); - return; - } - if (split[0].compare("unload") == 0) - { - LOG("Num loaded chunks before: %i", cRoot::Get()->GetTotalChunkCount() ); - cRoot::Get()->GetDefaultWorld()->UnloadUnusedChunks(); // TODO: Iterate through ALL worlds - LOG("Num loaded chunks after: %i", cRoot::Get()->GetTotalChunkCount() ); - return; - } - if (split[0].compare("list") == 0) - { - class cPlayerLogger : public cPlayerListCallback - { - virtual bool Item(cPlayer * a_Player) override - { - LOG("\t%s @ %s", a_Player->GetName().c_str(), a_Player->GetClientHandle()->GetIPString().c_str()); - return false; - } - } Logger; - cRoot::Get()->ForEachPlayer(Logger); - return; - } - if (split[0].compare("numchunks") == 0) - { - LOG("Num loaded chunks: %i", cRoot::Get()->GetTotalChunkCount() ); - return; - } - if (split[0].compare("chunkstats") == 0) - { - cRoot::Get()->LogChunkStats(); - return; - } - - if (split[0].compare("monsters") == 0) - { - // TODO: cWorld::ListMonsters(); - return; - } - - if (split.size() > 1) - { - if (split[0].compare("say") == 0) - { - AString ToSay = a_Cmd.substr(a_Cmd.find_first_of("say") + 4); - AString Message = cChatColor::Purple + "[SERVER] " + ToSay; - LOG("[SERVER]: %s", ToSay.c_str()); - BroadcastChat(Message); - return; - } - } - printf("Unknown command, type 'help' for all commands.\n"); - // LOG("You didn't enter anything? (%s)", a_Cmd.c_str() ); -} - - - - - -void cServer::SendMessage(const AString & a_Message, cPlayer * a_Player /* = NULL */, bool a_bExclude /* = false */ ) -{ - if ((a_Player != NULL) && !a_bExclude) - { - cClientHandle * Client = a_Player->GetClientHandle(); - if (Client != NULL) - { - Client->SendChat(a_Message); - } - return; - } - - BroadcastChat(a_Message, (a_Player != NULL) ? a_Player->GetClientHandle() : NULL); -} - - - - - -void cServer::Shutdown() -{ - m_bRestarting = true; - m_pState->RestartEvent.Wait(); - - cRoot::Get()->SaveAllChunks(); - - cCSLock Lock(m_CSClients); - for( ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr ) - { - (*itr)->Destroy(); - delete *itr; - } - m_Clients.clear(); -} - - - - - -const AString & cServer::GetServerID(void) const -{ - return m_pState->ServerID; -} - - - - - -void cServer::KickUser(int a_ClientID, const AString & a_Reason) -{ - cCSLock Lock(m_CSClients); - for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) - { - if ((*itr)->GetUniqueID() == a_ClientID) - { - (*itr)->Kick(a_Reason); - } - } // for itr - m_Clients[] -} - - - - - -void cServer::AuthenticateUser(int a_ClientID) -{ - cCSLock Lock(m_CSClients); - for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) - { - if ((*itr)->GetUniqueID() == a_ClientID) - { - (*itr)->Authenticate(); - } - } // for itr - m_Clients[] -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cServer::cClientPacketThread: - -cServer::cNotifyWriteThread::cNotifyWriteThread(void) : - super("ClientPacketThread"), - m_Server(NULL) -{ -} - - - - - -cServer::cNotifyWriteThread::~cNotifyWriteThread() -{ - m_ShouldTerminate = true; - m_Event.Set(); - Wait(); -} - - - - - -bool cServer::cNotifyWriteThread::Start(cServer * a_Server) -{ - m_Server = a_Server; - return super::Start(); -} - - - - - -void cServer::cNotifyWriteThread::Execute(void) -{ - cClientHandleList Clients; - while (!m_ShouldTerminate) - { - cCSLock Lock(m_CS); - while (m_Clients.size() == 0) - { - cCSUnlock Unlock(Lock); - m_Event.Wait(); - if (m_ShouldTerminate) - { - return; - } - } - - // Copy the clients to notify and unlock the CS: - Clients.splice(Clients.begin(), m_Clients); - Lock.Unlock(); - - for (cClientHandleList::iterator itr = Clients.begin(); itr != Clients.end(); ++itr) - { - m_Server->m_SocketThreads.NotifyWrite(*itr); - } // for itr - Clients[] - Clients.clear(); - } // while (!mShouldTerminate) -} - - - - - -void cServer::cNotifyWriteThread::NotifyClientWrite(const cClientHandle * a_Client) -{ - { - cCSLock Lock(m_CS); - m_Clients.remove(const_cast(a_Client)); // Put it there only once - m_Clients.push_back(const_cast(a_Client)); - } - m_Event.Set(); -} - - - - diff --git a/source/cServer.h b/source/cServer.h deleted file mode 100644 index b3599aa1e..000000000 --- a/source/cServer.h +++ /dev/null @@ -1,141 +0,0 @@ - -// cServer.h - -// Interfaces to the cServer object representing the network server - - - - - -#pragma once -#ifndef CSERVER_H_INCLUDED -#define CSERVER_H_INCLUDED - -#include "OSSupport/SocketThreads.h" -#include "CryptoPP/rsa.h" -#include "CryptoPP/randpool.h" - - - - - -class cPlayer; -class cClientHandle; - -typedef std::list cClientHandleList; - - - - - -class cServer //tolua_export -{ //tolua_export -public: //tolua_export - static cServer * GetServer(); //tolua_export - - bool InitServer( int a_Port = 25565 ); - - int GetPort() { return m_iServerPort; } - bool IsConnected(){return m_bIsConnected;} // returns connection status - void StartListenClient(); // Listen to client - - void BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude = NULL); - - bool Tick(float a_Dt); - - void StartListenThread(); - - bool Command(cClientHandle & a_Client, const AString & a_Cmd); - void ServerCommand(const AString & a_Cmd); //tolua_export - void Shutdown(); - - void SendMessage(const AString & a_Message, cPlayer * a_Player = NULL, bool a_bExclude = false ); // tolua_export - - void KickUser(int a_ClientID, const AString & a_Reason); - void AuthenticateUser(int a_ClientID); // Called by cAuthenticator to auth the specified user - - static void ServerListenThread( void* a_Args ); - - const AString & GetServerID(void) const; - - void ClientDestroying(const cClientHandle * a_Client); // Called by cClientHandle::Destroy(); stop m_SocketThreads from calling back into a_Client - - void NotifyClientWrite(const cClientHandle * a_Client); // Notifies m_SocketThreads that client has something to be written - - void WriteToClient(const cSocket * a_Socket, const AString & a_Data); // Queues outgoing data for the socket through m_SocketThreads - - void QueueClientClose(const cSocket * a_Socket); // Queues the socket to close when all its outgoing data is sent - - void RemoveClient(const cSocket * a_Socket); // Removes the socket from m_SocketThreads - - CryptoPP::RSA::PrivateKey & GetPrivateKey(void) { return m_PrivateKey; } - CryptoPP::RSA::PublicKey & GetPublicKey (void) { return m_PublicKey; } - -private: - - friend class cRoot; // so cRoot can create and destroy cServer - - /// When NotifyClientWrite() is called, it is queued for this thread to process (to avoid deadlocks between cSocketThreads, cClientHandle and cChunkMap) - class cNotifyWriteThread : - public cIsThread - { - typedef cIsThread super; - - cEvent m_Event; // Set when m_Clients gets appended - cServer * m_Server; - - cCriticalSection m_CS; - cClientHandleList m_Clients; - - virtual void Execute(void); - - public: - - cNotifyWriteThread(void); - ~cNotifyWriteThread(); - - bool Start(cServer * a_Server); - - void NotifyClientWrite(const cClientHandle * a_Client); - } ; - - struct sServerState; - sServerState* m_pState; - - cNotifyWriteThread m_NotifyWriteThread; - - cCriticalSection m_CSClients; // Locks client list - cClientHandleList m_Clients; // Clients that are connected to the server - - cSocketThreads m_SocketThreads; - - int m_ClientViewDistance; // The default view distance for clients; settable in Settings.ini - - // Time since server was started - float m_Millisecondsf; - unsigned int m_Milliseconds; - - bool m_bIsConnected; // true - connected false - not connected - int m_iServerPort; - - bool m_bRestarting; - - CryptoPP::RSA::PrivateKey m_PrivateKey; - CryptoPP::RSA::PublicKey m_PublicKey; - - cServer(); - ~cServer(); - - /// Loads, or generates, if missing, RSA keys for protocol encryption - void PrepareKeys(void); -}; //tolua_export - - - - - -#endif // CSERVER_H_INCLUDED - - - - diff --git a/source/cSign.h b/source/cSign.h deleted file mode 100644 index 9daa8234c..000000000 --- a/source/cSign.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -class cSign //tolua_export -{ //tolua_export -public: - static char RotationToMetaData( float a_Rotation ) //tolua_export - { //tolua_export - a_Rotation += 180 + (180/16); // So its not aligned with axis - if( a_Rotation > 360.f ) a_Rotation -= 360.f; - - a_Rotation = (a_Rotation/360) * 16; - - return ((char)a_Rotation) % 16; - } //tolua_export - static char DirectionToMetaData( char a_Direction ) //tolua_export - { //tolua_export - switch( a_Direction ) - { - case 0x2: - return 0x2; - case 0x3: - return 0x3; - case 0x4: - return 0x4; - case 0x5: - return 0x5; - default: - break; - }; - return 0x2; - } -}; //tolua_export \ No newline at end of file diff --git a/source/cSignEntity.cpp b/source/cSignEntity.cpp deleted file mode 100644 index 6caff7add..000000000 --- a/source/cSignEntity.cpp +++ /dev/null @@ -1,130 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cSignEntity.h" - -#include "cPlayer.h" -#include "cClientHandle.h" -#include "cWorld.h" -#include "cRoot.h" - -#include - - - - - -cSignEntity::cSignEntity(ENUM_BLOCK_ID a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World) - : cBlockEntity(a_BlockType, a_X, a_Y, a_Z, a_World) -{ -} - - - - - -cSignEntity::~cSignEntity() -{ -} - - - - - -// It don't do anything when 'used' -void cSignEntity::UsedBy( cPlayer * a_Player ) -{ - (void)a_Player; -} - - - - - -void cSignEntity::SetLines( const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 ) -{ - m_Line[0] = a_Line1; - m_Line[1] = a_Line2; - m_Line[2] = a_Line3; - m_Line[3] = a_Line4; -} - - - - - -void cSignEntity::SetLine( int a_Index, const AString & a_Line ) -{ - if( a_Index < 4 && a_Index > -1 ) - { - m_Line[a_Index] = a_Line; - } -} - - - - - -AString cSignEntity::GetLine( int a_Index ) const -{ - if( a_Index < 4 && a_Index > -1 ) - { - return m_Line[a_Index]; - } - return ""; -} - - - - - -void cSignEntity::SendTo(cClientHandle & a_Client) -{ - a_Client.SendUpdateSign(m_PosX, m_PosY, m_PosZ, m_Line[0], m_Line[1], m_Line[2], m_Line[3]); -} - - - - - -#define READ(File, Var) \ - if (File.Read(&Var, sizeof(Var)) != sizeof(Var)) \ - { \ - LOGERROR("ERROR READING cSignEntity %s FROM FILE (line %d)", #Var, __LINE__); \ - return false; \ - } - - - - - - -bool cSignEntity::LoadFromJson( const Json::Value & a_Value ) -{ - m_PosX = a_Value.get("x", 0).asInt(); - m_PosY = a_Value.get("y", 0).asInt(); - m_PosZ = a_Value.get("z", 0).asInt(); - - m_Line[0] = a_Value.get("Line1", "").asString(); - m_Line[1] = a_Value.get("Line2", "").asString(); - m_Line[2] = a_Value.get("Line3", "").asString(); - m_Line[3] = a_Value.get("Line4", "").asString(); - - return true; -} - -void cSignEntity::SaveToJson( Json::Value & a_Value ) -{ - a_Value["x"] = m_PosX; - a_Value["y"] = m_PosY; - a_Value["z"] = m_PosZ; - - a_Value["Line1"] = m_Line[0]; - a_Value["Line2"] = m_Line[1]; - a_Value["Line3"] = m_Line[2]; - a_Value["Line4"] = m_Line[3]; -} - - - - diff --git a/source/cSignEntity.h b/source/cSignEntity.h deleted file mode 100644 index 4066d5070..000000000 --- a/source/cSignEntity.h +++ /dev/null @@ -1,41 +0,0 @@ - -#pragma once - -#include "cBlockEntity.h" - - - - - -namespace Json -{ - class Value; -} - - -class cSignEntity : - public cBlockEntity -{ -public: - cSignEntity(ENUM_BLOCK_ID a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World); - virtual ~cSignEntity(); - - bool LoadFromJson( const Json::Value& a_Value ); - virtual void SaveToJson(Json::Value& a_Value ) override; - - void SetLines( const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 ); - void SetLine( int a_Index, const AString & a_Line ); - - AString GetLine( int a_Index ) const; - - virtual void UsedBy( cPlayer * a_Player ) override; - virtual void SendTo(cClientHandle & a_Client) override; - -private: - - AString m_Line[4]; -}; - - - - diff --git a/source/cSimulator.cpp b/source/cSimulator.cpp deleted file mode 100644 index 377d69eb3..000000000 --- a/source/cSimulator.cpp +++ /dev/null @@ -1,33 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cSimulator.h" -#include "cWorld.h" -#include "Vector3i.h" -#include "BlockID.h" -#include "Defines.h" - - - - - -cSimulator::cSimulator( cWorld* a_World ) - : m_World(a_World) -{ - -} - -cSimulator::~cSimulator() -{ -} - -void cSimulator::WakeUp( int a_X, int a_Y, int a_Z ) -{ - AddBlock( a_X, a_Y, a_Z ); - AddBlock( a_X-1, a_Y, a_Z ); - AddBlock( a_X+1, a_Y, a_Z ); - AddBlock( a_X, a_Y-1, a_Z ); - AddBlock( a_X, a_Y+1, a_Z ); - AddBlock( a_X, a_Y, a_Z-1 ); - AddBlock( a_X, a_Y, a_Z+1 ); -} diff --git a/source/cSimulator.h b/source/cSimulator.h deleted file mode 100644 index 158e74b99..000000000 --- a/source/cSimulator.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -class Vector3i; -class cWorld; -class cSimulator -{ -public: - cSimulator( cWorld* a_World ); - ~cSimulator(); - - virtual void Simulate( float a_Dt ) = 0; - virtual void WakeUp( int a_X, int a_Y, int a_Z ); - - virtual bool IsAllowedBlock( char a_BlockID ) = 0; - -protected: - virtual void AddBlock(int a_X, int a_Y, int a_Z) = 0; - - cWorld * m_World; -}; \ No newline at end of file diff --git a/source/cSimulatorManager.cpp b/source/cSimulatorManager.cpp deleted file mode 100644 index 0d7ef356d..000000000 --- a/source/cSimulatorManager.cpp +++ /dev/null @@ -1,67 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cSimulatorManager.h" - - - - - -cSimulatorManager::cSimulatorManager() -{ - -} - - - - - -cSimulatorManager::~cSimulatorManager() -{ - for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr ) - { - delete *itr; - } // for itr - m_Simulators[] -} - - - - - -void cSimulatorManager::Simulate( float a_Dt ) -{ - m_Ticks++; - for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr ) - { - if(m_Ticks % (*itr)->second == 0) - (*itr)->first->Simulate(a_Dt); - } -} - - - - - -void cSimulatorManager::WakeUp(int a_X, int a_Y, int a_Z) -{ - for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr ) - { - (*itr)->first->WakeUp(a_X, a_Y, a_Z); - } -} - - - - - -void cSimulatorManager::RegisterSimulator(cSimulator *a_Simulator, short a_Rate) -{ - //TODO needs some checking - std::pair *Pair = new std::pair(a_Simulator, a_Rate); - - m_Simulators.push_back(Pair); -} - - - - diff --git a/source/cSimulatorManager.h b/source/cSimulatorManager.h deleted file mode 100644 index ec8804992..000000000 --- a/source/cSimulatorManager.h +++ /dev/null @@ -1,39 +0,0 @@ - -// cSimulatorManager.h - - - - -#pragma once - - - - -#include "cSimulator.h" - - - - - -class cSimulatorManager -{ -public: - cSimulatorManager(); - ~cSimulatorManager(); - - void Simulate( float a_Dt ); - void WakeUp(int a_X, int a_Y, int a_Z); - - void RegisterSimulator(cSimulator * a_Simulator, short a_Rate); // Takes ownership of the simulator object! - -protected: - - typedef std::vector *> cSimulators; - - cSimulators m_Simulators; - long long m_Ticks; -}; - - - - diff --git a/source/cSquirrelCommandBinder.cpp b/source/cSquirrelCommandBinder.cpp deleted file mode 100644 index e2a34dfb6..000000000 --- a/source/cSquirrelCommandBinder.cpp +++ /dev/null @@ -1,98 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cSquirrelCommandBinder.h" -#include "cPlayer.h" -#include "cPlugin.h" -#include "cPlugin_Squirrel.h" -#include "squirrelbindings/SquirrelArray.h" - - -cSquirrelCommandBinder::cSquirrelCommandBinder() -{ -} - -cSquirrelCommandBinder::~cSquirrelCommandBinder() -{ -} - -void cSquirrelCommandBinder::ClearBindings() -{ - m_BoundCommands.clear(); -} - -void cSquirrelCommandBinder::RemoveBindingsForPlugin( cPlugin* a_Plugin ) -{ - for( CommandMap::iterator itr = m_BoundCommands.begin(); itr != m_BoundCommands.end(); ) - { - if( itr->second.Plugin == a_Plugin ) - { - LOGINFO("Unbinding %s ", itr->first.c_str( ) ); - CommandMap::iterator eraseme = itr; - ++itr; - m_BoundCommands.erase( eraseme ); - continue; - } - ++itr; - } -} - -bool cSquirrelCommandBinder::BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, Sqrat::Function a_Callback ) -{ - if( !a_Plugin->CanBindCommands() ) - { - LOGERROR("ERROR: Trying to bind command \"%s\" to a plugin that is not initialized.", a_Command.c_str() ); - return false; - } - if( m_BoundCommands.find( a_Command ) != m_BoundCommands.end() ) - { - LOGERROR("ERROR: Trying to bind command \"%s\" that has already been bound.", a_Command.c_str() ); - return false; - } - LOGINFO("Binding %s (%s)", a_Command.c_str(), a_Permission.c_str() ); - - BoundFunction Callback; - Callback.Callback = a_Callback; - Callback.Plugin = a_Plugin; - Callback.Permission = a_Permission; - - m_BoundCommands[ a_Command ] = Callback; - return true; -} - -bool cSquirrelCommandBinder::HandleCommand( const std::string & a_Command, cPlayer* a_Player ) -{ - AStringVector Split = StringSplit(a_Command, " "); - if (Split.size() == 0) - { - return false; - } - - CommandMap::iterator FoundCommand = m_BoundCommands.find( Split[0] ); - if( FoundCommand != m_BoundCommands.end() ) - { - const BoundFunction & func = FoundCommand->second; - if( func.Permission.size() > 0 ) - { - if( !a_Player->HasPermission( func.Permission.c_str() ) ) - { - return false; - } - } - - - // Push the split - SquirrelStringArray SplitData; - - std::vector::const_iterator iter = Split.begin(); - while(iter != Split.end()) { - SplitData.Add(*iter); - ++iter; - } - - // Push player - Sqrat::Function callback = func.Callback; - return callback.Evaluate(&SplitData, a_Player); - } - return false; -} diff --git a/source/cSquirrelCommandBinder.h b/source/cSquirrelCommandBinder.h deleted file mode 100644 index 50e026d06..000000000 --- a/source/cSquirrelCommandBinder.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once -#include -#include - -class cPlugin; -class cPlayer; - -class cSquirrelCommandBinder -{ -public: - cSquirrelCommandBinder(); - ~cSquirrelCommandBinder(); - - bool HandleCommand( const std::string & a_Command, cPlayer* a_Player ); - - bool BindCommand( const std::string & a_Command, const std::string & a_Permission, cPlugin* a_Plugin, Sqrat::Function a_Callback); - - void ClearBindings(); - void RemoveBindingsForPlugin( cPlugin* a_Plugin ); -private: - struct BoundFunction - { - Sqrat::Function Callback; - cPlugin *Plugin; - std::string Permission; - }; - - typedef std::map< std::string, BoundFunction > CommandMap; - CommandMap m_BoundCommands; -}; - - - - - diff --git a/source/cStairs.h b/source/cStairs.h deleted file mode 100644 index e17f4abc1..000000000 --- a/source/cStairs.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -class cStairs //tolua_export -{ //tolua_export -public: - static char RotationToMetaData( float a_Rotation, int a_Direction ) //tolua_export - { //tolua_export - a_Rotation += 90 + 45; // So its not aligned with axis - char result = 0x0; - if( a_Direction == 0) - { - - result = 0x4; - } - - if( a_Rotation > 360.f ) a_Rotation -= 360.f; - if( a_Rotation >= 0.f && a_Rotation < 90.f ) - return result; - else if( a_Rotation >= 180 && a_Rotation < 270 ) - result += 0x1; - else if( a_Rotation >= 90 && a_Rotation < 180 ) - result += 0x2; - else - result += 0x3; - - return result; - } //tolua_export -}; //tolua_export diff --git a/source/cStringMap.cpp b/source/cStringMap.cpp deleted file mode 100644 index 26b9cf5fe..000000000 --- a/source/cStringMap.cpp +++ /dev/null @@ -1,23 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cStringMap.h" - - - - - -unsigned int cStringMap::size() const -{ - return m_StringMap.size(); -} - -void cStringMap::clear() -{ - m_StringMap.clear(); -} - -std::string & cStringMap::get( const std::string & index ) -{ - return m_StringMap[index]; -} \ No newline at end of file diff --git a/source/cStringMap.h b/source/cStringMap.h deleted file mode 100644 index 1cf20048d..000000000 --- a/source/cStringMap.h +++ /dev/null @@ -1,29 +0,0 @@ - -// A std::map interface for Lua - -#pragma once - -#include "tolua++.h" - - - - - -class cStringMap // tolua_export -{ // tolua_export -public: // tolua_export - cStringMap(std::map< std::string, std::string > a_StringMap) : m_StringMap( a_StringMap ) {} - void clear(); // tolua_export - - unsigned int size() const; // tolua_export - - std::string & get( const std::string & index ); //tolua_export - - std::map< std::string, std::string >& GetStringMap() { return m_StringMap; } -private: - std::map< std::string, std::string > m_StringMap; -}; // tolua_export - - - - diff --git a/source/cTorch.h b/source/cTorch.h deleted file mode 100644 index fa301c3d4..000000000 --- a/source/cTorch.h +++ /dev/null @@ -1,77 +0,0 @@ - -#pragma once -#include "Vector3i.h" -#include "Defines.h" - - - - - -class cTorch //tolua_export -{ //tolua_export -public: - - static char DirectionToMetaData( char a_Direction ) //tolua_export - { //tolua_export - switch (a_Direction) - { - case BLOCK_FACE_BOTTOM: ASSERT(!"Shouldn't be getting this direction"); return 0; - case BLOCK_FACE_TOP: return E_META_TORCH_FLOOR; - case BLOCK_FACE_EAST: return E_META_TORCH_EAST; - case BLOCK_FACE_WEST: return E_META_TORCH_WEST; - case BLOCK_FACE_NORTH: return E_META_TORCH_NORTH; - case BLOCK_FACE_SOUTH: return E_META_TORCH_SOUTH; - default: - { - ASSERT(!"Unhandled torch direction!"); - break; - } - }; - return 0x0; - } //tolua_export - - - static char MetaDataToDirection(char a_MetaData) //tolua_export - { //tolua_export - switch (a_MetaData) - { - case 0: return BLOCK_FACE_TOP; // by default, the torches stand on the ground - case E_META_TORCH_FLOOR: return BLOCK_FACE_TOP; - case E_META_TORCH_EAST: return BLOCK_FACE_EAST; - case E_META_TORCH_WEST: return BLOCK_FACE_WEST; - case E_META_TORCH_NORTH: return BLOCK_FACE_NORTH; - case E_META_TORCH_SOUTH: return BLOCK_FACE_SOUTH; - default: - { - ASSERT(!"Unhandled torch metadata"); - break; - } - } - return 0; - } //tolua_export - - - static bool IsAttachedTo(const Vector3i & a_TorchPos, char a_TorchMeta, const Vector3i & a_BlockPos) - { - switch (a_TorchMeta) - { - case 0x0: - case E_META_TORCH_FLOOR: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 1, 0))); - case E_META_TORCH_EAST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, -1))); - case E_META_TORCH_WEST: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(0, 0, 1))); - case E_META_TORCH_NORTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(-1, 0, 0))); - case E_META_TORCH_SOUTH: return ((a_TorchPos - a_BlockPos).Equals(Vector3i(1, 0, 0))); - default: - { - ASSERT(!"Unhandled torch meta!"); - break; - } - } - return false; - } - -} ; //tolua_export - - - - diff --git a/source/cTracer.cpp b/source/cTracer.cpp deleted file mode 100644 index fba0f908c..000000000 --- a/source/cTracer.cpp +++ /dev/null @@ -1,350 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cTracer.h" -#include "cWorld.h" - -#include "Vector3f.h" -#include "Vector3i.h" -#include "Vector3d.h" - -#include "BlockID.h" -#include "cEntity.h" - -#include "Defines.h" - -#ifndef _WIN32 - #include // abs() -#endif - -cTracer::cTracer(cWorld* a_World) - : m_World( a_World ) -{ - m_NormalTable[0].Set(-1, 0, 0); - m_NormalTable[1].Set( 0, 0,-1); - m_NormalTable[2].Set( 1, 0, 0); - m_NormalTable[3].Set( 0, 0, 1); - m_NormalTable[4].Set( 0, 1, 0); - m_NormalTable[5].Set( 0,-1, 0); -} - -cTracer::~cTracer() -{ -} - -float cTracer::SigNum( float a_Num ) -{ - if (a_Num < 0.f) return -1.f; - if (a_Num > 0.f) return 1.f; - return 0.f; -} - -void cTracer::SetValues( const Vector3f & a_Start, const Vector3f & a_Direction ) -{ - // calculate the direction of the ray (linear algebra) - dir = a_Direction; - - // decide which direction to start walking in - step.x = (int) SigNum(dir.x); - step.y = (int) SigNum(dir.y); - step.z = (int) SigNum(dir.z); - - // normalize the direction vector - if( dir.SqrLength() > 0.f ) dir.Normalize(); - - // how far we must move in the ray direction before - // we encounter a new voxel in x-direction - // same but y-direction - if( dir.x != 0.f ) tDelta.x = 1/fabs(dir.x); - else tDelta.x = 0; - if( dir.y != 0.f ) tDelta.y = 1/fabs(dir.y); - else tDelta.y = 0; - if( dir.z != 0.f ) tDelta.z = 1/fabs(dir.z); - else tDelta.z = 0; - - // start voxel coordinates - // use your - // transformer - // function here - pos.x = (int)floorf(a_Start.x); - pos.y = (int)floorf(a_Start.y); - pos.z = (int)floorf(a_Start.z); - - // calculate distance to first intersection in the voxel we start from - if(dir.x < 0) - { - tMax.x = ((float)pos.x - a_Start.x) / dir.x; - } - else - { - tMax.x = (((float)pos.x + 1) - a_Start.x) / dir.x; - } - - if(dir.y < 0) - { - tMax.y = ((float)pos.y - a_Start.y) / dir.y; - } - else - { - tMax.y = (((float)pos.y + 1) - a_Start.y) / dir.y; - } - - if(dir.z < 0) - { - tMax.z = ((float)pos.z - a_Start.z) / dir.z; - } - else - { - tMax.z = (((float)pos.z + 1) - a_Start.z) / dir.z; - } -} - -int cTracer::Trace( const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance) -{ - SetValues( a_Start, a_Direction ); - - const Vector3f End = a_Start + (dir * (float)a_Distance); - - // end voxel coordinates - end1.x = (int)floorf(End.x); - end1.y = (int)floorf(End.y); - end1.z = (int)floorf(End.z); - - // check if first is occupied - if( pos.Equals( end1 ) ) - { - LOG("WARNING: cTracer: Start and end in same block"); - return 0; - } - - bool reachedX = false, reachedY = false, reachedZ = false; - - int Iterations = 0; - while ( Iterations < a_Distance ) - { - Iterations++; - if(tMax.x < tMax.y && tMax.x < tMax.z) - { - tMax.x += tDelta.x; - pos.x += step.x; - } - else if(tMax.y < tMax.z) - { - tMax.y += tDelta.y; - pos.y += step.y; - } - else - { - tMax.z += tDelta.z; - pos.z += step.z; - } - - if(step.x > 0.0f) - { - if(pos.x >= end1.x) - { - reachedX = true; - } - } - else if(pos.x <= end1.x) - { - reachedX = true; - } - - if(step.y > 0.0f) - { - if(pos.y >= end1.y) - { - reachedY = true; - } - } - else if(pos.y <= end1.y) - { - reachedY = true; - } - - if(step.z > 0.0f) - { - if(pos.z >= end1.z) - { - reachedZ = true; - } - } - else if(pos.z <= end1.z) - { - reachedZ = true; - } - - if (reachedX && reachedY && reachedZ) - { - return false; - } - - char BlockID = m_World->GetBlock( pos.x, pos.y, pos.z ); - //No collision with water ;) - if ( BlockID != E_BLOCK_AIR || IsBlockWater(BlockID)) - { - BlockHitPosition = pos; - int Normal = GetHitNormal(a_Start, End, pos ); - if(Normal > 0) - { - HitNormal = m_NormalTable[Normal-1]; - } - return 1; - } - } - return 0; -} - -// return 1 = hit, other is not hit -int LinesCross(float x0,float y0,float x1,float y1,float x2,float y2,float x3,float y3) -{ - //float linx, liny; - - float d=(x1-x0)*(y3-y2)-(y1-y0)*(x3-x2); - if (abs(d)<0.001) {return 0;} - float AB=((y0-y2)*(x3-x2)-(x0-x2)*(y3-y2))/d; - if (AB>=0.0 && AB<=1.0) - { - float CD=((y0-y2)*(x1-x0)-(x0-x2)*(y1-y0))/d; - if (CD>=0.0 && CD<=1.0) - { - //linx=x0+AB*(x1-x0); - //liny=y0+AB*(y1-y0); - return 1; - } - } - return 0; -} - -// intersect3D_SegmentPlane(): intersect a segment and a plane -// Input: a_Ray = a segment, and a_Plane = a plane = {Point V0; Vector n;} -// Output: *I0 = the intersect point (when it exists) -// Return: 0 = disjoint (no intersection) -// 1 = intersection in the unique point *I0 -// 2 = the segment lies in the plane -int cTracer::intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f & a_End, const Vector3f & a_PlanePos, const Vector3f & a_PlaneNormal ) -{ - Vector3f u = a_End - a_Origin;//a_Ray.P1 - S.P0; - Vector3f w = a_Origin - a_PlanePos;//S.P0 - Pn.V0; - - float D = a_PlaneNormal.Dot( u );//dot(Pn.n, u); - float N = -(a_PlaneNormal.Dot( w ) );//-dot(a_Plane.n, w); - - const float EPSILON = 0.0001f; - if (fabs(D) < EPSILON) { // segment is parallel to plane - if (N == 0) // segment lies in plane - return 2; - return 0; // no intersection - } - // they are not parallel - // compute intersect param - float sI = N / D; - if (sI < 0 || sI > 1) - return 0; // no intersection - - //Vector3f I ( a_Ray->GetOrigin() + sI * u );//S.P0 + sI * u; // compute segment intersect point - RealHit = a_Origin + u * sI; - return 1; -} - -int cTracer::GetHitNormal(const Vector3f & start, const Vector3f & end, const Vector3i & a_BlockPos) -{ - Vector3i SmallBlockPos = a_BlockPos; - char BlockID = m_World->GetBlock( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z ); - - if( BlockID == E_BLOCK_AIR || IsBlockWater(BlockID)) - return 0; - - Vector3f BlockPos; - BlockPos = Vector3f(SmallBlockPos); - - Vector3f Look = (end - start); - Look.Normalize(); - - float dot = Look.Dot( Vector3f(-1, 0, 0) ); // first face normal is x -1 - if(dot < 0) - { - int Lines = LinesCross( start.x, start.y, end.x, end.y, BlockPos.x, BlockPos.y, BlockPos.x, BlockPos.y + 1 ); - if(Lines == 1) - { - Lines = LinesCross( start.x, start.z, end.x, end.z, BlockPos.x, BlockPos.z, BlockPos.x, BlockPos.z + 1 ); - if(Lines == 1) - { - intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(-1, 0, 0) ); - return 1; - } - } - } - dot = Look.Dot( Vector3f(0, 0, -1) ); // second face normal is z -1 - if(dot < 0) - { - int Lines = LinesCross( start.z, start.y, end.z, end.y, BlockPos.z, BlockPos.y, BlockPos.z, BlockPos.y + 1 ); - if(Lines == 1) - { - Lines = LinesCross( start.z, start.x, end.z, end.x, BlockPos.z, BlockPos.x, BlockPos.z, BlockPos.x + 1 ); - if(Lines == 1) - { - intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(0, 0, -1) ); - return 2; - } - } - } - dot = Look.Dot( Vector3f(1, 0, 0) ); // third face normal is x 1 - if(dot < 0) - { - int Lines = LinesCross( start.x, start.y, end.x, end.y, BlockPos.x + 1, BlockPos.y, BlockPos.x + 1, BlockPos.y + 1 ); - if(Lines == 1) - { - Lines = LinesCross( start.x, start.z, end.x, end.z, BlockPos.x + 1, BlockPos.z, BlockPos.x + 1, BlockPos.z + 1 ); - if(Lines == 1) - { - intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(1, 0, 0), Vector3f(1, 0, 0) ); - return 3; - } - } - } - dot = Look.Dot( Vector3f(0, 0, 1) ); // fourth face normal is z 1 - if(dot < 0) - { - int Lines = LinesCross( start.z, start.y, end.z, end.y, BlockPos.z + 1, BlockPos.y, BlockPos.z + 1, BlockPos.y + 1 ); - if(Lines == 1) - { - Lines = LinesCross( start.z, start.x, end.z, end.x, BlockPos.z + 1, BlockPos.x, BlockPos.z + 1, BlockPos.x + 1 ); - if(Lines == 1) - { - intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(0, 0, 1), Vector3f(0, 0, 1) ); - return 4; - } - } - } - dot = Look.Dot( Vector3f(0, 1, 0) ); // fifth face normal is y 1 - if(dot < 0) - { - int Lines = LinesCross( start.y, start.x, end.y, end.x, BlockPos.y + 1, BlockPos.x, BlockPos.y + 1, BlockPos.x + 1 ); - if(Lines == 1) - { - Lines = LinesCross( start.y, start.z, end.y, end.z, BlockPos.y + 1, BlockPos.z, BlockPos.y + 1, BlockPos.z + 1 ); - if(Lines == 1) - { - intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(0, 1, 0), Vector3f(0, 1, 0) ); - return 5; - } - } - } - dot = Look.Dot( Vector3f(0, -1, 0) ); // sixth face normal is y -1 - if(dot < 0) - { - int Lines = LinesCross( start.y, start.x, end.y, end.x, BlockPos.y, BlockPos.x, BlockPos.y, BlockPos.x + 1 ); - if(Lines == 1) - { - Lines = LinesCross( start.y, start.z, end.y, end.z, BlockPos.y, BlockPos.z, BlockPos.y, BlockPos.z + 1 ); - if(Lines == 1) - { - intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(0, -1, 0) ); - return 6; - } - } - } - return 0; -} diff --git a/source/cTracer.h b/source/cTracer.h deleted file mode 100644 index fa9d3c673..000000000 --- a/source/cTracer.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "Vector3i.h" -#include "Vector3f.h" - - -class cWorld; -class cTracer //tolua_export -{ //tolua_export -public: //tolua_export - Vector3f DotPos; - Vector3f BoxOffset; - cTracer( cWorld* a_World); //tolua_export - ~cTracer(); //tolua_export - int Trace( const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance ); //tolua_export - void SetValues( const Vector3f & a_Start, const Vector3f & a_Direction ); //tolua_export - Vector3f BlockHitPosition; //tolua_export - Vector3f HitNormal; //tolua_export - Vector3f RealHit; //tolua_export -private: - int intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f & a_End, const Vector3f & a_PlanePos, const Vector3f & a_PlaneNormal ); - int GetHitNormal( const Vector3f & start, const Vector3f & end, const Vector3i & a_BlockPos); - float SigNum( float a_Num ); - cWorld* m_World; - - Vector3f m_NormalTable[6]; - - Vector3f dir; - Vector3f tDelta; - Vector3i pos; - Vector3i end1; - Vector3i step; - Vector3f tMax; -}; //tolua_export \ No newline at end of file diff --git a/source/cVine.h b/source/cVine.h deleted file mode 100644 index d4bc99c1a..000000000 --- a/source/cVine.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -class cVine //tolua_export -{ //tolua_export -public: - - static char DirectionToMetaData( char a_Direction ) //tolua_export - { //tolua_export - switch (a_Direction) - { - case 0x2: - return 0x1; - case 0x3: - return 0x4; - case 0x4: - return 0x8; - case 0x5: - return 0x2; - default: - return 0xf; - }; - } //tolua_export -}; //tolua_export \ No newline at end of file diff --git a/source/cWaterSimulator.cpp b/source/cWaterSimulator.cpp deleted file mode 100644 index 642d54aac..000000000 --- a/source/cWaterSimulator.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "Globals.h" -#include "cWaterSimulator.h" -#include "Defines.h" -#include "cWorld.h" - - - -cWaterSimulator::cWaterSimulator(cWorld *a_World) - : cFluidSimulator(a_World) -{ - m_FluidBlock = E_BLOCK_WATER; - m_StationaryFluidBlock = E_BLOCK_STATIONARY_WATER; - m_MaxHeight = 7; - m_FlowReduction = 1; -} - - -bool cWaterSimulator::IsAllowedBlock(char a_BlockID) -{ - return IsBlockWater(a_BlockID); -} - diff --git a/source/cWaterSimulator.h b/source/cWaterSimulator.h deleted file mode 100644 index 7d4a4a19b..000000000 --- a/source/cWaterSimulator.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include "cFluidSimulator.h" - -class cWaterSimulator : public cFluidSimulator -{ -public: - cWaterSimulator( cWorld* a_World ); - - virtual bool IsAllowedBlock( char a_BlockID ); - -}; \ No newline at end of file diff --git a/source/cWebAdmin.cpp b/source/cWebAdmin.cpp deleted file mode 100644 index 32cf5e338..000000000 --- a/source/cWebAdmin.cpp +++ /dev/null @@ -1,365 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cWebAdmin.h" -#include "cStringMap.h" - -#include "cWebPlugin.h" - -#include "cPluginManager.h" -#include "cPlugin.h" - -#include "cWorld.h" -#include "cPlayer.h" -#include "cServer.h" -#include "cRoot.h" - -#include "../iniFile/iniFile.h" - -#ifdef _WIN32 - #include -#else - #include -#endif - - - - - -/// Helper class - appends all player names together in a HTML list -class cPlayerAccum : - public cPlayerListCallback -{ - virtual bool Item(cPlayer * a_Player) override - { - m_Contents.append("
  • "); - m_Contents.append(a_Player->GetName()); - m_Contents.append("
  • "); - return false; - } - -public: - - AString m_Contents; -} ; - - - - - -cWebAdmin * WebAdmin = NULL; - - - - - -cWebAdmin::cWebAdmin( int a_Port /* = 8080 */ ) - : m_Port( a_Port ) - , m_bConnected( false ) -{ - WebAdmin = this; - m_Event = new cEvent(); - Init( m_Port ); -} - -cWebAdmin::~cWebAdmin() -{ - WebAdmin = 0; - m_WebServer->Stop(); - - while( m_Plugins.begin() != m_Plugins.end() ) - { - delete *m_Plugins.begin(); - //m_Plugins.remove( *m_Plugins.begin() ); - } - delete m_WebServer; - delete m_IniFile; - - m_Event->Wait(); - delete m_Event; -} - -void cWebAdmin::AddPlugin( cWebPlugin * a_Plugin ) -{ - m_Plugins.remove( a_Plugin ); - m_Plugins.push_back( a_Plugin ); -} - -void cWebAdmin::RemovePlugin( cWebPlugin * a_Plugin ) -{ - m_Plugins.remove( a_Plugin ); -} - - - - - -void cWebAdmin::Request_Handler(webserver::http_request* r) -{ - if( WebAdmin == 0 ) return; - LOG("Path: %s", r->path_.c_str() ); - - if (r->path_ == "/") - { - r->answer_ += "

    MCServer WebAdmin

    "; - r->answer_ += "
    "; - r->answer_ += "
    "; - r->answer_ += ""; - r->answer_ += "
    "; - r->answer_ += "
    "; - return; - } - - if (r->path_.empty() || r->path_[0] != '/') - { - r->answer_ += "

    Bad request

    "; - r->answer_ += "

    "; - r->answer_ = r->path_; // TODO: Shouldn't we sanitize this? Possible security issue. - r->answer_ += "

    "; - return; - } - - AStringVector Split = StringSplit(r->path_.substr(1), "/"); - - if (Split.empty() || (Split[0] != "webadmin" && Split[0] != "~webadmin")) - { - r->answer_ += "

    Bad request

    "; - return; - } - - if (!r->authentication_given_) - { - r->answer_ += "no auth"; - r->auth_realm_ = "MCServer WebAdmin"; - } - - bool bDontShowTemplate = false; - if (Split[0] == "~webadmin") - { - bDontShowTemplate = true; - } - - std::string UserPassword = WebAdmin->m_IniFile->GetValue( "User:"+r->username_, "Password", ""); - if ((UserPassword != "") && (r->password_ == UserPassword)) - { - std::string BaseURL = "./"; - if (Split.size() > 1) - { - for (unsigned int i = 0; i < Split.size(); i++) - { - BaseURL += "../"; - } - BaseURL += "webadmin/"; - } - - std::string Menu; - std::string Content; - std::string Template = bDontShowTemplate ? "{CONTENT}" : WebAdmin->GetTemplate(); - std::string FoundPlugin; - - for (PluginList::iterator itr = WebAdmin->m_Plugins.begin(); itr != WebAdmin->m_Plugins.end(); ++itr) - { - cWebPlugin* WebPlugin = *itr; - std::list< std::pair > NameList = WebPlugin->GetTabNames(); - for( std::list< std::pair >::iterator Names = NameList.begin(); Names != NameList.end(); ++Names ) - { - Menu += "
  • " + (*Names).first + "
  • "; - } - } - - HTTPRequest Request; - Request.Username = r->username_; - Request.Method = r->method_; - Request.Params = r->params_; - Request.PostParams = r->params_post_; - Request.Path = r->path_.substr(1); - - for( unsigned int i = 0; i < r->multipart_formdata_.size(); ++i ) - { - webserver::formdata& fd = r->multipart_formdata_[i]; - - HTTPFormData HTTPfd;//( fd.value_ ); - HTTPfd.Value = fd.value_; - HTTPfd.Type = fd.content_type_; - HTTPfd.Name = fd.name_; - LOGINFO("Form data name: %s", fd.name_.c_str() ); - Request.FormData[ fd.name_ ] = HTTPfd; - } - - if( Split.size() > 1 ) - { - for( PluginList::iterator itr = WebAdmin->m_Plugins.begin(); itr != WebAdmin->m_Plugins.end(); ++itr ) - { - if( (*itr)->GetName() == Split[1] ) - { - Content = (*itr)->HandleWebRequest( &Request ); - cWebPlugin* WebPlugin = *itr; - FoundPlugin = WebPlugin->GetName(); - AString TabName = WebPlugin->GetTabNameForRequest( &Request ).first; - if( TabName.empty() == false ) - { - FoundPlugin += " - " + TabName; - } - break; - } - } - } - - if( FoundPlugin.empty() ) // Default page - { - Content.clear(); - FoundPlugin = "Current Game"; - Content += "

    Server Name:

    "; - Content += "

    " + std::string( cRoot::Get()->GetServer()->GetServerID() ) + "

    "; - - Content += "

    Plugins:

      "; - cPluginManager* PM = cRoot::Get()->GetPluginManager(); - if( PM ) - { - const cPluginManager::PluginList & List = PM->GetAllPlugins(); - for( cPluginManager::PluginList::const_iterator itr = List.begin(); itr != List.end(); ++itr ) - { - AString VersionNum; - AppendPrintf(Content, "
    • %s V.%i
    • ", (*itr)->GetName().c_str(), (*itr)->GetVersion()); - } - } - Content += "
    "; - Content += "

    Players:

      "; - - cPlayerAccum PlayerAccum; - cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players - if( World != NULL ) - { - World->ForEachPlayer(PlayerAccum); - Content.append(PlayerAccum.m_Contents); - } - Content += "

    "; - } - - - - if (bDontShowTemplate == false && Split.size() > 1) - { - Content += "\n

    Go back

    "; - } - - // mem usage -#ifndef _WIN32 - rusage resource_usage; - if (getrusage(RUSAGE_SELF, &resource_usage) != 0) - { - ReplaceString( Template, std::string("{MEM}"), "Error :(" ); - } - else - { - AString MemUsage; - Printf(MemUsage, "%0.2f", ((double)resource_usage.ru_maxrss / 1024 / 1024) ); - ReplaceString(Template, std::string("{MEM}"), MemUsage); - } -#else - HANDLE hProcess = GetCurrentProcess(); - PROCESS_MEMORY_COUNTERS pmc; - if( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc) ) ) - { - AString MemUsage; - Printf(MemUsage, "%0.2f", (pmc.WorkingSetSize / 1024.f / 1024.f) ); - ReplaceString( Template, "{MEM}", MemUsage ); - } -#endif - // end mem usage - - ReplaceString( Template, "{USERNAME}", r->username_ ); - ReplaceString( Template, "{MENU}", Menu ); - ReplaceString( Template, "{PLUGIN_NAME}", FoundPlugin ); - ReplaceString( Template, "{CONTENT}", Content ); - ReplaceString( Template, "{TITLE}", "MCServer" ); - - AString NumChunks; - Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount()); - ReplaceString(Template, "{NUMCHUNKS}", NumChunks); - - r->answer_ = Template; - } - else - { - r->answer_ += "Wrong username/password"; - r->auth_realm_ = "MCServer WebAdmin"; - } -} - - - - - -bool cWebAdmin::Init( int a_Port ) -{ - m_Port = a_Port; - - m_IniFile = new cIniFile("webadmin.ini"); - if( m_IniFile->ReadFile() ) - { - m_Port = m_IniFile->GetValueI("WebAdmin", "Port", 8080 ); - } - - LOG("Starting WebAdmin on port %i", m_Port); - -#ifdef _WIN32 - HANDLE hThread = CreateThread( - NULL, // default security - 0, // default stack size - ListenThread, // name of the thread function - this, // thread parameters - 0, // default startup flags - NULL); - CloseHandle( hThread ); // Just close the handle immediately -#else - pthread_t LstnThread; - pthread_create( &LstnThread, 0, ListenThread, this); -#endif - - return true; -} - - - - - -#ifdef _WIN32 -DWORD WINAPI cWebAdmin::ListenThread(LPVOID lpParam) -#else -void *cWebAdmin::ListenThread( void *lpParam ) -#endif -{ - cWebAdmin* self = (cWebAdmin*)lpParam; - - self->m_WebServer = new webserver(self->m_Port, Request_Handler ); - if (!self->m_WebServer->Begin()) - { - LOGWARN("WebServer failed to start! WebAdmin is disabled"); - } - - self->m_Event->Set(); - return 0; -} - - - - - -std::string cWebAdmin::GetTemplate() -{ - std::string retVal = ""; - - char SourceFile[] = "webadmin/template.html"; - - cFile f; - if (!f.Open(SourceFile, cFile::fmRead)) - { - return ""; - } - - // copy the file into the buffer: - f.ReadRestOfFile(retVal); - - return retVal; -} \ No newline at end of file diff --git a/source/cWebAdmin.h b/source/cWebAdmin.h deleted file mode 100644 index f50de3f64..000000000 --- a/source/cWebAdmin.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "../WebServer/WebServer.h" -#include "OSSupport/Socket.h" - -class cStringMap; - -struct HTTPFormData //tolua_export -{ //tolua_export - std::string Name; //tolua_export - std::string Value; //tolua_export - std::string Type; //tolua_export -};//tolua_export - -struct HTTPRequest //tolua_export -{ //tolua_export - typedef std::map< std::string, std::string > StringStringMap; - typedef std::map< std::string, HTTPFormData > FormDataMap; - std::string Method; //tolua_export - std::string Path; //tolua_export - StringStringMap Params; // >> EXPORTED IN MANUALBINDINGS << - StringStringMap PostParams; // >> EXPORTED IN MANUALBINDINGS << - std::string Username; //tolua_export - FormDataMap FormData; // >> EXPORTED IN MANUALBINDINGS << -}; //tolua_export - -struct lua_State; -class cEvent; -class cIniFile; -class cWebPlugin; -class cWebAdmin -{ -public: - cWebAdmin( int a_Port = 8080 ); - ~cWebAdmin(); - - bool Init( int a_Port ); - - void AddPlugin( cWebPlugin* a_Plugin ); - void RemovePlugin( cWebPlugin* a_Plugin ); - - typedef std::list< cWebPlugin* > PluginList; - PluginList GetPlugins() { return m_Plugins; } - - static void Request_Handler(webserver::http_request* r); -private: - -#ifdef _WIN32 - static DWORD WINAPI ListenThread(LPVOID lpParam); -#else - static void *ListenThread( void *lpParam ); -#endif - - std::string GetTemplate(); - - int m_Port; - - bool m_bConnected; - cSocket m_ListenSocket; - - cIniFile* m_IniFile; - PluginList m_Plugins; - - cEvent* m_Event; - - webserver* m_WebServer; -}; \ No newline at end of file diff --git a/source/cWebPlugin.cpp b/source/cWebPlugin.cpp deleted file mode 100644 index 47e7d9e49..000000000 --- a/source/cWebPlugin.cpp +++ /dev/null @@ -1,109 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "cWebPlugin.h" -#include "cWebAdmin.h" -#include "cServer.h" -#include "cRoot.h" - - - - - -cWebPlugin::cWebPlugin() -{ - LOG("cWebPlugin::cWebPlugin()"); - cWebAdmin* WebAdmin = cRoot::Get()->GetWebAdmin(); - if( WebAdmin ) WebAdmin->AddPlugin( this ); -} - - - - - -cWebPlugin::~cWebPlugin() -{ - LOG("~cWebPlugin::cWebPlugin()"); - cWebAdmin* WebAdmin = cRoot::Get()->GetWebAdmin(); - if( WebAdmin ) WebAdmin->RemovePlugin( this ); - - for( TabList::iterator itr = m_Tabs.begin(); itr != m_Tabs.end(); ++itr ) - { - delete *itr; - } - m_Tabs.clear(); -} - - - - - -std::list< std::pair > cWebPlugin::GetTabNames() -{ - std::list< std::pair< AString, AString > > NameList; - for( TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr ) - { - std::pair< AString, AString > StringPair; - StringPair.first = (*itr)->Title; - StringPair.second = (*itr)->SafeTitle; - NameList.push_back( StringPair ); - } - return NameList; -} - - - - - -std::pair< AString, AString > cWebPlugin::GetTabNameForRequest( HTTPRequest* a_Request ) -{ - std::pair< AString, AString > Names; - AStringVector Split = StringSplit(a_Request->Path, "/"); - - if( Split.size() > 1 ) - { - sWebPluginTab* Tab = 0; - if( Split.size() > 2 ) // If we got the tab name, show that page - { - for( TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr ) - { - if( (*itr)->SafeTitle.compare( Split[2] ) == 0 ) // This is the one! Rawr - { - Tab = *itr; - break; - } - } - } - else // Otherwise show the first tab - { - if( GetTabs().size() > 0 ) - Tab = *GetTabs().begin(); - } - - if( Tab ) - { - Names.first = Tab->Title; - Names.second = Tab->SafeTitle; - } - } - - return Names; -} - - - - -AString cWebPlugin::SafeString( const AString & a_String ) -{ - AString RetVal; - for( unsigned int i = 0; i < a_String.size(); ++i ) - { - char c = a_String[i]; - if( c == ' ' ) - { - c = '_'; - } - RetVal.push_back( c ); - } - return RetVal; -} \ No newline at end of file diff --git a/source/cWebPlugin.h b/source/cWebPlugin.h deleted file mode 100644 index 35af88167..000000000 --- a/source/cWebPlugin.h +++ /dev/null @@ -1,47 +0,0 @@ - -#pragma once - -struct lua_State; -struct HTTPRequest; - - - - - -// tolua_begin -class cWebPlugin -{ -public: - // tolua_end - cWebPlugin(); - virtual ~cWebPlugin(); - - virtual const AString & GetName(void) const = 0; - // tolua_begin - - virtual AString HandleWebRequest( HTTPRequest * a_Request ) = 0; - - static AString SafeString( const AString & a_String ); - //tolua_end - - struct sWebPluginTab - { - std::string Title; - std::string SafeTitle; - - int UserData; - }; - - typedef std::list< sWebPluginTab* > TabList; - TabList & GetTabs() { return m_Tabs; } - - std::list< std::pair > GetTabNames(); - std::pair< AString, AString > GetTabNameForRequest( HTTPRequest* a_Request ); - -private: - TabList m_Tabs; -}; // tolua_export - - - - diff --git a/source/cWorld.cpp b/source/cWorld.cpp deleted file mode 100644 index dd5f216d1..000000000 --- a/source/cWorld.cpp +++ /dev/null @@ -1,2097 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "BlockID.h" -#include "cWorld.h" -#include "cRedstone.h" -#include "ChunkDef.h" -#include "cClientHandle.h" -#include "cPickup.h" -#include "cPlayer.h" -#include "cServer.h" -#include "cItem.h" -#include "cRoot.h" -#include "../iniFile/iniFile.h" -#include "cChunkMap.h" -#include "cSimulatorManager.h" -#include "cWaterSimulator.h" -#include "cLavaSimulator.h" -#include "cFireSimulator.h" -#include "cSandSimulator.h" -#include "cRedstoneSimulator.h" - -// Mobs: -#include "Mobs/Chicken.h" -#include "Mobs/Spider.h" -#include "Mobs/Cow.h" -#include "Mobs/Squid.h" -#include "Mobs/Wolf.h" -#include "Mobs/Slime.h" -#include "Mobs/Skeleton.h" -#include "Mobs/Silverfish.h" -#include "Mobs/Pig.h" -#include "Mobs/Sheep.h" -#include "Mobs/Zombie.h" -#include "Mobs/Enderman.h" -#include "Mobs/Creeper.h" -#include "Mobs/Cavespider.h" -#include "Mobs/Ghast.h" -#include "Mobs/Zombiepigman.h" - -#include "OSSupport/MakeDir.h" -#include "MersenneTwister.h" -#include "cTracer.h" -#include "Generating/Trees.h" -#include "cPluginManager.h" -#include "blocks/Block.h" -#include "Vector3d.h" - -#include "tolua++.h" - -#ifndef _WIN32 - #include -#endif - - - - - -/// Up to this many m_SpreadQueue elements are handled each world tick -const int MAX_LIGHTING_SPREAD_PER_TICK = 10; - - - - - -float cWorld::m_Time = 0.f; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cWorldLoadProgress: - -/// A simple thread that displays the progress of world loading / saving in cWorld::InitializeSpawn() -class cWorldLoadProgress : - public cIsThread -{ -public: - cWorldLoadProgress(cWorld * a_World) : - cIsThread("cWorldLoadProgress"), - m_World(a_World) - { - Start(); - } - - void Stop(void) - { - m_ShouldTerminate = true; - Wait(); - } - -protected: - - cWorld * m_World; - - virtual void Execute(void) override - { - for (;;) - { - LOG("%d chunks to load, %d chunks to generate", - m_World->GetStorage().GetLoadQueueLength(), - m_World->GetGenerator().GetQueueLength() - ); - - // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish - for (int i = 0; i < 20; i++) - { - cSleep::MilliSleep(100); - if (m_ShouldTerminate) - { - return; - } - } - } // for (-ever) - } - -} ; - - - - - -/// A simple thread that displays the progress of world lighting in cWorld::InitializeSpawn() -class cWorldLightingProgress : - public cIsThread -{ -public: - cWorldLightingProgress(cLightingThread * a_Lighting) : - cIsThread("cWorldLightingProgress"), - m_Lighting(a_Lighting) - { - Start(); - } - - void Stop(void) - { - m_ShouldTerminate = true; - Wait(); - } - -protected: - - cLightingThread * m_Lighting; - - virtual void Execute(void) override - { - for (;;) - { - LOG("%d chunks remaining to light", m_Lighting->GetQueueLength() - ); - - // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish - for (int i = 0; i < 20; i++) - { - cSleep::MilliSleep(100); - if (m_ShouldTerminate) - { - return; - } - } - } // for (-ever) - } - -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cWorld: - -cWorld* cWorld::GetWorld() -{ - LOGWARN("WARNING: Using deprecated function cWorld::GetWorld() use cRoot::Get()->GetDefaultWorld() instead!"); - return cRoot::Get()->GetDefaultWorld(); -} - - - - - -cWorld::~cWorld() -{ - { - cCSLock Lock(m_CSEntities); - while( m_AllEntities.begin() != m_AllEntities.end() ) - { - cEntity* Entity = *m_AllEntities.begin(); - m_AllEntities.remove( Entity ); - if ( !Entity->IsDestroyed() ) - { - Entity->Destroy(); - } - delete Entity; - } - } - - delete m_SimulatorManager; - delete m_SandSimulator; - delete m_WaterSimulator; - delete m_LavaSimulator; - delete m_FireSimulator; - delete m_RedstoneSimulator; - - UnloadUnusedChunks(); - - m_Storage.WaitForFinish(); - - delete m_ChunkMap; -} - - - - - -cWorld::cWorld( const AString & a_WorldName ) - : m_SpawnMonsterTime( 0.f ) - , m_RSList ( 0 ) - , m_Weather ( eWeather_Sunny ) -{ - LOG("cWorld::cWorld(%s)", a_WorldName.c_str()); - m_WorldName = a_WorldName; - m_IniFileName = m_WorldName + "/world.ini"; - - cMakeDir::MakeDir(m_WorldName.c_str()); - - MTRand r1; - m_SpawnX = (double)((r1.randInt() % 1000) - 500); - m_SpawnY = cChunkDef::Height; - m_SpawnZ = (double)((r1.randInt() % 1000) - 500); - m_GameMode = eGameMode_Creative; - - AString StorageSchema("Default"); - - cIniFile IniFile(m_IniFileName); - IniFile.ReadFile(); - m_SpawnX = IniFile.GetValueSetF("SpawnPosition", "X", m_SpawnX); - m_SpawnY = IniFile.GetValueSetF("SpawnPosition", "Y", m_SpawnY); - m_SpawnZ = IniFile.GetValueSetF("SpawnPosition", "Z", m_SpawnZ); - StorageSchema = IniFile.GetValueSet ("Storage", "Schema", StorageSchema); - m_MaxCactusHeight = IniFile.GetValueSetI("Plants", "MaxCactusHeight", 3); - m_MaxSugarcaneHeight = IniFile.GetValueSetI("Plants", "MaxSugarcaneHeight", 3); - m_IsCropsBonemealable = IniFile.GetValueSetB("Plants", "IsCropsBonemealable", true); - m_IsGrassBonemealable = IniFile.GetValueSetB("Plants", "IsGrassBonemealable", true); - m_IsSaplingBonemealable = IniFile.GetValueSetB("Plants", "IsSaplingBonemealable", true); - m_IsMelonStemBonemealable = IniFile.GetValueSetB("Plants", "IsMelonStemBonemealable", true); - m_IsMelonBonemealable = IniFile.GetValueSetB("Plants", "IsMelonBonemealable", false); - m_IsPumpkinStemBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinStemBonemealable", true); - m_IsPumpkinBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinBonemealable", false); - m_IsSugarcaneBonemealable = IniFile.GetValueSetB("Plants", "IsSugarcaneBonemealable", false); - m_IsCactusBonemealable = IniFile.GetValueSetB("Plants", "IsCactusBonemealable", false); - - m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode ); - - if (!IniFile.WriteFile()) - { - LOG("WARNING: Could not write to %s", m_IniFileName.c_str()); - } - - m_Lighting.Start(this); - m_Storage.Start(this, StorageSchema); - m_Generator.Start(this, IniFile); - - m_bAnimals = true; - m_SpawnMonsterRate = 10; - cIniFile IniFile2("settings.ini"); - if( IniFile2.ReadFile() ) - { - m_bAnimals = IniFile2.GetValueB("Monsters", "AnimalsOn", true ); - m_SpawnMonsterRate = (float)IniFile2.GetValueF("Monsters", "AnimalSpawnInterval", 10); - SetMaxPlayers(IniFile2.GetValueI("Server", "MaxPlayers", 9001)); - m_Description = IniFile2.GetValue("Server", "Description", "MCServer! - It's OVER 9000!").c_str(); - } - - m_ChunkMap = new cChunkMap(this ); - - m_ChunkSender.Start(this); - - m_Time = 0; - m_WorldTimeFraction = 0.f; - m_WorldTime = 0; - m_LastSave = 0; - m_LastUnload = 0; - - //preallocate some memory for ticking blocks so we donīt need to allocate that often - m_BlockTickQueue.reserve(1000); - m_BlockTickQueueCopy.reserve(1000); - - //Simulators: - m_WaterSimulator = new cWaterSimulator( this ); - m_LavaSimulator = new cLavaSimulator( this ); - m_SandSimulator = new cSandSimulator(this); - m_FireSimulator = new cFireSimulator(this); - m_RedstoneSimulator = new cRedstoneSimulator(this); - - m_SimulatorManager = new cSimulatorManager(); - m_SimulatorManager->RegisterSimulator(m_WaterSimulator, 6); - m_SimulatorManager->RegisterSimulator(m_LavaSimulator, 12); - m_SimulatorManager->RegisterSimulator(m_SandSimulator, 1); - m_SimulatorManager->RegisterSimulator(m_FireSimulator, 10); - m_SimulatorManager->RegisterSimulator(m_RedstoneSimulator, 1); -} - - - - - -void cWorld::SetWeather(eWeather a_Weather) -{ - switch (a_Weather) - { - case eWeather_Sunny: - case eWeather_Rain: - case eWeather_ThunderStorm: - { - m_Weather = a_Weather; - BroadcastWeather(a_Weather); - break; - } - - default: - { - LOGWARN("Trying to set unknown weather %d", a_Weather); - break; - } - } -} - - - - - -void cWorld::CastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) -{ - BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cWorld::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - return m_ChunkMap->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ); -} - - - - - -void cWorld::InitializeSpawn(void) -{ - int ChunkX = 0, ChunkY = 0, ChunkZ = 0; - BlockToChunk( (int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ, ChunkX, ChunkY, ChunkZ ); - - // For the debugging builds, don't make the server build too much world upon start: - #ifdef _DEBUG - int ViewDist = 9; - #else - int ViewDist = 20; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is - #endif // _DEBUG - - LOG("Preparing spawn area in world \"%s\"...", m_WorldName.c_str()); - for (int x = 0; x < ViewDist; x++) - { - for (int z = 0; z < ViewDist; z++) - { - m_ChunkMap->TouchChunk( x + ChunkX-(ViewDist - 1) / 2, ZERO_CHUNK_Y, z + ChunkZ-(ViewDist - 1) / 2 ); // Queue the chunk in the generator / loader - } - } - - { - // Display progress during this process: - cWorldLoadProgress Progress(this); - - // Wait for the loader to finish loading - m_Storage.WaitForQueuesEmpty(); - - // Wait for the generator to finish generating - m_Generator.WaitForQueueEmpty(); - - Progress.Stop(); - } - - // Light all chunks that have been newly generated: - LOG("Lighting spawn area in world \"%s\"...", m_WorldName.c_str()); - - for (int x = 0; x < ViewDist; x++) - { - int ChX = x + ChunkX-(ViewDist - 1) / 2; - for (int z = 0; z < ViewDist; z++) - { - int ChZ = z + ChunkZ-(ViewDist - 1) / 2; - if (!m_ChunkMap->IsChunkLighted(ChX, ChZ)) - { - m_Lighting.QueueChunk(ChX, ChZ); // Queue the chunk in the lighting thread - } - } // for z - } // for x - - { - cWorldLightingProgress Progress(&m_Lighting); - m_Lighting.WaitForQueueEmpty(); - Progress.Stop(); - } - - // TODO: Better spawn detection - move spawn out of the water if it isn't set in the INI already - m_SpawnY = (double)GetHeight( (int)m_SpawnX, (int)m_SpawnZ ) + 1.6f; // +1.6f eye height -} - - - - - -void cWorld::StopThreads(void) -{ - m_Generator.Stop(); - m_ChunkSender.Stop(); -} - - - - - -void cWorld::Tick(float a_Dt) -{ - m_Time += a_Dt / 1000.f; - - CurrentTick++; - - bool bSendTime = false; - m_WorldTimeFraction += a_Dt / 1000.f; - while (m_WorldTimeFraction > 1.f) - { - m_WorldTimeFraction -= 1.f; - m_WorldTime += 20; - bSendTime = true; - } - m_WorldTime %= 24000; // 24000 units in a day - - if (bSendTime) - { - BroadcastTimeUpdate(); - } - - { - cCSLock Lock(m_CSEntities); - for (cEntityList::iterator itr = m_AllEntities.begin(); itr != m_AllEntities.end();) - { - if ((*itr)->IsDestroyed()) - { - LOGD("Destroying entity #%i", (*itr)->GetUniqueID()); - cEntity * RemoveMe = *itr; - itr = m_AllEntities.erase( itr ); - m_RemoveEntityQueue.push_back( RemoveMe ); - continue; - } - (*itr)->Tick(a_Dt); - itr++; - } - } - - m_ChunkMap->Tick(a_Dt, m_TickRand); - - TickQueuedBlocks(a_Dt); - - GetSimulatorManager()->Simulate(a_Dt); - - TickWeather(a_Dt); - - // Asynchronously set blocks: - sSetBlockList FastSetBlockQueueCopy; - { - cCSLock Lock(m_CSFastSetBlock); - std::swap(FastSetBlockQueueCopy, m_FastSetBlockQueue); - } - m_ChunkMap->FastSetBlocks(FastSetBlockQueueCopy); - if (!FastSetBlockQueueCopy.empty()) - { - // Some blocks failed, store them for next tick: - cCSLock Lock(m_CSFastSetBlock); - m_FastSetBlockQueue.splice(m_FastSetBlockQueue.end(), FastSetBlockQueueCopy); - } - - if( m_Time - m_LastSave > 60 * 5 ) // Save each 5 minutes - { - SaveAllChunks(); - } - - if( m_Time - m_LastUnload > 10 ) // Unload every 10 seconds - { - UnloadUnusedChunks(); - } - - // Delete entities queued for removal: - for (cEntityList::iterator itr = m_RemoveEntityQueue.begin(); itr != m_RemoveEntityQueue.end(); ++itr) - { - delete *itr; - } - m_RemoveEntityQueue.clear(); - - TickSpawnMobs(a_Dt); - - std::vector m_RSList_copy(m_RSList); - - m_RSList.clear(); - - std::vector::const_iterator cii; // FIXME - Please rename this variable, WTF is cii??? Use human readable variable names or common abbreviations (i, idx, itr, iter) - for(cii=m_RSList_copy.begin(); cii!=m_RSList_copy.end();) - { - int tempX = *cii;cii++; - int tempY = *cii;cii++; - int tempZ = *cii;cii++; - int state = *cii;cii++; - - if ( (state == 11111) && ( (int)GetBlock( tempX, tempY, tempZ ) == E_BLOCK_REDSTONE_TORCH_OFF ) ) - { - FastSetBlock( tempX, tempY, tempZ, E_BLOCK_REDSTONE_TORCH_ON, (int)GetBlockMeta( tempX, tempY, tempZ ) ); - cRedstone Redstone(this); - Redstone.ChangeRedstone( tempX, tempY, tempZ, true ); - } - else if ( (state == 00000) && ( (int)GetBlock( tempX, tempY, tempZ ) == E_BLOCK_REDSTONE_TORCH_ON ) ) - { - FastSetBlock( tempX, tempY, tempZ, E_BLOCK_REDSTONE_TORCH_OFF, (int)GetBlockMeta( tempX, tempY, tempZ ) ); - cRedstone Redstone(this); - Redstone.ChangeRedstone( tempX, tempY, tempZ, false ); - } - } - m_RSList_copy.erase(m_RSList_copy.begin(),m_RSList_copy.end()); -} - - - - - -void cWorld::ChangeWeather() -{ - unsigned randWeather = (m_TickRand.randInt() % 99); - - if (GetWeather() == eWeather_Sunny) - { - if (randWeather < 20) - { - LOG("Starting rainstorm!"); - SetWeather( eWeather_Rain ); - } - } - - else if (GetWeather() == eWeather_Rain) - { - if (randWeather < 5) - { - LOG("Thunderstorm!"); - SetWeather( eWeather_ThunderStorm ); - } - - else if (randWeather < 60) - { - LOG("Back to sunshine"); - SetWeather( eWeather_Sunny ); - } - } - - else if (GetWeather() == eWeather_ThunderStorm) - { - if (randWeather < 70) - { - SetWeather(eWeather_Sunny); - LOG("Thunder ended abruptly, returning to lovely sunshine"); - } - else if (randWeather < 85) - { - SetWeather(eWeather_Rain); - LOG("Thunder ended, but rain persists."); - } - else - { - return; - } - } -} - - - - - -void cWorld::TickWeather(float a_Dt) -{ - if (m_WeatherInterval == 0) - { - ChangeWeather(); - - cRoot::Get()->GetPluginManager()->CallHookWeatherChanged(this); - - switch (GetWeather()) - { - case eWeather_Sunny: - m_WeatherInterval = 14400 + (m_TickRand.randInt() % 4800); // 12 - 16 minutes - break; - case eWeather_Rain: - m_WeatherInterval = 9600 + (m_TickRand.randInt() % 7200); // 8 - 14 minutes - break; - case eWeather_ThunderStorm: - m_WeatherInterval = 2400 + (m_TickRand.randInt() % 4800); // 2 - 6 minutes - break; - default: - LOG("Unknown weather occurred"); - break; - } - } - - else - { - m_WeatherInterval--; - } - - if ( GetWeather() == 2 ) // if thunderstorm - { - if (m_TickRand.randInt() % 199 == 0) // 0.5% chance per tick of thunderbolt - { - CastThunderbolt ( 0, 0, 0 ); // TODO: find random possitions near players to cast thunderbolts. - } - } -} - - - - - -void cWorld::TickSpawnMobs(float a_Dt) -{ - if (!m_bAnimals || (m_Time - m_SpawnMonsterTime <= m_SpawnMonsterRate)) - { - return; - } - - m_SpawnMonsterTime = m_Time; - Vector3d SpawnPos; - { - cCSLock Lock(m_CSPlayers); - if ( m_Players.size() <= 0) - { - return; - } - int RandomPlayerIdx = m_TickRand.randInt() & m_Players.size(); - cPlayerList::iterator itr = m_Players.begin(); - for( int i = 1; i < RandomPlayerIdx; i++ ) - { - itr++; - } - SpawnPos = (*itr)->GetPosition(); - } - - cMonster * Monster = NULL; - int dayRand = m_TickRand.randInt() % 6; - int nightRand = m_TickRand.randInt() % 10; - - SpawnPos += Vector3d( (double)(m_TickRand.randInt() % 64) - 32, (double)(m_TickRand.randInt() % 64) - 32, (double)(m_TickRand.randInt() % 64) - 32 ); - int Height = GetHeight( (int)SpawnPos.x, (int)SpawnPos.z ); - - if (m_WorldTime >= 12000 + 1000) - { - if (GetBiomeAt((int)SpawnPos.x, (int)SpawnPos.z) == biHell) // Spawn nether mobs - { - if (nightRand == 1) - Monster = new cZombie(); - else if (nightRand == 5) - Monster = new cGhast(); - else if (nightRand == 6) - Monster = new cZombiepigman(); - } - else - { - if (nightRand == 0) //random percent to spawn for night - Monster = new cSpider(); - else if (nightRand == 2) - Monster = new cEnderman(); - else if (nightRand == 3) - Monster = new cCreeper(); - else if (nightRand == 4) - Monster = new cCavespider(); - else if (nightRand == 7) - Monster = new cSlime(); - else if (nightRand == 8) - Monster = new cSilverfish(); - else if (nightRand == 9) - Monster = new cSkeleton(); - } - //end random percent to spawn for night - } - else - { - if (dayRand == 0) //random percent to spawn for day - Monster = new cChicken(); - else if (dayRand == 1) - Monster = new cCow(); - else if (dayRand == 2) - Monster = new cPig(); - else if (dayRand == 3) - Monster = new cSheep(); - else if (dayRand == 4) - Monster = new cSquid(); - else if (dayRand == 5) - Monster = new cWolf(); - //end random percent to spawn for day - } - - if( Monster ) - { - Monster->Initialize(this); - Monster->TeleportTo(SpawnPos.x, (double)(Height) + 2, SpawnPos.z); - BroadcastSpawn(*Monster); - } -} - - - - - -bool cWorld::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback) -{ - return m_ChunkMap->ForEachChestInChunk(a_ChunkX, a_ChunkZ, a_Callback); -} - - - - - -bool cWorld::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback) -{ - return m_ChunkMap->ForEachFurnaceInChunk(a_ChunkX, a_ChunkZ, a_Callback); -} - - - - - -bool cWorld::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback) -{ - return m_ChunkMap->DoWithChestAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); -} - - - - - -bool cWorld::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) -{ - return m_ChunkMap->DoWithFurnaceAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); -} - - - - - -bool cWorld::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) -{ - return m_ChunkMap->GetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); -} - - - - - -void cWorld::GrowTree( int a_X, int a_Y, int a_Z ) -{ - if (GetBlock(a_X, a_Y, a_Z) == E_BLOCK_SAPLING) - { - // There is a sapling here, grow a tree according to its type: - GrowTreeFromSapling(a_X, a_Y, a_Z, GetBlockMeta(a_X, a_Y, a_Z)); - } - else - { - // There is nothing here, grow a tree based on the current biome here: - GrowTreeByBiome(a_X, a_Y, a_Z); - } -} - - - - - -void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, char a_SaplingMeta) -{ - cNoise Noise(m_Generator.GetSeed()); - sSetBlockVector Logs, Other; - switch (a_SaplingMeta & 0x07) - { - case E_META_SAPLING_APPLE: GetAppleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldTime & 0xffffffff), Logs, Other); break; - case E_META_SAPLING_BIRCH: GetBirchTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldTime & 0xffffffff), Logs, Other); break; - case E_META_SAPLING_CONIFER: GetConiferTreeImage(a_X, a_Y, a_Z, Noise, (int)(m_WorldTime & 0xffffffff), Logs, Other); break; - case E_META_SAPLING_JUNGLE: GetJungleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldTime & 0xffffffff), Logs, Other); break; - } - Other.insert(Other.begin(), Logs.begin(), Logs.end()); - Logs.clear(); - GrowTreeImage(Logs); -} - - - - - -void cWorld::GrowTreeByBiome(int a_X, int a_Y, int a_Z) -{ - cNoise Noise(m_Generator.GetSeed()); - sSetBlockVector Logs, Other; - GetTreeImageByBiome(a_X, a_Y, a_Z, Noise, (int)(m_WorldTime & 0xffffffff), (EMCSBiome)GetBiomeAt(a_X, a_Z), Logs, Other); - Other.insert(Other.begin(), Logs.begin(), Logs.end()); - Logs.clear(); - GrowTreeImage(Other); -} - - - - - -void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks) -{ - // Check that the tree has place to grow - - // Make a copy of the log blocks: - sSetBlockVector b2; - for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) - { - if (itr->BlockType == E_BLOCK_LOG) - { - b2.push_back(*itr); - } - } // for itr - a_Blocks[] - - // Query blocktypes and metas at those log blocks: - if (!GetBlocks(b2, false)) - { - return; - } - - // Check that at each log's coord there's an block allowed to be overwritten: - for (sSetBlockVector::const_iterator itr = b2.begin(); itr != b2.end(); ++itr) - { - switch (itr->BlockType) - { - CASE_TREE_ALLOWED_BLOCKS: - { - break; - } - default: - { - return; - } - } - } // for itr - b2[] - - // All ok, replace blocks with the tree image: - m_ChunkMap->ReplaceTreeBlocks(a_Blocks); -} - - - - - -bool cWorld::GrowPlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal) -{ - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - switch (BlockType) - { - case E_BLOCK_CROPS: - { - if (a_IsByBonemeal && !m_IsGrassBonemealable) - { - return false; - } - if (BlockMeta < 7) - { - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); - } - return true; - } - - case E_BLOCK_MELON_STEM: - { - if (BlockMeta < 7) - { - if (a_IsByBonemeal && !m_IsMelonStemBonemealable) - { - return false; - } - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); - } - else - { - if (a_IsByBonemeal && !m_IsMelonBonemealable) - { - return false; - } - GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType); - } - return true; - } - - case E_BLOCK_PUMPKIN_STEM: - { - if (BlockMeta < 7) - { - if (a_IsByBonemeal && !m_IsPumpkinStemBonemealable) - { - return false; - } - FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, 7); - } - else - { - if (a_IsByBonemeal && !m_IsPumpkinBonemealable) - { - return false; - } - GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, BlockType); - } - return true; - } - - case E_BLOCK_SAPLING: - { - if (a_IsByBonemeal && !m_IsSaplingBonemealable) - { - return false; - } - GrowTreeFromSapling(a_BlockX, a_BlockY, a_BlockZ, BlockMeta); - return true; - } - - case E_BLOCK_GRASS: - { - if (a_IsByBonemeal && !m_IsGrassBonemealable) - { - return false; - } - MTRand r1; - for (int i = 0; i < 60; i++) - { - int OfsX = (r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3; - int OfsY = r1.randInt(3) + r1.randInt(3) - 3; - int OfsZ = (r1.randInt(3) + r1.randInt(3) + r1.randInt(3) + r1.randInt(3)) / 2 - 3; - BLOCKTYPE Ground = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY, a_BlockZ + OfsZ); - if (Ground != E_BLOCK_GRASS) - { - continue; - } - BLOCKTYPE Above = GetBlock(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ); - if (Above != E_BLOCK_AIR) - { - continue; - } - BLOCKTYPE SpawnType; - NIBBLETYPE SpawnMeta = 0; - switch (r1.randInt(10)) - { - case 0: SpawnType = E_BLOCK_YELLOW_FLOWER; break; - case 1: SpawnType = E_BLOCK_RED_ROSE; break; - default: - { - SpawnType = E_BLOCK_TALL_GRASS; - SpawnMeta = E_META_TALL_GRASS_GRASS; - break; - } - } // switch (random spawn block) - FastSetBlock(a_BlockX + OfsX, a_BlockY + OfsY + 1, a_BlockZ + OfsZ, SpawnType, SpawnMeta); - } // for i - 50 times - return true; - } - - case E_BLOCK_SUGARCANE: - { - if (a_IsByBonemeal && !m_IsSugarcaneBonemealable) - { - return false; - } - m_ChunkMap->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, m_MaxSugarcaneHeight); - return true; - } - - case E_BLOCK_CACTUS: - { - if (a_IsByBonemeal && !m_IsCactusBonemealable) - { - return false; - } - m_ChunkMap->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, m_MaxCactusHeight); - return true; - } - } // switch (BlockType) - return false; -} - - - - - -void cWorld::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockType) -{ - MTRand Rand; - m_ChunkMap->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, Rand); -} - - - - - -int cWorld::GetBiomeAt (int a_BlockX, int a_BlockZ) -{ - return m_ChunkMap->GetBiomeAt(a_BlockX, a_BlockZ); -} - - - - - -void cWorld::SetBlock( int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) -{ - if(a_BlockType == E_BLOCK_AIR) - { - BlockHandler(GetBlock(a_X, a_Y, a_Z))->OnDestroyed(this, a_X, a_Y, a_Z); - } - m_ChunkMap->SetBlock(a_X, a_Y, a_Z, a_BlockType, a_BlockMeta); - - GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z); - BlockHandler(a_BlockType)->OnPlaced(this, a_X, a_Y, a_Z, a_BlockMeta); -} - - - - - -void cWorld::FastSetBlock( int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) -{ - cCSLock Lock(m_CSFastSetBlock); - m_FastSetBlockQueue.push_back(sSetBlock(a_X, a_Y, a_Z, a_BlockType, a_BlockMeta)); -} - - - - - -BLOCKTYPE cWorld::GetBlock(int a_X, int a_Y, int a_Z) -{ - // First check if it isn't queued in the m_FastSetBlockQueue: - { - int X = a_X, Y = a_Y, Z = a_Z; - int ChunkX, ChunkY, ChunkZ; - AbsoluteToRelative(X, Y, Z, ChunkX, ChunkY, ChunkZ); - - cCSLock Lock(m_CSFastSetBlock); - for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr) - { - if ((itr->x == X) && (itr->y == Y) && (itr->z == Z) && (itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) - { - return itr->BlockType; - } - } // for itr - m_FastSetBlockQueue[] - } - - return m_ChunkMap->GetBlock(a_X, a_Y, a_Z); -} - - - - - -NIBBLETYPE cWorld::GetBlockMeta( int a_X, int a_Y, int a_Z ) -{ - // First check if it isn't queued in the m_FastSetBlockQueue: - { - cCSLock Lock(m_CSFastSetBlock); - for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr) - { - if ((itr->x == a_X) && (itr->y == a_Y) && (itr->y == a_Y)) - { - return itr->BlockMeta; - } - } // for itr - m_FastSetBlockQueue[] - } - - return m_ChunkMap->GetBlockMeta(a_X, a_Y, a_Z); -} - - - - - -void cWorld::SetBlockMeta( int a_X, int a_Y, int a_Z, NIBBLETYPE a_MetaData ) -{ - m_ChunkMap->SetBlockMeta(a_X, a_Y, a_Z, a_MetaData); -} - - - - - -NIBBLETYPE cWorld::GetBlockSkyLight( int a_X, int a_Y, int a_Z ) -{ - return m_ChunkMap->GetBlockSkyLight(a_X, a_Y, a_Z); -} - - - - - -void cWorld::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) -{ - m_ChunkMap->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, (BLOCKTYPE &)a_BlockType, (NIBBLETYPE &)a_BlockMeta); -} - - - - - -void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed) -{ - MTRand r1; - a_FlyAwaySpeed /= 1000; // Pre-divide, so that we don't have to divide each time inside the loop - for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr) - { - float SpeedX = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500)); - float SpeedY = (float)(a_FlyAwaySpeed * r1.randInt(1000)); - float SpeedZ = (float)(a_FlyAwaySpeed * (r1.randInt(1000) - 500)); - cPickup * Pickup = new cPickup( - (int)(a_BlockX * 32) + r1.randInt(16) + r1.randInt(16), - (int)(a_BlockY * 32) + r1.randInt(16) + r1.randInt(16), - (int)(a_BlockZ * 32) + r1.randInt(16) + r1.randInt(16), - *itr, SpeedX, SpeedY, SpeedZ - ); - Pickup->Initialize(this); - } -} - - - - - -void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ) -{ - MTRand r1; - for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr) - { - cPickup * Pickup = new cPickup( - (int)(a_BlockX * 32) + r1.randInt(16) + r1.randInt(16), - (int)(a_BlockY * 32) + r1.randInt(16) + r1.randInt(16), - (int)(a_BlockZ * 32) + r1.randInt(16) + r1.randInt(16), - *itr, (float)a_SpeedX, (float)a_SpeedY, (float)a_SpeedZ - ); - Pickup->Initialize(this); - } -} - - - - - -void cWorld::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType) -{ - m_ChunkMap->ReplaceBlocks(a_Blocks, a_FilterBlockType); -} - - - - - -bool cWorld::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) -{ - return m_ChunkMap->GetBlocks(a_Blocks, a_ContinueOnFailure); -} - - - - - -bool cWorld::DigBlock( int a_X, int a_Y, int a_Z) -{ - cBlockHandler *Handler = cBlockHandler::GetBlockHandler(GetBlock(a_X, a_Y, a_Z)); - Handler->OnDestroyed(this, a_X, a_Y, a_Z); - return m_ChunkMap->DigBlock(a_X, a_Y, a_Z); -} - - - - - -void cWorld::SendBlockTo( int a_X, int a_Y, int a_Z, cPlayer * a_Player ) -{ - m_ChunkMap->SendBlockTo(a_X, a_Y, a_Z, a_Player); -} - - - - - -int cWorld::GetHeight( int a_X, int a_Z ) -{ - return m_ChunkMap->GetHeight(a_X, a_Z); -} - - - - - -void cWorld::BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - cClientHandle * ch = (*itr)->GetClientHandle(); - if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) - { - continue; - } - ch->SendChat(a_Message); - } -} - - - - - -void cWorld::BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastPlayerAnimation(a_Player, a_Animation, a_Exclude); -} - - - - - -void cWorld::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastEntityEquipment(a_Entity, a_SlotNum, a_Item, a_Exclude); -} - - - - - -void cWorld::BroadcastTeleportEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - cClientHandle * ch = (*itr)->GetClientHandle(); - if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) - { - continue; - } - ch->SendTeleportEntity(a_Entity); - } -} - - - - - -void cWorld::BroadcastEntRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastEntRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); -} - - - - - -void cWorld::BroadcastEntRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastEntRelMove(a_Entity, a_RelX, a_RelY, a_RelZ, a_Exclude); -} - - - - - -void cWorld::BroadcastEntLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastEntLook(a_Entity, a_Exclude); -} - - - - - -void cWorld::BroadcastEntHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastEntHeadLook(a_Entity, a_Exclude); -} - - - - - -void cWorld::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType, a_Exclude); -} - - - - - -void cWorld::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastDestroyEntity(a_Entity, a_Exclude); -} - - - - - -void cWorld::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastEntityStatus(a_Entity, a_Status, a_Exclude); -} - - - - - -void cWorld::BroadcastMetadata(const cPawn & a_Pawn, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastMetadata(a_Pawn, a_Exclude); -} - - - - - -void cWorld::BroadcastSpawn(cEntity & a_Entity, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastSpawn(a_Entity, a_Exclude); -} - - - - - -void cWorld::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastCollectPickup(a_Pickup, a_Player, a_Exclude); -} - - - - - -void cWorld::BroadcastWeather(eWeather a_Weather, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - cClientHandle * ch = (*itr)->GetClientHandle(); - if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) - { - continue; - } - ch->SendWeather(a_Weather); - } -} - - - - - -void cWorld::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastThunderbolt(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); -} - - - - - -void cWorld::BroadcastTimeUpdate(const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - cClientHandle * ch = (*itr)->GetClientHandle(); - if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) - { - continue; - } - ch->SendTimeUpdate(m_WorldTime); - } -} - - - - - -void cWorld::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastChunkData(a_ChunkX, a_ChunkZ, a_Serializer, a_Exclude); -} - - - - - -void cWorld::BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude) -{ - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - cClientHandle * ch = (*itr)->GetClientHandle(); - if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) - { - continue; - } - ch->SendPlayerListItem(a_Player, a_IsOnline); - } -} - - - - - -void cWorld::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch, a_Exclude); -} - - - - - -void cWorld::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) -{ - m_ChunkMap->BroadcastBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); -} - - - - - -void cWorld::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client) -{ - m_ChunkMap->SendBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Client); -} - - - - - -void cWorld::MarkChunkDirty (int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - m_ChunkMap->MarkChunkDirty (a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -void cWorld::MarkChunkSaving(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - m_ChunkMap->MarkChunkSaving(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -void cWorld::MarkChunkSaved (int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - m_ChunkMap->MarkChunkSaved (a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -void cWorld::SetChunkData( - int a_ChunkX, int a_ChunkY, int a_ChunkZ, - const BLOCKTYPE * a_BlockTypes, - const NIBBLETYPE * a_BlockMeta, - const NIBBLETYPE * a_BlockLight, - const NIBBLETYPE * a_BlockSkyLight, - const cChunkDef::HeightMap * a_HeightMap, - const cChunkDef::BiomeMap * a_BiomeMap, - cEntityList & a_Entities, - cBlockEntityList & a_BlockEntities, - bool a_MarkDirty -) -{ - // Validate biomes, if needed: - cChunkDef::BiomeMap BiomeMap; - const cChunkDef::BiomeMap * Biomes = a_BiomeMap; - if (a_BiomeMap == NULL) - { - // The biomes are not assigned, get them from the generator: - Biomes = &BiomeMap; - m_Generator.GenerateBiomes(a_ChunkX, a_ChunkZ, BiomeMap); - } - - m_ChunkMap->SetChunkData( - a_ChunkX, a_ChunkY, a_ChunkZ, - a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, - a_HeightMap, *Biomes, - a_Entities, a_BlockEntities, - a_MarkDirty - ); - - // If a client is requesting this chunk, send it to them: - if (m_ChunkMap->HasChunkAnyClients(a_ChunkX, a_ChunkY, a_ChunkZ)) - { - m_ChunkSender.ChunkReady(a_ChunkX, a_ChunkY, a_ChunkZ); - } - - // Notify the lighting thread that the chunk has become valid (in case it is a neighbor of a postponed chunk): - m_Lighting.ChunkReady(a_ChunkX, a_ChunkZ); -} - - - - - -void cWorld::ChunkLighted( - int a_ChunkX, int a_ChunkZ, - const cChunkDef::BlockNibbles & a_BlockLight, - const cChunkDef::BlockNibbles & a_SkyLight -) -{ - m_ChunkMap->ChunkLighted(a_ChunkX, a_ChunkZ, a_BlockLight, a_SkyLight); -} - - - - - -bool cWorld::GetChunkData(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkDataCallback & a_Callback) -{ - return m_ChunkMap->GetChunkData(a_ChunkX, a_ChunkY, a_ChunkZ, a_Callback); -} - - - - - -bool cWorld::GetChunkBlockTypes(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockTypes) -{ - return m_ChunkMap->GetChunkBlockTypes(a_ChunkX, a_ChunkY, a_ChunkZ, a_BlockTypes); -} - - - - - -bool cWorld::GetChunkBlockData(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData) -{ - return m_ChunkMap->GetChunkBlockData(a_ChunkX, a_ChunkY, a_ChunkZ, a_BlockData); -} - - - - - -bool cWorld::IsChunkValid(int a_ChunkX, int a_ChunkY, int a_ChunkZ) const -{ - return m_ChunkMap->IsChunkValid(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -bool cWorld::HasChunkAnyClients(int a_ChunkX, int a_ChunkY, int a_ChunkZ) const -{ - return m_ChunkMap->HasChunkAnyClients(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -void cWorld::UnloadUnusedChunks(void ) -{ - m_LastUnload = m_Time; - m_ChunkMap->UnloadUnusedChunks(); -} - - - - - -void cWorld::CollectPickupsByPlayer(cPlayer * a_Player) -{ - m_ChunkMap->CollectPickupsByPlayer(a_Player); -} - - - - - -void cWorld::SetMaxPlayers(int iMax) -{ - m_MaxPlayers = MAX_PLAYERS; - if (iMax > 0 && iMax < MAX_PLAYERS) - { - m_MaxPlayers = iMax; - } -} - - - - - -void cWorld::AddPlayer( cPlayer* a_Player ) -{ - cCSLock Lock(m_CSPlayers); - - ASSERT(std::find(m_Players.begin(), m_Players.end(), a_Player) == m_Players.end()); // Is it already in the list? HOW? - - m_Players.remove( a_Player ); // Make sure the player is registered only once - m_Players.push_back( a_Player ); -} - - - - - -void cWorld::RemovePlayer( cPlayer* a_Player ) -{ - cCSLock Lock(m_CSPlayers); - m_Players.remove( a_Player ); -} - - - - - -bool cWorld::ForEachPlayer(cPlayerListCallback & a_Callback) -{ - // Calls the callback for each player in the list - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(), itr2 = itr; itr != m_Players.end(); itr = itr2) - { - ++itr2; - if (a_Callback.Item(*itr)) - { - return false; - } - } // for itr - m_Players[] - return true; -} - - - - - -bool cWorld::DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback) -{ - // Calls the callback for each player in the list - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - if (NoCaseCompare((*itr)->GetName(), a_PlayerName) == 0) - { - a_Callback.Item(*itr); - return true; - } - } // for itr - m_Players[] - return false; -} - - - - - -bool cWorld::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback) -{ - cPlayer* BestMatch = 0; - unsigned int BestRating = 0; - unsigned int NumMatches = 0; - unsigned int NameLength = a_PlayerName.length(); - - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - unsigned int Rating = RateCompareString (a_PlayerName, (*itr)->GetName()); - if (Rating > 0 && Rating >= BestRating) - { - BestMatch = *itr; - if( Rating > BestRating ) NumMatches = 0; - BestRating = Rating; - ++NumMatches; - } - if (Rating == NameLength) // Perfect match - { - break; - } - } // for itr - m_Players[] - - if (NumMatches == 1) - { - LOG("Compared %s and %s with rating %i", a_PlayerName.c_str(), BestMatch->GetName().c_str(), BestRating ); - return a_Callback.Item (BestMatch); - } - return false; -} - - - - - -// TODO: This interface is dangerous! -cPlayer * cWorld::FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit) -{ - cTracer LineOfSight(this); - - float ClosestDistance = a_SightLimit; - cPlayer* ClosestPlayer = NULL; - - cCSLock Lock(m_CSPlayers); - for (cPlayerList::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - Vector3f Pos = (*itr)->GetPosition(); - float Distance = (Pos - a_Pos).Length(); - - if (Distance <= a_SightLimit) - { - if (!LineOfSight.Trace(a_Pos,(Pos - a_Pos),(int)(Pos - a_Pos).Length())) - { - if (Distance < ClosestDistance) - { - ClosestDistance = Distance; - ClosestPlayer = *itr; - } - } - } - } - return ClosestPlayer; -} - - - - - -void cWorld::SendPlayerList(cPlayer * a_DestPlayer) -{ - // Sends the playerlist to a_DestPlayer - cCSLock Lock(m_CSPlayers); - for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) - { - cClientHandle * ch = (*itr)->GetClientHandle(); - if ((ch != NULL) && !ch->IsDestroyed()) - { - a_DestPlayer->GetClientHandle()->SendPlayerListItem(*(*itr), true); - } - } -} - - - - - -void cWorld::RemoveEntityFromChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - m_ChunkMap->RemoveEntityFromChunk(a_Entity, a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -void cWorld::MoveEntityToChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - m_ChunkMap->MoveEntityToChunk(a_Entity, a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -bool cWorld::ForEachEntity(cEntityCallback & a_Callback) -{ - cCSLock Lock(m_CSEntities); - for (cEntityList::iterator itr = m_AllEntities.begin(), itr2 = itr; itr != m_AllEntities.end(); itr = itr2) - { - ++itr2; - if (a_Callback.Item(*itr)) - { - return false; - } - } // for itr - m_AllEntities[] - return false; -} - - - - - -bool cWorld::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback) -{ - return m_ChunkMap->ForEachEntityInChunk(a_ChunkX, a_ChunkZ, a_Callback); -} - - - - - -bool cWorld::DoWithEntityByID( int a_UniqueID, cEntityCallback & a_Callback ) -{ - cCSLock Lock(m_CSEntities); - for (cEntityList::iterator itr = m_AllEntities.begin(); itr != m_AllEntities.end(); ++itr ) - { - if( (*itr)->GetUniqueID() == a_UniqueID ) - { - return a_Callback.Item(*itr); - } - } // for itr - m_AllEntities[] - return false; -} - - - - - -void cWorld::CompareChunkClients(int a_ChunkX1, int a_ChunkY1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkY2, int a_ChunkZ2, cClientDiffCallback & a_Callback) -{ - m_ChunkMap->CompareChunkClients(a_ChunkX1, a_ChunkY1, a_ChunkZ1, a_ChunkX2, a_ChunkY2, a_ChunkZ2, a_Callback); -} - - - - - -bool cWorld::AddChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) -{ - return m_ChunkMap->AddChunkClient(a_ChunkX, a_ChunkY, a_ChunkZ, a_Client); -} - - - - - -void cWorld::RemoveChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) -{ - m_ChunkMap->RemoveChunkClient(a_ChunkX, a_ChunkY, a_ChunkZ, a_Client); -} - - - - - -void cWorld::RemoveClientFromChunks(cClientHandle * a_Client) -{ - m_ChunkMap->RemoveClientFromChunks(a_Client); -} - - - - - -void cWorld::SendChunkTo(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) -{ - m_ChunkSender.QueueSendChunkTo(a_ChunkX, a_ChunkY, a_ChunkZ, a_Client); -} - - - - - -void cWorld::RemoveClientFromChunkSender(cClientHandle * a_Client) -{ - m_ChunkSender.RemoveClient(a_Client); -} - - - - - -void cWorld::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -bool cWorld::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - return m_ChunkMap->LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -void cWorld::LoadChunks(const cChunkCoordsList & a_Chunks) -{ - m_ChunkMap->LoadChunks(a_Chunks); -} - - - - - -void cWorld::ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -{ - m_ChunkMap->ChunkLoadFailed(a_ChunkX, a_ChunkY, a_ChunkZ); -} - - - - - -void cWorld::UpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player) -{ - AString Line1(a_Line1); - AString Line2(a_Line2); - AString Line3(a_Line3); - AString Line4(a_Line4); - if (cRoot::Get()->GetPluginManager()->CallHookUpdatingSign(this, a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4, a_Player)) - { - return; - } - m_ChunkMap->UpdateSign(a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4); - cRoot::Get()->GetPluginManager()->CallHookUpdatedSign(this, a_BlockX, a_BlockY, a_BlockZ, Line1, Line2, Line3, Line4, a_Player); -} - - - - - -void cWorld::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay) -{ - m_ChunkMap->ChunksStay(a_Chunks, a_Stay); -} - - - - - -void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ) -{ - m_ChunkMap->MarkChunkRegenerating(a_ChunkX, a_ChunkZ); - - // Trick: use Y=1 to force the chunk generation even though the chunk data is already present - m_Generator.QueueGenerateChunk(a_ChunkX, 1, a_ChunkZ); -} - - - - - -void cWorld::GenerateChunk(int a_ChunkX, int a_ChunkZ) -{ - m_Generator.QueueGenerateChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); -} - - - - - -void cWorld::QueueLightChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback) -{ - m_Lighting.QueueChunk(a_ChunkX, a_ChunkZ, a_Callback); -} - - - - - -bool cWorld::IsChunkLighted(int a_ChunkX, int a_ChunkZ) -{ - return m_ChunkMap->IsChunkLighted(a_ChunkX, a_ChunkZ); -} - - - - - -bool cWorld::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback) -{ - return m_ChunkMap->ForEachChunkInRect(a_MinChunkX, a_MaxChunkX, a_MinChunkZ, a_MaxChunkZ, a_Callback); -} - - - - - -void cWorld::SaveAllChunks(void) -{ - LOGINFO("Saving all chunks..."); - m_LastSave = m_Time; - m_ChunkMap->SaveAllChunks(); - m_Storage.QueueSavedMessage(); -} - - - - - -/************************************************************************/ -/* Get and set */ -/************************************************************************/ -// void cWorld::AddClient( cClientHandle* a_Client ) -// { -// m_m_Clients.push_back( a_Client ); -// } -// cWorld::ClientList & cWorld::GetClients() -// { -// return m_m_Clients; -// } - - - - - -void cWorld::AddEntity( cEntity* a_Entity ) -{ - cCSLock Lock(m_CSEntities); - m_AllEntities.push_back( a_Entity ); -} - - - - - -unsigned int cWorld::GetNumPlayers() -{ - cCSLock Lock(m_CSPlayers); - return m_Players.size(); -} - - - - - -int cWorld::GetNumChunks(void) const -{ - return m_ChunkMap->GetNumChunks(); -} - - - - - -void cWorld::GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue) -{ - m_ChunkMap->GetChunkStats(a_NumValid, a_NumDirty); - a_NumInLightingQueue = (int) m_Lighting.GetQueueLength(); -} - - - - -void cWorld::TickQueuedBlocks(float a_Dt) -{ - if(m_BlockTickQueue.empty()) - return; - m_BlockTickQueueCopy.clear(); - m_BlockTickQueue.swap(m_BlockTickQueueCopy); - - for(std::vector::iterator itr = m_BlockTickQueueCopy.begin(); itr != m_BlockTickQueueCopy.end(); itr++) - { - BlockTickQueueItem *Block = (*itr); - Block->ToWait -= a_Dt; - if(Block->ToWait <= 0) - { - BlockHandler(GetBlock(Block->X, Block->Y, Block->Z))->OnUpdate(this, Block->X, Block->Y, Block->Z); - delete Block; //We donīt have to remove it from the vector, this will happen automatically on the next tick - }else{ - m_BlockTickQueue.push_back(Block); //Keep the block in the queue - } - } - -} - - -void cWorld::QueueBlockForTick(int a_X, int a_Y, int a_Z, float a_Time) -{ - BlockTickQueueItem *Block = new BlockTickQueueItem; - Block->X = a_X; - Block->Y = a_Y; - Block->Z = a_Z; - Block->ToWait = a_Time; - - m_BlockTickQueue.push_back(Block); -} - - -bool cWorld::IsBlockDirectlyWatered(int a_X, int a_Y, int a_Z) -{ - return IsBlockWater(GetBlock(a_X - 1, a_Y, a_Z)) - || IsBlockWater(GetBlock(a_X + 1, a_Y, a_Z)) - || IsBlockWater(GetBlock(a_X, a_Y, a_Z - 1)) - || IsBlockWater(GetBlock(a_X, a_Y, a_Z + 1)); -} \ No newline at end of file diff --git a/source/cWorld.h b/source/cWorld.h deleted file mode 100644 index a65368bfe..000000000 --- a/source/cWorld.h +++ /dev/null @@ -1,477 +0,0 @@ - -#pragma once - -#ifndef _WIN32 - #include "BlockID.h" -#else - enum ENUM_ITEM_ID; -#endif - -#define MAX_PLAYERS 65535 - -#include "cSimulatorManager.h" -#include "MersenneTwister.h" -#include "cChunkMap.h" -#include "WorldStorage/WorldStorage.h" -#include "Generating/ChunkGenerator.h" -#include "Vector3i.h" -#include "Vector3f.h" -#include "ChunkSender.h" -#include "Defines.h" -#include "LightingThread.h" -#include "cItem.h" - - - - - -class cRedstone; -class cFireSimulator; -class cWaterSimulator; -class cLavaSimulator; -class cSandSimulator; -class cRedstoneSimulator; -class cItem; -class cPlayer; -class cClientHandle; -class cEntity; -class cBlockEntity; -class cWorldGenerator; // The generator that actually generates the chunks for a single world -class cChunkGenerator; // The thread responsible for generating chunks -class cChestEntity; -class cFurnaceEntity; - -typedef std::list< cPlayer * > cPlayerList; - -typedef cItemCallback cPlayerListCallback; -typedef cItemCallback cEntityCallback; -typedef cItemCallback cChestCallback; -typedef cItemCallback cFurnaceCallback; - - - - - - - -class cWorld //tolua_export -{ //tolua_export -public: - - OBSOLETE static cWorld * GetWorld(); - - // Return time in seconds - inline static float GetTime() //tolua_export - { - return m_Time; - } - long long GetWorldTime(void) const { return m_WorldTime; } //tolua_export - - eGameMode GetGameMode(void) const { return m_GameMode; } //tolua_export - - void SetWorldTime(long long a_WorldTime) { m_WorldTime = a_WorldTime; } //tolua_export - - int GetHeight( int a_X, int a_Z ); //tolua_export - - void BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude = NULL); - void BroadcastPlayerAnimation(const cPlayer & a_Player, char a_Animation, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); - void BroadcastTeleportEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); - void BroadcastEntRelMove (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude = NULL); - void BroadcastEntLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntHeadLook (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL); - void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); - void BroadcastMetadata (const cPawn & a_Pawn, const cClientHandle * a_Exclude = NULL); - void BroadcastSpawn (cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); - void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); - void BroadcastWeather (eWeather a_Weather, const cClientHandle * a_Exclude = NULL); - void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); - void BroadcastTimeUpdate (const cClientHandle * a_Exclude = NULL); - void BroadcastChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); - void BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude = NULL); - void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8 - - /// If there is a block entity at the specified coods, sends it to all clients except a_Exclude - void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); - - /// If there is a block entity at the specified coords, sends it to the client specified - void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); - - void MarkChunkDirty (int a_ChunkX, int a_ChunkY, int a_ChunkZ); - void MarkChunkSaving(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - void MarkChunkSaved (int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /** Sets the chunk data as either loaded from the storage or generated. - a_BlockLight and a_BlockSkyLight are optional, if not present, chunk will be marked as unlighted. - a_BiomeMap is optional, if not present, biomes will be calculated by the generator - a_HeightMap is optional, if not present, will be calculated. - If a_MarkDirty is set, the chunk is set as dirty (used after generating) - */ - void SetChunkData( - int a_ChunkX, int a_ChunkY, int a_ChunkZ, - const BLOCKTYPE * a_BlockTypes, - const NIBBLETYPE * a_BlockMeta, - const NIBBLETYPE * a_BlockLight, - const NIBBLETYPE * a_BlockSkyLight, - const cChunkDef::HeightMap * a_HeightMap, - const cChunkDef::BiomeMap * a_BiomeMap, - cEntityList & a_Entities, - cBlockEntityList & a_BlockEntities, - bool a_MarkDirty - ); - - void ChunkLighted( - int a_ChunkX, int a_ChunkZ, - const cChunkDef::BlockNibbles & a_BlockLight, - const cChunkDef::BlockNibbles & a_SkyLight - ); - - bool GetChunkData (int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkDataCallback & a_Callback); - - /// Gets the chunk's blocks, only the block types - bool GetChunkBlockTypes(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockTypes); - - /// Gets the chunk's blockdata, the entire 4 arrays (Types, Meta, Light, SkyLight) - bool GetChunkBlockData (int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData); - - bool IsChunkValid (int a_ChunkX, int a_ChunkY, int a_ChunkZ) const; - bool HasChunkAnyClients(int a_ChunkX, int a_ChunkY, int a_ChunkZ) const; - - void UnloadUnusedChunks(void); // tolua_export - - void CollectPickupsByPlayer(cPlayer * a_Player); - - // MOTD - const AString & GetDescription(void) const {return m_Description; } // FIXME: This should not be in cWorld - - // Max Players - unsigned int GetMaxPlayers(void) const {return m_MaxPlayers; } //tolua_export - void SetMaxPlayers(int iMax); //tolua_export - - void AddPlayer( cPlayer* a_Player ); - void RemovePlayer( cPlayer* a_Player ); - - /// Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true - bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << - - /// Calls the callback for the player of the given name; returns true if the player was found and the callback called, false if player not found. Callback return ignored - bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << - - /// Finds a player from a partial or complete player name and calls the callback - case-insensitive - bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << - - unsigned int GetNumPlayers(); //tolua_export - - // TODO: This interface is dangerous - rewrite to DoWithClosestPlayer(pos, sight, action) - cPlayer * FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit); - - void SendPlayerList(cPlayer * a_DestPlayer); // Sends playerlist to the player - - void AddEntity( cEntity* a_Entity ); - - /// Removes the entity from the chunk specified - void RemoveEntityFromChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Moves the entity from its current chunk to the new chunk specified - void MoveEntityToChunk(cEntity * a_Entity, int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true - bool ForEachEntity(cEntityCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true - bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param - bool DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback); // TODO: Exported in ManualBindings.cpp - - /// Compares clients of two chunks, calls the callback accordingly - void CompareChunkClients(int a_ChunkX1, int a_ChunkY1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkY2, int a_ChunkZ2, cClientDiffCallback & a_Callback); - - /// Adds client to a chunk, if not already present; returns true if added, false if present - bool AddChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); - - /// Removes client from the chunk specified - void RemoveChunkClient(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); - - /// Removes the client from all chunks it is present in - void RemoveClientFromChunks(cClientHandle * a_Client); - - /// Sends the chunk to the client specified, if the chunk is valid. If not valid, the request is postponed (ChunkSender will send that chunk when it becomes valid+lighted) - void SendChunkTo(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); - - /// Removes client from ChunkSender's queue of chunks to be sent - void RemoveClientFromChunkSender(cClientHandle * a_Client); - - /// Touches the chunk, causing it to be loaded or generated - void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) - bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() - void LoadChunks(const cChunkCoordsList & a_Chunks); - - /// Marks the chunk as failed-to-load: - void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - - /// Updates the sign, askin gplugins for permission forst. a_Player is the player who changed the sign, may be NULL - void UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = NULL); //tolua_export - - /// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable. To be used only by cChunkStay! - void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true); - - /// Regenerate the given chunk: - void RegenerateChunk(int a_ChunkX, int a_ChunkZ); //tolua_export - - /// Generates the given chunk, if not already generated - void GenerateChunk(int a_ChunkX, int a_ChunkZ); //tolua_export - - /// Queues a chunk for lighting; a_Callback is called after the chunk is lighted - void QueueLightChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = NULL); - - bool IsChunkLighted(int a_ChunkX, int a_ChunkZ); - - /// Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully - bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback); - - void SetBlock (int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ); //tolua_export - void FastSetBlock (int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ); //tolua_export - BLOCKTYPE GetBlock(int a_X, int a_Y, int a_Z ); //tolua_export - BLOCKTYPE GetBlock(const Vector3i & a_Pos ) { return GetBlock( a_Pos.x, a_Pos.y, a_Pos.z ); } //tolua_export - NIBBLETYPE GetBlockMeta(int a_X, int a_Y, int a_Z ); //tolua_export - NIBBLETYPE GetBlockMeta(const Vector3i & a_Pos ) { return GetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z ); } //tolua_export - void SetBlockMeta(int a_X, int a_Y, int a_Z, NIBBLETYPE a_MetaData ); //tolua_export - void SetBlockMeta(const Vector3i & a_Pos, NIBBLETYPE a_MetaData ) { SetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z, a_MetaData ); } //tolua_export - NIBBLETYPE GetBlockSkyLight( int a_X, int a_Y, int a_Z ); //tolua_export - // TODO: NIBBLETYPE GetBlockActualLight(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export - void GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); // tolua_export - - /// Spawns item pickups for each item in the list. May compress pickups if too many entities: - void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed = 1.0); - - /// Spawns item pickups for each item in the list. May compress pickups if too many entities. All pickups get the speed specified: - void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ); - - /// Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType - void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType); - - /// Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. - bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure); - - bool DigBlock (int a_X, int a_Y, int a_Z); //tolua_export - void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player ); //tolua_export - - const double & GetSpawnX(void) const { return m_SpawnX; } // tolua_export - const double & GetSpawnY(void) const { return m_SpawnY; } // tolua_export - const double & GetSpawnZ(void) const { return m_SpawnZ; } // tolua_export - - inline cSimulatorManager *GetSimulatorManager() { return m_SimulatorManager; } - inline cWaterSimulator *GetWaterSimulator() { return m_WaterSimulator; } - inline cLavaSimulator *GetLavaSimulator() { return m_LavaSimulator; } - - /// Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true - bool ForEachChestInChunk (int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true - bool ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found - bool DoWithChestAt (int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords, true if found - bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp - - /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found - bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // tolua_export - - /// a_Player is using block entity at [x, y, z], handle that: - void UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) {m_ChunkMap->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); } - - void GrowTree (int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export - void GrowTreeFromSapling(int a_BlockX, int a_BlockY, int a_BlockZ, char a_SaplingMeta); // tolua_export - void GrowTreeByBiome (int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export - - void GrowTreeImage(const sSetBlockVector & a_Blocks); - - /// Grows the plant at the specified block to its ripe stage (bonemeal used); returns false if the block is not growable. If a_IsBonemeal is true, block is not grown if not allowed in world.ini - bool GrowPlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal = false); // tolua_export - - /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem) - void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockType); // tolua_export - - int GetBiomeAt (int a_BlockX, int a_BlockZ); // tolua_export - - const AString & GetName(void) const { return m_WorldName; } //tolua_export - const AString & GetIniFileName(void) const {return m_IniFileName; } - - inline static void AbsoluteToRelative( int & a_X, int & a_Y, int & a_Z, int & a_ChunkX, int & a_ChunkY, int & a_ChunkZ ) - { - // TODO: Use floor() instead of weird if statements - // Also fix Y - a_ChunkX = a_X/cChunkDef::Width; - if(a_X < 0 && a_X % cChunkDef::Width != 0) a_ChunkX--; - a_ChunkY = 0; - a_ChunkZ = a_Z/cChunkDef::Width; - if(a_Z < 0 && a_Z % cChunkDef::Width != 0) a_ChunkZ--; - - a_X = a_X - a_ChunkX*cChunkDef::Width; - a_Y = a_Y - a_ChunkY*cChunkDef::Height; - a_Z = a_Z - a_ChunkZ*cChunkDef::Width; - } - - inline static void BlockToChunk( int a_X, int a_Y, int a_Z, int & a_ChunkX, int & a_ChunkY, int & a_ChunkZ ) - { - // TODO: Use floor() instead of weird if statements - // Also fix Y - (void)a_Y; // not unused anymore - a_ChunkX = a_X/cChunkDef::Width; - if(a_X < 0 && a_X % cChunkDef::Width != 0) a_ChunkX--; - a_ChunkY = 0; - a_ChunkZ = a_Z/cChunkDef::Width; - if(a_Z < 0 && a_Z % cChunkDef::Width != 0) a_ChunkZ--; - } - - void SaveAllChunks(void); //tolua_export - - /// Returns the number of chunks loaded - int GetNumChunks() const; //tolua_export - - /// Returns the number of chunks loaded and dirty, and in the lighting queue - void GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue); - - // Various queues length queries (cannot be const, they lock their CS): - inline int GetGeneratorQueueLength (void) { return m_Generator.GetQueueLength(); } // tolua_export - inline int GetLightingQueueLength (void) { return m_Lighting.GetQueueLength(); } // tolua_export - inline int GetStorageLoadQueueLength(void) { return m_Storage.GetLoadQueueLength(); } // tolua_export - inline int GetStorageSaveQueueLength(void) { return m_Storage.GetSaveQueueLength(); } // tolua_export - - void Tick(float a_Dt); - - void InitializeSpawn(void); - - /// Stops threads that belong to this world (part of deinit) - void StopThreads(void); - void TickQueuedBlocks(float a_Dt); - - struct BlockTickQueueItem - { - int X; - int Y; - int Z; - float ToWait; - }; - - void QueueBlockForTick(int a_BlockX, int a_BlockY, int a_BlockZ, float a_Time); - - void CastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ); //tolua_export - void SetWeather ( eWeather a_Weather ); //tolua_export - void ChangeWeather(); //tolua_export - eWeather GetWeather() { return m_Weather; }; //tolua_export - - cChunkGenerator & GetGenerator(void) { return m_Generator; } - cWorldStorage & GetStorage (void) { return m_Storage; } - cChunkMap * GetChunkMap (void) { return m_ChunkMap; } - - /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call - void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export - - int GetMaxSugarcaneHeight(void) const { return m_MaxSugarcaneHeight; } // tolua_export - int GetMaxCactusHeight (void) const { return m_MaxCactusHeight; } // tolua_export - - bool IsBlockDirectlyWatered(int a_X, int a_Y, int a_Z); - -private: - - friend class cRoot; - - // This random generator is to be used only in the Tick() method, and thus only in the World-Tick-thread (MTRand is not exactly thread-safe) - MTRand m_TickRand; - - double m_SpawnX; - double m_SpawnY; - double m_SpawnZ; - - float m_LastUnload; - float m_LastSave; - static float m_Time; // Time in seconds - long long m_WorldTime; // Time in seconds*20, this is sent to clients (is wrapped) - unsigned long long CurrentTick; - eGameMode m_GameMode; - float m_WorldTimeFraction; // When this > 1.f m_WorldTime is incremented by 20 - - // The cRedstone class simulates redstone and needs access to m_RSList - friend class cRedstone; - std::vector m_RSList; - - std::vector m_BlockTickQueue; - std::vector m_BlockTickQueueCopy; //Second is for safely removing the objects from the queue - - cSimulatorManager * m_SimulatorManager; - cSandSimulator * m_SandSimulator; - cWaterSimulator * m_WaterSimulator; - cLavaSimulator * m_LavaSimulator; - cFireSimulator * m_FireSimulator; - cRedstoneSimulator * m_RedstoneSimulator; - - cCriticalSection m_CSClients; - cCriticalSection m_CSEntities; - cCriticalSection m_CSPlayers; - - cWorldStorage m_Storage; - - AString m_Description; - - unsigned int m_MaxPlayers; - - cChunkMap * m_ChunkMap; - - bool m_bAnimals; - float m_SpawnMonsterTime; - float m_SpawnMonsterRate; - - eWeather m_Weather; - int m_WeatherInterval; - - int m_MaxCactusHeight; - int m_MaxSugarcaneHeight; - bool m_IsCropsBonemealable; - bool m_IsGrassBonemealable; - bool m_IsSaplingBonemealable; - bool m_IsMelonStemBonemealable; - bool m_IsMelonBonemealable; - bool m_IsPumpkinStemBonemealable; - bool m_IsPumpkinBonemealable; - bool m_IsSugarcaneBonemealable; - bool m_IsCactusBonemealable; - - cEntityList m_RemoveEntityQueue; - cEntityList m_AllEntities; - cClientHandleList m_Clients; - cPlayerList m_Players; - - cCriticalSection m_CSFastSetBlock; - sSetBlockList m_FastSetBlockQueue; - - cChunkGenerator m_Generator; - - cChunkSender m_ChunkSender; - cLightingThread m_Lighting; - - AString m_WorldName; - AString m_IniFileName; - - cWorld(const AString & a_WorldName); - ~cWorld(); - - void TickWeather(float a_Dt); // Handles weather each tick - void TickSpawnMobs(float a_Dt); // Handles mob spawning each tick - - void RemoveEntity( cEntity * a_Entity ); -}; //tolua_export - - - - diff --git a/source/items/Item.cpp b/source/items/Item.cpp deleted file mode 100644 index f1b376493..000000000 --- a/source/items/Item.cpp +++ /dev/null @@ -1,418 +0,0 @@ - -#include "Globals.h" -#include "Item.h" -#include "../cItem.h" -#include "../cWorld.h" -#include "../cPlayer.h" - -//Handler -#include "ItemCloth.h" -#include "ItemHoe.h" -#include "ItemSlab.h" -#include "ItemWood.h" -#include "ItemShears.h" -#include "ItemLeaves.h" -#include "ItemSapling.h" -#include "ItemBucket.h" -#include "ItemLighter.h" -#include "ItemRedstoneDust.h" -#include "ItemRedstoneRepeater.h" -#include "ItemSeeds.h" -#include "ItemDye.h" -#include "ItemSugarcane.h" -#include "ItemPickaxe.h" -#include "ItemShovel.h" -#include "ItemSword.h" -#include "ItemDoor.h" -#include "ItemFood.h" -#include "ItemSign.h" - -#include "../blocks/Block.h" - - - - - -bool cItemHandler::m_HandlerInitialized = false; -cItemHandler * cItemHandler::m_ItemHandler[2266]; - - - - - -cItemHandler *cItemHandler::GetItemHandler(int a_ItemID) -{ - if(a_ItemID < 0) a_ItemID = 0; - - if(!m_HandlerInitialized) - { //We have to initialize - memset(m_ItemHandler, 0, sizeof(m_ItemHandler)); - m_HandlerInitialized = true; - } - if(m_ItemHandler[a_ItemID]) - return m_ItemHandler[a_ItemID]; - m_ItemHandler[a_ItemID] = CreateItemHandler(a_ItemID); - return m_ItemHandler[a_ItemID]; -} - - - - - -cItemHandler *cItemHandler::CreateItemHandler(int a_ItemID) -{ - switch(a_ItemID) - { - default: return new cItemHandler(a_ItemID); - - // Single item per handler: - case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemID); - case E_ITEM_LEAVES: return new cItemLeavesHandler(a_ItemID); - case E_ITEM_SAPLING: return new cItemSaplingHandler(a_ItemID); - case E_ITEM_DYE: return new cItemDyeHandler(a_ItemID); - case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemID); - case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemID); - case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemID); - case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemID); - case E_ITEM_WOOL: return new cItemClothHandler(a_ItemID); - - case E_ITEM_WOODEN_HOE: - case E_ITEM_STONE_HOE: - case E_ITEM_IRON_HOE: - case E_ITEM_GOLD_HOE: - case E_ITEM_DIAMOND_HOE: - { - return new cItemHoeHandler(a_ItemID); - } - - case E_ITEM_WOODEN_PICKAXE: - case E_ITEM_STONE_PICKAXE: - case E_ITEM_IRON_PICKAXE: - case E_ITEM_GOLD_PICKAXE: - case E_ITEM_DIAMOND_PICKAXE: - { - return new cItemPickaxeHandler(a_ItemID); - } - - case E_ITEM_WOODEN_SHOVEL: - case E_ITEM_STONE_SHOVEL: - case E_ITEM_IRON_SHOVEL: - case E_ITEM_GOLD_SHOVEL: - case E_ITEM_DIAMOND_SHOVEL: - { - return new cItemShovelHandler(a_ItemID); - } - - case E_ITEM_WOODEN_SWORD: - case E_ITEM_STONE_SWORD: - case E_ITEM_IRON_SWORD: - case E_ITEM_GOLD_SWORD: - case E_ITEM_DIAMOND_SWORD: - { - return new cItemSwordHandler(a_ItemID); - } - - case E_ITEM_STONE_SLAB: - case E_ITEM_WOODEN_SLAB: - { - return new cItemSlabHandler(a_ItemID); - } - - case E_ITEM_LOG: - case E_ITEM_PLANKS: - { - return new cItemWoodHandler(a_ItemID); - } - - case E_ITEM_BUCKET: - case E_ITEM_WATER_BUCKET: - case E_ITEM_LAVA_BUCKET: - { - return new cItemBucketHandler(a_ItemID); - } - - case E_ITEM_PUMPKIN_SEEDS: - case E_ITEM_MELON_SEEDS: - case E_ITEM_SEEDS: - { - return new cItemSeedsHandler(a_ItemID); - } - - case E_ITEM_IRON_DOOR: - case E_ITEM_WOODEN_DOOR: - { - return new cItemDoorHandler(a_ItemID); - } - - // Food: - case E_ITEM_BREAD: - case E_ITEM_COOKIE: - case E_ITEM_MELON_SLICE: - case E_ITEM_RAW_CHICKEN: - case E_ITEM_COOKED_CHICKEN: - case E_ITEM_RAW_BEEF: - case E_ITEM_RAW_MEAT: - case E_ITEM_STEAK: - case E_ITEM_COOKED_MEAT: - case E_ITEM_RAW_FISH: - case E_ITEM_COOKED_FISH: - case E_ITEM_RED_APPLE: - case E_ITEM_GOLDEN_APPLE: - case E_ITEM_ROTTEN_FLESH: - case E_ITEM_SPIDER_EYE: - { - return new cItemFoodHandler(a_ItemID); - } - - case E_ITEM_SIGN: - return new cItemSignHandler(a_ItemID); - } -} - - - - - -void cItemHandler::Deinit() -{ - for(int i = 0; i < 2266; i++) - { - delete m_ItemHandler[i]; - } - m_HandlerInitialized = false; -} - - - - - -cItemHandler::cItemHandler(int a_ItemID) -{ - m_ItemID = a_ItemID; -} - - - - - -bool cItemHandler::OnItemUse(cWorld *a_World, cPlayer *a_Player, cItem *a_Item, int a_X, int a_Y, int a_Z, char a_Dir) -{ - return false; -} - - - - - -bool cItemHandler::OnDiggingBlock(cWorld *a_World, cPlayer *a_Player, cItem *a_Item, int a_X, int a_Y, int a_Z, char a_Dir) -{ - return false; -} - - - - - -void cItemHandler::OnBlockDestroyed(cWorld *a_World, cPlayer *a_Player, cItem *a_Item, int a_X, int a_Y, int a_Z) -{ - char Block = a_World->GetBlock(a_X, a_Y, a_Z); - cBlockHandler *Handler = cBlockHandler::GetBlockHandler(Block); - - if(a_Player->GetGameMode() == eGameMode_Survival) - { - if(!BlockRequiresSpecialTool(Block) || CanHarvestBlock(Block)) - { - Handler->DropBlock(a_World, a_X, a_Y, a_Z); - } - } - - a_Player->UseEquippedItem(); -} - - - - - -void cItemHandler::OnFoodEaten(cWorld *a_World, cPlayer *a_Player, cItem *a_Item) -{ - -} - - - - - -char cItemHandler::GetMaxStackSize(void) -{ - if (m_ItemID < 256) - { - // All blocks can stack up to 64 - return 64; - } - - switch (m_ItemID) - { - case E_ITEM_APPLE: return 64; - case E_ITEM_ARROW: return 64; - case E_ITEM_BLAZE_POWDER: return 64; - case E_ITEM_BLAZE_ROD: return 64; - case E_ITEM_BONE: return 64; - case E_ITEM_BOOK: return 64; - case E_ITEM_BOWL: return 64; - case E_ITEM_BREAD: return 64; - case E_ITEM_BROWN_MUSHROOM: return 64; - case E_ITEM_BUCKET: return 1; // TODO: change this to 16 when turning compatibility to 1.3 - case E_ITEM_COAL: return 64; - case E_ITEM_COOKED_CHICKEN: return 64; - case E_ITEM_COOKED_FISH: return 64; - case E_ITEM_COOKED_PORKCHOP: return 64; - case E_ITEM_DIAMOND: return 64; - case E_ITEM_FEATHER: return 64; - case E_ITEM_FLINT: return 64; - case E_ITEM_GOLD: return 64; - case E_ITEM_GUNPOWDER: return 64; - case E_ITEM_IRON: return 64; - case E_ITEM_RAW_PORKCHOP: return 64; - case E_ITEM_SEEDS: return 64; - case E_ITEM_STICK: return 64; - case E_ITEM_STRING: return 64; - case E_ITEM_WHEAT: return 64; - } - // By default items don't stack: - return 1; -} - - - - - -bool cItemHandler::IsTool() -{ - return - (m_ItemID >= 256 && m_ItemID <= 259) - || (m_ItemID == 261) - || (m_ItemID >= 267 && m_ItemID <= 279) - || (m_ItemID >= 283 && m_ItemID <= 286) - || (m_ItemID >= 290 && m_ItemID <= 294) - || (m_ItemID >= 256 && m_ItemID <= 259) - || (m_ItemID == 325) - || (m_ItemID == 346); -} - - - - - -bool cItemHandler::IsFood() -{ - return - (m_ItemID == 260) - || (m_ItemID == 282) - || (m_ItemID == 297) - || (m_ItemID >= 319 && m_ItemID <= 320) - || (m_ItemID == 335) - || (m_ItemID >= 349 && m_ItemID <= 350) - || (m_ItemID == 357) - || (m_ItemID == 360) - || (m_ItemID >= 363 && m_ItemID <= 366); -} - - - - - -bool cItemHandler::IsPlaceable() -{ - return m_ItemID >= 1 && m_ItemID <= 136; -} - - - - - -bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockID) -{ - return false; -} - - - - - -BLOCKTYPE cItemHandler::GetBlockType() -{ - ASSERT(m_ItemID < 256); // Items with IDs above 255 should all be handled by specific handlers - - #ifdef _DEBUG - if (m_ItemID > 256) - { - LOGERROR("Item %d has no valid block!", m_ItemID); - } - #endif // _DEBUG - - return (BLOCKTYPE) m_ItemID; -} - - - - - -NIBBLETYPE cItemHandler::GetBlockMeta(short a_ItemDamage) -{ - return (NIBBLETYPE)a_ItemDamage & 0x0f; // This keeps most textures. The few other items have to override this -} - - - - - -void cItemHandler::PlaceBlock(cWorld *a_World, cPlayer *a_Player, cItem *a_Item, int a_X, int a_Y, int a_Z, char a_Dir) -{ - BLOCKTYPE Block = GetBlockType(); - cBlockHandler *Handler = cBlockHandler::GetBlockHandler(Block); - Handler->PlaceBlock(a_World, a_Player, GetBlockMeta(a_Item->m_ItemHealth), a_X, a_Y, a_Z, a_Dir); - if(a_Player->GetGameMode() == eGameMode_Survival) - { - cItem Item(a_Item->m_ItemID, 1); - a_Player->GetInventory().RemoveItem(Item); - } -} - - - - - -bool cItemHandler::EatItem(cPlayer *a_Player, cItem *a_Item) -{ - FoodInfo Info = GetFoodInfo(); - - if(Info.FoodLevel > 0 || Info.Saturation > 0.f) - { - bool Success = a_Player->Feed(Info.FoodLevel, Info.Saturation); - if(Success && Info.PoisionChance > 0) - { - MTRand r1; - if((r1.randInt(100) - Info.PoisionChance) <= 0) - { //Unlucky guy :D - //TODO: Make player ill - } - } - - return Success; - } - - return false; -} - - - - - -cItemHandler::FoodInfo cItemHandler::GetFoodInfo() -{ - return FoodInfo(0, 0.f); -} - - - - diff --git a/source/items/Item.h b/source/items/Item.h deleted file mode 100644 index 67935ecb4..000000000 --- a/source/items/Item.h +++ /dev/null @@ -1,83 +0,0 @@ - -#pragma once -#include "../Defines.h" - - - - - -// fwd: -class cWorld; -class cPlayer; - - - - - -class cItemHandler -{ -public: - cItemHandler(int a_ItemID); - // Called when the player tries to use the item. Return false to make the item unusable. DEFAULT: False - virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, cItem *a_Item, int a_X, int a_Y, int a_Z, char a_Dir); //eg for fishing or hoes - // Called while the player diggs a block using this item - virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, cItem * a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace); - // Called when the player destroys a block using this item. This also calls the drop function for the destroyed block - virtual void OnBlockDestroyed(cWorld *a_World, cPlayer *a_Player, cItem *a_Item, int a_X, int a_Y, int a_Z); - // Called after the player has eaten this item. - virtual void OnFoodEaten(cWorld *a_World, cPlayer *a_Player, cItem *a_Item); - - /// Returns the maximum stack size for a given item - virtual char GetMaxStackSize(void); - - struct FoodInfo - { - FoodInfo(short a_FoodLevel, float a_Saturation, char a_PoisionChance = 0) - { - FoodLevel = a_FoodLevel; - Saturation = a_Saturation; - PoisionChance = a_PoisionChance; - } - short FoodLevel; - float Saturation; - char PoisionChance; //0 - 100 - }; - - // Returns the FoodInfo for this item. (FoodRecovery, Saturation and PoisionChance) - virtual FoodInfo GetFoodInfo(); - - // Lets the player eat a selected item. Returns true if the player ate the item - virtual bool EatItem(cPlayer *a_Player, cItem *a_Item); - - // Places the current block and removes the item from the player inventory - virtual void PlaceBlock(cWorld *a_World, cPlayer *a_Player, cItem *a_Item, int a_X, int a_Y, int a_Z, char a_Dir); - - // Indicates if this item is a tool - virtual bool IsTool(); - // Indicates if this item is food - virtual bool IsFood(); - //Blocks simply get placed - virtual bool IsPlaceable(); - - // Returns the block type on placement - virtual BLOCKTYPE GetBlockType(); - //Returns the block meta on placement - virtual NIBBLETYPE GetBlockMeta(short a_ItemDamage); - // Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood canīt) DEFAULT: False - virtual bool CanHarvestBlock(BLOCKTYPE a_BlockID); - - static cItemHandler *GetItemHandler(int a_ItemID); - - static void Deinit(); - - -protected: - int m_ItemID; - static cItemHandler *CreateItemHandler(int m_ItemID); - - static cItemHandler *m_ItemHandler[2266]; - static bool m_HandlerInitialized; //used to detect if the itemhandlers are initialized -}; - -//Short function -inline cItemHandler *ItemHandler(int a_ItemID) { return cItemHandler::GetItemHandler(a_ItemID); } \ No newline at end of file diff --git a/source/items/ItemBucket.h b/source/items/ItemBucket.h index 3e233e4f6..736112960 100644 --- a/source/items/ItemBucket.h +++ b/source/items/ItemBucket.h @@ -1,7 +1,8 @@ + #pragma once -#include "Item.h" -#include "../cWorld.h" +#include "ItemHandler.h" +#include "../World.h" class cItemBucketHandler : public cItemHandler { diff --git a/source/items/ItemCloth.h b/source/items/ItemCloth.h index d67e14864..5de064397 100644 --- a/source/items/ItemCloth.h +++ b/source/items/ItemCloth.h @@ -1,7 +1,7 @@ #pragma once -#include "Item.h" +#include "ItemHandler.h" diff --git a/source/items/ItemDoor.h b/source/items/ItemDoor.h index ac5bc6872..6e841333f 100644 --- a/source/items/ItemDoor.h +++ b/source/items/ItemDoor.h @@ -1,7 +1,8 @@ + #pragma once -#include "Item.h" -#include "../cWorld.h" +#include "ItemHandler.h" +#include "../World.h" class cItemDoorHandler : public cItemHandler { diff --git a/source/items/ItemDye.h b/source/items/ItemDye.h index 2203f410b..5b21a501c 100644 --- a/source/items/ItemDye.h +++ b/source/items/ItemDye.h @@ -1,7 +1,9 @@ + #pragma once -#include "Item.h" -#include "../cWorld.h" -#include "../cPlayer.h" + +#include "ItemHandler.h" +#include "../World.h" +#include "../Player.h" class cItemDyeHandler : public cItemHandler { diff --git a/source/items/ItemFood.h b/source/items/ItemFood.h index 8998fd503..6e5a27d5d 100644 --- a/source/items/ItemFood.h +++ b/source/items/ItemFood.h @@ -1,6 +1,7 @@ + #pragma once -#include "Item.h" +#include "ItemHandler.h" class cItemFoodHandler : public cItemHandler diff --git a/source/items/ItemHandler.cpp b/source/items/ItemHandler.cpp new file mode 100644 index 000000000..44f6438e1 --- /dev/null +++ b/source/items/ItemHandler.cpp @@ -0,0 +1,418 @@ + +#include "Globals.h" +#include "ItemHandler.h" +#include "../Item.h" +#include "../World.h" +#include "../Player.h" + +//Handler +#include "ItemCloth.h" +#include "ItemHoe.h" +#include "ItemSlab.h" +#include "ItemWood.h" +#include "ItemShears.h" +#include "ItemLeaves.h" +#include "ItemSapling.h" +#include "ItemBucket.h" +#include "ItemLighter.h" +#include "ItemRedstoneDust.h" +#include "ItemRedstoneRepeater.h" +#include "ItemSeeds.h" +#include "ItemDye.h" +#include "ItemSugarcane.h" +#include "ItemPickaxe.h" +#include "ItemShovel.h" +#include "ItemSword.h" +#include "ItemDoor.h" +#include "ItemFood.h" +#include "ItemSign.h" + +#include "../blocks/BlockHandler.h" + + + + + +bool cItemHandler::m_HandlerInitialized = false; +cItemHandler * cItemHandler::m_ItemHandler[2266]; + + + + + +cItemHandler *cItemHandler::GetItemHandler(int a_ItemID) +{ + if(a_ItemID < 0) a_ItemID = 0; + + if(!m_HandlerInitialized) + { //We have to initialize + memset(m_ItemHandler, 0, sizeof(m_ItemHandler)); + m_HandlerInitialized = true; + } + if(m_ItemHandler[a_ItemID]) + return m_ItemHandler[a_ItemID]; + m_ItemHandler[a_ItemID] = CreateItemHandler(a_ItemID); + return m_ItemHandler[a_ItemID]; +} + + + + + +cItemHandler *cItemHandler::CreateItemHandler(int a_ItemID) +{ + switch(a_ItemID) + { + default: return new cItemHandler(a_ItemID); + + // Single item per handler: + case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemID); + case E_ITEM_LEAVES: return new cItemLeavesHandler(a_ItemID); + case E_ITEM_SAPLING: return new cItemSaplingHandler(a_ItemID); + case E_ITEM_DYE: return new cItemDyeHandler(a_ItemID); + case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemID); + case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemID); + case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemID); + case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemID); + case E_ITEM_WOOL: return new cItemClothHandler(a_ItemID); + + case E_ITEM_WOODEN_HOE: + case E_ITEM_STONE_HOE: + case E_ITEM_IRON_HOE: + case E_ITEM_GOLD_HOE: + case E_ITEM_DIAMOND_HOE: + { + return new cItemHoeHandler(a_ItemID); + } + + case E_ITEM_WOODEN_PICKAXE: + case E_ITEM_STONE_PICKAXE: + case E_ITEM_IRON_PICKAXE: + case E_ITEM_GOLD_PICKAXE: + case E_ITEM_DIAMOND_PICKAXE: + { + return new cItemPickaxeHandler(a_ItemID); + } + + case E_ITEM_WOODEN_SHOVEL: + case E_ITEM_STONE_SHOVEL: + case E_ITEM_IRON_SHOVEL: + case E_ITEM_GOLD_SHOVEL: + case E_ITEM_DIAMOND_SHOVEL: + { + return new cItemShovelHandler(a_ItemID); + } + + case E_ITEM_WOODEN_SWORD: + case E_ITEM_STONE_SWORD: + case E_ITEM_IRON_SWORD: + case E_ITEM_GOLD_SWORD: + case E_ITEM_DIAMOND_SWORD: + { + return new cItemSwordHandler(a_ItemID); + } + + case E_ITEM_STONE_SLAB: + case E_ITEM_WOODEN_SLAB: + { + return new cItemSlabHandler(a_ItemID); + } + + case E_ITEM_LOG: + case E_ITEM_PLANKS: + { + return new cItemWoodHandler(a_ItemID); + } + + case E_ITEM_BUCKET: + case E_ITEM_WATER_BUCKET: + case E_ITEM_LAVA_BUCKET: + { + return new cItemBucketHandler(a_ItemID); + } + + case E_ITEM_PUMPKIN_SEEDS: + case E_ITEM_MELON_SEEDS: + case E_ITEM_SEEDS: + { + return new cItemSeedsHandler(a_ItemID); + } + + case E_ITEM_IRON_DOOR: + case E_ITEM_WOODEN_DOOR: + { + return new cItemDoorHandler(a_ItemID); + } + + // Food: + case E_ITEM_BREAD: + case E_ITEM_COOKIE: + case E_ITEM_MELON_SLICE: + case E_ITEM_RAW_CHICKEN: + case E_ITEM_COOKED_CHICKEN: + case E_ITEM_RAW_BEEF: + case E_ITEM_RAW_MEAT: + case E_ITEM_STEAK: + case E_ITEM_COOKED_MEAT: + case E_ITEM_RAW_FISH: + case E_ITEM_COOKED_FISH: + case E_ITEM_RED_APPLE: + case E_ITEM_GOLDEN_APPLE: + case E_ITEM_ROTTEN_FLESH: + case E_ITEM_SPIDER_EYE: + { + return new cItemFoodHandler(a_ItemID); + } + + case E_ITEM_SIGN: + return new cItemSignHandler(a_ItemID); + } +} + + + + + +void cItemHandler::Deinit() +{ + for(int i = 0; i < 2266; i++) + { + delete m_ItemHandler[i]; + } + m_HandlerInitialized = false; +} + + + + + +cItemHandler::cItemHandler(int a_ItemID) +{ + m_ItemID = a_ItemID; +} + + + + + +bool cItemHandler::OnItemUse(cWorld *a_World, cPlayer *a_Player, cItem *a_Item, int a_X, int a_Y, int a_Z, char a_Dir) +{ + return false; +} + + + + + +bool cItemHandler::OnDiggingBlock(cWorld *a_World, cPlayer *a_Player, cItem *a_Item, int a_X, int a_Y, int a_Z, char a_Dir) +{ + return false; +} + + + + + +void cItemHandler::OnBlockDestroyed(cWorld *a_World, cPlayer *a_Player, cItem *a_Item, int a_X, int a_Y, int a_Z) +{ + char Block = a_World->GetBlock(a_X, a_Y, a_Z); + cBlockHandler *Handler = cBlockHandler::GetBlockHandler(Block); + + if(a_Player->GetGameMode() == eGameMode_Survival) + { + if(!BlockRequiresSpecialTool(Block) || CanHarvestBlock(Block)) + { + Handler->DropBlock(a_World, a_X, a_Y, a_Z); + } + } + + a_Player->UseEquippedItem(); +} + + + + + +void cItemHandler::OnFoodEaten(cWorld *a_World, cPlayer *a_Player, cItem *a_Item) +{ + +} + + + + + +char cItemHandler::GetMaxStackSize(void) +{ + if (m_ItemID < 256) + { + // All blocks can stack up to 64 + return 64; + } + + switch (m_ItemID) + { + case E_ITEM_APPLE: return 64; + case E_ITEM_ARROW: return 64; + case E_ITEM_BLAZE_POWDER: return 64; + case E_ITEM_BLAZE_ROD: return 64; + case E_ITEM_BONE: return 64; + case E_ITEM_BOOK: return 64; + case E_ITEM_BOWL: return 64; + case E_ITEM_BREAD: return 64; + case E_ITEM_BROWN_MUSHROOM: return 64; + case E_ITEM_BUCKET: return 1; // TODO: change this to 16 when turning compatibility to 1.3 + case E_ITEM_COAL: return 64; + case E_ITEM_COOKED_CHICKEN: return 64; + case E_ITEM_COOKED_FISH: return 64; + case E_ITEM_COOKED_PORKCHOP: return 64; + case E_ITEM_DIAMOND: return 64; + case E_ITEM_FEATHER: return 64; + case E_ITEM_FLINT: return 64; + case E_ITEM_GOLD: return 64; + case E_ITEM_GUNPOWDER: return 64; + case E_ITEM_IRON: return 64; + case E_ITEM_RAW_PORKCHOP: return 64; + case E_ITEM_SEEDS: return 64; + case E_ITEM_STICK: return 64; + case E_ITEM_STRING: return 64; + case E_ITEM_WHEAT: return 64; + } + // By default items don't stack: + return 1; +} + + + + + +bool cItemHandler::IsTool() +{ + return + (m_ItemID >= 256 && m_ItemID <= 259) + || (m_ItemID == 261) + || (m_ItemID >= 267 && m_ItemID <= 279) + || (m_ItemID >= 283 && m_ItemID <= 286) + || (m_ItemID >= 290 && m_ItemID <= 294) + || (m_ItemID >= 256 && m_ItemID <= 259) + || (m_ItemID == 325) + || (m_ItemID == 346); +} + + + + + +bool cItemHandler::IsFood() +{ + return + (m_ItemID == 260) + || (m_ItemID == 282) + || (m_ItemID == 297) + || (m_ItemID >= 319 && m_ItemID <= 320) + || (m_ItemID == 335) + || (m_ItemID >= 349 && m_ItemID <= 350) + || (m_ItemID == 357) + || (m_ItemID == 360) + || (m_ItemID >= 363 && m_ItemID <= 366); +} + + + + + +bool cItemHandler::IsPlaceable() +{ + return m_ItemID >= 1 && m_ItemID <= 136; +} + + + + + +bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockID) +{ + return false; +} + + + + + +BLOCKTYPE cItemHandler::GetBlockType() +{ + ASSERT(m_ItemID < 256); // Items with IDs above 255 should all be handled by specific handlers + + #ifdef _DEBUG + if (m_ItemID > 256) + { + LOGERROR("Item %d has no valid block!", m_ItemID); + } + #endif // _DEBUG + + return (BLOCKTYPE) m_ItemID; +} + + + + + +NIBBLETYPE cItemHandler::GetBlockMeta(short a_ItemDamage) +{ + return (NIBBLETYPE)a_ItemDamage & 0x0f; // This keeps most textures. The few other items have to override this +} + + + + + +void cItemHandler::PlaceBlock(cWorld *a_World, cPlayer *a_Player, cItem *a_Item, int a_X, int a_Y, int a_Z, char a_Dir) +{ + BLOCKTYPE Block = GetBlockType(); + cBlockHandler *Handler = cBlockHandler::GetBlockHandler(Block); + Handler->PlaceBlock(a_World, a_Player, GetBlockMeta(a_Item->m_ItemHealth), a_X, a_Y, a_Z, a_Dir); + if(a_Player->GetGameMode() == eGameMode_Survival) + { + cItem Item(a_Item->m_ItemID, 1); + a_Player->GetInventory().RemoveItem(Item); + } +} + + + + + +bool cItemHandler::EatItem(cPlayer *a_Player, cItem *a_Item) +{ + FoodInfo Info = GetFoodInfo(); + + if(Info.FoodLevel > 0 || Info.Saturation > 0.f) + { + bool Success = a_Player->Feed(Info.FoodLevel, Info.Saturation); + if(Success && Info.PoisionChance > 0) + { + MTRand r1; + if((r1.randInt(100) - Info.PoisionChance) <= 0) + { //Unlucky guy :D + //TODO: Make player ill + } + } + + return Success; + } + + return false; +} + + + + + +cItemHandler::FoodInfo cItemHandler::GetFoodInfo() +{ + return FoodInfo(0, 0.f); +} + + + + diff --git a/source/items/ItemHandler.h b/source/items/ItemHandler.h new file mode 100644 index 000000000..67935ecb4 --- /dev/null +++ b/source/items/ItemHandler.h @@ -0,0 +1,83 @@ + +#pragma once +#include "../Defines.h" + + + + + +// fwd: +class cWorld; +class cPlayer; + + + + + +class cItemHandler +{ +public: + cItemHandler(int a_ItemID); + // Called when the player tries to use the item. Return false to make the item unusable. DEFAULT: False + virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, cItem *a_Item, int a_X, int a_Y, int a_Z, char a_Dir); //eg for fishing or hoes + // Called while the player diggs a block using this item + virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, cItem * a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace); + // Called when the player destroys a block using this item. This also calls the drop function for the destroyed block + virtual void OnBlockDestroyed(cWorld *a_World, cPlayer *a_Player, cItem *a_Item, int a_X, int a_Y, int a_Z); + // Called after the player has eaten this item. + virtual void OnFoodEaten(cWorld *a_World, cPlayer *a_Player, cItem *a_Item); + + /// Returns the maximum stack size for a given item + virtual char GetMaxStackSize(void); + + struct FoodInfo + { + FoodInfo(short a_FoodLevel, float a_Saturation, char a_PoisionChance = 0) + { + FoodLevel = a_FoodLevel; + Saturation = a_Saturation; + PoisionChance = a_PoisionChance; + } + short FoodLevel; + float Saturation; + char PoisionChance; //0 - 100 + }; + + // Returns the FoodInfo for this item. (FoodRecovery, Saturation and PoisionChance) + virtual FoodInfo GetFoodInfo(); + + // Lets the player eat a selected item. Returns true if the player ate the item + virtual bool EatItem(cPlayer *a_Player, cItem *a_Item); + + // Places the current block and removes the item from the player inventory + virtual void PlaceBlock(cWorld *a_World, cPlayer *a_Player, cItem *a_Item, int a_X, int a_Y, int a_Z, char a_Dir); + + // Indicates if this item is a tool + virtual bool IsTool(); + // Indicates if this item is food + virtual bool IsFood(); + //Blocks simply get placed + virtual bool IsPlaceable(); + + // Returns the block type on placement + virtual BLOCKTYPE GetBlockType(); + //Returns the block meta on placement + virtual NIBBLETYPE GetBlockMeta(short a_ItemDamage); + // Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood canīt) DEFAULT: False + virtual bool CanHarvestBlock(BLOCKTYPE a_BlockID); + + static cItemHandler *GetItemHandler(int a_ItemID); + + static void Deinit(); + + +protected: + int m_ItemID; + static cItemHandler *CreateItemHandler(int m_ItemID); + + static cItemHandler *m_ItemHandler[2266]; + static bool m_HandlerInitialized; //used to detect if the itemhandlers are initialized +}; + +//Short function +inline cItemHandler *ItemHandler(int a_ItemID) { return cItemHandler::GetItemHandler(a_ItemID); } \ No newline at end of file diff --git a/source/items/ItemHoe.h b/source/items/ItemHoe.h index 64dc4ca34..e7ea9135c 100644 --- a/source/items/ItemHoe.h +++ b/source/items/ItemHoe.h @@ -1,7 +1,9 @@ + #pragma once -#include "Item.h" -#include "../cWorld.h" -#include "../cPlayer.h" + +#include "ItemHandler.h" +#include "../World.h" +#include "../Player.h" class cItemHoeHandler : public cItemHandler { diff --git a/source/items/ItemLeaves.h b/source/items/ItemLeaves.h index 01b090a2f..f96adb0b8 100644 --- a/source/items/ItemLeaves.h +++ b/source/items/ItemLeaves.h @@ -1,7 +1,7 @@ #pragma once -#include "Item.h" +#include "ItemHandler.h" diff --git a/source/items/ItemLighter.h b/source/items/ItemLighter.h index 213677e4e..92279aafa 100644 --- a/source/items/ItemLighter.h +++ b/source/items/ItemLighter.h @@ -1,8 +1,8 @@ #pragma once -#include "Item.h" -#include "../cWorld.h" -#include "../cPlayer.h" +#include "ItemHandler.h" +#include "../World.h" +#include "../Player.h" class cItemLighterHandler : public cItemHandler { diff --git a/source/items/ItemPickaxe.h b/source/items/ItemPickaxe.h index a179d154f..158aca44f 100644 --- a/source/items/ItemPickaxe.h +++ b/source/items/ItemPickaxe.h @@ -1,7 +1,9 @@ + #pragma once -#include "Item.h" -#include "../cWorld.h" -#include "../cPlayer.h" + +#include "ItemHandler.h" +#include "../World.h" +#include "../Player.h" class cItemPickaxeHandler : public cItemHandler { diff --git a/source/items/ItemRedstoneDust.h b/source/items/ItemRedstoneDust.h index 4d1459bc6..1ce316d9e 100644 --- a/source/items/ItemRedstoneDust.h +++ b/source/items/ItemRedstoneDust.h @@ -1,6 +1,7 @@ #pragma once -#include "Item.h" + +#include "ItemHandler.h" diff --git a/source/items/ItemRedstoneRepeater.h b/source/items/ItemRedstoneRepeater.h index 85500c9f4..c31c2af53 100644 --- a/source/items/ItemRedstoneRepeater.h +++ b/source/items/ItemRedstoneRepeater.h @@ -1,6 +1,7 @@ #pragma once -#include "Item.h" + +#include "ItemHandler.h" diff --git a/source/items/ItemSapling.h b/source/items/ItemSapling.h index 7edbc76c9..b0c223f29 100644 --- a/source/items/ItemSapling.h +++ b/source/items/ItemSapling.h @@ -1,7 +1,7 @@ #pragma once -#include "Item.h" +#include "ItemHandler.h" diff --git a/source/items/ItemSeeds.h b/source/items/ItemSeeds.h index 07cf0e5de..3eb9d6e86 100644 --- a/source/items/ItemSeeds.h +++ b/source/items/ItemSeeds.h @@ -1,8 +1,8 @@ #pragma once -#include "Item.h" -#include "../cWorld.h" +#include "ItemHandler.h" +#include "../World.h" diff --git a/source/items/ItemShears.h b/source/items/ItemShears.h index ab2981aae..2f3476735 100644 --- a/source/items/ItemShears.h +++ b/source/items/ItemShears.h @@ -1,8 +1,9 @@ + #pragma once -#include "Item.h" -#include "../cWorld.h" -#include "../cPlayer.h" +#include "ItemHandler.h" +#include "../World.h" +#include "../Player.h" class cItemShearsHandler : public cItemHandler { diff --git a/source/items/ItemShovel.h b/source/items/ItemShovel.h index 3279ef419..24928efb8 100644 --- a/source/items/ItemShovel.h +++ b/source/items/ItemShovel.h @@ -1,9 +1,15 @@ + #pragma once -#include "Item.h" -#include "../cWorld.h" -#include "../cPlayer.h" -#include "../blocks/Block.h" +#include "ItemHandler.h" +#include "../World.h" +#include "../Player.h" + +#include "../blocks/BlockHandler.h" + + + + class cItemShovelHandler : public cItemHandler { diff --git a/source/items/ItemSign.h b/source/items/ItemSign.h index 06116f0e8..a0c6aaa9c 100644 --- a/source/items/ItemSign.h +++ b/source/items/ItemSign.h @@ -1,7 +1,8 @@ + #pragma once -#include "Item.h" -#include "../cWorld.h" +#include "ItemHandler.h" +#include "../World.h" class cItemSignHandler : public cItemHandler { diff --git a/source/items/ItemSlab.h b/source/items/ItemSlab.h index 955d3acfc..0bab038a2 100644 --- a/source/items/ItemSlab.h +++ b/source/items/ItemSlab.h @@ -1,8 +1,8 @@ #pragma once -#include "Item.h" -#include "../cWorld.h" +#include "ItemHandler.h" +#include "../World.h" diff --git a/source/items/ItemSugarcane.h b/source/items/ItemSugarcane.h index 74557479e..4fdf43428 100644 --- a/source/items/ItemSugarcane.h +++ b/source/items/ItemSugarcane.h @@ -1,8 +1,8 @@ #pragma once -#include "Item.h" -#include "../cWorld.h" +#include "ItemHandler.h" +#include "../World.h" diff --git a/source/items/ItemSword.h b/source/items/ItemSword.h index cbe172c8f..eb4aa0ef9 100644 --- a/source/items/ItemSword.h +++ b/source/items/ItemSword.h @@ -1,7 +1,9 @@ + #pragma once -#include "Item.h" -#include "../cWorld.h" -#include "../cPlayer.h" + +#include "ItemHandler.h" +#include "../World.h" +#include "../Player.h" class cItemSwordHandler : public cItemHandler { diff --git a/source/items/ItemWood.h b/source/items/ItemWood.h index 7e874efc2..d6f66d1c5 100644 --- a/source/items/ItemWood.h +++ b/source/items/ItemWood.h @@ -1,7 +1,7 @@ #pragma once -#include "Item.h" +#include "ItemHandler.h" diff --git a/source/main.cpp b/source/main.cpp index 0fdaf2d50..5e67531ab 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,7 +1,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules -#include "cRoot.h" +#include "Root.h" #include //std::exception #include //std::signal diff --git a/source/squirrelbindings/SquirrelBaseClass.h b/source/squirrelbindings/SquirrelBaseClass.h new file mode 100644 index 000000000..ef03c1a55 --- /dev/null +++ b/source/squirrelbindings/SquirrelBaseClass.h @@ -0,0 +1,41 @@ +#pragma once +#include "SquirrelBindings.h" +#include "../Plugin_Squirrel.h" +#include "../PluginManager.h" +#include "../Root.h" +#include "../SquirrelCommandBinder.h" + +//The baseclass for squirrel plugins +class cSquirrelBaseClass +{ +public: + cSquirrelBaseClass() + : m_Instance(0) + { + } + + void setInstance(cPlugin_Squirrel *a_Instance) + { + m_Instance = a_Instance; + } + + void AddHook(short a_Hook) + { + if(m_Instance) + cRoot::Get()->GetPluginManager()->AddHook(m_Instance, (cPluginManager::PluginHook) a_Hook); + } + + void AddCommand( std::string a_Command, std::string a_Description, std::string a_Permission ) + { + if(m_Instance) m_Instance->AddCommand(a_Command, a_Description, a_Permission); + } + + bool BindCommand( const std::string a_Command, const std::string a_Permission, Sqrat::Function a_Callback) + { + if(!m_Instance) return false; + return cRoot::Get()->GetPluginManager()->GetSquirrelCommandBinder()->BindCommand(a_Command, a_Permission, m_Instance, a_Callback); + } + +protected: + cPlugin_Squirrel *m_Instance; +}; \ No newline at end of file diff --git a/source/squirrelbindings/SquirrelBindings.cpp b/source/squirrelbindings/SquirrelBindings.cpp index 206d8bbcd..5c6b585f4 100644 --- a/source/squirrelbindings/SquirrelBindings.cpp +++ b/source/squirrelbindings/SquirrelBindings.cpp @@ -2,10 +2,10 @@ #include "SquirrelBindings.h" #include "SquirrelFunctions.h" -#include "cSquirrelBaseClass.h" +#include "SquirrelBaseClass.h" #include "SquirrelArray.h" -#include "../cPlayer.h" +#include "../Player.h" using namespace Sqrat; diff --git a/source/squirrelbindings/cSquirrelBaseClass.h b/source/squirrelbindings/cSquirrelBaseClass.h deleted file mode 100644 index 279c47027..000000000 --- a/source/squirrelbindings/cSquirrelBaseClass.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#include "SquirrelBindings.h" -#include "../cPlugin_Squirrel.h" -#include "../cPluginManager.h" -#include "../cRoot.h" -#include "../cSquirrelCommandBinder.h" - -//The baseclass for squirrel plugins -class cSquirrelBaseClass -{ -public: - cSquirrelBaseClass() - : m_Instance(0) - { - } - - void setInstance(cPlugin_Squirrel *a_Instance) - { - m_Instance = a_Instance; - } - - void AddHook(short a_Hook) - { - if(m_Instance) - cRoot::Get()->GetPluginManager()->AddHook(m_Instance, (cPluginManager::PluginHook) a_Hook); - } - - void AddCommand( std::string a_Command, std::string a_Description, std::string a_Permission ) - { - if(m_Instance) m_Instance->AddCommand(a_Command, a_Description, a_Permission); - } - - bool BindCommand( const std::string a_Command, const std::string a_Permission, Sqrat::Function a_Callback) - { - if(!m_Instance) return false; - return cRoot::Get()->GetPluginManager()->GetSquirrelCommandBinder()->BindCommand(a_Command, a_Permission, m_Instance, a_Callback); - } - -protected: - cPlugin_Squirrel *m_Instance; -}; \ No newline at end of file -- cgit v1.2.3