diff options
Diffstat (limited to '')
202 files changed, 4458 insertions, 1316 deletions
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 2f5d173fd..01d3ac687 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -839,6 +839,13 @@ void cLuaState::Push(void * a_Ptr) m_NumCurrentFunctionArgs += 1; } +void cLuaState::Push(std::chrono::milliseconds a_Value) +{ + ASSERT(IsValid()); + + tolua_pushnumber(m_LuaState, static_cast<lua_Number>(a_Value.count())); + m_NumCurrentFunctionArgs += 1; +} diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index c13e36188..7ac4120e1 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -217,6 +217,7 @@ public: void Push(Vector3d * a_Vector); void Push(Vector3i * a_Vector); void Push(void * a_Ptr); + void Push(std::chrono::milliseconds a_time); /** Retrieve value at a_StackPos, if it is a valid bool. If not, a_Value is unchanged */ void GetStackValue(int a_StackPos, bool & a_Value); diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index 08677553c..6210dbed4 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -76,8 +76,8 @@ public: virtual bool OnPlayerJoined (cPlayer & a_Player) = 0; virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) = 0; virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0; - virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; - virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; + virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) = 0; + virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) = 0; virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0; virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) = 0; virtual bool OnPlayerShooting (cPlayer & a_Player) = 0; @@ -104,7 +104,7 @@ public: virtual bool OnWeatherChanged (cWorld & a_World) = 0; virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather) = 0; virtual bool OnWorldStarted (cWorld & a_World) = 0; - virtual bool OnWorldTick (cWorld & a_World, float a_Dt, int a_LastTickDurationMSec) = 0; + virtual bool OnWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) = 0; /** Handles the command split into a_Split, issued by player a_Player. Command permissions have already been checked. diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index ea782ea3f..500913e76 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -857,14 +857,19 @@ bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPositi -bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { cCSLock Lock(m_CriticalSection); bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACED_BLOCK]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { - m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); + m_LuaState.Call((int)(**itr), &a_Player, + a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), + a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta, + cLuaState::Return, + res + ); if (res) { return true; @@ -877,14 +882,19 @@ bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_Blo -bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { cCSLock Lock(m_CriticalSection); bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACING_BLOCK]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { - m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); + m_LuaState.Call((int)(**itr), &a_Player, + a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), + a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta, + cLuaState::Return, + res + ); if (res) { return true; @@ -1420,7 +1430,7 @@ bool cPluginLua::OnWorldStarted(cWorld & a_World) -bool cPluginLua::OnWorldTick(cWorld & a_World, float a_Dt, int a_LastTickDurationMSec) +bool cPluginLua::OnWorldTick(cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) { cCSLock Lock(m_CriticalSection); cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_TICK]; diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index 7de5ffec4..f443f5fc0 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -100,8 +100,8 @@ public: virtual bool OnPlayerJoined (cPlayer & a_Player) override; virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) override; virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) override; - virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; - virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; + virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) override; + virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) override; virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) override; virtual bool OnPlayerShooting (cPlayer & a_Player) override; @@ -128,7 +128,7 @@ public: virtual bool OnWeatherChanged (cWorld & a_World) override; virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather) override; virtual bool OnWorldStarted (cWorld & a_World) override; - virtual bool OnWorldTick (cWorld & a_World, float a_Dt, int a_LastTickDurationMSec) override; + virtual bool OnWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) override; virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player) override; diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 406a540f4..9d86c64a2 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -771,7 +771,7 @@ bool cPluginManager::CallHookPlayerFoodLevelChange(cPlayer & a_Player, int a_New -bool cPluginManager::CallHookPlayerFished(cPlayer & a_Player, const cItems a_Reward) +bool cPluginManager::CallHookPlayerFished(cPlayer & a_Player, const cItems & a_Reward) { FIND_HOOK(HOOK_PLAYER_FISHED); VERIFY_HOOK; @@ -847,7 +847,7 @@ bool cPluginManager::CallHookPlayerLeftClick(cPlayer & a_Player, int a_BlockX, i -bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition) +bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) { FIND_HOOK(HOOK_PLAYER_MOVING); VERIFY_HOOK; @@ -866,14 +866,14 @@ bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player, const Vector3d a_O -bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { FIND_HOOK(HOOK_PLAYER_PLACED_BLOCK); VERIFY_HOOK; for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) { - if ((*itr)->OnPlayerPlacedBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) + if ((*itr)->OnPlayerPlacedBlock(a_Player, a_BlockChange)) { return true; } @@ -885,14 +885,14 @@ bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, -bool cPluginManager::CallHookPlayerPlacingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +bool cPluginManager::CallHookPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { FIND_HOOK(HOOK_PLAYER_PLACING_BLOCK); VERIFY_HOOK; for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) { - if ((*itr)->OnPlayerPlacingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) + if ((*itr)->OnPlayerPlacingBlock(a_Player, a_BlockChange)) { return true; } @@ -1394,7 +1394,7 @@ bool cPluginManager::CallHookWorldStarted(cWorld & a_World) -bool cPluginManager::CallHookWorldTick(cWorld & a_World, float a_Dt, int a_LastTickDurationMSec) +bool cPluginManager::CallHookWorldTick(cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) { FIND_HOOK(HOOK_WORLD_TICK); VERIFY_HOOK; diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index 3a2aecc92..97e91c1df 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -203,14 +203,14 @@ public: bool CallHookPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); bool CallHookPlayerDestroyed (cPlayer & a_Player); bool CallHookPlayerEating (cPlayer & a_Player); - bool CallHookPlayerFished (cPlayer & a_Player, const cItems a_Reward); + bool CallHookPlayerFished (cPlayer & a_Player, const cItems & a_Reward); bool CallHookPlayerFishing (cPlayer & a_Player, cItems a_Reward); bool CallHookPlayerFoodLevelChange (cPlayer & a_Player, int a_NewFoodLevel); bool CallHookPlayerJoined (cPlayer & a_Player); - bool CallHookPlayerMoving (cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition); bool CallHookPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status); - bool CallHookPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - bool CallHookPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + bool CallHookPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition); + bool CallHookPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange); + bool CallHookPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange); bool CallHookPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ); bool CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity); bool CallHookPlayerShooting (cPlayer & a_Player); @@ -237,7 +237,7 @@ public: bool CallHookWeatherChanged (cWorld & a_World); bool CallHookWeatherChanging (cWorld & a_World, eWeather & a_NewWeather); bool CallHookWorldStarted (cWorld & a_World); - bool CallHookWorldTick (cWorld & a_World, float a_Dt, int a_LastTickDurationMSec); + bool CallHookWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec); bool DisablePlugin(const AString & a_PluginName); // tolua_export bool LoadPlugin (const AString & a_PluginName); // tolua_export diff --git a/src/BlockEntities/BeaconEntity.cpp b/src/BlockEntities/BeaconEntity.cpp index 18e697b19..7038a98c7 100644 --- a/src/BlockEntities/BeaconEntity.cpp +++ b/src/BlockEntities/BeaconEntity.cpp @@ -267,7 +267,7 @@ void cBeaconEntity::GiveEffects(void) -bool cBeaconEntity::Tick(float a_Dt, cChunk & a_Chunk) +bool cBeaconEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // Update the beacon every 4 seconds if ((GetWorld()->GetWorldAge() % 80) == 0) diff --git a/src/BlockEntities/BeaconEntity.h b/src/BlockEntities/BeaconEntity.h index bc27e92b0..4f723c617 100644 --- a/src/BlockEntities/BeaconEntity.h +++ b/src/BlockEntities/BeaconEntity.h @@ -30,7 +30,7 @@ public: // cBlockEntity overrides: virtual void SendTo(cClientHandle & a_Client) override; - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void UsedBy(cPlayer * a_Player) override; /** Modify the beacon level. (It is needed to load the beacon corectly) */ diff --git a/src/BlockEntities/BlockEntity.h b/src/BlockEntities/BlockEntity.h index 056a88721..85f75a523 100644 --- a/src/BlockEntities/BlockEntity.h +++ b/src/BlockEntities/BlockEntity.h @@ -110,7 +110,7 @@ public: virtual void SendTo(cClientHandle & a_Client) = 0; /// Ticks the entity; returns true if the chunk should be marked as dirty as a result of this ticking. By default does nothing. - virtual bool Tick(float a_Dt, cChunk & a_Chunk) + virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); return false; diff --git a/src/BlockEntities/CommandBlockEntity.cpp b/src/BlockEntities/CommandBlockEntity.cpp index b7f938ffd..13c2637dc 100644 --- a/src/BlockEntities/CommandBlockEntity.cpp +++ b/src/BlockEntities/CommandBlockEntity.cpp @@ -125,7 +125,7 @@ void cCommandBlockEntity::SetRedstonePower(bool a_IsPowered) -bool cCommandBlockEntity::Tick(float a_Dt, cChunk & a_Chunk) +bool cCommandBlockEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); UNUSED(a_Chunk); diff --git a/src/BlockEntities/CommandBlockEntity.h b/src/BlockEntities/CommandBlockEntity.h index d8ac054f0..c525ebc26 100644 --- a/src/BlockEntities/CommandBlockEntity.h +++ b/src/BlockEntities/CommandBlockEntity.h @@ -33,7 +33,7 @@ public: /// Creates a new empty command block entity cCommandBlockEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void SendTo(cClientHandle & a_Client) override; virtual void UsedBy(cPlayer * a_Player) override; diff --git a/src/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp index 97a3aecb0..039f5d360 100644 --- a/src/BlockEntities/DropSpenserEntity.cpp +++ b/src/BlockEntities/DropSpenserEntity.cpp @@ -126,7 +126,7 @@ void cDropSpenserEntity::SetRedstonePower(bool a_IsPowered) -bool cDropSpenserEntity::Tick(float a_Dt, cChunk & a_Chunk) +bool cDropSpenserEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); if (!m_ShouldDropSpense) diff --git a/src/BlockEntities/DropSpenserEntity.h b/src/BlockEntities/DropSpenserEntity.h index 6c23a402f..83500addb 100644 --- a/src/BlockEntities/DropSpenserEntity.h +++ b/src/BlockEntities/DropSpenserEntity.h @@ -47,7 +47,7 @@ public: virtual ~cDropSpenserEntity(); // cBlockEntity overrides: - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void SendTo(cClientHandle & a_Client) override; virtual void UsedBy(cPlayer * a_Player) override; diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp index c6d575eac..2621b560b 100644 --- a/src/BlockEntities/FurnaceEntity.cpp +++ b/src/BlockEntities/FurnaceEntity.cpp @@ -90,7 +90,7 @@ bool cFurnaceEntity::ContinueCooking(void) -bool cFurnaceEntity::Tick(float a_Dt, cChunk & a_Chunk) +bool cFurnaceEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h index fbe9d6c75..8b3ba3e36 100644 --- a/src/BlockEntities/FurnaceEntity.h +++ b/src/BlockEntities/FurnaceEntity.h @@ -42,7 +42,7 @@ public: // cBlockEntity overrides: virtual void SendTo(cClientHandle & a_Client) override; - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void UsedBy(cPlayer * a_Player) override; virtual void Destroy() override { diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp index 7884639bd..bfd4b8322 100644 --- a/src/BlockEntities/HopperEntity.cpp +++ b/src/BlockEntities/HopperEntity.cpp @@ -55,7 +55,7 @@ bool cHopperEntity::GetOutputBlockPos(NIBBLETYPE a_BlockMeta, int & a_OutputX, i -bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk) +bool cHopperEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge(); diff --git a/src/BlockEntities/HopperEntity.h b/src/BlockEntities/HopperEntity.h index da65aa671..af99c526d 100644 --- a/src/BlockEntities/HopperEntity.h +++ b/src/BlockEntities/HopperEntity.h @@ -48,7 +48,7 @@ protected: Int64 m_LastMoveItemsOutTick; // cBlockEntity overrides: - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void SendTo(cClientHandle & a_Client) override; virtual void UsedBy(cPlayer * a_Player) override; diff --git a/src/BlockEntities/MobSpawnerEntity.cpp b/src/BlockEntities/MobSpawnerEntity.cpp index a7d29638a..9b3f605f9 100644 --- a/src/BlockEntities/MobSpawnerEntity.cpp +++ b/src/BlockEntities/MobSpawnerEntity.cpp @@ -73,7 +73,7 @@ void cMobSpawnerEntity::UpdateActiveState(void) -bool cMobSpawnerEntity::Tick(float a_Dt, cChunk & a_Chunk) +bool cMobSpawnerEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // Update the active flag every 5 seconds if ((m_World->GetWorldAge() % 100) == 0) diff --git a/src/BlockEntities/MobSpawnerEntity.h b/src/BlockEntities/MobSpawnerEntity.h index 594b5301e..b572e6657 100644 --- a/src/BlockEntities/MobSpawnerEntity.h +++ b/src/BlockEntities/MobSpawnerEntity.h @@ -29,7 +29,7 @@ public: virtual void SendTo(cClientHandle & a_Client) override; virtual void UsedBy(cPlayer * a_Player) override; - virtual bool Tick(float a_Dt, cChunk & a_Chunk) override; + virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; // tolua_begin diff --git a/src/BlockID.h b/src/BlockID.h index 8f2cee02e..41ccf90b5 100644 --- a/src/BlockID.h +++ b/src/BlockID.h @@ -959,6 +959,7 @@ enum E_META_SPAWN_EGG_WITHER = 64, E_META_SPAWN_EGG_BAT = 65, E_META_SPAWN_EGG_WITCH = 66, + E_META_SPAWN_EGG_GUARDIAN = 68, E_META_SPAWN_EGG_PIG = 90, E_META_SPAWN_EGG_SHEEP = 91, E_META_SPAWN_EGG_COW = 92, @@ -970,6 +971,7 @@ enum E_META_SPAWN_EGG_OCELOT = 98, E_META_SPAWN_EGG_IRON_GOLEM = 99, E_META_SPAWN_EGG_HORSE = 100, + E_META_SPAWN_EGG_RABBIT = 101, E_META_SPAWN_EGG_VILLAGER = 120, E_META_SPAWN_EGG_ENDER_CRYSTAL = 200, } ; diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp index 3b6328b38..57b9855d0 100644 --- a/src/Blocks/BlockBed.cpp +++ b/src/Blocks/BlockBed.cpp @@ -14,24 +14,6 @@ -void cBlockBedHandler::OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta -) -{ - if (a_BlockMeta < 8) - { - Vector3i Direction = MetaDataToDirection(a_BlockMeta); - a_ChunkInterface.SetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, a_BlockMeta | 0x8); - } -} - - - - - void cBlockBedHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) { NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); @@ -151,7 +133,7 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface cPlayerBedStateUnsetter Unsetter(Vector3i(a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z), a_WorldInterface); a_WorldInterface.ForEachPlayer(Unsetter); a_WorldInterface.SetTimeOfDay(0); - a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0xB); // Where 0xB = 1011, and zero is to make sure 'occupied' bit is always unset + a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0x0b); // Clear the "occupied" bit of the bed's block } } } diff --git a/src/Blocks/BlockBed.h b/src/Blocks/BlockBed.h index a8b5be899..5b746110a 100644 --- a/src/Blocks/BlockBed.h +++ b/src/Blocks/BlockBed.h @@ -23,7 +23,6 @@ public: } - virtual void OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override; virtual void OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; diff --git a/src/Blocks/BlockBigFlower.h b/src/Blocks/BlockBigFlower.h index 3577bdd40..5240ddf53 100644 --- a/src/Blocks/BlockBigFlower.h +++ b/src/Blocks/BlockBigFlower.h @@ -85,18 +85,6 @@ public: } - virtual void OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override - { - int Meta = (((int)floor(a_Player->GetYaw() * 4.0 / 360.0 + 0.5) & 0x3) + 2) % 4; - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, 0x8 | Meta); - } - - virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override { NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); diff --git a/src/Blocks/BlockChest.h b/src/Blocks/BlockChest.h index 201f2309b..01fec7f8b 100644 --- a/src/Blocks/BlockChest.h +++ b/src/Blocks/BlockChest.h @@ -62,50 +62,11 @@ public: } // Single chest, get meta from rotation only - a_BlockMeta = RotationToMetaData(yaw); + a_BlockMeta = PlayerYawToMetaData(yaw); return true; } - virtual void OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override - { - // Check if this forms a doublechest, if so, need to adjust the meta: - cBlockArea Area; - if (!Area.Read(&a_ChunkInterface, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1)) - { - return; - } - - double rot = a_Player->GetYaw(); // FIXME: Rename rot to yaw - // Choose meta from player rotation, choose only between 2 or 3 - NIBBLETYPE NewMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3; - if ( - CheckAndAdjustNeighbor(a_ChunkInterface, Area, 0, 1, NewMeta) || - CheckAndAdjustNeighbor(a_ChunkInterface, Area, 2, 1, NewMeta) - ) - { - // Forming a double chest in the X direction - return; - } - // Choose meta from player rotation, choose only between 4 or 5 - NewMeta = (rot < 0) ? 4 : 5; - if ( - CheckAndAdjustNeighbor(a_ChunkInterface, Area, 1, 0, NewMeta) || - CheckAndAdjustNeighbor(a_ChunkInterface, Area, 2, 2, NewMeta) - ) - { - // Forming a double chest in the Z direction - return; - } - - // Single chest, no further processing needed - } - virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override { int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; @@ -180,30 +141,30 @@ public: } - /// Translates player rotation when placing a chest into the chest block metadata. Valid for single chests only - static NIBBLETYPE RotationToMetaData(double a_Rotation) + /** Translates player yaw when placing a chest into the chest block metadata. Valid for single chests only */ + static NIBBLETYPE PlayerYawToMetaData(double a_Yaw) { - a_Rotation += 90 + 45; // So its not aligned with axis + a_Yaw += 90 + 45; // So its not aligned with axis - if (a_Rotation > 360.f) + if (a_Yaw > 360.f) { - a_Rotation -= 360.f; + a_Yaw -= 360.f; } - if ((a_Rotation >= 0.f) && (a_Rotation < 90.f)) + if ((a_Yaw >= 0.f) && (a_Yaw < 90.f)) { - return 0x4; + return 0x04; } - else if ((a_Rotation >= 180) && (a_Rotation < 270)) + else if ((a_Yaw >= 180) && (a_Yaw < 270)) { - return 0x5; + return 0x05; } - else if ((a_Rotation >= 90) && (a_Rotation < 180)) + else if ((a_Yaw >= 90) && (a_Yaw < 180)) { - return 0x2; + return 0x02; } else { - return 0x3; + return 0x03; } } diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp index 90b7b15c2..d2bf180be 100644 --- a/src/Blocks/BlockDoor.cpp +++ b/src/Blocks/BlockDoor.cpp @@ -23,7 +23,7 @@ void cBlockDoorHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldIn if (OldMeta & 8) { // Was upper part of door - if (IsDoor(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) + if (IsDoorBlockType(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) { a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); } @@ -31,7 +31,7 @@ void cBlockDoorHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldIn else { // Was lower part - if (IsDoor(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))) + if (IsDoorBlockType(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))) { a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_AIR, 0); } @@ -84,52 +84,34 @@ void cBlockDoorHandler::OnCancelRightClick(cChunkInterface & a_ChunkInterface, c -void cBlockDoorHandler::OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta -) -{ - NIBBLETYPE a_TopBlockMeta = 8; - if ( - ((a_BlockMeta == 0) && (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) == m_BlockType)) || - ((a_BlockMeta == 1) && (a_ChunkInterface.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) == m_BlockType)) || - ((a_BlockMeta == 2) && (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) == m_BlockType)) || - ((a_BlockMeta == 3) && (a_ChunkInterface.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) == m_BlockType)) - ) - { - a_TopBlockMeta = 9; - } - a_ChunkInterface.SetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, a_TopBlockMeta); -} - - - - - NIBBLETYPE cBlockDoorHandler::MetaRotateCCW(NIBBLETYPE a_Meta) { if (a_Meta & 0x08) { + // The meta doesn't change for the top block return a_Meta; } else { + // Rotate the bottom block return super::MetaRotateCCW(a_Meta); } } + + NIBBLETYPE cBlockDoorHandler::MetaRotateCW(NIBBLETYPE a_Meta) { if (a_Meta & 0x08) { + // The meta doesn't change for the top block return a_Meta; } else { + // Rotate the bottom block return super::MetaRotateCW(a_Meta); } } @@ -138,8 +120,10 @@ NIBBLETYPE cBlockDoorHandler::MetaRotateCW(NIBBLETYPE a_Meta) NIBBLETYPE cBlockDoorHandler::MetaMirrorXY(NIBBLETYPE a_Meta) { - // Top bit (0x08) contains door panel type (Top/Bottom panel) Only Bottom panels contain position data - // Return a_Meta if panel is a top panel (0x08 bit is set to 1) + /* + Top bit (0x08) contains door block position (Top / Bottom). Only Bottom blocks contain position data + Return a_Meta if panel is a top panel (0x08 bit is set to 1) + */ // Note: Currently, you can not properly mirror the hinges on a double door. The orientation of the door is stored // in only the bottom tile while the hinge position is in the top tile. This function only operates on one tile at a time, diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h index 92ad8da12..53f84b553 100644 --- a/src/Blocks/BlockDoor.h +++ b/src/Blocks/BlockDoor.h @@ -101,14 +101,6 @@ public: } - virtual void OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override; - - virtual bool IsUseable(void) override { return true; @@ -117,11 +109,20 @@ public: virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override { - return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); + return ((a_RelY > 0) && CanBeOn(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ))); + } + + + /** Returns true if door can be placed on the specified block type. */ + static bool CanBeOn(BLOCKTYPE a_BlockType) + { + // Vanilla refuses to place doors on transparent blocks + // We need to keep the door compatible with itself, otherwise the top half drops while the bottom half stays + return !cBlockInfo::IsTransparent(a_BlockType) || IsDoorBlockType(a_BlockType); } - bool CanReplaceBlock(BLOCKTYPE a_BlockType) + static bool CanReplaceBlock(BLOCKTYPE a_BlockType) { switch (a_BlockType) { @@ -170,8 +171,21 @@ public: } + /** Returns a vector pointing one block in the direction the door is facing (where the outside is). */ + inline static Vector3i GetRelativeDirectionToOutside(NIBBLETYPE a_BlockMeta) + { + switch (a_BlockMeta & 0x03) + { + case 0: return Vector3i(-1, 0, 0); // Facing West / XM + case 1: return Vector3i( 0, 0, -1); // Facing North / ZM + case 2: return Vector3i( 1, 0, 0); // Facing East / XP + default: return Vector3i( 0, 0, 1); // Facing South / ZP + } + } + + /** Returns true if the specified blocktype is any kind of door */ - inline static bool IsDoor(BLOCKTYPE a_Block) + inline static bool IsDoorBlockType(BLOCKTYPE a_Block) { switch (a_Block) { @@ -193,6 +207,8 @@ public: } + /** Returns true iff the door at the specified coords is open. + The coords may point to either the top part or the bottom part of the door. */ static NIBBLETYPE IsOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) { NIBBLETYPE Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); @@ -237,7 +253,7 @@ public: static void SetOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ, bool a_Open) { BLOCKTYPE Block = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ); - if (!IsDoor(Block)) + if (!IsDoorBlockType(Block)) { return; } diff --git a/src/Blocks/BlockFire.h b/src/Blocks/BlockFire.h index 07fcefe16..bafd385ab 100644 --- a/src/Blocks/BlockFire.h +++ b/src/Blocks/BlockFire.h @@ -12,7 +12,8 @@ class cBlockFireHandler : { public: cBlockFireHandler(BLOCKTYPE a_BlockType) - : cBlockHandler(a_BlockType) + : cBlockHandler(a_BlockType), + XZP(0), XZM(0), Dir(0) { } diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp index d532aa1dc..2de4a3e4c 100644 --- a/src/Blocks/BlockHandler.cpp +++ b/src/Blocks/BlockHandler.cpp @@ -369,7 +369,7 @@ void cBlockHandler::OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface -void cBlockHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +void cBlockHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, const sSetBlock & a_BlockChange) { } diff --git a/src/Blocks/BlockHandler.h b/src/Blocks/BlockHandler.h index f2298afb5..4dec0dc95 100644 --- a/src/Blocks/BlockHandler.h +++ b/src/Blocks/BlockHandler.h @@ -42,15 +42,12 @@ public: BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta ); - /// Called by cWorld::SetBlock() after the block has been set + /** Called by cWorld::SetBlock() after the block has been set */ virtual void OnPlaced(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); - /// Called by cClientHandle::HandlePlaceBlock() after the player has placed a new block. Called after OnPlaced(). + /** Called by cPlayer::PlaceBlocks() for each block after it has been set to the world. Called after OnPlaced(). */ virtual void OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta + cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, const sSetBlock & a_BlockChange ); /// Called before the player has destroyed a block @@ -96,7 +93,8 @@ public: */ // virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); - /// Called to check whether this block supports a rclk action. If it returns true, OnUse() is called + /** 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. @@ -109,20 +107,21 @@ public: */ virtual bool DoesIgnoreBuildCollision(void); - /// <summary>Similar to DoesIgnoreBuildCollision(void), but is used for cases where block meta/player item-in-hand is needed to determine collision (thin snow)</summary> + /** Similar to DoesIgnoreBuildCollision(void), but is used for cases where block's meta or + player's item-in-hand is needed to determine collision (thin snow) */ virtual bool DoesIgnoreBuildCollision(cPlayer *, NIBBLETYPE a_Meta) { UNUSED(a_Meta); return DoesIgnoreBuildCollision(); } - /// <summary>Returns if this block drops if it gets destroyed by an unsuitable situation. Default: true</summary> + /** Returns if this block drops if it gets destroyed by an unsuitable situation. + Default: true */ virtual bool DoesDropOnUnsuitable(void); /** Called when one of the neighbors gets set; equivalent to MC block update. By default drops if position no more suitable (CanBeAt(), DoesDropOnUnsuitable(), Drop()), - and wakes up all simulators on the block. - */ + and wakes up all simulators on the block. */ virtual void Check(cChunkInterface & ChunkInterface, cBlockPluginInterface & a_PluginInterface, int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk); /// <summary>Rotates a given block meta counter-clockwise. Default: no change</summary> diff --git a/src/Blocks/BlockMobHead.h b/src/Blocks/BlockMobHead.h index e21e42334..cb8143749 100644 --- a/src/Blocks/BlockMobHead.h +++ b/src/Blocks/BlockMobHead.h @@ -12,16 +12,18 @@ class cBlockMobHeadHandler : public cBlockEntityHandler { public: - cBlockMobHeadHandler(BLOCKTYPE a_BlockType) - : cBlockEntityHandler(a_BlockType) + cBlockMobHeadHandler(BLOCKTYPE a_BlockType): + cBlockEntityHandler(a_BlockType) { } + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override { - // The drop spawn is in OnDestroyed method + // The drop spawn is in the OnDestroyedByPlayer method } + virtual void OnDestroyedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override { if (a_Player->IsGameModeCreative()) @@ -61,202 +63,6 @@ public: a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback); } - - bool TrySpawnWither(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) - { - if (a_BlockY < 2) - { - return false; - } - - class cCallback : public cBlockEntityCallback - { - bool m_IsWither; - - virtual bool Item(cBlockEntity * a_BlockEntity) - { - if (a_BlockEntity->GetBlockType() != E_BLOCK_HEAD) - { - return false; - } - cMobHeadEntity * MobHeadEntity = static_cast<cMobHeadEntity*>(a_BlockEntity); - - m_IsWither = (MobHeadEntity->GetType() == SKULL_TYPE_WITHER); - return false; - } - - public: - cCallback () : m_IsWither(false) {} - - bool IsWither(void) const { return m_IsWither; } - - void Reset(void) { m_IsWither = false; } - - } CallbackA, CallbackB; - - class cPlayerCallback : public cPlayerListCallback - { - Vector3f m_Pos; - - virtual bool Item(cPlayer * a_Player) - { - // TODO 2014-05-21 xdot: Vanilla minecraft uses an AABB check instead of a radius one - double Dist = (a_Player->GetPosition() - m_Pos).Length(); - if (Dist < 50.0) - { - // If player is close, award achievement - a_Player->AwardAchievement(achSpawnWither); - } - return false; - } - - public: - cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {} - - } PlayerCallback(Vector3f((float)a_BlockX, (float)a_BlockY, (float)a_BlockZ)); - - a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, CallbackA); - - if (!CallbackA.IsWither()) - { - return false; - } - - CallbackA.Reset(); - - BLOCKTYPE BlockY1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); - BLOCKTYPE BlockY2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ); - - if ((BlockY1 != E_BLOCK_SOULSAND) || (BlockY2 != E_BLOCK_SOULSAND)) - { - return false; - } - - a_WorldInterface.DoWithBlockEntityAt(a_BlockX - 1, a_BlockY, a_BlockZ, CallbackA); - a_WorldInterface.DoWithBlockEntityAt(a_BlockX + 1, a_BlockY, a_BlockZ, CallbackB); - - BLOCKTYPE Block1 = a_ChunkInterface.GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ); - BLOCKTYPE Block2 = a_ChunkInterface.GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ); - - if ((Block1 == E_BLOCK_SOULSAND) && (Block2 == E_BLOCK_SOULSAND) && CallbackA.IsWither() && CallbackB.IsWither()) - { - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - - // Block entities - a_ChunkInterface.SetBlock(a_BlockX + 1, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.SetBlock(a_BlockX - 1, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - - // Spawn the wither: - a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtWither); - - // Award Achievement - a_WorldInterface.ForEachPlayer(PlayerCallback); - - return true; - } - - CallbackA.Reset(); - CallbackB.Reset(); - - a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ - 1, CallbackA); - a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ + 1, CallbackB); - - Block1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1); - Block2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1); - - if ((Block1 == E_BLOCK_SOULSAND) && (Block2 == E_BLOCK_SOULSAND) && CallbackA.IsWither() && CallbackB.IsWither()) - { - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - - // Block entities - a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ + 1, E_BLOCK_AIR, 0); - a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ - 1, E_BLOCK_AIR, 0); - - // Spawn the wither: - a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtWither); - - // Award Achievement - a_WorldInterface.ForEachPlayer(PlayerCallback); - - return true; - } - - return false; - } - - virtual void OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override - { - class cCallback : public cBlockEntityCallback - { - cPlayer * m_Player; - NIBBLETYPE m_OldBlockMeta; - NIBBLETYPE m_NewBlockMeta; - - virtual bool Item(cBlockEntity * a_BlockEntity) - { - if (a_BlockEntity->GetBlockType() != E_BLOCK_HEAD) - { - return false; - } - cMobHeadEntity * MobHeadEntity = static_cast<cMobHeadEntity*>(a_BlockEntity); - - int Rotation = 0; - if (m_NewBlockMeta == 1) - { - Rotation = (int) floor(m_Player->GetYaw() * 16.0F / 360.0F + 0.5) & 0xF; - } - - MobHeadEntity->SetType(static_cast<eMobHeadType>(m_OldBlockMeta)); - MobHeadEntity->SetRotation(static_cast<eMobHeadRotation>(Rotation)); - MobHeadEntity->GetWorld()->BroadcastBlockEntity(MobHeadEntity->GetPosX(), MobHeadEntity->GetPosY(), MobHeadEntity->GetPosZ()); - return false; - } - - public: - cCallback (cPlayer * a_CBPlayer, NIBBLETYPE a_OldBlockMeta, NIBBLETYPE a_NewBlockMeta) : - m_Player(a_CBPlayer), - m_OldBlockMeta(a_OldBlockMeta), - m_NewBlockMeta(a_NewBlockMeta) - {} - }; - cCallback Callback(a_Player, a_BlockMeta, static_cast<NIBBLETYPE>(a_BlockFace)); - - a_BlockMeta = (NIBBLETYPE)a_BlockFace; - a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback); - a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta); - - if (a_BlockMeta == SKULL_TYPE_WITHER) - { - static const Vector3i Coords[] = - { - Vector3i( 0, 0, 0), - Vector3i( 1, 0, 0), - Vector3i(-1, 0, 0), - Vector3i( 0, 0, 1), - Vector3i( 0, 0, -1), - }; - for (size_t i = 0; i < ARRAYCOUNT(Coords); ++i) - { - if (TrySpawnWither(a_ChunkInterface, a_WorldInterface, a_BlockX + Coords[i].x, a_BlockY, a_BlockZ + Coords[i].z)) - { - break; - } - } // for i - Coords[] - } - } } ; diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h index 275d1422a..af00fbe8e 100644 --- a/src/Blocks/BlockPumpkin.h +++ b/src/Blocks/BlockPumpkin.h @@ -17,70 +17,6 @@ public: } - virtual void OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override - { - // Check whether the pumpkin is a part of a golem or a snowman - - if (a_BlockY < 2) - { - // The pumpkin is too low for a golem / snowman - return; - } - - BLOCKTYPE BlockY1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); - BLOCKTYPE BlockY2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ); - - // Check for a snow golem: - if ((BlockY1 == E_BLOCK_SNOW_BLOCK) && (BlockY2 == E_BLOCK_SNOW_BLOCK)) - { - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtSnowGolem); - return; - } - - // Check for an iron golem. First check only the body and legs, since those are the same for both orientations: - if ((BlockY1 != E_BLOCK_IRON_BLOCK) || (BlockY2 != E_BLOCK_IRON_BLOCK)) - { - // One of the blocks is not an iron, no chance of a golem here - return; - } - - // Now check both orientations for hands: - if ( - (a_ChunkInterface.GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) && - (a_ChunkInterface.GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) - ) - { - // Remove the iron blocks: - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - - // Spawn the golem: - a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtIronGolem); - } - else if ( - (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) && - (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK) - ) - { - // Remove the iron blocks: - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0); - a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); - - // Spawn the golem: - a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtIronGolem); - } - } - - virtual bool GetPlacementBlockTypeMeta( cChunkInterface & a_ChunkInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, diff --git a/src/Blocks/BlockSignPost.h b/src/Blocks/BlockSignPost.h index d97501651..99c000633 100644 --- a/src/Blocks/BlockSignPost.h +++ b/src/Blocks/BlockSignPost.h @@ -53,17 +53,6 @@ public: } - virtual void OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override - { - a_Player->GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); - } - - virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override { return (a_Meta + 4) & 0x0f; diff --git a/src/Blocks/BlockWallSign.h b/src/Blocks/BlockWallSign.h index 0abe9c52c..b6599d033 100644 --- a/src/Blocks/BlockWallSign.h +++ b/src/Blocks/BlockWallSign.h @@ -27,17 +27,6 @@ public: } - virtual void OnPlacedByPlayer( - cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta - ) override - { - a_Player->GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); - } - - virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override { int BlockX = (a_Chunk.GetPosX() * cChunkDef::Width) + a_RelX; diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp index 080176dcd..f3dc44d91 100644 --- a/src/ByteBuffer.cpp +++ b/src/ByteBuffer.cpp @@ -13,6 +13,15 @@ +/** When defined, each access to a cByteBuffer object is checked whether it's done in the same thread. +cByteBuffer assumes that it is not used by multiple threads at once, this macro adds a runtime check for that. +Unfortunately it is very slow, so it is disabled even for regular DEBUG builds. */ +// #define DEBUG_SINGLE_THREAD_ACCESS + + + + + // Try to determine endianness: #if ( \ defined(__i386__) || defined(__alpha__) || \ @@ -109,7 +118,7 @@ public: -#ifdef _DEBUG +#ifdef DEBUG_SINGLE_THREAD_ACCESS /** Simple RAII class that is used for checking that no two threads are using an object simultanously. It requires the monitored object to provide the storage for a thread ID. @@ -122,7 +131,7 @@ public: { ASSERT( (*a_ThreadID == std::this_thread::get_id()) || // Either the object is used by current thread... - (*a_ThreadID == std::thread::id()) // ... or by no thread at all + (*a_ThreadID == m_EmptyThreadID) // ... or by no thread at all ); // Mark as being used by this thread: @@ -138,8 +147,13 @@ public: protected: /** Points to the storage used for ID of the thread using the object. */ std::thread::id * m_ThreadID; + + /** The value of an unassigned thread ID, used to speed up checking. */ + static std::thread::id m_EmptyThreadID; }; + std::thread::id cSingleThreadAccessChecker::m_EmptyThreadID; + #define CHECK_THREAD cSingleThreadAccessChecker Checker(&m_ThreadID); #else @@ -334,8 +348,24 @@ bool cByteBuffer::ReadBEShort(short & a_Value) CHECK_THREAD CheckValid(); NEEDBYTES(2); + Int16 val; + ReadBuf(&val, 2); + val = ntohs(val); + a_Value = *(reinterpret_cast<short *>(&val)); + return true; +} + + + + + +bool cByteBuffer::ReadBEUInt16(UInt16 & a_Value) +{ + CHECK_THREAD + CheckValid(); + NEEDBYTES(2); ReadBuf(&a_Value, 2); - a_Value = (short)ntohs((u_short)a_Value); + a_Value = ntohs(a_Value); return true; } @@ -357,6 +387,20 @@ bool cByteBuffer::ReadBEInt(int & a_Value) +bool cByteBuffer::ReadBEUInt32(UInt32 & a_Value) +{ + CHECK_THREAD + CheckValid(); + NEEDBYTES(4); + ReadBuf(&a_Value, 4); + a_Value = ntohl(a_Value); + return true; +} + + + + + bool cByteBuffer::ReadBEInt64(Int64 & a_Value) { CHECK_THREAD diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h index 2a316fa32..f480ad557 100644 --- a/src/ByteBuffer.h +++ b/src/ByteBuffer.h @@ -55,7 +55,9 @@ public: bool ReadChar (char & a_Value); bool ReadByte (unsigned char & a_Value); bool ReadBEShort (short & a_Value); + bool ReadBEUInt16 (UInt16 & a_Value); bool ReadBEInt (int & a_Value); + bool ReadBEUInt32 (UInt32 & a_Value); bool ReadBEInt64 (Int64 & a_Value); bool ReadBEFloat (float & a_Value); bool ReadBEDouble (double & a_Value); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 997326cc7..c39f5f6e6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,7 @@ project (MCServer) include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/") include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/jsoncpp/include") include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/polarssl/include") +include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/libevent/include") set(FOLDERS OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++ Bindings @@ -323,4 +324,4 @@ endif () if (WIN32) target_link_libraries(${EXECUTABLE} expat tolualib ws2_32.lib Psapi.lib) endif() -target_link_libraries(${EXECUTABLE} luaexpat jsoncpp polarssl zlib sqlite lua SQLiteCpp) +target_link_libraries(${EXECUTABLE} luaexpat jsoncpp polarssl zlib sqlite lua SQLiteCpp event_core event_extra) diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 39e97f5cf..979492b46 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -49,14 +49,14 @@ //////////////////////////////////////////////////////////////////////////////// // 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) +sSetBlock::sSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta): + m_RelX(a_BlockX), + m_RelY(a_BlockY), + m_RelZ(a_BlockZ), + m_BlockType(a_BlockType), + m_BlockMeta(a_BlockMeta) { - cChunkDef::AbsoluteToRelative(x, y, z, ChunkX, ChunkZ); + cChunkDef::AbsoluteToRelative(m_RelX, m_RelY, m_RelZ, m_ChunkX, m_ChunkZ); } @@ -73,6 +73,7 @@ cChunk::cChunk( cAllocationPool<cChunkData::sChunkSection> & a_Pool ) : m_Presence(cpInvalid), + m_ShouldGenerateIfLoadFailed(false), m_IsLightValid(false), m_IsDirty(false), m_IsSaving(false), @@ -93,6 +94,7 @@ cChunk::cChunk( m_WaterSimulatorData(a_World->GetWaterSimulator()->CreateChunkData()), m_LavaSimulatorData (a_World->GetLavaSimulator ()->CreateChunkData()), m_RedstoneSimulatorData(a_World->GetRedstoneSimulator()->CreateChunkData()), + m_IsRedstoneDirty(false), m_AlwaysTicked(0) { if (a_NeighborXM != nullptr) @@ -599,7 +601,7 @@ void cChunk::SpawnMobs(cMobSpawner& a_MobSpawner) -void cChunk::Tick(float a_Dt) +void cChunk::Tick(std::chrono::milliseconds a_Dt) { BroadcastPendingBlockChanges(); diff --git a/src/Chunk.h b/src/Chunk.h index 6f4ac5cac..1ce862371 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -160,7 +160,7 @@ public: /** Try to Spawn Monsters inside chunk */ void SpawnMobs(cMobSpawner& a_MobSpawner); - void Tick(float a_Dt); + void Tick(std::chrono::milliseconds a_Dt); /** Ticks a single block. Used by cWorld::TickQueuedBlocks() to tick the queued blocks */ void TickBlock(int a_RelX, int a_RelY, int a_RelZ); diff --git a/src/ChunkDef.h b/src/ChunkDef.h index 8f1d416ad..2bfa2949c 100644 --- a/src/ChunkDef.h +++ b/src/ChunkDef.h @@ -347,18 +347,32 @@ public: struct sSetBlock { - int x, y, z; - int ChunkX, ChunkZ; - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - - sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // absolute block position - sSetBlock(int a_ChunkX, int a_ChunkZ, int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) : - x(a_X), y(a_Y), z(a_Z), - ChunkX(a_ChunkX), ChunkZ(a_ChunkZ), - BlockType(a_BlockType), - BlockMeta(a_BlockMeta) - {} + int m_RelX, m_RelY, m_RelZ; + int m_ChunkX, m_ChunkZ; + BLOCKTYPE m_BlockType; + NIBBLETYPE m_BlockMeta; + + sSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + sSetBlock(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) : + m_RelX(a_RelX), m_RelY(a_RelY), m_RelZ(a_RelZ), + m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ), + m_BlockType(a_BlockType), + m_BlockMeta(a_BlockMeta) + { + ASSERT((a_RelX >= 0) && (a_RelX < cChunkDef::Width)); + ASSERT((a_RelZ >= 0) && (a_RelZ < cChunkDef::Width)); + } + + /** Returns the absolute X coord of the stored block. */ + int GetX(void) const { return m_RelX + cChunkDef::Width * m_ChunkX; } + + /** Returns the absolute Y coord of the stored block. + Is the same as relative Y coords, because there's no Y relativization. */ + int GetY(void) const { return m_RelY; } + + /** Returns the absolute Z coord of the stored block. */ + int GetZ(void) const { return m_RelZ + cChunkDef::Width * m_ChunkZ; } }; typedef std::list<sSetBlock> sSetBlockList; @@ -385,6 +399,17 @@ public: typedef std::list<cChunkCoords> cChunkCoordsList; typedef std::vector<cChunkCoords> cChunkCoordsVector; +/** A simple hash function for chunk coords, we assume that chunk coords won't use more than 16 bits, so the hash is almost an identity. +Used for std::unordered_map<cChunkCoords, ...> */ +class cChunkCoordsHash +{ +public: + size_t operator () (const cChunkCoords & a_Coords) const + { + return (static_cast<size_t>(a_Coords.m_ChunkX) << 16) ^ static_cast<size_t>(a_Coords.m_ChunkZ); + } +}; + diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 6aff3a754..2dd04f801 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -1101,17 +1101,17 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList) // 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; + int ChunkX = a_BlockList.front().m_ChunkX; + int ChunkZ = a_BlockList.front().m_ChunkZ; cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ); if ((Chunk != nullptr) && Chunk->IsValid()) { for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();) { - if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ)) + if ((itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ)) { - Chunk->FastSetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + Chunk->FastSetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); itr = a_BlockList.erase(itr); } else @@ -1125,7 +1125,7 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList) // 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)) + if ((itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ)) { Failed.push_back(*itr); itr = a_BlockList.erase(itr); @@ -1146,6 +1146,34 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList) +void cChunkMap::SetBlocks(const sSetBlockVector & a_Blocks) +{ + cCSLock lock(m_CSLayers); + cChunkPtr chunk = nullptr; + int lastChunkX = 0x7fffffff; // Bogus coords so that chunk is updated on first pass + int lastChunkZ = 0x7fffffff; + for (auto block: a_Blocks) + { + // Update the chunk, if different from last time: + if ((block.m_ChunkX != lastChunkX) || (block.m_ChunkZ != lastChunkZ)) + { + lastChunkX = block.m_ChunkX; + lastChunkZ = block.m_ChunkZ; + chunk = GetChunk(lastChunkX, lastChunkZ); + } + + // If the chunk is valid, set the block: + if (chunk != nullptr) + { + chunk->SetBlock(block.m_RelX, block.m_RelY, block.m_RelZ, block.m_BlockType, block.m_BlockMeta); + } + } // for block - a_Blocks[] +} + + + + + void cChunkMap::CollectPickupsByPlayer(cPlayer & a_Player) { int BlockX = (int)(a_Player.GetPosX()); // Truncating doesn't matter much; we're scanning entire chunks anyway @@ -1175,28 +1203,28 @@ void cChunkMap::CollectPickupsByPlayer(cPlayer & a_Player) BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ) { + int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); + // First check if it isn't queued in the m_FastSetBlockQueue: { - int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, 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)) + if ((itr->m_RelX == X) && (itr->m_RelY == Y) && (itr->m_RelZ == Z) && (itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ)) { - return itr->BlockType; + return itr->m_BlockType; } } // for itr - m_FastSetBlockQueue[] } - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - + + // Not in the queue, query the chunk, if loaded: cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ); if ((Chunk != nullptr) && Chunk->IsValid()) { - return Chunk->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + return Chunk->GetBlock(X, Y, Z); } return 0; } @@ -1207,25 +1235,28 @@ BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ) NIBBLETYPE cChunkMap::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) { + int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; + int ChunkX, ChunkZ; + cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); + // 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_BlockX) && (itr->y == a_BlockY) && (itr->z == a_BlockZ)) + if ((itr->m_RelX == X) && (itr->m_RelY == Y) && (itr->m_RelZ == Z) && (itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ)) { - return itr->BlockMeta; + return itr->m_BlockMeta; } } // for itr - m_FastSetBlockQueue[] } - int ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ); - + + // Not in the queue, query the chunk, if loaded: cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ); + cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ); if ((Chunk != nullptr) && Chunk->IsValid()) { - return Chunk->GetMeta(a_BlockX, a_BlockY, a_BlockZ); + return Chunk->GetMeta(X, Y, Z); } return 0; } @@ -1373,14 +1404,14 @@ void cChunkMap::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_Filt cCSLock Lock(m_CSLayers); for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) { - cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ); + cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ); if ((Chunk == nullptr) || !Chunk->IsValid()) { continue; } - if (Chunk->GetBlock(itr->x, itr->y, itr->z) == a_FilterBlockType) + if (Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ) == a_FilterBlockType) { - Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + Chunk->SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); } } } @@ -1394,24 +1425,24 @@ 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, itr->ChunkZ); + cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ); if ((Chunk == nullptr) || !Chunk->IsValid()) { continue; } - switch (Chunk->GetBlock(itr->x, itr->y, itr->z)) + switch (Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ)) { CASE_TREE_OVERWRITTEN_BLOCKS: { - Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + Chunk->SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); break; } case E_BLOCK_LEAVES: case E_BLOCK_NEW_LEAVES: { - if ((itr->BlockType == E_BLOCK_LOG) || (itr->BlockType == E_BLOCK_NEW_LOG)) + if ((itr->m_BlockType == E_BLOCK_LOG) || (itr->m_BlockType == E_BLOCK_NEW_LOG)) { - Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + Chunk->SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); } break; } @@ -1507,7 +1538,7 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) cCSLock Lock(m_CSLayers); for (sSetBlockVector::iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) { - cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ); + cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ); if ((Chunk == nullptr) || !Chunk->IsValid()) { if (!a_ContinueOnFailure) @@ -1517,8 +1548,8 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) res = false; continue; } - itr->BlockType = Chunk->GetBlock(itr->x, itr->y, itr->z); - itr->BlockMeta = Chunk->GetMeta(itr->x, itr->y, itr->z); + itr->m_BlockType = Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ); + itr->m_BlockMeta = Chunk->GetMeta(itr->m_RelX, itr->m_RelY, itr->m_RelZ); } return res; } @@ -1527,11 +1558,11 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) -bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z) +bool cChunkMap::DigBlock(int a_BlockX, int a_BlockY, int a_BlockZ) { - int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkZ; + int PosX = a_BlockX, PosY = a_BlockY, PosZ = a_BlockZ, ChunkX, ChunkZ; - cChunkDef::AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkZ); + cChunkDef::AbsoluteToRelative(PosX, PosY, PosZ, ChunkX, ChunkZ); { cCSLock Lock(m_CSLayers); @@ -1542,7 +1573,7 @@ bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z) } DestChunk->SetBlock(PosX, PosY, PosZ, E_BLOCK_AIR, 0); - m_World->GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z, DestChunk); + m_World->GetSimulatorManager()->WakeUp(a_BlockX, a_BlockY, a_BlockZ, DestChunk); } return true; @@ -2689,7 +2720,7 @@ void cChunkMap::SpawnMobs(cMobSpawner& a_MobSpawner) -void cChunkMap::Tick(float a_Dt) +void cChunkMap::Tick(std::chrono::milliseconds a_Dt) { cCSLock Lock(m_CSLayers); for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) @@ -2917,7 +2948,7 @@ void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner& a_MobSpawner) -void cChunkMap::cChunkLayer::Tick(float a_Dt) +void cChunkMap::cChunkLayer::Tick(std::chrono::milliseconds a_Dt) { for (size_t i = 0; i < ARRAYCOUNT(m_Chunks); i++) { diff --git a/src/ChunkMap.h b/src/ChunkMap.h index 6a858b06d..f08d02337 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -143,7 +143,13 @@ public: void FastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); void FastSetQueuedBlocks(); - void FastSetBlocks (sSetBlockList & a_BlockList); + void FastSetBlocks(sSetBlockList & a_BlockList); + + /** Performs the specified single-block set operations simultaneously, as if SetBlock() was called for each item. + Is more efficient than calling SetBlock() multiple times. + If the chunk for any of the blocks is not loaded, the set operation is ignored silently. */ + void SetBlocks(const sSetBlockVector & a_Blocks); + void CollectPickupsByPlayer(cPlayer & a_Player); BLOCKTYPE GetBlock (int a_BlockX, int a_BlockY, int a_BlockZ); @@ -173,11 +179,20 @@ public: (Re)sends the chunks to their relevant clients if successful. */ bool SetAreaBiome(int a_MinX, int a_MaxX, int a_MinZ, int a_MaxZ, EMCSBiome a_Biome); - /** 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. */ + /** Retrieves block types and metas of the specified blocks. + If a chunk is not loaded, doesn't modify the block and consults a_ContinueOnFailure whether to process the rest of the array. + Returns true if all blocks were read, false if any one failed. */ 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); + /** Removes the block at the specified coords and wakes up simulators. + Returns false if the chunk is not loaded (and the block is not dug). + Returns true if successful. */ + bool DigBlock(int a_BlockX, int a_BlockY, int a_BlockZ); + + /** Sends the block at the specified coords to the specified player. + Uses a blockchange packet to send the block. + If the relevant chunk isn't loaded, doesn't do anything. */ + void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, cPlayer * a_Player); /** Compares clients of two chunks, calls the callback accordingly */ void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback); @@ -333,7 +348,7 @@ public: /** Try to Spawn Monsters inside all Chunks */ void SpawnMobs(cMobSpawner& a_MobSpawner); - void Tick(float a_Dt); + void Tick(std::chrono::milliseconds a_Dt); /** Ticks a single block. Used by cWorld::TickQueuedBlocks() to tick the queued blocks */ void TickBlock(int a_BlockX, int a_BlockY, int a_BlockZ); @@ -400,7 +415,7 @@ private: /** Try to Spawn Monsters inside all Chunks */ void SpawnMobs(cMobSpawner& a_MobSpawner); - void Tick(float a_Dt); + void Tick(std::chrono::milliseconds a_Dt); void RemoveClient(cClientHandle * a_Client); diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 974b75556..09e1d76b7 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -1349,12 +1349,19 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e if (ItemHandler->IsPlaceable() && (a_BlockFace != BLOCK_FACE_NONE)) { - HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler); + if (!ItemHandler->OnPlayerPlace(*World, *m_Player, Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + // Placement failed, bail out + return; + } } else if ((ItemHandler->IsFood() || ItemHandler->IsDrinkable(EquippedDamage))) { - if ((m_Player->IsSatiated() || m_Player->IsGameModeCreative()) && - ItemHandler->IsFood() && (Equipped.m_ItemType != E_ITEM_GOLDEN_APPLE)) + if ( + (m_Player->IsSatiated() || m_Player->IsGameModeCreative()) && // Only creative or hungry players can eat + ItemHandler->IsFood() && + (Equipped.m_ItemType != E_ITEM_GOLDEN_APPLE) // Golden apple is a special case, it is used instead of eaten + ) { // The player is satiated or in creative, and trying to eat return; @@ -1382,151 +1389,6 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e -void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler) -{ - BLOCKTYPE EquippedBlock = (BLOCKTYPE)(m_Player->GetEquippedItem().m_ItemType); - if (a_BlockFace < 0) - { - // Clicked in air - return; - } - - cWorld * World = m_Player->GetWorld(); - - BLOCKTYPE ClickedBlock; - NIBBLETYPE ClickedBlockMeta; - NIBBLETYPE EquippedBlockDamage = (NIBBLETYPE)(m_Player->GetEquippedItem().m_ItemDamage); - - if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) - { - // The block is being placed outside the world, ignore this packet altogether (#128) - return; - } - - World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); - - // Special slab handling - placing a slab onto another slab produces a dblslab instead: - if ( - cBlockSlabHandler::IsAnySlabType(ClickedBlock) && // Is there a slab already? - cBlockSlabHandler::IsAnySlabType(EquippedBlock) && // Is the player placing another slab? - ((ClickedBlockMeta & 0x07) == EquippedBlockDamage) && // Is it the same slab type? - ( - (a_BlockFace == BLOCK_FACE_TOP) || // Clicking the top of a bottom slab - (a_BlockFace == BLOCK_FACE_BOTTOM) // Clicking the bottom of a top slab - ) - ) - { - // Coordinates at clicked block, which was an eligible slab, and either top or bottom faces were clicked - // If clicked top face and slab occupies the top voxel, we want a slab to be placed above it (therefore increment Y) - // Else if clicked bottom face and slab occupies the bottom voxel, decrement Y for the same reason - // Don't touch coordinates if anything else because a dblslab opportunity is present - if ((ClickedBlockMeta & 0x08) && (a_BlockFace == BLOCK_FACE_TOP)) - { - ++a_BlockY; - } - else if (!(ClickedBlockMeta & 0x08) && (a_BlockFace == BLOCK_FACE_BOTTOM)) - { - --a_BlockY; - } - World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); - } - else - { - // Check if the block ignores build collision (water, grass etc.): - if ( - BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() || - BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(m_Player, ClickedBlockMeta) - ) - { - cChunkInterface ChunkInterface(World->GetChunkMap()); - BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ); - } - else - { - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - - if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) - { - // The block is being placed outside the world, ignore this packet altogether (#128) - return; - } - - NIBBLETYPE PlaceMeta; - BLOCKTYPE PlaceBlock; - World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlock, PlaceMeta); - - // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed. - // No need to do combinability (dblslab) checks, client will do that here. - if (cBlockSlabHandler::IsAnySlabType(PlaceBlock)) - { - // It's a slab, don't do checks and proceed to double-slabbing - } - else - { - if ( - !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision() && - !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(m_Player, PlaceMeta) - ) - { - // Tried to place a block *into* another? - // Happens when you place a block aiming at side of block with a torch on it or stem beside it - return; - } - } - } - } - - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if (!a_ItemHandler.GetPlacementBlockTypeMeta(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) - { - // Handler refused the placement, send that information back to the client: - World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - m_Player->GetInventory().SendEquippedSlot(); - return; - } - - cBlockHandler * NewBlock = BlockHandler(BlockType); - - if (cRoot::Get()->GetPluginManager()->CallHookPlayerPlacingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) - { - // A plugin doesn't agree with placing the block, revert the block on the client: - World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player); - m_Player->GetInventory().SendEquippedSlot(); - return; - } - - // The actual block placement: - World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); - if (!m_Player->IsGameModeCreative()) - { - m_Player->GetInventory().RemoveOneEquippedItem(); - } - - cChunkInterface ChunkInterface(World->GetChunkMap()); - NewBlock->OnPlacedByPlayer(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); - - AString PlaceSound = cBlockInfo::GetPlaceSound(BlockType); - float Volume = 1.0f, Pitch = 0.8f; - if (PlaceSound == "dig.metal") - { - Pitch = 1.2f; - PlaceSound = "dig.stone"; - } - else if (PlaceSound == "random.anvil_land") - { - Volume = 0.65f; - } - - World->BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch); - - cRoot::Get()->GetPluginManager()->CallHookPlayerPlacedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); -} - - - - - void cClientHandle::HandleChat(const AString & a_Message) { // We no longer need to postpone message processing, because the messages already arrive in the Tick thread @@ -1586,7 +1448,7 @@ void cClientHandle::HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_ -void cClientHandle::HandleAnimation(char a_Animation) +void cClientHandle::HandleAnimation(int a_Animation) { if (cPluginManager::Get()->CallHookPlayerAnimation(*m_Player, a_Animation)) { @@ -2973,7 +2835,7 @@ void cClientHandle::PacketUnknown(UInt32 a_PacketType) -void cClientHandle::PacketError(unsigned char a_PacketType) +void cClientHandle::PacketError(UInt32 a_PacketType) { LOGERROR("Protocol error while parsing packet type 0x%02x; disconnecting client \"%s\"", a_PacketType, m_Username.c_str()); SendDisconnect("Protocol error"); diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 25dd250d9..03ae38cfd 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -251,10 +251,10 @@ public: // Calls that cProtocol descendants use to report state: void PacketBufferFull(void); void PacketUnknown(UInt32 a_PacketType); - void PacketError(unsigned char a_PacketType); + void PacketError(UInt32 a_PacketType); // Calls that cProtocol descendants use for handling packets: - void HandleAnimation(char a_Animation); + void HandleAnimation(int a_Animation); /** Called when the protocol receives a MC|ItemName plugin message, indicating that the player named an item in the anvil UI. */ @@ -459,9 +459,6 @@ private: UInt32 m_ProtocolVersion; - /** Handles the block placing packet when it is a real block placement (not block-using, item-using or eating) */ - void HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler); - /** Returns true if the rate block interactions is within a reasonable limit (bot protection) */ bool CheckBlockInteractionsRate(void); diff --git a/src/Enchantments.cpp b/src/Enchantments.cpp index 36c451b81..5ed18de6b 100644 --- a/src/Enchantments.cpp +++ b/src/Enchantments.cpp @@ -183,6 +183,7 @@ int cEnchantments::StringToEnchantmentID(const AString & a_EnchantmentName) { enchRespiration, "Respiration"}, { enchAquaAffinity, "AquaAffinity"}, { enchThorns, "Thorns"}, + { enchDepthStrider, "DepthStrider"}, { enchSharpness, "Sharpness"}, { enchSmite, "Smite"}, { enchBaneOfArthropods, "BaneOfArthropods"}, @@ -506,6 +507,20 @@ void cEnchantments::AddItemEnchantmentWeights(cWeightedEnchantments & a_Enchantm { AddEnchantmentWeightToVector(a_Enchantments, 5, enchFeatherFalling, 1); } + + // Depth Strider + if ((a_EnchantmentLevel >= 30) && (a_EnchantmentLevel <= 45)) + { + AddEnchantmentWeightToVector(a_Enchantments, 2, enchDepthStrider, 3); + } + else if ((a_EnchantmentLevel >= 20) && (a_EnchantmentLevel <= 35)) + { + AddEnchantmentWeightToVector(a_Enchantments, 2, enchDepthStrider, 2); + } + else if ((a_EnchantmentLevel >= 10) && (a_EnchantmentLevel <= 25)) + { + AddEnchantmentWeightToVector(a_Enchantments, 2, enchDepthStrider, 1); + } } } @@ -1021,26 +1036,34 @@ cEnchantments cEnchantments::GetRandomEnchantmentFromVector(cWeightedEnchantment -cEnchantments cEnchantments::GenerateEnchantmentFromVector(cWeightedEnchantments & a_Enchantments, int a_Seed) +cEnchantments cEnchantments::SelectEnchantmentFromVector(const cWeightedEnchantments & a_Enchantments, int a_Seed) { + // Sum up all the enchantments' weights: int AllWeights = 0; for (const auto Enchantment : a_Enchantments) { AllWeights += Enchantment.m_Weight; } + // If there's no weight for any of the enchantments, return an empty enchantment + if (AllWeights <= 0) + { + return cEnchantments(); + } + + // Pick a random enchantment: cNoise Noise(a_Seed); int RandomNumber = Noise.IntNoise1DInt(AllWeights) / 7 % AllWeights; - for (const auto Enchantment : a_Enchantments) { RandomNumber -= Enchantment.m_Weight; - if (RandomNumber < 0) + if (RandomNumber <= 0) { return Enchantment.m_Enchantments; } } + // No enchantment picked, return an empty one (we probably shouldn't ever get here): return cEnchantments(); } diff --git a/src/Enchantments.h b/src/Enchantments.h index e4390a5f2..e8e84d43c 100644 --- a/src/Enchantments.h +++ b/src/Enchantments.h @@ -53,6 +53,7 @@ public: enchRespiration = 5, enchAquaAffinity = 6, enchThorns = 7, + enchDepthStrider = 8, enchSharpness = 16, enchSmite = 17, enchBaneOfArthropods = 18, @@ -128,8 +129,10 @@ public: /** Gets random enchantment from Vector and returns it */ static cEnchantments GetRandomEnchantmentFromVector(cWeightedEnchantments & a_Enchantments); - /** Returns an enchantment from a Vector using cNoise. Mostly used for generators.*/ - static cEnchantments GenerateEnchantmentFromVector(cWeightedEnchantments & a_Enchantments, int a_Seed); + /** Selects one enchantment from a Vector using cNoise. Mostly used for generators. + Uses the enchantments' weights for the random distribution. + If a_Enchantments is empty, returns an empty enchantment. */ + static cEnchantments SelectEnchantmentFromVector(const cWeightedEnchantments & a_Enchantments, int a_Seed); /** Returns true if a_Other doesn't contain exactly the same enchantments and levels */ bool operator !=(const cEnchantments & a_Other) const; diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp index 30f18f677..0fbbfb681 100644 --- a/src/Entities/ArrowEntity.cpp +++ b/src/Entities/ArrowEntity.cpp @@ -174,20 +174,20 @@ void cArrowEntity::CollectedBy(cPlayer & a_Dest) -void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk) +void cArrowEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); m_Timer += a_Dt; if (m_bIsCollected) { - if (m_Timer > 500.f) // 0.5 seconds + if (m_Timer > std::chrono::milliseconds(500)) { Destroy(); return; } } - else if (m_Timer > 1000 * 60 * 5) // 5 minutes + else if (m_Timer > std::chrono::minutes(5)) { Destroy(); return; @@ -202,7 +202,7 @@ void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk) if (!m_HasTeleported) // Sent a teleport already, don't do again { - if (m_HitGroundTimer > 500.f) // Send after half a second, could be less, but just in case + if (m_HitGroundTimer > std::chrono::milliseconds(500)) { m_World->BroadcastTeleportEntity(*this); m_HasTeleported = true; diff --git a/src/Entities/ArrowEntity.h b/src/Entities/ArrowEntity.h index 436ec0293..2ecc16d1c 100644 --- a/src/Entities/ArrowEntity.h +++ b/src/Entities/ArrowEntity.h @@ -85,10 +85,10 @@ protected: bool m_IsCritical; /** Timer for pickup collection animation or five minute timeout */ - float m_Timer; + std::chrono::milliseconds m_Timer; /** Timer for client arrow position confirmation via TeleportEntity */ - float m_HitGroundTimer; + std::chrono::milliseconds m_HitGroundTimer; // Whether the arrow has already been teleported into the proper position in the ground. bool m_HasTeleported; @@ -103,6 +103,6 @@ protected: virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; virtual void CollectedBy(cPlayer & a_Player) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; }; // tolua_export diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp index 953213bca..6d8b4ef31 100644 --- a/src/Entities/Boat.cpp +++ b/src/Entities/Boat.cpp @@ -91,7 +91,7 @@ void cBoat::OnRightClicked(cPlayer & a_Player) -void cBoat::Tick(float a_Dt, cChunk & a_Chunk) +void cBoat::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); BroadcastMovementUpdate(); diff --git a/src/Entities/Boat.h b/src/Entities/Boat.h index 8de88d165..a873ff822 100644 --- a/src/Entities/Boat.h +++ b/src/Entities/Boat.h @@ -27,7 +27,7 @@ public: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; virtual void OnRightClicked(cPlayer & a_Player) override; virtual bool DoTakeDamage(TakeDamageInfo & TDI) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void HandleSpeedFromAttachee(float a_Forward, float a_Sideways) override; cBoat(double a_X, double a_Y, double a_Z); diff --git a/src/Entities/EnderCrystal.cpp b/src/Entities/EnderCrystal.cpp index 30df2c110..7a911d4db 100644 --- a/src/Entities/EnderCrystal.cpp +++ b/src/Entities/EnderCrystal.cpp @@ -29,7 +29,7 @@ void cEnderCrystal::SpawnOn(cClientHandle & a_ClientHandle) -void cEnderCrystal::Tick(float a_Dt, cChunk & a_Chunk) +void cEnderCrystal::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); // No further processing (physics e.t.c.) is needed diff --git a/src/Entities/EnderCrystal.h b/src/Entities/EnderCrystal.h index c98c3b681..8f7e2e9b9 100644 --- a/src/Entities/EnderCrystal.h +++ b/src/Entities/EnderCrystal.h @@ -23,7 +23,7 @@ private: // cEntity overrides: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void KilledBy(TakeDamageInfo & a_TDI) override; }; // tolua_export diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 54b9f2a20..c51a27961 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -772,7 +772,7 @@ void cEntity::SetHealth(int a_Health) -void cEntity::Tick(float a_Dt, cChunk & a_Chunk) +void cEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { m_TicksAlive++; @@ -841,7 +841,7 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk) -void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) +void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { int BlockX = POSX_TOINT; int BlockY = POSY_TOINT; @@ -851,15 +851,15 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) GET_AND_VERIFY_CURRENT_CHUNK(NextChunk, BlockX, BlockZ) // TODO Add collision detection with entities. - a_Dt /= 1000; // Convert from msec to sec + auto DtSec = std::chrono::duration_cast<std::chrono::duration<double>>(a_Dt); Vector3d NextPos = Vector3d(GetPosX(), GetPosY(), GetPosZ()); Vector3d NextSpeed = Vector3d(GetSpeedX(), GetSpeedY(), GetSpeedZ()); if ((BlockY >= cChunkDef::Height) || (BlockY < 0)) { // Outside of the world - AddSpeedY(m_Gravity * a_Dt); - AddPosition(GetSpeed() * a_Dt); + AddSpeedY(m_Gravity * DtSec.count()); + AddPosition(GetSpeed() * DtSec.count()); return; } @@ -927,11 +927,11 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) if (!m_bOnGround) { - float fallspeed; + double fallspeed; if (IsBlockWater(BlockIn)) { - fallspeed = m_Gravity * a_Dt / 3; // Fall 3x slower in water - ApplyFriction(NextSpeed, 0.7, a_Dt); + fallspeed = m_Gravity * DtSec.count() / 3; // Fall 3x slower in water + ApplyFriction(NextSpeed, 0.7, static_cast<float>(DtSec.count())); } else if (BlockIn == E_BLOCK_COBWEB) { @@ -941,13 +941,13 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) else { // Normal gravity - fallspeed = m_Gravity * a_Dt; + fallspeed = m_Gravity * DtSec.count(); } - NextSpeed.y += fallspeed; + NextSpeed.y += static_cast<float>(fallspeed); } else { - ApplyFriction(NextSpeed, 0.7, a_Dt); + ApplyFriction(NextSpeed, 0.7, static_cast<float>(DtSec.count())); } // Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we @@ -1002,14 +1002,14 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) { cTracer Tracer(GetWorld()); // Distance traced is an integer, so we round up from the distance we should go (Speed * Delta), else we will encounter collision detection failurse - int DistanceToTrace = (int)(ceil((NextSpeed * a_Dt).SqrLength()) * 2); + int DistanceToTrace = (int)(ceil((NextSpeed * DtSec.count()).SqrLength()) * 2); bool HasHit = Tracer.Trace(NextPos, NextSpeed, DistanceToTrace); if (HasHit) { // Oh noez! We hit something: verify that the (hit position - current) was smaller or equal to the (position that we should travel without obstacles - current) // This is because previously, we traced with a length that was rounded up (due to integer limitations), and in the case that something was hit, we don't want to overshoot our projected movement - if ((Tracer.RealHit - NextPos).SqrLength() <= (NextSpeed * a_Dt).SqrLength()) + if ((Tracer.RealHit - NextPos).SqrLength() <= (NextSpeed * DtSec.count()).SqrLength()) { // Block hit was within our projected path // Begin by stopping movement in the direction that we hit something. The Normal is the line perpendicular to a 2D face and in this case, stores what block face was hit through either -1 or 1. @@ -1044,13 +1044,13 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) // and that this piece of software will come to be hailed as the epitome of performance and functionality in C++, never before seen, and of such a like that will never // be henceforth seen again in the time of programmers and man alike // </&sensationalist> - NextPos += (NextSpeed * a_Dt); + NextPos += (NextSpeed * DtSec.count()); } } else { // We didn't hit anything, so move =] - NextPos += (NextSpeed * a_Dt); + NextPos += (NextSpeed * DtSec.count()); } } diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index af545fe4a..de9b88dfb 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -326,10 +326,10 @@ public: // tolua_end - virtual void Tick(float a_Dt, cChunk & a_Chunk); + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); /// Handles the physics of the entity - updates position based on speed, updates speed based on environment - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk); + virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); /// Updates the state related to this entity being on fire virtual void TickBurning(cChunk & a_Chunk); diff --git a/src/Entities/ExpOrb.cpp b/src/Entities/ExpOrb.cpp index 751308661..db7f6f2c8 100644 --- a/src/Entities/ExpOrb.cpp +++ b/src/Entities/ExpOrb.cpp @@ -8,7 +8,7 @@ cExpOrb::cExpOrb(double a_X, double a_Y, double a_Z, int a_Reward) : cEntity(etExpOrb, a_X, a_Y, a_Z, 0.98, 0.98) , m_Reward(a_Reward) - , m_Timer(0.f) + , m_Timer(0) { SetMaxHealth(5); SetHealth(5); @@ -21,7 +21,7 @@ cExpOrb::cExpOrb(double a_X, double a_Y, double a_Z, int a_Reward) cExpOrb::cExpOrb(const Vector3d & a_Pos, int a_Reward) : cEntity(etExpOrb, a_Pos.x, a_Pos.y, a_Pos.z, 0.98, 0.98) , m_Reward(a_Reward) - , m_Timer(0.f) + , m_Timer(0) { SetMaxHealth(5); SetHealth(5); @@ -42,7 +42,7 @@ void cExpOrb::SpawnOn(cClientHandle & a_Client) -void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk) +void cExpOrb::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { cPlayer * a_ClosestPlayer(m_World->FindClosestPlayer(Vector3f(GetPosition()), 5)); if (a_ClosestPlayer != nullptr) @@ -70,7 +70,7 @@ void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk) HandlePhysics(a_Dt, a_Chunk); m_Timer += a_Dt; - if (m_Timer >= 1000 * 60 * 5) // 5 minutes + if (m_Timer >= std::chrono::minutes(5)) { Destroy(true); } diff --git a/src/Entities/ExpOrb.h b/src/Entities/ExpOrb.h index bdb9a5b19..9aac4f748 100644 --- a/src/Entities/ExpOrb.h +++ b/src/Entities/ExpOrb.h @@ -22,14 +22,14 @@ public: cExpOrb(const Vector3d & a_Pos, int a_Reward); // Override functions - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void SpawnOn(cClientHandle & a_Client) override; /** Returns the number of ticks that this entity has existed */ - int GetAge(void) const { return (int)(m_Timer / 50); } // tolua_export + int GetAge(void) const { return std::chrono::duration_cast<cTickTime>(m_Timer).count(); } // tolua_export /** Set the number of ticks that this entity has existed */ - void SetAge(int a_Age) { m_Timer = (float)(a_Age * 50); } // tolua_export + void SetAge(int a_Age) { m_Timer = cTickTime(a_Age); } // tolua_export /** Get the exp amount */ int GetReward(void) const { return m_Reward; } // tolua_export @@ -41,5 +41,5 @@ protected: int m_Reward; /** The number of ticks that the entity has existed / timer between collect and destroy; in msec */ - float m_Timer; + std::chrono::milliseconds m_Timer; } ; // tolua_export diff --git a/src/Entities/FallingBlock.cpp b/src/Entities/FallingBlock.cpp index 111c5fa84..75105a0cd 100644 --- a/src/Entities/FallingBlock.cpp +++ b/src/Entities/FallingBlock.cpp @@ -31,7 +31,7 @@ void cFallingBlock::SpawnOn(cClientHandle & a_ClientHandle) -void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk) +void cFallingBlock::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // GetWorld()->BroadcastTeleportEntity(*this); // Test position @@ -82,7 +82,7 @@ void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk) return; } - float MilliDt = a_Dt * 0.001f; + float MilliDt = a_Dt.count() * 0.001f; AddSpeedY(MilliDt * -9.8f); AddPosition(GetSpeed() * MilliDt); diff --git a/src/Entities/FallingBlock.h b/src/Entities/FallingBlock.h index c20fe8eb9..884938f4d 100644 --- a/src/Entities/FallingBlock.h +++ b/src/Entities/FallingBlock.h @@ -30,7 +30,7 @@ public: // cEntity overrides: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; private: BLOCKTYPE m_BlockType; diff --git a/src/Entities/FireworkEntity.cpp b/src/Entities/FireworkEntity.cpp index 68d02640a..32eaf669a 100644 --- a/src/Entities/FireworkEntity.cpp +++ b/src/Entities/FireworkEntity.cpp @@ -19,7 +19,7 @@ cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, do -void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) +void cFireworkEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { int RelX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width; int RelZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width; @@ -28,7 +28,7 @@ void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) if ((PosY < 0) || (PosY >= cChunkDef::Height)) { AddSpeedY(1); - AddPosition(GetSpeed() * (a_Dt / 1000)); + AddPosition(GetSpeed() * (static_cast<double>(a_Dt.count()) / 1000)); return; } @@ -53,14 +53,14 @@ void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) } AddSpeedY(1); - AddPosition(GetSpeed() * (a_Dt / 1000)); + AddPosition(GetSpeed() * (static_cast<double>(a_Dt.count()) / 1000)); } -void cFireworkEntity::Tick(float a_Dt, cChunk & a_Chunk) +void cFireworkEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Entities/FireworkEntity.h b/src/Entities/FireworkEntity.h index 300ec571e..c0a38a943 100644 --- a/src/Entities/FireworkEntity.h +++ b/src/Entities/FireworkEntity.h @@ -49,8 +49,8 @@ public: protected: // cProjectileEntity overrides: - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; private: diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp index 5fe6a1238..cf8dd6c6f 100644 --- a/src/Entities/Floater.cpp +++ b/src/Entities/Floater.cpp @@ -125,7 +125,7 @@ void cFloater::SpawnOn(cClientHandle & a_Client) -void cFloater::Tick(float a_Dt, cChunk & a_Chunk) +void cFloater::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { HandlePhysics(a_Dt, a_Chunk); if (IsBlockWater(m_World->GetBlock((int) GetPosX(), (int) GetPosY(), (int) GetPosZ())) && m_World->GetBlockMeta((int) GetPosX(), (int) GetPosY(), (int) GetPosZ()) == 0) diff --git a/src/Entities/Floater.h b/src/Entities/Floater.h index 96d77ac82..d5715f89e 100644 --- a/src/Entities/Floater.h +++ b/src/Entities/Floater.h @@ -21,7 +21,7 @@ public: cFloater(double a_X, double a_Y, double a_Z, Vector3d a_Speed, int a_PlayerID, int a_CountDownTime); virtual void SpawnOn(cClientHandle & a_Client) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; // tolua_begin bool CanPickup(void) const { return m_CanPickupItem; } diff --git a/src/Entities/HangingEntity.h b/src/Entities/HangingEntity.h index 67146a20b..d1ef79a68 100644 --- a/src/Entities/HangingEntity.h +++ b/src/Entities/HangingEntity.h @@ -43,7 +43,7 @@ public: private: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override {} + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override {} eBlockFace m_Facing; diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp index a5ce0ac94..a32926838 100644 --- a/src/Entities/Minecart.cpp +++ b/src/Entities/Minecart.cpp @@ -112,7 +112,7 @@ void cMinecart::SpawnOn(cClientHandle & a_ClientHandle) -void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) +void cMinecart::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (IsDestroyed()) // Mainly to stop detector rails triggering again after minecart is dead { @@ -178,7 +178,7 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) default: VERIFY(!"Unhandled rail type despite checking if block was rail!"); break; } - AddPosition(GetSpeed() * (a_Dt / 1000)); // Commit changes; as we use our own engine when on rails, this needs to be done, whereas it is normally in Entity.cpp + AddPosition(GetSpeed() * (static_cast<double>(a_Dt.count()) / 1000)); // Commit changes; as we use our own engine when on rails, this needs to be done, whereas it is normally in Entity.cpp } else { @@ -206,7 +206,7 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) -void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt) +void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt) { /* NOTE: Please bear in mind that taking away from negatives make them even more negative, @@ -566,7 +566,7 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta) -void cMinecart::HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt) +void cMinecart::HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt) { m_World->SetBlockMeta(m_DetectorRailPosition, a_RailMeta | 0x08); @@ -577,7 +577,7 @@ void cMinecart::HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt) -void cMinecart::HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt) +void cMinecart::HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt) { HandleRailPhysics(a_RailMeta & 0x07, a_Dt); } @@ -1214,7 +1214,7 @@ void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player) -void cMinecartWithFurnace::Tick(float a_Dt, cChunk & a_Chunk) +void cMinecartWithFurnace::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h index f7d0d5dda..898776e71 100644 --- a/src/Entities/Minecart.h +++ b/src/Entities/Minecart.h @@ -38,7 +38,7 @@ public: // cEntity overrides: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; + virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual bool DoTakeDamage(TakeDamageInfo & TDI) override; virtual void Destroyed() override; @@ -56,7 +56,7 @@ protected: /** Handles physics on normal rails For each tick, slow down on flat rails, speed up or slow down on ascending/descending rails (depending on direction), and turn on curved rails */ - void HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt); + void HandleRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt); /** Handles powered rail physics Each tick, speed up or slow down cart, depending on metadata of rail (powered or not) @@ -66,10 +66,10 @@ protected: /** Handles detector rail activation Activates detector rails when a minecart is on them. Calls HandleRailPhysics() for physics simulations */ - void HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt); + void HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt); /** Handles activator rails - placeholder for future implementation */ - void HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt); + void HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, std::chrono::milliseconds a_Dt); /** Snaps a mincecart to a rail's axis, resetting its speed For curved rails, it changes the cart's direction as well as snapping it to axis */ @@ -171,7 +171,7 @@ public: // cEntity overrides: virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; // Set functions. void SetIsFueled(bool a_IsFueled, int a_FueledTimeLeft = -1) {m_IsFueled = a_IsFueled; m_FueledTimeLeft = a_FueledTimeLeft;} diff --git a/src/Entities/Painting.cpp b/src/Entities/Painting.cpp index 1aa6da0a1..6f6277f28 100644 --- a/src/Entities/Painting.cpp +++ b/src/Entities/Painting.cpp @@ -31,7 +31,7 @@ void cPainting::SpawnOn(cClientHandle & a_Client) -void cPainting::Tick(float a_Dt, cChunk & a_Chunk) +void cPainting::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); UNUSED(a_Chunk); diff --git a/src/Entities/Painting.h b/src/Entities/Painting.h index 078270b42..6e8a382fc 100644 --- a/src/Entities/Painting.h +++ b/src/Entities/Painting.h @@ -31,7 +31,7 @@ public: private: virtual void SpawnOn(cClientHandle & a_Client) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void GetDrops(cItems & a_Items, cEntity * a_Killer) override; virtual void KilledBy(TakeDamageInfo & a_TDI) override { diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp index fc8ca3d47..baf8a2f3b 100644 --- a/src/Entities/Pawn.cpp +++ b/src/Entities/Pawn.cpp @@ -19,7 +19,7 @@ cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) : -void cPawn::Tick(float a_Dt, cChunk & a_Chunk) +void cPawn::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // Iterate through this entity's applied effects for (tEffectMap::iterator iter = m_EntityEffects.begin(); iter != m_EntityEffects.end();) diff --git a/src/Entities/Pawn.h b/src/Entities/Pawn.h index d50bcd8af..e3e99651d 100644 --- a/src/Entities/Pawn.h +++ b/src/Entities/Pawn.h @@ -20,7 +20,7 @@ public: cPawn(eEntityType a_EntityType, double a_Width, double a_Height); - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void KilledBy(TakeDamageInfo & a_TDI) override; // tolua_begin diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp index e5e28446d..9f2609894 100644 --- a/src/Entities/Pickup.cpp +++ b/src/Entities/Pickup.cpp @@ -86,7 +86,7 @@ protected: cPickup::cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_Item, bool IsPlayerCreated, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */) : cEntity(etPickup, a_PosX, a_PosY, a_PosZ, 0.2, 0.2) - , m_Timer(0.f) + , m_Timer(0) , m_Item(a_Item) , m_bCollected(false) , m_bIsPlayerCreated(IsPlayerCreated) @@ -110,7 +110,7 @@ void cPickup::SpawnOn(cClientHandle & a_Client) -void cPickup::Tick(float a_Dt, cChunk & a_Chunk) +void cPickup::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); BroadcastMovementUpdate(); // Notify clients of position @@ -141,9 +141,9 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk) ) { m_bCollected = true; - m_Timer = 0; // We have to reset the timer. + m_Timer = std::chrono::milliseconds(0); // We have to reset the timer. m_Timer += a_Dt; // In case we have to destroy the pickup in the same tick. - if (m_Timer > 500.f) + if (m_Timer > std::chrono::milliseconds(500)) { Destroy(true); return; @@ -167,14 +167,14 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk) } else { - if (m_Timer > 500.f) // 0.5 second + if (m_Timer > std::chrono::milliseconds(500)) // 0.5 second { Destroy(true); return; } } - if (m_Timer > 1000 * 60 * 5) // 5 minutes + if (m_Timer > std::chrono::minutes(5)) // 5 minutes { Destroy(true); return; @@ -200,7 +200,7 @@ bool cPickup::CollectedBy(cPlayer & a_Dest) } // Two seconds if player created the pickup (vomiting), half a second if anything else - if (m_Timer < (m_bIsPlayerCreated ? 2000.f : 500.f)) + if (m_Timer < (m_bIsPlayerCreated ? std::chrono::seconds(2) : std::chrono::milliseconds(500))) { // LOG("Pickup %d cannot be collected by \"%s\", because it is not old enough.", m_UniqueID, a_Dest->GetName().c_str()); return false; // Not old enough @@ -234,7 +234,7 @@ bool cPickup::CollectedBy(cPlayer & a_Dest) // All of the pickup has been collected, schedule the pickup for destroying m_bCollected = true; } - m_Timer = 0; + m_Timer = std::chrono::milliseconds(0); return true; } diff --git a/src/Entities/Pickup.h b/src/Entities/Pickup.h index d1176a7cf..ed5949f37 100644 --- a/src/Entities/Pickup.h +++ b/src/Entities/Pickup.h @@ -34,13 +34,13 @@ public: bool CollectedBy(cPlayer & a_Dest); // tolua_export - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; /** Returns the number of ticks that this entity has existed */ - int GetAge(void) const { return static_cast<int>(m_Timer / 50); } // tolua_export + int GetAge(void) const { return std::chrono::duration_cast<cTickTime>(m_Timer).count(); } // tolua_export /** Set the number of ticks that this entity has existed */ - void SetAge(int a_Age) { m_Timer = static_cast<float>(a_Age * 50); } // tolua_export + void SetAge(int a_Age) { m_Timer = cTickTime(a_Age); } // tolua_export /** Returns true if the pickup has already been collected */ bool IsCollected(void) const { return m_bCollected; } // tolua_export @@ -51,7 +51,7 @@ public: private: /** The number of ticks that the entity has existed / timer between collect and destroy; in msec */ - float m_Timer; + std::chrono::milliseconds m_Timer; cItem m_Item; diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 716e20663..67de2e23a 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -2,6 +2,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Player.h" +#include <unordered_map> #include "../ChatColor.h" #include "../Server.h" #include "../UI/InventoryWindow.h" @@ -19,6 +20,10 @@ #include "../WorldStorage/StatSerializer.h" #include "../CompositeChat.h" +#include "../Blocks/BlockHandler.h" +#include "../Blocks/BlockSlab.h" +#include "../Blocks/ChunkInterface.h" + #include "../IniFile.h" #include "json/json.h" @@ -186,7 +191,7 @@ void cPlayer::SpawnOn(cClientHandle & a_Client) -void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) +void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (m_ClientHandle != nullptr) { @@ -2168,6 +2173,97 @@ void cPlayer::LoadRank(void) +bool cPlayer::PlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +{ + sSetBlockVector blk{{a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta}}; + return PlaceBlocks(blk); +} + + + + + +void cPlayer::SendBlocksAround(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Range) +{ + // Collect the coords of all the blocks to send: + sSetBlockVector blks; + for (int y = a_BlockY - a_Range + 1; y < a_BlockY + a_Range; y++) + { + for (int z = a_BlockZ - a_Range + 1; z < a_BlockZ + a_Range; z++) + { + for (int x = a_BlockX - a_Range + 1; x < a_BlockX + a_Range; x++) + { + blks.emplace_back(x, y, z, E_BLOCK_AIR, 0); // Use fake blocktype, it will get set later on. + }; + }; + } // for y + + // Get the values of all the blocks: + if (!m_World->GetBlocks(blks, false)) + { + LOGD("%s: Cannot query all blocks, not sending an update", __FUNCTION__); + return; + } + + // Divide the block changes by their respective chunks: + std::unordered_map<cChunkCoords, sSetBlockVector, cChunkCoordsHash> Changes; + for (const auto & blk: blks) + { + Changes[cChunkCoords(blk.m_ChunkX, blk.m_ChunkZ)].push_back(blk); + } // for blk - blks[] + blks.clear(); + + // Send the blocks for each affected chunk: + for (auto itr = Changes.cbegin(), end = Changes.cend(); itr != end; ++itr) + { + m_ClientHandle->SendBlockChanges(itr->first.m_ChunkX, itr->first.m_ChunkZ, itr->second); + } +} + + + + + +bool cPlayer::PlaceBlocks(const sSetBlockVector & a_Blocks) +{ + // Call the "placing" hooks; if any fail, abort: + cPluginManager * pm = cPluginManager::Get(); + for (auto blk: a_Blocks) + { + if (pm->CallHookPlayerPlacingBlock(*this, blk)) + { + // Abort - re-send all the current blocks in the a_Blocks' coords to the client: + for (auto blk2: a_Blocks) + { + m_World->SendBlockTo(blk2.GetX(), blk2.GetY(), blk2.GetZ(), this); + } + return false; + } + } // for blk - a_Blocks[] + + // Set the blocks: + m_World->SetBlocks(a_Blocks); + + // Notify the blockhandlers: + cChunkInterface ChunkInterface(m_World->GetChunkMap()); + for (auto blk: a_Blocks) + { + cBlockHandler * newBlock = BlockHandler(blk.m_BlockType); + newBlock->OnPlacedByPlayer(ChunkInterface, *m_World, this, blk); + } + + // Call the "placed" hooks: + for (auto blk: a_Blocks) + { + pm->CallHookPlayerPlacedBlock(*this, blk); + } + return true; +} + + + + + void cPlayer::Detach() { super::Detach(); diff --git a/src/Entities/Player.h b/src/Entities/Player.h index c643aaa8e..d3ed46db6 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -46,9 +46,9 @@ public: virtual void SpawnOn(cClientHandle & a_Client) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - virtual void HandlePhysics(float a_Dt, cChunk &) override { UNUSED(a_Dt); } + virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk &) override { UNUSED(a_Dt); } /** Returns the curently equipped weapon; empty item if none */ virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); } @@ -440,8 +440,27 @@ public: Loads the m_Rank, m_Permissions, m_MsgPrefix, m_MsgSuffix and m_MsgNameColorCode members. */ void LoadRank(void); + /** Calls the block-placement hook and places the block in the world, unless refused by the hook. + If the hook prevents the placement, sends the current block at the specified coords back to the client. + Assumes that the block is in a currently loaded chunk. + Returns true if the block is successfully placed. */ + bool PlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /** Sends the block in the specified range around the specified coord to the client + as a block change packet. + The blocks in range (a_BlockX - a_Range, a_BlockX + a_Range) are sent (NY-metric). */ + void SendBlocksAround(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Range = 1); + // tolua_end + /** Calls the block placement hooks and places the blocks in the world. + First the "placing" hooks for all the blocks are called, then the blocks are placed, and finally + the "placed" hooks are called. + If the any of the "placing" hooks aborts, none of the blocks are placed and the function returns false. + Returns true if all the blocks are placed. + Assumes that all the blocks are in currently loaded chunks. */ + bool PlaceBlocks(const sSetBlockVector & a_Blocks); + // cEntity overrides: virtual bool IsCrouched (void) const { return m_IsCrouched; } virtual bool IsSprinting(void) const { return m_IsSprinting; } diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp index 1768714f8..4f20bfae6 100644 --- a/src/Entities/ProjectileEntity.cpp +++ b/src/Entities/ProjectileEntity.cpp @@ -331,7 +331,7 @@ AString cProjectileEntity::GetMCAClassName(void) const -void cProjectileEntity::Tick(float a_Dt, cChunk & a_Chunk) +void cProjectileEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); @@ -346,7 +346,7 @@ void cProjectileEntity::Tick(float a_Dt, cChunk & a_Chunk) -void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) +void cProjectileEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (m_IsInGround) { diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h index 2a98e31c7..93e442d8c 100644 --- a/src/Entities/ProjectileEntity.h +++ b/src/Entities/ProjectileEntity.h @@ -118,8 +118,8 @@ protected: bool m_IsInGround; // cEntity overrides: - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void SpawnOn(cClientHandle & a_Client) override; } ; // tolua_export diff --git a/src/Entities/SplashPotionEntity.h b/src/Entities/SplashPotionEntity.h index 9302d8292..264dc0eb9 100644 --- a/src/Entities/SplashPotionEntity.h +++ b/src/Entities/SplashPotionEntity.h @@ -58,7 +58,7 @@ protected: // cProjectileEntity overrides: virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - virtual void Tick (float a_Dt, cChunk & a_Chunk) override + virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override { if (m_DestroyTimer > 0) { diff --git a/src/Entities/TNTEntity.cpp b/src/Entities/TNTEntity.cpp index 53af446cc..a89d2f300 100644 --- a/src/Entities/TNTEntity.cpp +++ b/src/Entities/TNTEntity.cpp @@ -50,7 +50,7 @@ void cTNTEntity::Explode(void) -void cTNTEntity::Tick(float a_Dt, cChunk & a_Chunk) +void cTNTEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); BroadcastMovementUpdate(); diff --git a/src/Entities/TNTEntity.h b/src/Entities/TNTEntity.h index 48503cf76..9f894338e 100644 --- a/src/Entities/TNTEntity.h +++ b/src/Entities/TNTEntity.h @@ -21,7 +21,7 @@ public: // cEntity overrides: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; // tolua_begin diff --git a/src/Entities/ThrownEggEntity.cpp b/src/Entities/ThrownEggEntity.cpp index 24c946a9c..e9ef29239 100644 --- a/src/Entities/ThrownEggEntity.cpp +++ b/src/Entities/ThrownEggEntity.cpp @@ -44,7 +44,7 @@ void cThrownEggEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_Hit -void cThrownEggEntity::Tick(float a_Dt, cChunk & a_Chunk) +void cThrownEggEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (m_DestroyTimer > 0) { diff --git a/src/Entities/ThrownEggEntity.h b/src/Entities/ThrownEggEntity.h index 6ffedf5b5..620927c5d 100644 --- a/src/Entities/ThrownEggEntity.h +++ b/src/Entities/ThrownEggEntity.h @@ -35,7 +35,7 @@ protected: // cProjectileEntity overrides: virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; // Randomly decides whether to spawn a chicken where the egg lands. void TrySpawnChicken(const Vector3d & a_HitPos); diff --git a/src/Entities/ThrownEnderPearlEntity.cpp b/src/Entities/ThrownEnderPearlEntity.cpp index 8f1b62934..f01cdc18c 100644 --- a/src/Entities/ThrownEnderPearlEntity.cpp +++ b/src/Entities/ThrownEnderPearlEntity.cpp @@ -46,7 +46,7 @@ void cThrownEnderPearlEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d -void cThrownEnderPearlEntity::Tick(float a_Dt, cChunk & a_Chunk) +void cThrownEnderPearlEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (m_DestroyTimer > 0) { diff --git a/src/Entities/ThrownEnderPearlEntity.h b/src/Entities/ThrownEnderPearlEntity.h index 475ebde87..94f3ab5cb 100644 --- a/src/Entities/ThrownEnderPearlEntity.h +++ b/src/Entities/ThrownEnderPearlEntity.h @@ -35,7 +35,7 @@ protected: // cProjectileEntity overrides: virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; /** Teleports the creator where the ender pearl lands */ void TeleportCreator(const Vector3d & a_HitPos); diff --git a/src/Entities/ThrownSnowballEntity.cpp b/src/Entities/ThrownSnowballEntity.cpp index 88e39d22e..24db1e7ee 100644 --- a/src/Entities/ThrownSnowballEntity.cpp +++ b/src/Entities/ThrownSnowballEntity.cpp @@ -48,7 +48,7 @@ void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & -void cThrownSnowballEntity::Tick(float a_Dt, cChunk & a_Chunk) +void cThrownSnowballEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (m_DestroyTimer > 0) { diff --git a/src/Entities/ThrownSnowballEntity.h b/src/Entities/ThrownSnowballEntity.h index f806996cc..391b0c40b 100644 --- a/src/Entities/ThrownSnowballEntity.h +++ b/src/Entities/ThrownSnowballEntity.h @@ -35,7 +35,7 @@ protected: // cProjectileEntity overrides: virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; private: diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp index 2a4dbe794..2cc810d3b 100644 --- a/src/Generating/BioGen.cpp +++ b/src/Generating/BioGen.cpp @@ -733,7 +733,19 @@ cBioGenTwoLevel::cBioGenTwoLevel(int a_Seed) : m_Noise3(a_Seed + 5003), m_Noise4(a_Seed + 5004), m_Noise5(a_Seed + 5005), - m_Noise6(a_Seed + 5006) + m_Noise6(a_Seed + 5006), + m_FreqX1(0.0), + m_AmpX1(0.0), + m_FreqX2(0.0), + m_AmpX2(0.0), + m_FreqX3(0.0), + m_AmpX3(0.0), + m_FreqZ1(0.0), + m_AmpZ1(0.0), + m_FreqZ2(0.0), + m_AmpZ2(0.0), + m_FreqZ3(0.0), + m_AmpZ3(0.0) { } diff --git a/src/Generating/EndGen.cpp b/src/Generating/EndGen.cpp index 89d6117bb..c559e765f 100644 --- a/src/Generating/EndGen.cpp +++ b/src/Generating/EndGen.cpp @@ -39,7 +39,13 @@ cEndGen::cEndGen(int a_Seed) : m_IslandSizeZ(256), m_FrequencyX(80), m_FrequencyY(80), - m_FrequencyZ(80) + m_FrequencyZ(80), + m_MinChunkX(0), + m_MaxChunkX(0), + m_MinChunkZ(0), + m_MaxChunkZ(0), + m_LastChunkX(0x7fffffff), // Use dummy coords that won't ever be used by real chunks + m_LastChunkZ(0x7fffffff) { m_Perlin.AddOctave(1, 1); m_Perlin.AddOctave(2, 0.5); diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp index b43a1a6de..eadc66a4e 100644 --- a/src/Generating/Noise3DGenerator.cpp +++ b/src/Generating/Noise3DGenerator.cpp @@ -379,7 +379,20 @@ cNoise3DComposable::cNoise3DComposable(int a_Seed) : m_ChoiceNoise(a_Seed), m_DensityNoiseA(a_Seed + 1), m_DensityNoiseB(a_Seed + 2), - m_BaseNoise(a_Seed + 3) + m_BaseNoise(a_Seed + 3), + m_HeightAmplification(0.0), + m_MidPoint(0.0), + m_FrequencyX(0.0), + m_FrequencyY(0.0), + m_FrequencyZ(0.0), + m_BaseFrequencyX(0.0), + m_BaseFrequencyZ(0.0), + m_ChoiceFrequencyX(0.0), + m_ChoiceFrequencyY(0.0), + m_ChoiceFrequencyZ(0.0), + m_AirThreshold(0.0), + m_LastChunkX(0x7fffffff), // Use dummy coords that won't ever be used by real chunks + m_LastChunkZ(0x7fffffff) { } diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp index 2f685c808..2d5a73739 100644 --- a/src/Generating/StructGen.cpp +++ b/src/Generating/StructGen.cpp @@ -123,18 +123,18 @@ void cStructGenTrees::GenerateSingleTree( // Check if the generated image fits the terrain. Only the logs are checked: for (sSetBlockVector::const_iterator itr = TreeLogs.begin(); itr != TreeLogs.end(); ++itr) { - if ((itr->ChunkX != a_ChunkX) || (itr->ChunkZ != a_ChunkZ)) + if ((itr->m_ChunkX != a_ChunkX) || (itr->m_ChunkZ != a_ChunkZ)) { // Outside the chunk continue; } - if (itr->y >= cChunkDef::Height) + if (itr->m_RelY >= cChunkDef::Height) { // Above the chunk, cut off (this shouldn't happen too often, we're limiting trees to y < 230) continue; } - BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z); + BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->m_RelX, itr->m_RelY, itr->m_RelZ); switch (Block) { CASE_TREE_ALLOWED_BLOCKS: @@ -167,14 +167,14 @@ void cStructGenTrees::ApplyTreeImage( // Put the generated image into a_BlockTypes, push things outside this chunk into a_Blocks for (sSetBlockVector::const_iterator itr = a_Image.begin(), end = a_Image.end(); itr != end; ++itr) { - if ((itr->ChunkX == a_ChunkX) && (itr->ChunkZ == a_ChunkZ) && (itr->y < cChunkDef::Height)) + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ) && (itr->m_RelY < cChunkDef::Height)) { // Inside this chunk, integrate into a_ChunkDesc: - switch (a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z)) + switch (a_ChunkDesc.GetBlockType(itr->m_RelX, itr->m_RelY, itr->m_RelZ)) { case E_BLOCK_LEAVES: { - if (itr->BlockType != E_BLOCK_LOG) + if (itr->m_BlockType != E_BLOCK_LOG) { break; } @@ -182,7 +182,7 @@ void cStructGenTrees::ApplyTreeImage( } CASE_TREE_OVERWRITTEN_BLOCKS: { - a_ChunkDesc.SetBlockTypeMeta(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta); + a_ChunkDesc.SetBlockTypeMeta(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta); break; } diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp index be8b0cd6b..a10e0f4f1 100644 --- a/src/Generating/Trees.cpp +++ b/src/Generating/Trees.cpp @@ -403,17 +403,17 @@ void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a for (auto itr : a_LogBlocks) { // Get the log's X and Z coordinates - int X = itr.ChunkX * 16 + itr.x; - int Z = itr.ChunkZ * 16 + itr.z; + int X = itr.GetX(); + int Z = itr.GetZ(); - a_OtherBlocks.push_back(sSetBlock(X, itr.y - 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); - PushCoordBlocks(X, itr.y - 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + a_OtherBlocks.push_back(sSetBlock(X, itr.m_RelY - 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); + PushCoordBlocks(X, itr.m_RelY - 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); for (int y = -1; y <= 1; y++) { - PushCoordBlocks (X, itr.y + y, Z, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + PushCoordBlocks (X, itr.m_RelY + y, Z, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); } - PushCoordBlocks(X, itr.y + 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); - a_OtherBlocks.push_back(sSetBlock(X, itr.y + 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); + PushCoordBlocks(X, itr.m_RelY + 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE); + a_OtherBlocks.push_back(sSetBlock(X, itr.m_RelY + 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE)); } // Trunk: diff --git a/src/Globals.h b/src/Globals.h index 61f500db9..654ede95f 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -268,33 +268,47 @@ template class SizeChecker<UInt16, 2>; #include "OSSupport/StackTrace.h" #else // Logging functions -void inline LOGERROR(const char* a_Format, ...) FORMATSTRING(1, 2); +void inline LOGERROR(const char * a_Format, ...) FORMATSTRING(1, 2); -void inline LOGERROR(const char* a_Format, ...) +void inline LOGERROR(const char * a_Format, ...) { va_list argList; va_start(argList, a_Format); vprintf(a_Format, argList); + putchar('\n'); va_end(argList); } -void inline LOGWARNING(const char* a_Format, ...) FORMATSTRING(1, 2); +void inline LOGWARNING(const char * a_Format, ...) FORMATSTRING(1, 2); -void inline LOGWARNING(const char* a_Format, ...) +void inline LOGWARNING(const char * a_Format, ...) { va_list argList; va_start(argList, a_Format); vprintf(a_Format, argList); + putchar('\n'); va_end(argList); } -void inline LOGD(const char* a_Format, ...) FORMATSTRING(1, 2); +void inline LOGD(const char * a_Format, ...) FORMATSTRING(1, 2); -void inline LOGD(const char* a_Format, ...) +void inline LOGD(const char * a_Format, ...) { va_list argList; va_start(argList, a_Format); vprintf(a_Format, argList); + putchar('\n'); + va_end(argList); +} + +void inline LOG(const char * a_Format, ...) FORMATSTRING(1, 2); + +void inline LOG(const char * a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + vprintf(a_Format, argList); + putchar('\n'); va_end(argList); } @@ -419,6 +433,9 @@ std::unique_ptr<T> make_unique(Args&&... args) return std::unique_ptr<T>(new T(args...)); } +// a tick is 50 ms +using cTickTime = std::chrono::duration<int, std::ratio_multiply<std::chrono::milliseconds::period, std::ratio<50>>>; +using cTickTimeLong = std::chrono::duration<Int64, cTickTime::period>; #ifndef TOLUA_TEMPLATE_BIND #define TOLUA_TEMPLATE_BIND(x) @@ -436,3 +453,4 @@ std::unique_ptr<T> make_unique(Args&&... args) + diff --git a/src/HTTPServer/HTTPMessage.cpp b/src/HTTPServer/HTTPMessage.cpp index f6c0204ae..d59ca438e 100644 --- a/src/HTTPServer/HTTPMessage.cpp +++ b/src/HTTPServer/HTTPMessage.cpp @@ -55,7 +55,7 @@ void cHTTPMessage::AddHeader(const AString & a_Key, const AString & a_Value) } else if (Key == "content-length") { - m_ContentLength = atoi(m_Headers[Key].c_str()); + m_ContentLength = static_cast<size_t>(atol(m_Headers[Key].c_str())); } } diff --git a/src/ItemGrid.cpp b/src/ItemGrid.cpp index d49ea9df1..06971a1ac 100644 --- a/src/ItemGrid.cpp +++ b/src/ItemGrid.cpp @@ -646,7 +646,7 @@ void cItemGrid::GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, s for (int j = 0; j <= NumEnchantments; j++) { - cEnchantments Enchantment = cEnchantments::GenerateEnchantmentFromVector(Enchantments, Noise.IntNoise2DInt(NumEnchantments, i)); + cEnchantments Enchantment = cEnchantments::SelectEnchantmentFromVector(Enchantments, Noise.IntNoise2DInt(NumEnchantments, i)); CurrentLoot.m_Enchantments.Add(Enchantment); cEnchantments::RemoveEnchantmentWeightFromVector(Enchantments, Enchantment); cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment); diff --git a/src/Items/CMakeLists.txt b/src/Items/CMakeLists.txt index 12a467672..c50ddb372 100644 --- a/src/Items/CMakeLists.txt +++ b/src/Items/CMakeLists.txt @@ -10,12 +10,14 @@ SET (SRCS SET (HDRS ItemArmor.h ItemBed.h + ItemBigFlower.h ItemBoat.h ItemBow.h ItemBrewingStand.h ItemBucket.h ItemCake.h ItemCauldron.h + ItemChest.h ItemCloth.h ItemComparator.h ItemDoor.h @@ -38,18 +40,21 @@ SET (HDRS ItemPainting.h ItemPickaxe.h ItemPotion.h + ItemPumpkin.h ItemRedstoneDust.h ItemRedstoneRepeater.h ItemSapling.h ItemSeeds.h ItemShears.h ItemShovel.h + ItemSlab.h ItemSign.h ItemSpawnEgg.h ItemString.h ItemSugarcane.h ItemSword.h - ItemThrowable.h) + ItemThrowable.h +) if(NOT MSVC) add_library(Items ${SRCS} ${HDRS}) diff --git a/src/Items/ItemBed.h b/src/Items/ItemBed.h index 94a14cf16..77d51d744 100644 --- a/src/Items/ItemBed.h +++ b/src/Items/ItemBed.h @@ -24,30 +24,36 @@ public: return true; } - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, + + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + int a_CursorX, int a_CursorY, int a_CursorZ ) override { + // Can only be placed on the floor: if (a_BlockFace != BLOCK_FACE_TOP) { - // Can only be placed on the floor return false; } + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetYaw()); + // The "foot" block: + sSetBlockVector blks; + NIBBLETYPE BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player.GetYaw()); + blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BED, BlockMeta); - // Check if there is empty space for the foot section: - Vector3i Direction = cBlockBedHandler::MetaDataToDirection(a_BlockMeta); - if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR) + // Check if there is empty space for the "head" block: + // (Vanilla only allows beds to be placed into air) + Vector3i Direction = cBlockBedHandler::MetaDataToDirection(BlockMeta); + if (a_World.GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR) { return false; } + blks.emplace_back(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, BlockMeta | 0x08); - a_BlockType = E_BLOCK_BED; - return true; + // Place both bed blocks: + return a_Player.PlaceBlocks(blks); } } ; diff --git a/src/Items/ItemBigFlower.h b/src/Items/ItemBigFlower.h new file mode 100644 index 000000000..4341a1a17 --- /dev/null +++ b/src/Items/ItemBigFlower.h @@ -0,0 +1,56 @@ + +// ItemBigFlower.h + +// Declares the cItemBigFlower class representing the cItemHandler for big flowers + + + + + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemBigFlowerHandler: + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemBigFlowerHandler(void): + super(E_BLOCK_BIG_FLOWER) + { + } + + + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ + ) override + { + // Can only be placed on the floor: + if (a_BlockFace != BLOCK_FACE_TOP) + { + return false; + } + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + // Place both blocks atomically: + sSetBlockVector blks; + blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BIG_FLOWER, a_EquippedItem.m_ItemDamage & 0x07); + if (a_BlockY < cChunkDef::Height - 1) + { + blks.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_BIG_FLOWER, (a_EquippedItem.m_ItemDamage & 0x07) | 0x08); + } + return a_Player.PlaceBlocks(blks); + } +}; + + + + diff --git a/src/Items/ItemBucket.h b/src/Items/ItemBucket.h index 3a533958f..871db821c 100644 --- a/src/Items/ItemBucket.h +++ b/src/Items/ItemBucket.h @@ -199,16 +199,16 @@ public: Vector3i m_Pos; BLOCKTYPE m_ReplacedBlock; - virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override + virtual bool OnNextBlock(int a_CBBlockX, int a_CBBlockY, int a_CBBlockZ, BLOCKTYPE a_CBBlockType, NIBBLETYPE a_CBBlockMeta, char a_CBEntryFace) override { - if (a_BlockType != E_BLOCK_AIR) + if (a_CBBlockType != E_BLOCK_AIR) { - m_ReplacedBlock = a_BlockType; - if (!cFluidSimulator::CanWashAway(a_BlockType) && !IsBlockLiquid(a_BlockType)) + m_ReplacedBlock = a_CBBlockType; + if (!cFluidSimulator::CanWashAway(a_CBBlockType) && !IsBlockLiquid(a_CBBlockType)) { - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, (eBlockFace)a_EntryFace); // Was an unwashawayable block, can't overwrite it! + AddFaceDirection(a_CBBlockX, a_CBBlockY, a_CBBlockZ, (eBlockFace)a_CBEntryFace); // Was an unwashawayable block, can't overwrite it! } - m_Pos.Set(a_BlockX, a_BlockY, a_BlockZ); // (Block could be washed away, replace it) + m_Pos.Set(a_CBBlockX, a_CBBlockY, a_CBBlockZ); // (Block could be washed away, replace it) return true; // Abort tracing } return false; diff --git a/src/Items/ItemChest.h b/src/Items/ItemChest.h new file mode 100644 index 000000000..b6579c423 --- /dev/null +++ b/src/Items/ItemChest.h @@ -0,0 +1,167 @@ + +// ItemChest.h + +// Declares the cItemChestHandler class representing the cItemHandler descendant responsible for chests + + + + + +#pragma once + +#include "ItemHandler.h" +#include "../Blocks/BlockChest.h" + + + + + +class cItemChestHandler: + public cItemHandler +{ + typedef cItemHandler super; +public: + cItemChestHandler(int a_ItemType): + super(a_ItemType) + { + } + + + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ + ) override + { + if (a_BlockFace < 0) + { + // Clicked in air + return false; + } + + if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) + { + // The clicked block is outside the world, ignore this call altogether (#128) + return false; + } + + // Check if the block ignores build collision (water, grass etc.): + BLOCKTYPE ClickedBlock; + NIBBLETYPE ClickedBlockMeta; + a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); + if ( + BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() || + BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(&a_Player, ClickedBlockMeta) + ) + { + cChunkInterface ChunkInterface(a_World.GetChunkMap()); + BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ); + } + else + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) + { + // The block is being placed outside the world, ignore this packet altogether (#128) + return false; + } + + NIBBLETYPE PlaceMeta; + BLOCKTYPE PlaceBlock; + a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlock, PlaceMeta); + + // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed. + // No need to do combinability (dblslab) checks, client will do that here. + if ( + !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision() && + !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&a_Player, PlaceMeta) + ) + { + // Tried to place a block *into* another? + // Happens when you place a block aiming at side of block with a torch on it or stem beside it + return false; + } + } + + // Check that there is at most one single neighbor of the same chest type: + static const Vector3i CrossCoords[] = + { + {-1, 0, 0}, + { 0, 0, -1}, + { 1, 0, 0}, + { 0, 0, 1}, + }; + int NeighborIdx = -1; + for (size_t i = 0; i < ARRAYCOUNT(CrossCoords); i++) + { + if (a_World.GetBlock(a_BlockX + CrossCoords[i].x, a_BlockY, a_BlockZ + CrossCoords[i].z) != m_ItemType) + { + continue; + } + if (NeighborIdx >= 0) + { + // Can't place here, there are already two neighbors, this would form a 3-block chest + return false; + } + NeighborIdx = static_cast<int>(i); + + // Check that this neighbor is a single chest: + int bx = a_BlockX + CrossCoords[i].x; + int bz = a_BlockZ + CrossCoords[i].z; + for (size_t j = 0; j < ARRAYCOUNT(CrossCoords); j++) + { + if (a_World.GetBlock(bx + CrossCoords[j].x, a_BlockY, bz + CrossCoords[j].z) == m_ItemType) + { + return false; + } + } // for j + } // for i + + // If there's no chest neighbor, place the single block chest and bail out: + BLOCKTYPE ChestBlockType = static_cast<BLOCKTYPE>(m_ItemType); + if (NeighborIdx < 0) + { + NIBBLETYPE Meta = cBlockChestHandler::PlayerYawToMetaData(a_Player.GetYaw()); + return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta); + } + + // There is a neighbor to which we need to adjust + double yaw = a_Player.GetYaw(); + if ((NeighborIdx == 0) || (NeighborIdx == 2)) + { + // The neighbor is in the X axis, form a X-axis-aligned dblchest: + NIBBLETYPE Meta = ((yaw >= -90) && (yaw < 90)) ? E_META_CHEST_FACING_ZM : E_META_CHEST_FACING_ZP; + + // Place the new chest: + if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta)) + { + return false; + } + + // Adjust the existing chest: + a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta); + return true; + } + + // The neighbor is in the Z axis, form a Z-axis-aligned dblchest: + NIBBLETYPE Meta = (yaw < 0) ? E_META_CHEST_FACING_XM : E_META_CHEST_FACING_XP; + + // Place the new chest: + if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta)) + { + return false; + } + + // Adjust the existing chest: + a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta); + return true; + } + +private: + cItemChestHandler(const cItemChestHandler &) = delete; +}; + + + + diff --git a/src/Items/ItemDoor.h b/src/Items/ItemDoor.h index cd5baf44f..dacf286e5 100644 --- a/src/Items/ItemDoor.h +++ b/src/Items/ItemDoor.h @@ -3,6 +3,7 @@ #include "ItemHandler.h" #include "../World.h" +#include "../Blocks/BlockDoor.h" @@ -18,27 +19,43 @@ public: } - virtual bool IsPlaceable(void) override - { - return true; - } - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + int a_CursorX, int a_CursorY, int a_CursorZ ) override { + // Vanilla only allows door placement while clicking on the top face of the block below the door: + if (a_BlockFace != BLOCK_FACE_TOP) + { + return false; + } + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + // Door (bottom block) can be placed in Y range of [1, 254]: + if ((a_BlockY < 1) || (a_BlockY + 2 >= cChunkDef::Height)) + { + return false; + } + + // The door needs a compatible block below it: + if ((a_BlockY > 0) && !cBlockDoorHandler::CanBeOn(a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) + { + return false; + } + + // Get the block type of the door to place: + BLOCKTYPE BlockType; switch (m_ItemType) { - case E_ITEM_WOODEN_DOOR: a_BlockType = E_BLOCK_WOODEN_DOOR; break; - case E_ITEM_IRON_DOOR: a_BlockType = E_BLOCK_IRON_DOOR; break; - case E_ITEM_SPRUCE_DOOR: a_BlockType = E_BLOCK_SPRUCE_DOOR; break; - case E_ITEM_BIRCH_DOOR: a_BlockType = E_BLOCK_BIRCH_DOOR; break; - case E_ITEM_JUNGLE_DOOR: a_BlockType = E_BLOCK_JUNGLE_DOOR; break; - case E_ITEM_DARK_OAK_DOOR: a_BlockType = E_BLOCK_DARK_OAK_DOOR; break; - case E_ITEM_ACACIA_DOOR: a_BlockType = E_BLOCK_ACACIA_DOOR; break; + case E_ITEM_WOODEN_DOOR: BlockType = E_BLOCK_WOODEN_DOOR; break; + case E_ITEM_IRON_DOOR: BlockType = E_BLOCK_IRON_DOOR; break; + case E_ITEM_SPRUCE_DOOR: BlockType = E_BLOCK_SPRUCE_DOOR; break; + case E_ITEM_BIRCH_DOOR: BlockType = E_BLOCK_BIRCH_DOOR; break; + case E_ITEM_JUNGLE_DOOR: BlockType = E_BLOCK_JUNGLE_DOOR; break; + case E_ITEM_DARK_OAK_DOOR: BlockType = E_BLOCK_DARK_OAK_DOOR; break; + case E_ITEM_ACACIA_DOOR: BlockType = E_BLOCK_ACACIA_DOOR; break; default: { ASSERT(!"Unhandled door type"); @@ -46,14 +63,47 @@ public: } } - cChunkInterface ChunkInterface(a_World->GetChunkMap()); - bool Meta = BlockHandler(a_BlockType)->GetPlacementBlockTypeMeta( - ChunkInterface, a_Player, - a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, - a_CursorX, a_CursorY, a_CursorZ, - a_BlockType, a_BlockMeta - ); - return Meta; + // Check the two blocks that will get replaced by the door: + BLOCKTYPE LowerBlockType = a_World.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ); + BLOCKTYPE UpperBlockType = a_World.GetBlock(a_BlockX, a_BlockY + 2, a_BlockZ); + if ( + !cBlockDoorHandler::CanReplaceBlock(LowerBlockType) || + !cBlockDoorHandler::CanReplaceBlock(UpperBlockType)) + { + return false; + } + + // Get the coords of the neighboring blocks: + NIBBLETYPE LowerBlockMeta = cBlockDoorHandler::PlayerYawToMetaData(a_Player.GetYaw()); + Vector3i RelDirToOutside = cBlockDoorHandler::GetRelativeDirectionToOutside(LowerBlockMeta); + Vector3i LeftNeighborPos = RelDirToOutside; + LeftNeighborPos.TurnCCW(); + LeftNeighborPos.Move(a_BlockX, a_BlockY, a_BlockZ); + Vector3i RightNeighborPos = RelDirToOutside; + RightNeighborPos.TurnCW(); + RightNeighborPos.Move(a_BlockX, a_BlockY, a_BlockZ); + + // Decide whether the hinge is on the left (default) or on the right: + NIBBLETYPE UpperBlockMeta = 0x08; + if ( + cBlockDoorHandler::IsDoorBlockType(a_World.GetBlock(LeftNeighborPos)) || // The block to the left is a door block + cBlockInfo::IsSolid(a_World.GetBlock(RightNeighborPos)) // The block to the right is solid + ) + { + UpperBlockMeta = 0x09; // Upper block | hinge on right + } + + // Set the blocks: + sSetBlockVector blks; + blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, BlockType, LowerBlockMeta); + blks.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, BlockType, UpperBlockMeta); + return a_Player.PlaceBlocks(blks); + } + + + virtual bool IsPlaceable(void) override + { + return true; } } ; diff --git a/src/Items/ItemDye.h b/src/Items/ItemDye.h index da978040d..bfcd0bac4 100644 --- a/src/Items/ItemDye.h +++ b/src/Items/ItemDye.h @@ -55,25 +55,16 @@ public: return false; } - // Check plugins - if (cRoot::Get()->GetPluginManager()->CallHookPlayerPlacingBlock(*a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, 0, 0, 0, E_BLOCK_COCOA_POD, BlockMeta)) + // Place the cocoa pod: + if (a_Player->PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COCOA_POD, BlockMeta)) { - a_World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); - a_Player->GetInventory().SendEquippedSlot(); - return false; - } - - // Set block and broadcast place sound - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COCOA_POD, BlockMeta); - a_World->BroadcastSoundEffect("dig.stone", a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 1.0f, 0.8f); - - // Remove one cocoa pod from the inventory - if (!a_Player->IsGameModeCreative()) - { - a_Player->GetInventory().RemoveOneEquippedItem(); + a_World->BroadcastSoundEffect("dig.stone", a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 1.0f, 0.8f); + if (a_Player->IsGameModeSurvival()) + { + a_Player->GetInventory().RemoveOneEquippedItem(); + } + return true; } - cRoot::Get()->GetPluginManager()->CallHookPlayerPlacedBlock(*a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, 0, 0, 0, E_BLOCK_COCOA_POD, BlockMeta); - return true; } return false; } diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp index 9272a723d..621cf9501 100644 --- a/src/Items/ItemHandler.cpp +++ b/src/Items/ItemHandler.cpp @@ -6,39 +6,43 @@ #include "../Entities/Player.h" #include "../FastRandom.h" #include "../BlockInServerPluginInterface.h" +#include "../Chunk.h" // Handlers: #include "ItemArmor.h" #include "ItemBed.h" +#include "ItemBigFlower.h" #include "ItemBoat.h" #include "ItemBow.h" #include "ItemBrewingStand.h" #include "ItemBucket.h" #include "ItemCake.h" #include "ItemCauldron.h" +#include "ItemChest.h" #include "ItemCloth.h" #include "ItemComparator.h" #include "ItemDoor.h" -#include "ItemMilk.h" #include "ItemDye.h" #include "ItemEmptyMap.h" #include "ItemFishingRod.h" #include "ItemFlowerPot.h" #include "ItemFood.h" #include "ItemGoldenApple.h" -#include "ItemItemFrame.h" #include "ItemHoe.h" +#include "ItemItemFrame.h" #include "ItemLeaves.h" #include "ItemLighter.h" #include "ItemLilypad.h" #include "ItemMap.h" +#include "ItemMilk.h" #include "ItemMinecart.h" +#include "ItemMobHead.h" #include "ItemMushroomSoup.h" #include "ItemNetherWart.h" #include "ItemPainting.h" #include "ItemPickaxe.h" #include "ItemPotion.h" -#include "ItemThrowable.h" +#include "ItemPumpkin.h" #include "ItemRedstoneDust.h" #include "ItemRedstoneRepeater.h" #include "ItemSapling.h" @@ -46,11 +50,12 @@ #include "ItemShears.h" #include "ItemShovel.h" #include "ItemSign.h" -#include "ItemMobHead.h" +#include "ItemSlab.h" #include "ItemSpawnEgg.h" #include "ItemString.h" #include "ItemSugarcane.h" #include "ItemSword.h" +#include "ItemThrowable.h" #include "../Blocks/BlockHandler.h" @@ -94,52 +99,59 @@ cItemHandler * cItemHandler::GetItemHandler(int a_ItemType) -cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType) +cItemHandler * cItemHandler::CreateItemHandler(int a_ItemType) { switch (a_ItemType) { default: return new cItemHandler(a_ItemType); // Single item per handler, alphabetically sorted: - case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType); - case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType); - case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType); - case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType); - case E_ITEM_BED: return new cItemBedHandler(a_ItemType); - case E_ITEM_BOAT: return new cItemBoatHandler(a_ItemType); + case E_BLOCK_BIG_FLOWER: return new cItemBigFlowerHandler; + case E_BLOCK_CHEST: return new cItemChestHandler(a_ItemType); + case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType); + case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType); + case E_BLOCK_HEAD: return new cItemMobHeadHandler(a_ItemType); + case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType); + case E_BLOCK_PUMPKIN: return new cItemPumpkinHandler; + case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType); + case E_BLOCK_STONE_SLAB: return new cItemSlabHandler(E_BLOCK_STONE_SLAB, E_BLOCK_DOUBLE_STONE_SLAB); + case E_BLOCK_TRAPPED_CHEST: return new cItemChestHandler(a_ItemType); + case E_BLOCK_WOODEN_SLAB: return new cItemSlabHandler(E_BLOCK_WOODEN_SLAB, E_BLOCK_DOUBLE_WOODEN_SLAB); + case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType); + case E_ITEM_BED: return new cItemBedHandler(a_ItemType); + case E_ITEM_BOAT: return new cItemBoatHandler(a_ItemType); case E_ITEM_BOTTLE_O_ENCHANTING: return new cItemBottleOEnchantingHandler(); - case E_ITEM_BOW: return new cItemBowHandler(); - case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType); - case E_ITEM_CAKE: return new cItemCakeHandler(a_ItemType); - case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType); - case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType); - case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType); - case E_ITEM_EGG: return new cItemEggHandler(); - case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler(); - case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler(); - case E_ITEM_FIRE_CHARGE: return new cItemLighterHandler(a_ItemType); - case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler(); - case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType); - case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType); - case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType); - case E_ITEM_GOLDEN_APPLE: return new cItemGoldenAppleHandler(); - case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType); - case E_ITEM_MAP: return new cItemMapHandler(); - case E_ITEM_MILK: return new cItemMilkHandler(); - case E_ITEM_MUSHROOM_SOUP: return new cItemMushroomSoupHandler(a_ItemType); - case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType); - case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType); - case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType); - case E_ITEM_POTIONS: return new cItemPotionHandler(); - case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType); - case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType); - case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType); - case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType); - case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType); - case E_ITEM_SNOWBALL: return new cItemSnowballHandler(); - case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType); - case E_ITEM_STRING: return new cItemStringHandler(a_ItemType); - case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType); + case E_ITEM_BOW: return new cItemBowHandler(); + case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType); + case E_ITEM_CAKE: return new cItemCakeHandler(a_ItemType); + case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType); + case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType); + case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType); + case E_ITEM_EGG: return new cItemEggHandler(); + case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler(); + case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler(); + case E_ITEM_FIRE_CHARGE: return new cItemLighterHandler(a_ItemType); + case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler(); + case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType); + case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType); + case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType); + case E_ITEM_GOLDEN_APPLE: return new cItemGoldenAppleHandler(); + case E_ITEM_MAP: return new cItemMapHandler(); + case E_ITEM_MILK: return new cItemMilkHandler(); + case E_ITEM_MUSHROOM_SOUP: return new cItemMushroomSoupHandler(a_ItemType); + case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType); + case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType); + case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType); + case E_ITEM_POTIONS: return new cItemPotionHandler(); + case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType); + case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType); + case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType); + case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType); + case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType); + case E_ITEM_SNOWBALL: return new cItemSnowballHandler(); + case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType); + case E_ITEM_STRING: return new cItemStringHandler(a_ItemType); + case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType); case E_ITEM_WOODEN_HOE: case E_ITEM_STONE_HOE: @@ -297,6 +309,108 @@ cItemHandler::cItemHandler(int a_ItemType) +bool cItemHandler::OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ +) +{ + if (a_BlockFace < 0) + { + // Clicked in air + return false; + } + + if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) + { + // The clicked block is outside the world, ignore this call altogether (#128) + return false; + } + + BLOCKTYPE ClickedBlock; + NIBBLETYPE ClickedBlockMeta; + + a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); + + // Check if the block ignores build collision (water, grass etc.): + if ( + BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() || + BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(&a_Player, ClickedBlockMeta) + ) + { + cChunkInterface ChunkInterface(a_World.GetChunkMap()); + BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ); + } + else + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) + { + // The block is being placed outside the world, ignore this packet altogether (#128) + return false; + } + + NIBBLETYPE PlaceMeta; + BLOCKTYPE PlaceBlock; + a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlock, PlaceMeta); + + // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed. + // No need to do combinability (dblslab) checks, client will do that here. + if ( + !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision() && + !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&a_Player, PlaceMeta) + ) + { + // Tried to place a block *into* another? + // Happens when you place a block aiming at side of block with a torch on it or stem beside it + return false; + } + } + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) + { + // Handler refused the placement, send that information back to the client: + a_World.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, &a_Player); + a_Player.GetInventory().SendEquippedSlot(); + return false; + } + + if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta)) + { + // The placement failed, the block has already been re-sent, re-send inventory: + a_Player.GetInventory().SendEquippedSlot(); + return false; + } + + AString PlaceSound = cBlockInfo::GetPlaceSound(BlockType); + float Volume = 1.0f, Pitch = 0.8f; + if (PlaceSound == "dig.metal") + { + Pitch = 1.2f; + PlaceSound = "dig.stone"; + } + else if (PlaceSound == "random.anvil_land") + { + Volume = 0.65f; + } + + a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch); + + // Remove the "placed" item: + if (a_Player.IsGameModeSurvival()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + return true; +} + + + + + bool cItemHandler::OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) { UNUSED(a_World); diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h index 67c250a97..3ac664798 100644 --- a/src/Items/ItemHandler.h +++ b/src/Items/ItemHandler.h @@ -31,10 +31,35 @@ public: /** Force virtual destructor */ virtual ~cItemHandler() {} + + + /** Called when the player tries to place the item (right mouse button, IsPlaceable() == true). + The default handler uses GetPlacementBlockTypeMeta and places the returned block. + Override this function for advanced behavior such as placing multiple blocks. + If the block placement is refused inside this call, it will automatically revert the client-side changes. + Returns true if the placement succeeded, false if the placement was aborted for any reason. */ + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ + ); + + /** Called when the player right-clicks with this item and IsPlaceable() == true, and OnPlace() is not overridden. + This function should provide the block type and meta for the placed block, or refuse the placement. + Returns true to allow placement, false to refuse. */ + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ); + + /** Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False */ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir); + /** Called when the client sends the SHOOT status in the lclk packet */ virtual void OnItemShoot(cPlayer *, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) { @@ -106,18 +131,8 @@ public: /** Can the anvil repair this item, when a_Item is the second input? */ virtual bool CanRepairWithRawMaterial(short a_ItemType); - /** Called before a block is placed into a world. - The handler should return true to allow placement, false to refuse. - Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block. - */ - virtual bool GetPlacementBlockTypeMeta( - cWorld * a_World, cPlayer * a_Player, - int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, - int a_CursorX, int a_CursorY, int a_CursorZ, - BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta - ); - - /** Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can't) DEFAULT: False */ + /** Returns whether this tool / item can harvest a specific block (e.g. iron pickaxe can harvest diamond ore, but wooden one can't). + Defaults to false unless overridden. */ virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType); static cItemHandler * GetItemHandler(int a_ItemType); diff --git a/src/Items/ItemMobHead.h b/src/Items/ItemMobHead.h index 4c36fe8d8..8780f7e4b 100644 --- a/src/Items/ItemMobHead.h +++ b/src/Items/ItemMobHead.h @@ -3,6 +3,7 @@ #include "ItemHandler.h" #include "../World.h" +#include "../BlockEntities/MobHeadEntity.h" @@ -18,6 +19,313 @@ public: } + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ + ) override + { + // Cannot place a head at "no face" and from the bottom: + if ((a_BlockFace == BLOCK_FACE_NONE) || (a_BlockFace == BLOCK_FACE_BOTTOM)) + { + return true; + } + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + + // If the placed head is a wither, try to spawn the wither first: + if (a_EquippedItem.m_ItemDamage == E_META_HEAD_WITHER) + { + if (TrySpawnWitherAround(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ)) + { + return true; + } + // Wither not created, proceed with regular head placement + } + + return PlaceRegularHead(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + } + + + /** Places a regular head block with no mob spawning checking. */ + bool PlaceRegularHead( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace + ) + { + // Place the block: + if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_HEAD, BlockFaceToBlockMeta(a_BlockFace))) + { + return false; + } + + // Use a callback to set the properties of the mob head block entity: + class cCallback : public cBlockEntityCallback + { + cPlayer & m_Player; + eMobHeadType m_HeadType; + NIBBLETYPE m_BlockMeta; + + virtual bool Item(cBlockEntity * a_BlockEntity) + { + if (a_BlockEntity->GetBlockType() != E_BLOCK_HEAD) + { + return false; + } + cMobHeadEntity * MobHeadEntity = static_cast<cMobHeadEntity *>(a_BlockEntity); + + int Rotation = 0; + if (m_BlockMeta == 1) + { + Rotation = FloorC(m_Player.GetYaw() * 16.0f / 360.0f + 0.5f) & 0x0f; + } + + MobHeadEntity->SetType(m_HeadType); + MobHeadEntity->SetRotation(static_cast<eMobHeadRotation>(Rotation)); + MobHeadEntity->GetWorld()->BroadcastBlockEntity(MobHeadEntity->GetPosX(), MobHeadEntity->GetPosY(), MobHeadEntity->GetPosZ()); + return false; + } + + public: + cCallback (cPlayer & a_CBPlayer, eMobHeadType a_HeadType, NIBBLETYPE a_BlockMeta) : + m_Player(a_CBPlayer), + m_HeadType(a_HeadType), + m_BlockMeta(a_BlockMeta) + {} + }; + cCallback Callback(a_Player, static_cast<eMobHeadType>(a_EquippedItem.m_ItemDamage), static_cast<NIBBLETYPE>(a_BlockFace)); + a_World.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback); + return true; + } + + + /** Spawns a wither if the wither skull placed at the specified coords completes wither's spawning formula. + Returns true if the wither was created. */ + bool TrySpawnWitherAround( + cWorld & a_World, cPlayer & a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ + ) + { + // No wither can be created at Y < 2 - not enough space for the formula: + if (a_BlockY < 2) + { + return false; + } + + // Check for all relevant wither locations: + static const Vector3i RelCoords[] = + { + { 0, 0, 0}, + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + }; + for (size_t i = 0; i < ARRAYCOUNT(RelCoords); ++i) + { + if (TrySpawnWitherAt( + a_World, a_Player, + a_BlockX, a_BlockY, a_BlockZ, + RelCoords[i].x, RelCoords[i].z + )) + { + return true; + } + } // for i - Coords[] + + return false; + } + + + /** Tries to spawn a wither at the specified offset from the placed head block. + PlacedHead coords are used to override the block query - at those coords the block is not queried from the world, + but assumed to be a head instead. + Offset is used to shift the image around the X and Z axis. + Returns true iff the wither was created successfully. */ + bool TrySpawnWitherAt( + cWorld & a_World, cPlayer & a_Player, + int a_PlacedHeadX, int a_PlacedHeadY, int a_PlacedHeadZ, + int a_OffsetX, int a_OffsetZ + ) + { + // Image for the wither at the X axis: + static const sSetBlock ImageWitherX[] = + { + {-1, 0, 0, E_BLOCK_HEAD, 0}, + { 0, 0, 0, E_BLOCK_HEAD, 0}, + { 1, 0, 0, E_BLOCK_HEAD, 0}, + {-1, -1, 0, E_BLOCK_SOULSAND, 0}, + { 0, -1, 0, E_BLOCK_SOULSAND, 0}, + { 1, -1, 0, E_BLOCK_SOULSAND, 0}, + {-1, -2, 0, E_BLOCK_AIR, 0}, + { 0, -2, 0, E_BLOCK_SOULSAND, 0}, + { 1, -2, 0, E_BLOCK_AIR, 0}, + }; + + // Image for the wither at the Z axis: + static const sSetBlock ImageWitherZ[] = + { + { 0, 0, -1, E_BLOCK_HEAD, 0}, + { 0, 0, 0, E_BLOCK_HEAD, 0}, + { 0, 0, 1, E_BLOCK_HEAD, 0}, + { 0, -1, -1, E_BLOCK_SOULSAND, 0}, + { 0, -1, 0, E_BLOCK_SOULSAND, 0}, + { 0, -1, 1, E_BLOCK_SOULSAND, 0}, + { 0, -2, -1, E_BLOCK_AIR, 0}, + { 0, -2, 0, E_BLOCK_SOULSAND, 0}, + { 0, -2, 1, E_BLOCK_AIR, 0}, + }; + + // Try to spawn the wither from each image: + return ( + TrySpawnWitherFromImage( + a_World, a_Player, ImageWitherX, ARRAYCOUNT(ImageWitherX), + a_PlacedHeadX, a_PlacedHeadY, a_PlacedHeadZ, + a_OffsetX, a_OffsetZ + ) || + TrySpawnWitherFromImage( + a_World, a_Player, ImageWitherZ, ARRAYCOUNT(ImageWitherZ), + a_PlacedHeadX, a_PlacedHeadY, a_PlacedHeadZ, + a_OffsetX, a_OffsetZ + ) + ); + } + + + /** Tries to spawn a wither from the specified image at the specified offset from the placed head block. + PlacedHead coords are used to override the block query - at those coords the block is not queried from the world, + but assumed to be a head instead. + Offset is used to shift the image around the X and Z axis. + Returns true iff the wither was created successfully. */ + bool TrySpawnWitherFromImage( + cWorld & a_World, cPlayer & a_Player, const sSetBlock * a_Image, size_t a_ImageCount, + int a_PlacedHeadX, int a_PlacedHeadY, int a_PlacedHeadZ, + int a_OffsetX, int a_OffsetZ + ) + { + // Check each block individually; simultaneously build the SetBlockVector for clearing the blocks: + sSetBlockVector AirBlocks; + AirBlocks.reserve(a_ImageCount); + for (size_t i = 0; i < a_ImageCount; i++) + { + // Get the absolute coords of the image: + int BlockX = a_PlacedHeadX + a_OffsetX + a_Image[i].GetX(); + int BlockY = a_PlacedHeadY + a_Image[i].GetY(); + int BlockZ = a_PlacedHeadZ + a_OffsetZ + a_Image[i].GetZ(); + + // If the query is for the placed head, short-circuit-evaluate it: + if ((BlockX == a_PlacedHeadX) && (BlockY == a_PlacedHeadY) && (BlockZ == a_PlacedHeadZ)) + { + if (a_Image[i].m_BlockType != E_BLOCK_HEAD) + { + return false; // Didn't match + } + continue; // Matched, continue checking the rest of the image + } + + // Query the world block: + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!a_World.GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta)) + { + // Cannot query block, assume unloaded chunk, fail to spawn the wither + return false; + } + + // Compare the world block: + if (BlockType != a_Image[i].m_BlockType) + { + return false; + } + + // If it is a mob head, check the correct head type using the block entity: + if (BlockType == E_BLOCK_HEAD) + { + class cHeadCallback: public cBlockEntityCallback + { + virtual bool Item(cBlockEntity * a_Entity) override + { + ASSERT(a_Entity->GetBlockType() == E_BLOCK_HEAD); + cMobHeadEntity * MobHead = static_cast<cMobHeadEntity *>(a_Entity); + m_IsWitherHead = (MobHead->GetType() == SKULL_TYPE_WITHER); + return true; + } + public: + cHeadCallback(void): + m_IsWitherHead(false) + { + } + bool m_IsWitherHead; + } callback; + a_World.DoWithBlockEntityAt(BlockX, BlockY, BlockZ, callback); + if (!callback.m_IsWitherHead) + { + return false; + } + } + // Matched, continue checking + AirBlocks.emplace_back(BlockX, BlockY, BlockZ, E_BLOCK_AIR, 0); + } // for i - a_Image + + // All image blocks matched, try replace the image with air blocks: + if (!a_Player.PlaceBlocks(AirBlocks)) + { + return false; + } + + // Spawn the wither: + int BlockX = a_PlacedHeadX + a_OffsetX; + int BlockZ = a_PlacedHeadZ + a_OffsetZ; + a_World.SpawnMob(static_cast<double>(BlockX) + 0.5, a_PlacedHeadY - 2, static_cast<double>(BlockZ) + 0.5, mtWither); + AwardSpawnWitherAchievement(a_World, BlockX, a_PlacedHeadY - 2, BlockZ); + return true; + } + + + /** Awards the achievement to all players close to the specified point. */ + void AwardSpawnWitherAchievement(cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ) + { + class cPlayerCallback : public cPlayerListCallback + { + Vector3f m_Pos; + + virtual bool Item(cPlayer * a_Player) + { + // If player is close, award achievement: + double Dist = (a_Player->GetPosition() - m_Pos).Length(); + if (Dist < 50.0) + { + a_Player->AwardAchievement(achSpawnWither); + } + return false; + } + + public: + cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {} + } PlayerCallback(Vector3f(static_cast<float>(a_BlockX), static_cast<float>(a_BlockY), static_cast<float>(a_BlockZ))); + a_World.ForEachPlayer(PlayerCallback); + } + + + /** Converts the block face of the placement (which face of the block was clicked to place the head) + into the block's metadata value. */ + static NIBBLETYPE BlockFaceToBlockMeta(int a_BlockFace) + { + switch (a_BlockFace) + { + case BLOCK_FACE_TOP: return 0x01; // On ground (rotation provided in block entity) + case BLOCK_FACE_XM: return 0x04; // west wall, facing east + case BLOCK_FACE_XP: return 0x05; // east wall, facing west + case BLOCK_FACE_ZM: return 0x02; // north wall, facing south + case BLOCK_FACE_ZP: return 0x03; // south wall, facing north + default: + { + ASSERT(!"Unhandled block face"); + return 0; + } + } + } + + virtual bool IsPlaceable(void) override { return true; diff --git a/src/Items/ItemPumpkin.h b/src/Items/ItemPumpkin.h new file mode 100644 index 000000000..fa00179d3 --- /dev/null +++ b/src/Items/ItemPumpkin.h @@ -0,0 +1,156 @@ + +// ItemPumpkin.h + +// Declares the cItemPumpkinHandler class representing the pumpkin block in its item form + + + + + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemPumpkinHandler: + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemPumpkinHandler(void): + super(E_BLOCK_PUMPKIN) + { + } + + + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ + ) override + { + // First try spawning a snow golem or an iron golem: + int PlacedBlockX = a_BlockX; + int PlacedBlockY = a_BlockY; + int PlacedBlockZ = a_BlockZ; + AddFaceDirection(PlacedBlockX, PlacedBlockY, PlacedBlockZ, a_BlockFace); + if (TrySpawnGolem(a_World, a_Player, PlacedBlockX, PlacedBlockY, PlacedBlockZ)) + { + // The client thinks that they placed the pumpkin, let them know it's been replaced: + a_Player.SendBlocksAround(PlacedBlockX, PlacedBlockY, PlacedBlockZ); + return true; + } + + // No golem at these coords, place the block normally: + return super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); + } + + + /** Spawns a snow / iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin. + Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched. */ + bool TrySpawnGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) + { + // A golem can't form with a pumpkin below level 2 or above level 255 + if ((a_BlockY < 2) || (a_BlockY >= cChunkDef::Height)) + { + return false; + } + + // Decide which golem to try spawning based on the block below the placed pumpkin: + switch (a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)) + { + case E_BLOCK_SNOW_BLOCK: return TrySpawnSnowGolem(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); + case E_BLOCK_IRON_BLOCK: return TrySpawnIronGolem(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); + default: + { + // No golem here + return false; + } + } + } + + + /** Spawns a snow golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin. + Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched. + Assumes that the block below the specified block has already been checked and is a snow block. */ + bool TrySpawnSnowGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) + { + // Need one more snow block 2 blocks below the pumpkin: + if (a_World.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) != E_BLOCK_SNOW_BLOCK) + { + return false; + } + + // Try to place air blocks where the original recipe blocks were: + sSetBlockVector AirBlocks; + AirBlocks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); // Head + AirBlocks.emplace_back(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); // Torso + AirBlocks.emplace_back(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Legs + if (!a_Player.PlaceBlocks(AirBlocks)) + { + return false; + } + + // Spawn the golem: + a_World.SpawnMob(static_cast<double>(a_BlockX) + 0.5, a_BlockY - 2, static_cast<double>(a_BlockZ) + 0.5, mtSnowGolem); + return true; + } + + + /** Spawns an iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin. + Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched. + Assumes that the block below the specified block has already been checked and is an iron block. */ + bool TrySpawnIronGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) + { + // Need one more iron block 2 blocks below the pumpkin: + if (a_World.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) != E_BLOCK_IRON_BLOCK) + { + return false; + } + + // Check the two arm directions (X, Z) using a loop over two sets of offset vectors: + static const Vector3i ArmOffsets[] = + { + {1, 0, 0}, + {0, 0, 1}, + }; + for (size_t i = 0; i < ARRAYCOUNT(ArmOffsets); i++) + { + // If the arm blocks don't match, bail out of this loop repetition: + if ( + (a_World.GetBlock(a_BlockX + ArmOffsets[i].x, a_BlockY - 1, a_BlockZ + ArmOffsets[i].z) != E_BLOCK_IRON_BLOCK) || + (a_World.GetBlock(a_BlockX - ArmOffsets[i].x, a_BlockY - 1, a_BlockZ - ArmOffsets[i].z) != E_BLOCK_IRON_BLOCK) + ) + { + continue; + } + + // Try to place air blocks where the original recipe blocks were: + sSetBlockVector AirBlocks; + AirBlocks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); // Head + AirBlocks.emplace_back(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); // Torso + AirBlocks.emplace_back(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Legs + AirBlocks.emplace_back(a_BlockX + ArmOffsets[i].x, a_BlockY - 1, a_BlockZ + ArmOffsets[i].z, E_BLOCK_AIR, 0); // Arm + AirBlocks.emplace_back(a_BlockX - ArmOffsets[i].x, a_BlockY - 1, a_BlockZ - ArmOffsets[i].z, E_BLOCK_AIR, 0); // Arm + if (!a_Player.PlaceBlocks(AirBlocks)) + { + return false; + } + + // Spawn the golem: + a_World.SpawnMob(static_cast<double>(a_BlockX) + 0.5, a_BlockY - 2, static_cast<double>(a_BlockZ) + 0.5, mtIronGolem); + return true; + } // for i - ArmOffsets[] + + // Neither arm offset matched, this thing is not a complete golem + return false; + } +}; + + + + diff --git a/src/Items/ItemRedstoneDust.h b/src/Items/ItemRedstoneDust.h index a2289239c..6d5fb521f 100644 --- a/src/Items/ItemRedstoneDust.h +++ b/src/Items/ItemRedstoneDust.h @@ -27,7 +27,20 @@ public: BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta ) override { - if (!cBlockInfo::FullyOccupiesVoxel(a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) // Some solid blocks, such as cocoa beans, are not suitable for dust + // Check if coords are out of range: + if ((a_BlockY <= 0) || (a_BlockY >= cChunkDef::Height)) + { + return false; + } + + // Check the block below, if it supports dust on top of it: + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + if (!a_World->GetBlockTypeMeta(a_BlockX, a_BlockY - 1, a_BlockZ, BlockType, BlockMeta)) + { + return false; + } + if (!IsBlockTypeUnderSuitable(BlockType, BlockMeta)) { return false; } @@ -36,6 +49,28 @@ public: a_BlockMeta = 0; return true; } + + + /** Returns true if the specified block type / meta is suitable to have redstone dust on top of it. */ + static bool IsBlockTypeUnderSuitable(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) + { + if (cBlockInfo::FullyOccupiesVoxel(a_BlockType)) + { + return true; + } + + switch (a_BlockType) + { + case E_BLOCK_NEW_STONE_SLAB: + case E_BLOCK_WOODEN_SLAB: + case E_BLOCK_STONE_SLAB: + { + // Slabs can support redstone if they're upside down: + return ((a_BlockMeta & 0x08) != 0); + } + } + return false; + } } ; diff --git a/src/Items/ItemSign.h b/src/Items/ItemSign.h index 0fa0fa0be..dabbdbba1 100644 --- a/src/Items/ItemSign.h +++ b/src/Items/ItemSign.h @@ -13,13 +13,33 @@ class cItemSignHandler : public cItemHandler { + typedef cItemHandler super; public: cItemSignHandler(int a_ItemType) : - cItemHandler(a_ItemType) + super(a_ItemType) { } + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ + ) + { + // If the regular placement doesn't work, do no further processing: + if (!super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) + { + return false; + } + + // After successfully placing the sign, open the sign editor for the player: + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); + a_Player.GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); + return true; + } + + virtual bool IsPlaceable(void) override { return true; diff --git a/src/Items/ItemSlab.h b/src/Items/ItemSlab.h new file mode 100644 index 000000000..1b68b9d0c --- /dev/null +++ b/src/Items/ItemSlab.h @@ -0,0 +1,93 @@ + +// ItemSlab.h + +// Declares the cItemSlabHandler responsible for handling slabs, when in their item form. + + + + + +#pragma once + +#include "ItemHandler.h" +#include "../Blocks/BlockSlab.h" + + + + + +class cItemSlabHandler: + public cItemHandler +{ + typedef cItemHandler super; + +public: + + /** Creates a new handler for the specified slab item type. + Sets the handler to use the specified doubleslab block type for combining self into doubleslabs. */ + cItemSlabHandler(int a_ItemType, BLOCKTYPE a_DoubleSlabBlockType): + super(a_ItemType), + m_DoubleSlabBlockType(a_DoubleSlabBlockType) + { + } + + + // cItemHandler overrides: + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ + ) override + { + // Special slab handling - placing a slab onto another slab produces a dblslab instead: + BLOCKTYPE ClickedBlockType; + NIBBLETYPE ClickedBlockMeta; + a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlockType, ClickedBlockMeta); + if ( + (ClickedBlockType == m_ItemType) && // Placing the same slab material + (ClickedBlockMeta == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single) + ) + { + // If clicking the top side of a bottom-half slab, combine into a doubleslab: + if ( + (a_BlockFace == BLOCK_FACE_TOP) && + ((ClickedBlockMeta & 0x08) == 0) + ) + { + return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07); + } + + // If clicking the bottom side of a top-half slab, combine into a doubleslab: + if ( + (a_BlockFace == BLOCK_FACE_BOTTOM) && + ((ClickedBlockMeta & 0x08) != 0) + ) + { + return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07); + } + } + + // The slabs didn't combine, use the default handler to place the slab: + bool res = super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); + + /* + The client has a bug when a slab replaces snow and there's a slab above it. + The client then combines the slab above, rather than replacing the snow. + We send the block above the currently placed block back to the client to fix the bug. + Ref.: http://forum.mc-server.org/showthread.php?tid=434&pid=17388#pid17388 + */ + if ((a_BlockFace == BLOCK_FACE_TOP) && (a_BlockY < cChunkDef::Height - 1)) + { + a_Player.SendBlocksAround(a_BlockX, a_BlockY + 1, a_BlockZ, 1); + } + return res; + } + +protected: + /** The block type to use when the slab combines into a doubleslab block. */ + BLOCKTYPE m_DoubleSlabBlockType; +}; + + + + diff --git a/src/Items/ItemSpawnEgg.h b/src/Items/ItemSpawnEgg.h index 617ecd808..dee8a9057 100644 --- a/src/Items/ItemSpawnEgg.h +++ b/src/Items/ItemSpawnEgg.h @@ -64,11 +64,13 @@ public: case E_META_SPAWN_EGG_CREEPER: return mtCreeper; case E_META_SPAWN_EGG_ENDERMAN: return mtEnderman; case E_META_SPAWN_EGG_GHAST: return mtGhast; + case E_META_SPAWN_EGG_GUARDIAN: return mtGuardian; case E_META_SPAWN_EGG_HORSE: return mtHorse; case E_META_SPAWN_EGG_MAGMA_CUBE: return mtMagmaCube; case E_META_SPAWN_EGG_MOOSHROOM: return mtMooshroom; case E_META_SPAWN_EGG_OCELOT: return mtOcelot; case E_META_SPAWN_EGG_PIG: return mtPig; + case E_META_SPAWN_EGG_RABBIT: return mtRabbit; case E_META_SPAWN_EGG_SHEEP: return mtSheep; case E_META_SPAWN_EGG_SILVERFISH: return mtSilverfish; case E_META_SPAWN_EGG_SKELETON: return mtSkeleton; diff --git a/src/LightingThread.cpp b/src/LightingThread.cpp index ced95d4e1..effde03d0 100644 --- a/src/LightingThread.cpp +++ b/src/LightingThread.cpp @@ -197,7 +197,7 @@ void cLightingThread::Execute(void) { { cCSLock Lock(m_CS); - if (m_Queue.size() == 0) + if (m_Queue.empty()) { cCSUnlock Unlock(Lock); m_evtItemAdded.Wait(); diff --git a/src/LineBlockTracer.cpp b/src/LineBlockTracer.cpp index 90f97cd23..e43a79566 100644 --- a/src/LineBlockTracer.cpp +++ b/src/LineBlockTracer.cpp @@ -15,7 +15,23 @@ cLineBlockTracer::cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) : - super(a_World, a_Callbacks) + super(a_World, a_Callbacks), + m_StartX(0.0), + m_StartY(0.0), + m_StartZ(0.0), + m_EndX(0.0), + m_EndY(0.0), + m_EndZ(0.0), + m_DiffX(0.0), + m_DiffY(0.0), + m_DiffZ(0.0), + m_DirX(0), + m_DirY(0), + m_DirZ(0), + m_CurrentX(0), + m_CurrentY(0), + m_CurrentZ(0), + m_CurrentFace(0) { } diff --git a/src/MobProximityCounter.cpp b/src/MobProximityCounter.cpp index 82ba771ff..cfd52440b 100644 --- a/src/MobProximityCounter.cpp +++ b/src/MobProximityCounter.cpp @@ -50,7 +50,7 @@ cMobProximityCounter::sIterablePair cMobProximityCounter::getMobWithinThosesDist a_DistanceMin *= a_DistanceMin;// this is because is use square distance a_DistanceMax *= a_DistanceMax; - if (m_DistanceToMonster.size() <= 0) + if (m_DistanceToMonster.empty()) { convertMaps(); } diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp index ee9e569a7..0a32d17ef 100644 --- a/src/MobSpawner.cpp +++ b/src/MobSpawner.cpp @@ -83,6 +83,7 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) addIfAllowed(mtSkeleton, allowedMobs); addIfAllowed(mtCreeper, allowedMobs); addIfAllowed(mtSquid, allowedMobs); + addIfAllowed(mtGuardian, allowedMobs); if ((a_Biome != biDesert) && (a_Biome != biBeach) && (a_Biome != biOcean)) { @@ -91,6 +92,7 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) addIfAllowed(mtCow, allowedMobs); addIfAllowed(mtChicken, allowedMobs); addIfAllowed(mtEnderman, allowedMobs); + addIfAllowed(mtRabbit, allowedMobs); addIfAllowed(mtSlime, allowedMobs); // MG TODO : much more complicated rule if ((a_Biome == biForest) || (a_Biome == biForestHills) || (a_Biome == biTaiga) || (a_Biome == biTaigaHills)) @@ -144,6 +146,11 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R switch (a_MobType) { + case mtGuardian: + { + return IsBlockWater(TargetBlock) && (a_RelY >= 45) && (a_RelY <= 62); + } + case mtSquid: { return IsBlockWater(TargetBlock) && (a_RelY >= 45) && (a_RelY <= 62); @@ -158,6 +165,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R case mtCow: case mtPig: case mtHorse: + case mtRabbit: case mtSheep: { return ( diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index 7ca7a9d66..72317d66b 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -22,7 +22,7 @@ cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eMonsterTyp // What to do if in Chasing State -void cAggressiveMonster::InStateChasing(float a_Dt) +void cAggressiveMonster::InStateChasing(std::chrono::milliseconds a_Dt) { super::InStateChasing(a_Dt); @@ -61,7 +61,7 @@ void cAggressiveMonster::EventSeePlayer(cEntity * a_Entity) -void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk) +void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); @@ -93,9 +93,9 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk) -void cAggressiveMonster::Attack(float a_Dt) +void cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt) { - m_AttackInterval += a_Dt * m_AttackRate; + m_AttackInterval += a_Dt.count() * m_AttackRate; if ((m_Target == nullptr) || (m_AttackInterval < 3.0)) { diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h index 2549ba2d3..f64c1103f 100644 --- a/src/Mobs/AggressiveMonster.h +++ b/src/Mobs/AggressiveMonster.h @@ -16,11 +16,11 @@ public: cAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - virtual void Tick (float a_Dt, cChunk & a_Chunk) override; - virtual void InStateChasing(float a_Dt) override; + virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void InStateChasing(std::chrono::milliseconds a_Dt) override; virtual void EventSeePlayer(cEntity *) override; - virtual void Attack(float a_Dt); + virtual void Attack(std::chrono::milliseconds a_Dt); protected: /** Whether this mob's destination is the same as its target's position. */ diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp index 1fa9d2c37..172ccd071 100644 --- a/src/Mobs/Blaze.cpp +++ b/src/Mobs/Blaze.cpp @@ -30,9 +30,9 @@ void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cBlaze::Attack(float a_Dt) +void cBlaze::Attack(std::chrono::milliseconds a_Dt) { - m_AttackInterval += a_Dt * m_AttackRate; + m_AttackInterval += a_Dt.count() * m_AttackRate; if ((m_Target != nullptr) && (m_AttackInterval > 3.0)) { diff --git a/src/Mobs/Blaze.h b/src/Mobs/Blaze.h index e2a4ad9f1..493953a14 100644 --- a/src/Mobs/Blaze.h +++ b/src/Mobs/Blaze.h @@ -18,5 +18,5 @@ public: CLASS_PROTODEF(cBlaze) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void Attack(float a_Dt) override; + virtual void Attack(std::chrono::milliseconds a_Dt) override; } ; diff --git a/src/Mobs/CMakeLists.txt b/src/Mobs/CMakeLists.txt index bbbb9287a..7a291dcf2 100644 --- a/src/Mobs/CMakeLists.txt +++ b/src/Mobs/CMakeLists.txt @@ -16,6 +16,7 @@ SET (SRCS Enderman.cpp Ghast.cpp Giant.cpp + Guardian.cpp Horse.cpp IronGolem.cpp MagmaCube.cpp @@ -24,6 +25,7 @@ SET (SRCS PassiveAggressiveMonster.cpp PassiveMonster.cpp Pig.cpp + Rabbit.cpp Sheep.cpp Skeleton.cpp Slime.cpp @@ -49,6 +51,7 @@ SET (HDRS Enderman.h Ghast.h Giant.h + Guardian.h Horse.h IncludeAllMonsters.h IronGolem.h @@ -60,6 +63,7 @@ SET (HDRS PassiveAggressiveMonster.h PassiveMonster.h Pig.h + Rabbit.h Sheep.h Silverfish.h Skeleton.h diff --git a/src/Mobs/CaveSpider.cpp b/src/Mobs/CaveSpider.cpp index 045b47e73..fa530db82 100644 --- a/src/Mobs/CaveSpider.cpp +++ b/src/Mobs/CaveSpider.cpp @@ -16,7 +16,7 @@ cCaveSpider::cCaveSpider(void) : -void cCaveSpider::Tick(float a_Dt, cChunk & a_Chunk) +void cCaveSpider::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); @@ -27,7 +27,7 @@ void cCaveSpider::Tick(float a_Dt, cChunk & a_Chunk) -void cCaveSpider::Attack(float a_Dt) +void cCaveSpider::Attack(std::chrono::milliseconds a_Dt) { super::Attack(a_Dt); diff --git a/src/Mobs/CaveSpider.h b/src/Mobs/CaveSpider.h index 494ba1360..d3e56fd2b 100644 --- a/src/Mobs/CaveSpider.h +++ b/src/Mobs/CaveSpider.h @@ -16,8 +16,8 @@ public: CLASS_PROTODEF(cCaveSpider) - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void Attack(float a_Dt) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void Attack(std::chrono::milliseconds a_Dt) override; virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; } ; diff --git a/src/Mobs/Chicken.cpp b/src/Mobs/Chicken.cpp index 634867cfa..b2b21d4ae 100644 --- a/src/Mobs/Chicken.cpp +++ b/src/Mobs/Chicken.cpp @@ -18,7 +18,7 @@ cChicken::cChicken(void) : -void cChicken::Tick(float a_Dt, cChunk & a_Chunk) +void cChicken::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Mobs/Chicken.h b/src/Mobs/Chicken.h index 07b921884..9349187c6 100644 --- a/src/Mobs/Chicken.h +++ b/src/Mobs/Chicken.h @@ -17,7 +17,7 @@ public: CLASS_PROTODEF(cChicken) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_SEEDS); } diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp index a073224cf..c4ae47f2f 100644 --- a/src/Mobs/Creeper.cpp +++ b/src/Mobs/Creeper.cpp @@ -23,7 +23,7 @@ cCreeper::cCreeper(void) : -void cCreeper::Tick(float a_Dt, cChunk & a_Chunk) +void cCreeper::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); @@ -119,7 +119,7 @@ bool cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI) -void cCreeper::Attack(float a_Dt) +void cCreeper::Attack(std::chrono::milliseconds a_Dt) { UNUSED(a_Dt); diff --git a/src/Mobs/Creeper.h b/src/Mobs/Creeper.h index bf3272e22..1827c416e 100644 --- a/src/Mobs/Creeper.h +++ b/src/Mobs/Creeper.h @@ -19,8 +19,8 @@ public: virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; - virtual void Attack(float a_Dt) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Attack(std::chrono::milliseconds a_Dt) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void OnRightClicked(cPlayer & a_Player) override; bool IsBlowing(void) const {return m_bIsBlowing; } diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp index 56ea10245..42c33884a 100644 --- a/src/Mobs/Enderman.cpp +++ b/src/Mobs/Enderman.cpp @@ -186,7 +186,7 @@ bool cEnderman::CheckLight() -void cEnderman::Tick(float a_Dt, cChunk & a_Chunk) +void cEnderman::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Mobs/Enderman.h b/src/Mobs/Enderman.h index 28bbceb84..8ccbf2ce7 100644 --- a/src/Mobs/Enderman.h +++ b/src/Mobs/Enderman.h @@ -21,7 +21,7 @@ public: virtual void CheckEventSeePlayer(void) override; virtual void CheckEventLostPlayer(void) override; virtual void EventLosePlayer(void) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; bool IsScreaming(void) const {return m_bIsScreaming; } BLOCKTYPE GetCarriedBlock(void) const {return CarriedBlock; } diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp index fc8de8362..ea0295102 100644 --- a/src/Mobs/Ghast.cpp +++ b/src/Mobs/Ghast.cpp @@ -32,9 +32,9 @@ void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cGhast::Attack(float a_Dt) +void cGhast::Attack(std::chrono::milliseconds a_Dt) { - m_AttackInterval += a_Dt * m_AttackRate; + m_AttackInterval += a_Dt.count() * m_AttackRate; if ((m_Target != nullptr) && (m_AttackInterval > 3.0)) { diff --git a/src/Mobs/Ghast.h b/src/Mobs/Ghast.h index a28940a01..431edaf6d 100644 --- a/src/Mobs/Ghast.h +++ b/src/Mobs/Ghast.h @@ -18,7 +18,7 @@ public: CLASS_PROTODEF(cGhast) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void Attack(float a_Dt) override; + virtual void Attack(std::chrono::milliseconds a_Dt) override; bool IsCharging(void) const {return false; } } ; diff --git a/src/Mobs/Guardian.cpp b/src/Mobs/Guardian.cpp new file mode 100644 index 000000000..5eb30785b --- /dev/null +++ b/src/Mobs/Guardian.cpp @@ -0,0 +1,65 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Guardian.h" +#include "../Vector3.h" +#include "../Chunk.h" + + + + + +cGuardian::cGuardian(void) : + super("Guardian", mtGuardian, "mob.guardian.idle", "mob.guardian.death", 0.875, 0.8) +{ +} + + + + + +void cGuardian::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + // Drops 0-3 Ink Sacs + int LootingLevel = 0; + if (a_Killer != nullptr) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_PRISMARINE_SHARD); + AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_RAW_FISH); + AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_PRISMARINE_CRYSTALS); // TODO: Prismarine Crystals only drop if the raw fish drop is 0 +} + + + + + +void cGuardian::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + // We must first process current location, and only then tick, otherwise we risk processing a location in a chunk + // that is not where the entity currently resides (FS #411) + + Vector3d Pos = GetPosition(); + + // TODO: Not a real behavior, but cool :D + int RelY = (int)floor(Pos.y); + if ((RelY < 0) || (RelY >= cChunkDef::Height)) + { + return; + } + int RelX = (int)floor(Pos.x) - a_Chunk.GetPosX() * cChunkDef::Width; + int RelZ = (int)floor(Pos.z) - a_Chunk.GetPosZ() * cChunkDef::Width; + BLOCKTYPE BlockType; + if (a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockType) && !IsBlockWater(BlockType) && !IsOnFire()) + { + // Burn for 10 ticks, then decide again + StartBurning(10); + } + + super::Tick(a_Dt, a_Chunk); +} + + + + diff --git a/src/Mobs/Guardian.h b/src/Mobs/Guardian.h new file mode 100644 index 000000000..6bc17947c --- /dev/null +++ b/src/Mobs/Guardian.h @@ -0,0 +1,31 @@ + +#pragma once + +#include "AggressiveMonster.h" + + + + + +class cGuardian : + public cAggressiveMonster +{ + typedef cAggressiveMonster super; + +public: + cGuardian(); + + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + + CLASS_PROTODEF(cGuardian) + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + + // Guardians do not drown (or float) + virtual void HandleAir(void) override {} + virtual void SetSwimState(cChunk & a_Chunk) override {} +} ; + + + + diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp index d92f0d023..5b4c78bfc 100644 --- a/src/Mobs/Horse.cpp +++ b/src/Mobs/Horse.cpp @@ -30,7 +30,7 @@ cHorse::cHorse(int Type, int Color, int Style, int TameTimes) : -void cHorse::Tick(float a_Dt, cChunk & a_Chunk) +void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h index 4c644e512..be283705e 100644 --- a/src/Mobs/Horse.h +++ b/src/Mobs/Horse.h @@ -18,7 +18,7 @@ public: CLASS_PROTODEF(cHorse) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void OnRightClicked(cPlayer & a_Player) override; bool IsSaddled (void) const {return m_bIsSaddled; } diff --git a/src/Mobs/IncludeAllMonsters.h b/src/Mobs/IncludeAllMonsters.h index 3460db993..53c709c2b 100644 --- a/src/Mobs/IncludeAllMonsters.h +++ b/src/Mobs/IncludeAllMonsters.h @@ -8,12 +8,14 @@ #include "EnderDragon.h" #include "Ghast.h" #include "Giant.h" +#include "Guardian.h" #include "Horse.h" #include "IronGolem.h" #include "MagmaCube.h" #include "Mooshroom.h" #include "Ocelot.h" #include "Pig.h" +#include "Rabbit.h" #include "Sheep.h" #include "Silverfish.h" #include "Skeleton.h" diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 7b8f763af..6e07bfbb6 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -38,12 +38,14 @@ static const struct {mtEnderman, "enderman", "Enderman"}, {mtEnderDragon, "enderdragon", "EnderDragon"}, {mtGhast, "ghast", "Ghast"}, + {mtGuardian, "guardian", "Guardian"}, {mtHorse, "horse", "EntityHorse"}, {mtIronGolem, "irongolem", "VillagerGolem"}, {mtMagmaCube, "magmacube", "LavaSlime"}, {mtMooshroom, "mooshroom", "MushroomCow"}, {mtOcelot, "ocelot", "Ozelot"}, {mtPig, "pig", "Pig"}, + {mtRabbit, "rabbit", "Rabbit"}, {mtSheep, "sheep", "Sheep"}, {mtSilverfish, "silverfish", "Silverfish"}, {mtSkeleton, "skeleton", "Skeleton"}, @@ -250,15 +252,15 @@ bool cMonster::ReachedFinalDestination() -void cMonster::Tick(float a_Dt, cChunk & a_Chunk) +void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); if (m_Health <= 0) { // The mob is dead, but we're still animating the "puff" they leave when they die - m_DestroyTimer += a_Dt / 1000; - if (m_DestroyTimer > 1) + m_DestroyTimer += a_Dt; + if (m_DestroyTimer > std::chrono::seconds(1)) { Destroy(true); } @@ -273,8 +275,6 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk) // Burning in daylight HandleDaylightBurning(a_Chunk); - a_Dt /= 1000; - if (m_bMovingToDestination) { if (m_bOnGround) @@ -498,6 +498,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI) case mtCow: case mtHorse: case mtPig: + case mtRabbit: case mtSheep: case mtSquid: case mtMooshroom: @@ -513,6 +514,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI) case mtCreeper: case mtEnderman: case mtGhast: + case mtGuardian: case mtSilverfish: case mtSkeleton: case mtSpider: @@ -553,7 +555,7 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI) { m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), Reward); } - m_DestroyTimer = 0; + m_DestroyTimer = std::chrono::milliseconds(0); } @@ -636,7 +638,7 @@ void cMonster::EventLosePlayer(void) -void cMonster::InStateIdle(float a_Dt) +void cMonster::InStateIdle(std::chrono::milliseconds a_Dt) { if (m_bMovingToDestination) { @@ -645,11 +647,11 @@ void cMonster::InStateIdle(float a_Dt) m_IdleInterval += a_Dt; - if (m_IdleInterval > 1) + if (m_IdleInterval > std::chrono::seconds(1)) { // At this interval the results are predictable int rem = m_World->GetTickRandomNumber(6) + 1; - m_IdleInterval -= 1; // So nothing gets dropped when the server hangs for a few seconds + m_IdleInterval -= std::chrono::seconds(1); // So nothing gets dropped when the server hangs for a few seconds Vector3d Dist; Dist.x = (double)m_World->GetTickRandomNumber(10) - 5; @@ -676,7 +678,7 @@ void cMonster::InStateIdle(float a_Dt) // What to do if in Chasing State // This state should always be defined in each child class -void cMonster::InStateChasing(float a_Dt) +void cMonster::InStateChasing(std::chrono::milliseconds a_Dt) { UNUSED(a_Dt); } @@ -686,7 +688,7 @@ void cMonster::InStateChasing(float a_Dt) // What to do if in Escaping State -void cMonster::InStateEscaping(float a_Dt) +void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt) { UNUSED(a_Dt); @@ -842,12 +844,14 @@ cMonster::eFamily cMonster::FamilyFromType(eMonsterType a_Type) case mtEnderman: return mfHostile; case mtGhast: return mfHostile; case mtGiant: return mfNoSpawn; + case mtGuardian: return mfWater; // Just because they have special spawning conditions. If Watertemples have been added, this needs to be edited! case mtHorse: return mfPassive; case mtIronGolem: return mfPassive; case mtMagmaCube: return mfHostile; case mtMooshroom: return mfHostile; case mtOcelot: return mfPassive; case mtPig: return mfPassive; + case mtRabbit: return mfPassive; case mtSheep: return mfPassive; case mtSilverfish: return mfHostile; case mtSkeleton: return mfHostile; @@ -955,10 +959,12 @@ cMonster * cMonster::NewMonsterFromType(eMonsterType a_MobType) case mtEnderman: toReturn = new cEnderman(); break; case mtGhast: toReturn = new cGhast(); break; case mtGiant: toReturn = new cGiant(); break; + case mtGuardian: toReturn = new cGuardian(); break; case mtIronGolem: toReturn = new cIronGolem(); break; case mtMooshroom: toReturn = new cMooshroom(); break; case mtOcelot: toReturn = new cOcelot(); break; case mtPig: toReturn = new cPig(); break; + case mtRabbit: toReturn = new cRabbit(); break; case mtSheep: toReturn = new cSheep(); break; case mtSilverfish: toReturn = new cSilverfish(); break; case mtSnowGolem: toReturn = new cSnowGolem(); break; diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index f04e45ac6..21ed0c25a 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -30,7 +30,7 @@ public: mfHostile = 0, // Spider, Zombies ... mfPassive = 1, // Cows, Pigs mfAmbient = 2, // Bats - mfWater = 3, // Squid + mfWater = 3, // Squid, Guardian mfNoSpawn, mfUnhandled, // Nothing. Be sure this is the last and the others are in order @@ -52,7 +52,7 @@ public: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; @@ -80,9 +80,9 @@ public: virtual void EventLosePlayer(void); virtual void CheckEventLostPlayer(void); - virtual void InStateIdle (float a_Dt); - virtual void InStateChasing (float a_Dt); - virtual void InStateEscaping(float a_Dt); + virtual void InStateIdle (std::chrono::milliseconds a_Dt); + virtual void InStateChasing (std::chrono::milliseconds a_Dt); + virtual void InStateEscaping(std::chrono::milliseconds a_Dt); int GetAttackRate() { return static_cast<int>(m_AttackRate); } void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; } @@ -217,8 +217,8 @@ protected: /* =========================== */ - float m_IdleInterval; - float m_DestroyTimer; + std::chrono::milliseconds m_IdleInterval; + std::chrono::milliseconds m_DestroyTimer; eMonsterType m_MobType; AString m_CustomName; diff --git a/src/Mobs/MonsterTypes.h b/src/Mobs/MonsterTypes.h index dc6dd3992..02bec267b 100644 --- a/src/Mobs/MonsterTypes.h +++ b/src/Mobs/MonsterTypes.h @@ -18,12 +18,14 @@ enum eMonsterType mtEnderman = E_META_SPAWN_EGG_ENDERMAN, mtGhast = E_META_SPAWN_EGG_GHAST, mtGiant = E_META_SPAWN_EGG_GIANT, + mtGuardian = E_META_SPAWN_EGG_GUARDIAN, mtHorse = E_META_SPAWN_EGG_HORSE, mtIronGolem = E_META_SPAWN_EGG_IRON_GOLEM, mtMagmaCube = E_META_SPAWN_EGG_MAGMA_CUBE, mtMooshroom = E_META_SPAWN_EGG_MOOSHROOM, mtOcelot = E_META_SPAWN_EGG_OCELOT, mtPig = E_META_SPAWN_EGG_PIG, + mtRabbit = E_META_SPAWN_EGG_RABBIT, mtSheep = E_META_SPAWN_EGG_SHEEP, mtSilverfish = E_META_SPAWN_EGG_SILVERFISH, mtSkeleton = E_META_SPAWN_EGG_SKELETON, @@ -37,6 +39,7 @@ enum eMonsterType mtWolf = E_META_SPAWN_EGG_WOLF, mtZombie = E_META_SPAWN_EGG_ZOMBIE, mtZombiePigman = E_META_SPAWN_EGG_ZOMBIE_PIGMAN, + mtMax = 120, // This is just a hotfix for http://forum.mc-server.org/showthread.php?tid=1616. Tolua is too bad to find the highest value, so this is needed. } ; // tolua_end diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp index 1048616d0..012ca9949 100644 --- a/src/Mobs/PassiveMonster.cpp +++ b/src/Mobs/PassiveMonster.cpp @@ -35,7 +35,7 @@ bool cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) -void cPassiveMonster::Tick(float a_Dt, cChunk & a_Chunk) +void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Mobs/PassiveMonster.h b/src/Mobs/PassiveMonster.h index 9221d9a6e..2ed2cd21d 100644 --- a/src/Mobs/PassiveMonster.h +++ b/src/Mobs/PassiveMonster.h @@ -15,7 +15,7 @@ class cPassiveMonster : public: cPassiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; /// When hit by someone, run away virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp index 1e4c35acd..edd4d9de4 100644 --- a/src/Mobs/Pig.cpp +++ b/src/Mobs/Pig.cpp @@ -80,7 +80,7 @@ void cPig::OnRightClicked(cPlayer & a_Player) -void cPig::Tick(float a_Dt, cChunk & a_Chunk) +void cPig::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Mobs/Pig.h b/src/Mobs/Pig.h index 0e026933a..0fe4b4fed 100644 --- a/src/Mobs/Pig.h +++ b/src/Mobs/Pig.h @@ -22,7 +22,7 @@ public: virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_CARROT); } diff --git a/src/Mobs/Rabbit.cpp b/src/Mobs/Rabbit.cpp new file mode 100644 index 000000000..cf49d2744 --- /dev/null +++ b/src/Mobs/Rabbit.cpp @@ -0,0 +1,34 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Rabbit.h" +#include "../Entities/Player.h" +#include "../World.h" + + + + + +cRabbit::cRabbit(void) : + super("Rabbit", mtRabbit, "mob.rabbit.idle", "mob.rabbit.death", 0.82, 0.68) +{ +} + + + + + +void cRabbit::GetDrops(cItems & a_Drops, cEntity * a_Killer) +{ + int LootingLevel = 0; + if (a_Killer != nullptr) + { + LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); + } + AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, IsOnFire() ? E_ITEM_COOKED_RABBIT : E_ITEM_RAW_RABBIT); + AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_RABBIT_HIDE); + cItems RareDrops; + RareDrops.Add(cItem(E_ITEM_RABBITS_FOOT)); + AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel); +} + diff --git a/src/Mobs/Rabbit.h b/src/Mobs/Rabbit.h new file mode 100644 index 000000000..e86c85579 --- /dev/null +++ b/src/Mobs/Rabbit.h @@ -0,0 +1,24 @@ + +#pragma once + +#include "PassiveMonster.h" + + + + + +class cRabbit : + public cPassiveMonster +{ + typedef cPassiveMonster super; + +public: + cRabbit(); + + CLASS_PROTODEF(cRabbit) + + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; + + virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_CARROT); } + +} ; diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp index c46404391..e4d1760e0 100644 --- a/src/Mobs/Sheep.cpp +++ b/src/Mobs/Sheep.cpp @@ -84,7 +84,7 @@ void cSheep::OnRightClicked(cPlayer & a_Player) -void cSheep::Tick(float a_Dt, cChunk & a_Chunk) +void cSheep::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); int PosX = POSX_TOINT; diff --git a/src/Mobs/Sheep.h b/src/Mobs/Sheep.h index 16d5fddd3..b6c99ac2a 100644 --- a/src/Mobs/Sheep.h +++ b/src/Mobs/Sheep.h @@ -24,7 +24,7 @@ public: virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_WHEAT); } diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp index da5ddc670..dd59d6454 100644 --- a/src/Mobs/Skeleton.cpp +++ b/src/Mobs/Skeleton.cpp @@ -67,9 +67,9 @@ void cSkeleton::MoveToPosition(const Vector3d & a_Position) -void cSkeleton::Attack(float a_Dt) +void cSkeleton::Attack(std::chrono::milliseconds a_Dt) { - m_AttackInterval += a_Dt * m_AttackRate; + m_AttackInterval += a_Dt.count() * m_AttackRate; if ((m_Target != nullptr) && (m_AttackInterval > 3.0)) { diff --git a/src/Mobs/Skeleton.h b/src/Mobs/Skeleton.h index cd1c6c3f0..9c49c52fb 100644 --- a/src/Mobs/Skeleton.h +++ b/src/Mobs/Skeleton.h @@ -19,7 +19,7 @@ public: virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; virtual void MoveToPosition(const Vector3d & a_Position) override; - virtual void Attack(float a_Dt) override; + virtual void Attack(std::chrono::milliseconds a_Dt) override; virtual void SpawnOn(cClientHandle & a_ClientHandle) override; virtual bool IsUndead(void) override { return true; } diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp index 1c68c5189..e42501e47 100644 --- a/src/Mobs/Slime.cpp +++ b/src/Mobs/Slime.cpp @@ -46,7 +46,7 @@ void cSlime::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cSlime::Attack(float a_Dt) +void cSlime::Attack(std::chrono::milliseconds a_Dt) { if (m_Size > 1) { diff --git a/src/Mobs/Slime.h b/src/Mobs/Slime.h index a177a279c..29605992d 100644 --- a/src/Mobs/Slime.h +++ b/src/Mobs/Slime.h @@ -20,7 +20,7 @@ public: // cAggressiveMonster overrides: virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; - virtual void Attack(float a_Dt) override; + virtual void Attack(std::chrono::milliseconds a_Dt) override; virtual void KilledBy(TakeDamageInfo & a_TDI) override; int GetSize(void) const { return m_Size; } diff --git a/src/Mobs/SnowGolem.cpp b/src/Mobs/SnowGolem.cpp index 8c4178beb..e1510b203 100644 --- a/src/Mobs/SnowGolem.cpp +++ b/src/Mobs/SnowGolem.cpp @@ -27,7 +27,7 @@ void cSnowGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cSnowGolem::Tick(float a_Dt, cChunk & a_Chunk) +void cSnowGolem::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); if (IsBiomeNoDownfall(m_World->GetBiomeAt((int) floor(GetPosX()), (int) floor(GetPosZ())))) diff --git a/src/Mobs/SnowGolem.h b/src/Mobs/SnowGolem.h index f036b1867..9c95e21c5 100644 --- a/src/Mobs/SnowGolem.h +++ b/src/Mobs/SnowGolem.h @@ -17,7 +17,7 @@ public: CLASS_PROTODEF(cSnowGolem) - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; } ; diff --git a/src/Mobs/Squid.cpp b/src/Mobs/Squid.cpp index 59ee963ae..3c508b65f 100644 --- a/src/Mobs/Squid.cpp +++ b/src/Mobs/Squid.cpp @@ -33,7 +33,7 @@ void cSquid::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cSquid::Tick(float a_Dt, cChunk & a_Chunk) +void cSquid::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { // We must first process current location, and only then tick, otherwise we risk processing a location in a chunk // that is not where the entity currently resides (FS #411) diff --git a/src/Mobs/Squid.h b/src/Mobs/Squid.h index a46d738c6..7e944a17e 100644 --- a/src/Mobs/Squid.h +++ b/src/Mobs/Squid.h @@ -15,7 +15,7 @@ class cSquid : public: cSquid(); - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; CLASS_PROTODEF(cSquid) diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp index 963595347..6f647ac18 100644 --- a/src/Mobs/Villager.cpp +++ b/src/Mobs/Villager.cpp @@ -51,7 +51,7 @@ bool cVillager::DoTakeDamage(TakeDamageInfo & a_TDI) -void cVillager::Tick(float a_Dt, cChunk & a_Chunk) +void cVillager::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Mobs/Villager.h b/src/Mobs/Villager.h index d3a38dbf0..2de79295c 100644 --- a/src/Mobs/Villager.h +++ b/src/Mobs/Villager.h @@ -31,7 +31,7 @@ public: // cEntity overrides virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; - virtual void Tick (float a_Dt, cChunk & a_Chunk) override; + virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; // cVillager functions /** return true if the given blocktype are: crops, potatoes or carrots.*/ diff --git a/src/Mobs/Wither.cpp b/src/Mobs/Wither.cpp index 578b47995..6ef81ce1b 100644 --- a/src/Mobs/Wither.cpp +++ b/src/Mobs/Wither.cpp @@ -66,7 +66,7 @@ bool cWither::DoTakeDamage(TakeDamageInfo & a_TDI) -void cWither::Tick(float a_Dt, cChunk & a_Chunk) +void cWither::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); diff --git a/src/Mobs/Wither.h b/src/Mobs/Wither.h index a20fed3d3..9e333c7fa 100644 --- a/src/Mobs/Wither.h +++ b/src/Mobs/Wither.h @@ -28,7 +28,7 @@ public: virtual bool Initialize(cWorld & a_World) override; virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void KilledBy(TakeDamageInfo & a_TDI) override; virtual bool IsUndead(void) override { return true; } diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp index 4711d5a7a..b3eefdf79 100644 --- a/src/Mobs/Wolf.cpp +++ b/src/Mobs/Wolf.cpp @@ -43,7 +43,7 @@ bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI) -void cWolf::Attack(float a_Dt) +void cWolf::Attack(std::chrono::milliseconds a_Dt) { UNUSED(a_Dt); @@ -145,7 +145,7 @@ void cWolf::OnRightClicked(cPlayer & a_Player) -void cWolf::Tick(float a_Dt, cChunk & a_Chunk) +void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { if (!IsAngry()) { diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h index 7500854f8..73ffb55c2 100644 --- a/src/Mobs/Wolf.h +++ b/src/Mobs/Wolf.h @@ -20,9 +20,9 @@ public: virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void TickFollowPlayer(); - virtual void Attack(float a_Dt) override; + virtual void Attack(std::chrono::milliseconds a_Dt) override; // Get functions bool IsSitting (void) const { return m_IsSitting; } diff --git a/src/OSSupport/CMakeLists.txt b/src/OSSupport/CMakeLists.txt index e943ceb18..9424b63da 100644 --- a/src/OSSupport/CMakeLists.txt +++ b/src/OSSupport/CMakeLists.txt @@ -10,12 +10,17 @@ SET (SRCS Event.cpp File.cpp GZipFile.cpp + HostnameLookup.cpp + IPLookup.cpp IsThread.cpp ListenThread.cpp + NetworkSingleton.cpp Semaphore.cpp + ServerHandleImpl.cpp Socket.cpp SocketThreads.cpp StackTrace.cpp + TCPLinkImpl.cpp ) SET (HDRS @@ -24,13 +29,19 @@ SET (HDRS Event.h File.h GZipFile.h + HostnameLookup.h + IPLookup.h IsThread.h ListenThread.h + Network.h + NetworkSingleton.h Queue.h Semaphore.h + ServerHandleImpl.h Socket.h SocketThreads.h StackTrace.h + TCPLinkImpl.h ) if(NOT MSVC) diff --git a/src/OSSupport/CriticalSection.cpp b/src/OSSupport/CriticalSection.cpp index 13a3e4d9f..5248356c5 100644 --- a/src/OSSupport/CriticalSection.cpp +++ b/src/OSSupport/CriticalSection.cpp @@ -1,5 +1,6 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules +#include "CriticalSection.h" diff --git a/src/OSSupport/HostnameLookup.cpp b/src/OSSupport/HostnameLookup.cpp new file mode 100644 index 000000000..3a2997ffd --- /dev/null +++ b/src/OSSupport/HostnameLookup.cpp @@ -0,0 +1,124 @@ + +// HostnameLookup.cpp + +// Implements the cHostnameLookup class representing an in-progress hostname-to-IP lookup + +#include "Globals.h" +#include "HostnameLookup.h" +#include <event2/dns.h> +#include "NetworkSingleton.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cHostnameLookup: + +cHostnameLookup::cHostnameLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks): + m_Callbacks(a_Callbacks) +{ +} + + + + + +void cHostnameLookup::Lookup(const AString & a_Hostname) +{ + // Store the hostname for the callback: + m_Hostname = a_Hostname; + + // Start the lookup: + // Note that we don't have to store the LibEvent lookup handle, LibEvent will free it on its own. + evutil_addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_protocol = IPPROTO_TCP; + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + hints.ai_flags = EVUTIL_AI_CANONNAME; + evdns_getaddrinfo(cNetworkSingleton::Get().GetDNSBase(), a_Hostname.c_str(), nullptr, &hints, Callback, this); +} + + + + + +void cHostnameLookup::Callback(int a_ErrCode, evutil_addrinfo * a_Addr, void * a_Self) +{ + // Get the Self class: + cHostnameLookup * Self = reinterpret_cast<cHostnameLookup *>(a_Self); + ASSERT(Self != nullptr); + + // If an error has occurred, notify the error callback: + if (a_ErrCode != 0) + { + Self->m_Callbacks->OnError(a_ErrCode, evutil_socket_error_to_string(a_ErrCode)); + cNetworkSingleton::Get().RemoveHostnameLookup(Self); + return; + } + + // Call the success handler for each entry received: + bool HasResolved = false; + evutil_addrinfo * OrigAddr = a_Addr; + for (;a_Addr != nullptr; a_Addr = a_Addr->ai_next) + { + char IP[128]; + switch (a_Addr->ai_family) + { + case AF_INET: // IPv4 + { + sockaddr_in * sin = reinterpret_cast<sockaddr_in *>(a_Addr->ai_addr); + evutil_inet_ntop(AF_INET, &(sin->sin_addr), IP, sizeof(IP)); + break; + } + case AF_INET6: // IPv6 + { + sockaddr_in6 * sin = reinterpret_cast<sockaddr_in6 *>(a_Addr->ai_addr); + evutil_inet_ntop(AF_INET6, &(sin->sin6_addr), IP, sizeof(IP)); + break; + } + default: + { + // Unknown address family, handle as if this entry wasn't received + continue; // for (a_Addr) + } + } + Self->m_Callbacks->OnNameResolved(Self->m_Hostname, IP); + HasResolved = true; + } // for (a_Addr) + + // If only unsupported families were reported, call the Error handler: + if (!HasResolved) + { + Self->m_Callbacks->OnError(DNS_ERR_NODATA, "The name does not resolve to any known address."); + } + else + { + Self->m_Callbacks->OnFinished(); + } + evutil_freeaddrinfo(OrigAddr); + cNetworkSingleton::Get().RemoveHostnameLookup(Self); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cNetwork API: + +bool cNetwork::HostnameToIP( + const AString & a_Hostname, + cNetwork::cResolveNameCallbacksPtr a_Callbacks +) +{ + auto Lookup = std::make_shared<cHostnameLookup>(a_Callbacks); + cNetworkSingleton::Get().AddHostnameLookup(Lookup); + Lookup->Lookup(a_Hostname); + return true; +} + + + + diff --git a/src/OSSupport/HostnameLookup.h b/src/OSSupport/HostnameLookup.h new file mode 100644 index 000000000..d69f24707 --- /dev/null +++ b/src/OSSupport/HostnameLookup.h @@ -0,0 +1,47 @@ + +// HostnameLookup.h + +// Declares the cHostnameLookup class representing an in-progress hostname-to-IP lookup + +// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead + + + + + +#pragma once + +#include "Network.h" +#include <event2/util.h> + + + + + +/** Holds information about an in-progress Hostname-to-IP lookup. */ +class cHostnameLookup +{ +public: + /** Creates the lookup object. Doesn't start the lookup yet. */ + cHostnameLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks); + + /** Starts the lookup. */ + void Lookup(const AString & a_Hostname); + +protected: + + /** The callbacks to call for resolved names / errors. */ + cNetwork::cResolveNameCallbacksPtr m_Callbacks; + + /** The hostname that was queried (needed for the callbacks). */ + AString m_Hostname; + + static void Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, void * a_Self); +}; +typedef SharedPtr<cHostnameLookup> cHostnameLookupPtr; +typedef std::vector<cHostnameLookupPtr> cHostnameLookupPtrs; + + + + + diff --git a/src/OSSupport/IPLookup.cpp b/src/OSSupport/IPLookup.cpp new file mode 100644 index 000000000..8cdc5132d --- /dev/null +++ b/src/OSSupport/IPLookup.cpp @@ -0,0 +1,111 @@ + +// IPLookup.cpp + +// Implements the cIPLookup class representing an IP-to-hostname lookup in progress. + +#include "Globals.h" +#include "IPLookup.h" +#include <event2/dns.h> +#include "NetworkSingleton.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cIPLookup: + +cIPLookup::cIPLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks): + m_Callbacks(a_Callbacks) +{ + ASSERT(a_Callbacks != nullptr); +} + + + + + +bool cIPLookup::Lookup(const AString & a_IP) +{ + // Parse the IP address string into a sockaddr structure: + m_IP = a_IP; + sockaddr_storage sa; + int salen = static_cast<int>(sizeof(sa)); + memset(&sa, 0, sizeof(sa)); + if (evutil_parse_sockaddr_port(a_IP.c_str(), reinterpret_cast<sockaddr *>(&sa), &salen) != 0) + { + LOGD("Failed to parse IP address \"%s\".", a_IP.c_str()); + return false; + } + + // Call the proper resolver based on the address family: + // Note that there's no need to store the evdns_request handle returned, LibEvent frees it on its own. + switch (sa.ss_family) + { + case AF_INET: + { + sockaddr_in * sa4 = reinterpret_cast<sockaddr_in *>(&sa); + evdns_base_resolve_reverse(cNetworkSingleton::Get().GetDNSBase(), &(sa4->sin_addr), 0, Callback, this); + break; + } + case AF_INET6: + { + sockaddr_in6 * sa6 = reinterpret_cast<sockaddr_in6 *>(&sa); + evdns_base_resolve_reverse_ipv6(cNetworkSingleton::Get().GetDNSBase(), &(sa6->sin6_addr), 0, Callback, this); + break; + } + default: + { + LOGWARNING("%s: Unknown address family: %d", __FUNCTION__, sa.ss_family); + ASSERT(!"Unknown address family"); + return false; + } + } // switch (address family) + return true; +} + + + + + +void cIPLookup::Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self) +{ + // Get the Self class: + cIPLookup * Self = reinterpret_cast<cIPLookup *>(a_Self); + ASSERT(Self != nullptr); + + // Call the proper callback based on the event received: + if ((a_Result != 0) || (a_Addresses == nullptr)) + { + // An error has occurred, notify the error callback: + Self->m_Callbacks->OnError(a_Result, evutil_socket_error_to_string(a_Result)); + } + else + { + // Call the success handler: + Self->m_Callbacks->OnNameResolved(*(reinterpret_cast<char **>(a_Addresses)), Self->m_IP); + Self->m_Callbacks->OnFinished(); + } + cNetworkSingleton::Get().RemoveIPLookup(Self); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cNetwork API: + +bool cNetwork::IPToHostName( + const AString & a_IP, + cNetwork::cResolveNameCallbacksPtr a_Callbacks +) +{ + auto res = std::make_shared<cIPLookup>(a_Callbacks); + cNetworkSingleton::Get().AddIPLookup(res); + return res->Lookup(a_IP); +} + + + + diff --git a/src/OSSupport/IPLookup.h b/src/OSSupport/IPLookup.h new file mode 100644 index 000000000..af878cbf1 --- /dev/null +++ b/src/OSSupport/IPLookup.h @@ -0,0 +1,49 @@ + +// IPLookup.h + +// Declares the cIPLookup class representing an IP-to-hostname lookup in progress. + +// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead + + + + + +#pragma once + +#include "Network.h" + + + + + +/** Holds information about an in-progress IP-to-Hostname lookup. */ +class cIPLookup +{ +public: + /** Creates the lookup object. Doesn't start the lookup yet. */ + cIPLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks); + + /** Starts the lookup. + Returns true if lookup started successfully, false on failure (invalid IP format etc.) */ + bool Lookup(const AString & a_IP); + +protected: + + /** The callbacks to call for resolved names / errors. */ + cNetwork::cResolveNameCallbacksPtr m_Callbacks; + + /** The IP that was queried (needed for the callbacks). */ + AString m_IP; + + + /** Callback that is called by LibEvent when there's an event for the request. */ + static void Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self); +}; +typedef SharedPtr<cIPLookup> cIPLookupPtr; +typedef std::vector<cIPLookupPtr> cIPLookupPtrs; + + + + + diff --git a/src/OSSupport/IsThread.cpp b/src/OSSupport/IsThread.cpp index 94bed1f56..55e96b622 100644 --- a/src/OSSupport/IsThread.cpp +++ b/src/OSSupport/IsThread.cpp @@ -68,11 +68,22 @@ cIsThread::~cIsThread() +void cIsThread::DoExecute(void) +{ + m_evtStart.Wait(); + Execute(); +} + + + + + bool cIsThread::Start(void) { try { - m_Thread = std::thread(&cIsThread::Execute, this); + // Initialize the thread: + m_Thread = std::thread(&cIsThread::DoExecute, this); #if defined (_MSC_VER) && defined(_DEBUG) if (!m_ThreadName.empty()) @@ -81,9 +92,12 @@ bool cIsThread::Start(void) } #endif + // Notify the thread that initialization is complete and it can run its code safely: + m_evtStart.Set(); + return true; } - catch (std::system_error & a_Exception) + catch (const std::system_error & a_Exception) { LOGERROR("cIsThread::Start error %i: could not construct thread %s; %s", a_Exception.code().value(), m_ThreadName.c_str(), a_Exception.code().message().c_str()); return false; diff --git a/src/OSSupport/IsThread.h b/src/OSSupport/IsThread.h index 131c6950e..f642c8724 100644 --- a/src/OSSupport/IsThread.h +++ b/src/OSSupport/IsThread.h @@ -25,23 +25,28 @@ In the descending class' constructor call the Start() method to start the thread class cIsThread { protected: - /// This is the main thread entrypoint + /** This is the main thread entrypoint. + This function, overloaded by the descendants, is called in the new thread. */ virtual void Execute(void) = 0; - /// The overriden Execute() method should check this value periodically and terminate if this is true + /** The overriden Execute() method should check this value periodically and terminate if this is true. */ volatile bool m_ShouldTerminate; +private: + /** Wrapper for Execute() that waits for the initialization event, to prevent race conditions in thread initialization. */ + void DoExecute(void); + public: cIsThread(const AString & a_ThreadName); virtual ~cIsThread(); - /// Starts the thread; returns without waiting for the actual start + /** Starts the thread; returns without waiting for the actual start. */ bool Start(void); - /// Signals the thread to terminate and waits until it's finished + /** Signals the thread to terminate and waits until it's finished. */ void Stop(void); - /// Waits for the thread to finish. Doesn't signalize the ShouldTerminate flag + /** Waits for the thread to finish. Doesn't signalize the ShouldTerminate flag. */ bool Wait(void); /** Returns true if the thread calling this function is the thread contained within this object. */ @@ -50,6 +55,10 @@ public: protected: AString m_ThreadName; std::thread m_Thread; + + /** The event that is used to wait with the thread's execution until the thread object is fully initialized. + This prevents the IsCurrentThread() call to fail because of a race-condition. */ + cEvent m_evtStart; } ; diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h new file mode 100644 index 000000000..cdf6ba0e9 --- /dev/null +++ b/src/OSSupport/Network.h @@ -0,0 +1,246 @@ + +// Network.h + +// Declares the classes used for the Network API + + + + + +#pragma once + + + + + +// fwd: +class cTCPLink; +typedef SharedPtr<cTCPLink> cTCPLinkPtr; +typedef std::vector<cTCPLinkPtr> cTCPLinkPtrs; +class cServerHandle; +typedef SharedPtr<cServerHandle> cServerHandlePtr; +typedef std::vector<cServerHandlePtr> cServerHandlePtrs; + + + + + +/** Interface that provides the methods available on a single TCP connection. */ +class cTCPLink +{ + friend class cNetwork; + +public: + class cCallbacks + { + public: + // Force a virtual destructor for all descendants: + virtual ~cCallbacks() {} + + /** Called when the cTCPLink for the connection is created. + The callback may store the cTCPLink instance for later use, but it should remove it in OnError(), OnRemoteClosed() or right after Close(). */ + virtual void OnLinkCreated(cTCPLinkPtr a_Link) = 0; + + /** Called when there's data incoming from the remote peer. */ + virtual void OnReceivedData(const char * a_Data, size_t a_Length) = 0; + + /** Called when the remote end closes the connection. + The link is still available for connection information query (IP / port). + Sending data on the link is not an error, but the data won't be delivered. */ + virtual void OnRemoteClosed(void) = 0; + + /** Called when an error is detected on the connection. */ + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0; + }; + typedef SharedPtr<cCallbacks> cCallbacksPtr; + + + // Force a virtual destructor for all descendants: + virtual ~cTCPLink() {} + + /** Queues the specified data for sending to the remote peer. + Returns true on success, false on failure. Note that this success or failure only reports the queue status, not the actual data delivery. */ + virtual bool Send(const void * a_Data, size_t a_Length) = 0; + + /** Queues the specified data for sending to the remote peer. + Returns true on success, false on failure. Note that this success or failure only reports the queue status, not the actual data delivery. */ + bool Send(const AString & a_Data) + { + return Send(a_Data.data(), a_Data.size()); + } + + /** Returns the IP address of the local endpoint of the connection. */ + virtual AString GetLocalIP(void) const = 0; + + /** Returns the port used by the local endpoint of the connection. */ + virtual UInt16 GetLocalPort(void) const = 0; + + /** Returns the IP address of the remote endpoint of the connection. */ + virtual AString GetRemoteIP(void) const = 0; + + /** Returns the port used by the remote endpoint of the connection. */ + virtual UInt16 GetRemotePort(void) const = 0; + + /** Closes the link gracefully. + The link will send any queued outgoing data, then it will send the FIN packet. + The link will still receive incoming data from remote until the remote closes the connection. */ + virtual void Shutdown(void) = 0; + + /** Drops the connection without any more processing. + Sends the RST packet, queued outgoing and incoming data is lost. */ + virtual void Close(void) = 0; + +protected: + /** Callbacks to be used for the various situations. */ + cCallbacksPtr m_Callbacks; + + + /** Creates a new link, with the specified callbacks. */ + cTCPLink(cCallbacksPtr a_Callbacks): + m_Callbacks(a_Callbacks) + { + } +}; + + + + + +/** Interface that provides the methods available on a listening server socket. */ +class cServerHandle +{ + friend class cNetwork; +public: + + // Force a virtual destructor for all descendants: + virtual ~cServerHandle() {} + + /** Stops the server, no more incoming connections will be accepted. + All current connections will be shut down (cTCPLink::Shutdown()). */ + virtual void Close(void) = 0; + + /** Returns true if the server has been started correctly and is currently listening for incoming connections. */ + virtual bool IsListening(void) const = 0; +}; + + + + + +class cNetwork +{ +public: + /** Callbacks used for connecting to other servers as a client. */ + class cConnectCallbacks + { + public: + // Force a virtual destructor for all descendants: + virtual ~cConnectCallbacks() {} + + /** Called when the Connect call succeeds. + Provides the newly created link that can be used for communication. */ + virtual void OnConnected(cTCPLink & a_Link) = 0; + + /** Called when the Connect call fails. */ + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0; + }; + typedef SharedPtr<cConnectCallbacks> cConnectCallbacksPtr; + + + /** Callbacks used when listening for incoming connections as a server. */ + class cListenCallbacks + { + public: + // Force a virtual destructor for all descendants: + virtual ~cListenCallbacks() {} + + /** Called when the TCP server created with Listen() receives a new incoming connection. + Returns the link callbacks that the server should use for the newly created link. + If a nullptr is returned, the connection is dropped immediately; + otherwise a new cTCPLink instance is created and OnAccepted() is called. */ + virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) = 0; + + /** Called when the TCP server created with Listen() creates a new link for an incoming connection. + Provides the newly created Link that can be used for communication. + Called right after a successful OnIncomingConnection(). */ + virtual void OnAccepted(cTCPLink & a_Link) = 0; + + /** Called when the socket fails to listen on the specified port. */ + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0; + }; + typedef SharedPtr<cListenCallbacks> cListenCallbacksPtr; + + + /** Callbacks used when resolving names to IPs. */ + class cResolveNameCallbacks + { + public: + // Force a virtual destructor for all descendants: + virtual ~cResolveNameCallbacks() {} + + /** Called when the hostname is successfully resolved into an IP address. + May be called multiple times if a name resolves to multiple addresses. + a_IP may be either an IPv4 or an IPv6 address with their proper formatting. */ + virtual void OnNameResolved(const AString & a_Name, const AString & a_IP) = 0; + + /** Called when an error is encountered while resolving. + If an error is reported, the OnFinished() callback is not called. */ + virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) = 0; + + /** Called when all the addresses resolved have been reported via the OnNameResolved() callback. + Only called if there was no error reported. */ + virtual void OnFinished(void) = 0; + }; + typedef SharedPtr<cResolveNameCallbacks> cResolveNameCallbacksPtr; + + + /** Queues a TCP connection to be made to the specified host. + Calls one the connection callbacks (success, error) when the connection is successfully established, or upon failure. + The a_LinkCallbacks is passed to the newly created cTCPLink. + Returns true if queueing was successful, false on failure to queue. + Note that the return value doesn't report the success of the actual connection; the connection is established asynchronously in the background. + Implemented in TCPLinkImpl.cpp. */ + static bool Connect( + const AString & a_Host, + UInt16 a_Port, + cConnectCallbacksPtr a_ConnectCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks + ); + + + /** Opens up the specified port for incoming connections. + Calls an OnAccepted callback for each incoming connection. + A cTCPLink with the specified link callbacks is created for each connection. + Returns a cServerHandle that can be used to query the operation status and close the server. + Implemented in ServerHandleImpl.cpp. */ + static cServerHandlePtr Listen( + UInt16 a_Port, + cListenCallbacksPtr a_ListenCallbacks + ); + + + /** Queues a DNS query to resolve the specified hostname to IP address. + Calls one of the callbacks when the resolving succeeds, or when it fails. + Returns true if queueing was successful, false if not. + Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. + Implemented in HostnameLookup.cpp. */ + static bool HostnameToIP( + const AString & a_Hostname, + cResolveNameCallbacksPtr a_Callbacks + ); + + + /** Queues a DNS query to resolve the specified IP address to a hostname. + Calls one of the callbacks when the resolving succeeds, or when it fails. + Returns true if queueing was successful, false if not. + Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background. + Implemented in IPLookup.cpp. */ + static bool IPToHostName( + const AString & a_IP, + cResolveNameCallbacksPtr a_Callbacks + ); +}; + + + + diff --git a/src/OSSupport/NetworkSingleton.cpp b/src/OSSupport/NetworkSingleton.cpp new file mode 100644 index 000000000..92f0604cd --- /dev/null +++ b/src/OSSupport/NetworkSingleton.cpp @@ -0,0 +1,245 @@ + +// NetworkSingleton.cpp + +// Implements the cNetworkSingleton class representing the storage for global data pertaining to network API +// such as a list of all connections, all listening sockets and the LibEvent dispatch thread. + +#include "Globals.h" +#include "NetworkSingleton.h" +#include <event2/event.h> +#include <event2/thread.h> +#include <event2/bufferevent.h> +#include <event2/dns.h> +#include <event2/listener.h> +#include "IPLookup.h" +#include "HostnameLookup.h" + + + + + +cNetworkSingleton::cNetworkSingleton(void) +{ + // Windows: initialize networking: + #ifdef _WIN32 + WSADATA wsaData; + memset(&wsaData, 0, sizeof(wsaData)); + int res = WSAStartup (MAKEWORD(2, 2), &wsaData); + if (res != 0) + { + int err = WSAGetLastError(); + LOGWARNING("WSAStartup failed: %d, WSAGLE = %d (%s)", res, err, evutil_socket_error_to_string(err)); + exit(1); + } + #endif // _WIN32 + + // Initialize LibEvent logging: + event_set_log_callback(LogCallback); + + // Initialize threading: + #if defined(EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED) + evthread_use_windows_threads(); + #elif defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) + evthread_use_pthreads(); + #else + #error No threading implemented for EVTHREAD + #endif + + // Create the main event_base: + m_EventBase = event_base_new(); + if (m_EventBase == nullptr) + { + LOGERROR("Failed to initialize LibEvent. The server will now terminate."); + abort(); + } + + // Create the DNS lookup helper: + m_DNSBase = evdns_base_new(m_EventBase, 1); + if (m_DNSBase == nullptr) + { + LOGERROR("Failed to initialize LibEvent's DNS subsystem. The server will now terminate."); + abort(); + } + + // Create the event loop thread: + std::thread EventLoopThread(RunEventLoop, this); + EventLoopThread.detach(); +} + + + + + +cNetworkSingleton::~cNetworkSingleton() +{ + // Wait for the LibEvent event loop to terminate: + event_base_loopbreak(m_EventBase); + m_EventLoopTerminated.Wait(); + + // Remove all objects: + { + cCSLock Lock(m_CS); + m_Connections.clear(); + m_Servers.clear(); + m_HostnameLookups.clear(); + m_IPLookups.clear(); + } + + // Free the underlying LibEvent objects: + evdns_base_free(m_DNSBase, true); + event_base_free(m_EventBase); + + libevent_global_shutdown(); +} + + + + + +cNetworkSingleton & cNetworkSingleton::Get(void) +{ + static cNetworkSingleton Instance; + return Instance; +} + + + + + +void cNetworkSingleton::LogCallback(int a_Severity, const char * a_Msg) +{ + switch (a_Severity) + { + case _EVENT_LOG_DEBUG: LOGD ("LibEvent: %s", a_Msg); break; + case _EVENT_LOG_MSG: LOG ("LibEvent: %s", a_Msg); break; + case _EVENT_LOG_WARN: LOGWARNING("LibEvent: %s", a_Msg); break; + case _EVENT_LOG_ERR: LOGERROR ("LibEvent: %s", a_Msg); break; + default: + { + LOGWARNING("LibEvent: Unknown log severity (%d): %s", a_Severity, a_Msg); + break; + } + } +} + + + + + +void cNetworkSingleton::RunEventLoop(cNetworkSingleton * a_Self) +{ + event_base_loop(a_Self->m_EventBase, EVLOOP_NO_EXIT_ON_EMPTY); + a_Self->m_EventLoopTerminated.Set(); +} + + + + + +void cNetworkSingleton::AddHostnameLookup(cHostnameLookupPtr a_HostnameLookup) +{ + cCSLock Lock(m_CS); + m_HostnameLookups.push_back(a_HostnameLookup); +} + + + + + +void cNetworkSingleton::RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup) +{ + cCSLock Lock(m_CS); + for (auto itr = m_HostnameLookups.begin(), end = m_HostnameLookups.end(); itr != end; ++itr) + { + if (itr->get() == a_HostnameLookup) + { + m_HostnameLookups.erase(itr); + return; + } + } // for itr - m_HostnameLookups[] +} + + + + + +void cNetworkSingleton::AddIPLookup(cIPLookupPtr a_IPLookup) +{ + cCSLock Lock(m_CS); + m_IPLookups.push_back(a_IPLookup); +} + + + + + +void cNetworkSingleton::RemoveIPLookup(const cIPLookup * a_IPLookup) +{ + cCSLock Lock(m_CS); + for (auto itr = m_IPLookups.begin(), end = m_IPLookups.end(); itr != end; ++itr) + { + if (itr->get() == a_IPLookup) + { + m_IPLookups.erase(itr); + return; + } + } // for itr - m_IPLookups[] +} + + + + + +void cNetworkSingleton::AddLink(cTCPLinkImplPtr a_Link) +{ + cCSLock Lock(m_CS); + m_Connections.push_back(a_Link); +} + + + + + +void cNetworkSingleton::RemoveLink(const cTCPLinkImpl * a_Link) +{ + cCSLock Lock(m_CS); + for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) + { + if (itr->get() == a_Link) + { + m_Connections.erase(itr); + return; + } + } // for itr - m_Connections[] +} + + + + + +void cNetworkSingleton::AddServer(cServerHandleImplPtr a_Server) +{ + cCSLock Lock(m_CS); + m_Servers.push_back(a_Server); +} + + + + + +void cNetworkSingleton::RemoveServer(const cServerHandleImpl * a_Server) +{ + cCSLock Lock(m_CS); + for (auto itr = m_Servers.begin(), end = m_Servers.end(); itr != end; ++itr) + { + if (itr->get() == a_Server) + { + m_Servers.erase(itr); + return; + } + } // for itr - m_Servers[] +} + + + + diff --git a/src/OSSupport/NetworkSingleton.h b/src/OSSupport/NetworkSingleton.h new file mode 100644 index 000000000..1d26fc8f4 --- /dev/null +++ b/src/OSSupport/NetworkSingleton.h @@ -0,0 +1,134 @@ + +// NetworkSingleton.h + +// Declares the cNetworkSingleton class representing the storage for global data pertaining to network API +// such as a list of all connections, all listening sockets and the LibEvent dispatch thread. + +// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead + + + + + +#pragma once + +#include "Network.h" +#include "CriticalSection.h" +#include "Event.h" + + + + + +// fwd: +struct event_base; +struct evdns_base; +class cTCPLinkImpl; +typedef SharedPtr<cTCPLinkImpl> cTCPLinkImplPtr; +typedef std::vector<cTCPLinkImplPtr> cTCPLinkImplPtrs; +class cServerHandleImpl; +typedef SharedPtr<cServerHandleImpl> cServerHandleImplPtr; +typedef std::vector<cServerHandleImplPtr> cServerHandleImplPtrs; +class cHostnameLookup; +typedef SharedPtr<cHostnameLookup> cHostnameLookupPtr; +typedef std::vector<cHostnameLookupPtr> cHostnameLookupPtrs; +class cIPLookup; +typedef SharedPtr<cIPLookup> cIPLookupPtr; +typedef std::vector<cIPLookupPtr> cIPLookupPtrs; + + + + + +class cNetworkSingleton +{ +public: + ~cNetworkSingleton(); + + /** Returns the singleton instance of this class */ + static cNetworkSingleton & Get(void); + + /** Returns the main LibEvent handle for event registering. */ + event_base * GetEventBase(void) { return m_EventBase; } + + /** Returns the LibEvent handle for DNS lookups. */ + evdns_base * GetDNSBase(void) { return m_DNSBase; } + + /** Adds the specified hostname lookup to m_HostnameLookups. + Used by the underlying lookup implementation when a new lookup is initiated. */ + void AddHostnameLookup(cHostnameLookupPtr a_HostnameLookup); + + /** Removes the specified hostname lookup from m_HostnameLookups. + Used by the underlying lookup implementation when the lookup is finished. */ + void RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup); + + /** Adds the specified IP lookup to M_IPLookups. + Used by the underlying lookup implementation when a new lookup is initiated. */ + void AddIPLookup(cIPLookupPtr a_IPLookup); + + /** Removes the specified IP lookup from m_IPLookups. + Used by the underlying lookup implementation when the lookup is finished. */ + void RemoveIPLookup(const cIPLookup * a_IPLookup); + + /** Adds the specified link to m_Connections. + Used by the underlying link implementation when a new link is created. */ + void AddLink(cTCPLinkImplPtr a_Link); + + /** Removes the specified link from m_Connections. + Used by the underlying link implementation when the link is closed / errored. */ + void RemoveLink(const cTCPLinkImpl * a_Link); + + /** Adds the specified link to m_Servers. + Used by the underlying server handle implementation when a new listening server is created. + Only servers that succeed in listening are added. */ + void AddServer(cServerHandleImplPtr a_Server); + + /** Removes the specified server from m_Servers. + Used by the underlying server handle implementation when the server is closed. */ + void RemoveServer(const cServerHandleImpl * a_Server); + +protected: + + /** The main LibEvent container for driving the event loop. */ + event_base * m_EventBase; + + /** The LibEvent handle for doing DNS lookups. */ + evdns_base * m_DNSBase; + + /** Container for all client connections, including ones with pending-connect. */ + cTCPLinkImplPtrs m_Connections; + + /** Container for all servers that are currently active. */ + cServerHandleImplPtrs m_Servers; + + /** Container for all pending hostname lookups. */ + cHostnameLookupPtrs m_HostnameLookups; + + /** Container for all pending IP lookups. */ + cIPLookupPtrs m_IPLookups; + + /** Mutex protecting all containers against multithreaded access. */ + cCriticalSection m_CS; + + /** Event that gets signalled when the event loop terminates. */ + cEvent m_EventLoopTerminated; + + + /** Initializes the LibEvent internals. */ + cNetworkSingleton(void); + + /** Converts LibEvent-generated log events into log messages in MCS log. */ + static void LogCallback(int a_Severity, const char * a_Msg); + + /** Implements the thread that runs LibEvent's event dispatcher loop. */ + static void RunEventLoop(cNetworkSingleton * a_Self); +}; + + + + + + + + + diff --git a/src/OSSupport/Queue.h b/src/OSSupport/Queue.h index 82f221453..afbde1b0d 100644 --- a/src/OSSupport/Queue.h +++ b/src/OSSupport/Queue.h @@ -86,7 +86,7 @@ public: bool TryDequeueItem(ItemType & item) { cCSLock Lock(m_CS); - if (m_Contents.size() == 0) + if (m_Contents.empty()) { return false; } @@ -101,7 +101,7 @@ public: ItemType DequeueItem(void) { cCSLock Lock(m_CS); - while (m_Contents.size() == 0) + while (m_Contents.empty()) { cCSUnlock Unlock(Lock); m_evtAdded.Wait(); diff --git a/src/OSSupport/ServerHandleImpl.cpp b/src/OSSupport/ServerHandleImpl.cpp new file mode 100644 index 000000000..ba38dbf2e --- /dev/null +++ b/src/OSSupport/ServerHandleImpl.cpp @@ -0,0 +1,334 @@ + +// ServerHandleImpl.cpp + +// Implements the cServerHandleImpl class implementing the TCP server functionality + +#include "Globals.h" +#include "ServerHandleImpl.h" +#include "TCPLinkImpl.h" +#include "NetworkSingleton.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// Globals: + +static bool IsValidSocket(evutil_socket_t a_Socket) +{ + #ifdef _WIN32 + return (a_Socket != INVALID_SOCKET); + #else // _WIN32 + return (a_Socket >= 0); + #endif // else _WIN32 +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cServerHandleImpl: + +cServerHandleImpl::cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks): + m_ListenCallbacks(a_ListenCallbacks), + m_ConnListener(nullptr), + m_SecondaryConnListener(nullptr), + m_IsListening(false), + m_ErrorCode(0) +{ +} + + + + + +cServerHandleImpl::~cServerHandleImpl() +{ + if (m_ConnListener != nullptr) + { + evconnlistener_free(m_ConnListener); + } + if (m_SecondaryConnListener != nullptr) + { + evconnlistener_free(m_SecondaryConnListener); + } +} + + + + + +void cServerHandleImpl::Close(void) +{ + // Stop the listener sockets: + evconnlistener_disable(m_ConnListener); + if (m_SecondaryConnListener != nullptr) + { + evconnlistener_disable(m_SecondaryConnListener); + } + m_IsListening = false; + + // Shutdown all connections: + cTCPLinkImplPtrs Conns; + { + cCSLock Lock(m_CS); + std::swap(Conns, m_Connections); + } + for (auto conn: Conns) + { + conn->Shutdown(); + } + + // Remove the ptr to self, so that the object may be freed: + m_SelfPtr.reset(); +} + + + + + +cServerHandleImplPtr cServerHandleImpl::Listen( + UInt16 a_Port, + cNetwork::cListenCallbacksPtr a_ListenCallbacks +) +{ + cServerHandleImplPtr res = cServerHandleImplPtr{new cServerHandleImpl(a_ListenCallbacks)}; + res->m_SelfPtr = res; + if (res->Listen(a_Port)) + { + cNetworkSingleton::Get().AddServer(res); + } + else + { + a_ListenCallbacks->OnError(res->m_ErrorCode, res->m_ErrorMsg); + res->m_SelfPtr.reset(); + } + return res; +} + + + + + +bool cServerHandleImpl::Listen(UInt16 a_Port) +{ + // Make sure the cNetwork internals are innitialized: + cNetworkSingleton::Get(); + + // Set up the main socket: + // It should listen on IPv6 with IPv4 fallback, when available; IPv4 when IPv6 is not available. + bool NeedsTwoSockets = false; + int err; + evutil_socket_t MainSock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + if (!IsValidSocket(MainSock)) + { + // Failed to create IPv6 socket, create an IPv4 one instead: + err = EVUTIL_SOCKET_ERROR(); + LOGD("Failed to create IPv6 MainSock: %d (%s)", err, evutil_socket_error_to_string(err)); + MainSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (!IsValidSocket(MainSock)) + { + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Cannot create socket for port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); + return false; + } + + // Bind to all interfaces: + sockaddr_in name; + memset(&name, 0, sizeof(name)); + name.sin_family = AF_INET; + name.sin_port = ntohs(a_Port); + if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0) + { + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Cannot bind IPv4 socket to port %d: %s", a_Port, evutil_socket_error_to_string(m_ErrorCode)); + evutil_closesocket(MainSock); + return false; + } + } + else + { + // IPv6 socket created, switch it into "dualstack" mode: + UInt32 Zero = 0; + #ifdef _WIN32 + // WinXP doesn't support this feature, so if the setting fails, create another socket later on: + int res = setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero)); + err = EVUTIL_SOCKET_ERROR(); + NeedsTwoSockets = ((res == SOCKET_ERROR) && (err == WSAENOPROTOOPT)); + LOGD("setsockopt(IPV6_V6ONLY) returned %d, err is %d (%s). %s", + res, err, evutil_socket_error_to_string(err), + NeedsTwoSockets ? "Second socket will be created" : "Second socket not needed" + ); + #else + setsockopt(MainSock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&Zero), sizeof(Zero)); + #endif + + // Bind to all interfaces: + sockaddr_in6 name; + memset(&name, 0, sizeof(name)); + name.sin6_family = AF_INET6; + name.sin6_port = ntohs(a_Port); + if (bind(MainSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0) + { + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Cannot bind IPv6 socket to port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode)); + evutil_closesocket(MainSock); + return false; + } + } + if (evutil_make_socket_nonblocking(MainSock) != 0) + { + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Cannot make socket on port %d non-blocking: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode)); + evutil_closesocket(MainSock); + return false; + } + if (listen(MainSock, 0) != 0) + { + m_ErrorCode = EVUTIL_SOCKET_ERROR(); + Printf(m_ErrorMsg, "Cannot listen on port %d: %d (%s)", a_Port, m_ErrorCode, evutil_socket_error_to_string(m_ErrorCode)); + evutil_closesocket(MainSock); + return false; + } + m_ConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, MainSock); + m_IsListening = true; + if (!NeedsTwoSockets) + { + return true; + } + + // If a secondary socket is required (WinXP dual-stack), create it here: + LOGD("Creating a second socket for IPv4"); + evutil_socket_t SecondSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (!IsValidSocket(SecondSock)) + { + err = EVUTIL_SOCKET_ERROR(); + LOGD("socket(AF_INET, ...) failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err)); + return true; // Report as success, the primary socket is working + } + + // Make the secondary socket nonblocking: + if (evutil_make_socket_nonblocking(SecondSock) != 0) + { + err = EVUTIL_SOCKET_ERROR(); + LOGD("evutil_make_socket_nonblocking() failed for secondary socket: %d, %s", err, evutil_socket_error_to_string(err)); + evutil_closesocket(SecondSock); + return true; // Report as success, the primary socket is working + } + + // Bind to all IPv4 interfaces: + sockaddr_in name; + memset(&name, 0, sizeof(name)); + name.sin_family = AF_INET; + name.sin_port = ntohs(a_Port); + if (bind(SecondSock, reinterpret_cast<const sockaddr *>(&name), sizeof(name)) != 0) + { + err = EVUTIL_SOCKET_ERROR(); + LOGD("Cannot bind secondary socket to port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); + evutil_closesocket(SecondSock); + return true; // Report as success, the primary socket is working + } + + if (listen(SecondSock, 0) != 0) + { + err = EVUTIL_SOCKET_ERROR(); + LOGD("Cannot listen on on secondary socket on port %d: %d (%s)", a_Port, err, evutil_socket_error_to_string(err)); + evutil_closesocket(SecondSock); + return true; // Report as success, the primary socket is working + } + + m_SecondaryConnListener = evconnlistener_new(cNetworkSingleton::Get().GetEventBase(), Callback, this, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 0, SecondSock); + return true; +} + + + + + +void cServerHandleImpl::Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self) +{ + // Cast to true self: + cServerHandleImpl * Self = reinterpret_cast<cServerHandleImpl *>(a_Self); + ASSERT(Self != nullptr); + ASSERT(Self->m_SelfPtr != nullptr); + + // Get the textual IP address and port number out of a_Addr: + char IPAddress[128]; + evutil_inet_ntop(a_Addr->sa_family, a_Addr->sa_data, IPAddress, ARRAYCOUNT(IPAddress)); + UInt16 Port = 0; + switch (a_Addr->sa_family) + { + case AF_INET: + { + sockaddr_in * sin = reinterpret_cast<sockaddr_in *>(a_Addr); + Port = ntohs(sin->sin_port); + break; + } + case AF_INET6: + { + sockaddr_in6 * sin6 = reinterpret_cast<sockaddr_in6 *>(a_Addr); + Port = ntohs(sin6->sin6_port); + break; + } + } + + // Call the OnIncomingConnection callback to get the link callbacks to use: + cTCPLink::cCallbacksPtr LinkCallbacks = Self->m_ListenCallbacks->OnIncomingConnection(IPAddress, Port); + if (LinkCallbacks == nullptr) + { + // Drop the connection: + evutil_closesocket(a_Socket); + return; + } + + // Create a new cTCPLink for the incoming connection: + cTCPLinkImplPtr Link = std::make_shared<cTCPLinkImpl>(a_Socket, LinkCallbacks, Self->m_SelfPtr, a_Addr, static_cast<socklen_t>(a_Len)); + { + cCSLock Lock(Self->m_CS); + Self->m_Connections.push_back(Link); + } // Lock(m_CS) + LinkCallbacks->OnLinkCreated(Link); + Link->Enable(Link); + + // Call the OnAccepted callback: + Self->m_ListenCallbacks->OnAccepted(*Link); +} + + + + + +void cServerHandleImpl::RemoveLink(const cTCPLinkImpl * a_Link) +{ + cCSLock Lock(m_CS); + for (auto itr = m_Connections.begin(), end = m_Connections.end(); itr != end; ++itr) + { + if (itr->get() == a_Link) + { + m_Connections.erase(itr); + return; + } + } // for itr - m_Connections[] +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cNetwork API: + +cServerHandlePtr cNetwork::Listen( + UInt16 a_Port, + cNetwork::cListenCallbacksPtr a_ListenCallbacks +) +{ + return cServerHandleImpl::Listen(a_Port, a_ListenCallbacks); +} + + + + + diff --git a/src/OSSupport/ServerHandleImpl.h b/src/OSSupport/ServerHandleImpl.h new file mode 100644 index 000000000..dbb18fc6d --- /dev/null +++ b/src/OSSupport/ServerHandleImpl.h @@ -0,0 +1,105 @@ + +// ServerHandleImpl.h + +// Declares the cServerHandleImpl class implementing the TCP server functionality + +// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead + + + + + +#pragma once + +#include "Network.h" +#include <event2/listener.h> +#include "CriticalSection.h" + + + + + +// fwd: +class cTCPLinkImpl; +typedef SharedPtr<cTCPLinkImpl> cTCPLinkImplPtr; +typedef std::vector<cTCPLinkImplPtr> cTCPLinkImplPtrs; +class cServerHandleImpl; +typedef SharedPtr<cServerHandleImpl> cServerHandleImplPtr; +typedef std::vector<cServerHandleImplPtr> cServerHandleImplPtrs; + + + + + +class cServerHandleImpl: + public cServerHandle +{ + typedef cServerHandle super; + friend class cTCPLinkImpl; + +public: + /** Closes the server, dropping all the connections. */ + ~cServerHandleImpl(); + + /** Creates a new server instance listening on the specified port. + Both IPv4 and IPv6 interfaces are used, if possible. + Always returns a server instance; in the event of a failure, the instance holds the error details. Use IsListening() to query success. */ + static cServerHandleImplPtr Listen( + UInt16 a_Port, + cNetwork::cListenCallbacksPtr a_ListenCallbacks + ); + + // cServerHandle overrides: + virtual void Close(void) override; + virtual bool IsListening(void) const override { return m_IsListening; } + +protected: + /** The callbacks used to notify about incoming connections. */ + cNetwork::cListenCallbacksPtr m_ListenCallbacks; + + /** The LibEvent handle representing the main listening socket. */ + evconnlistener * m_ConnListener; + + /** The LibEvent handle representing the secondary listening socket (only when side-by-side listening is needed, such as WinXP). */ + evconnlistener * m_SecondaryConnListener; + + /** Set to true when the server is initialized successfully and is listening for incoming connections. */ + bool m_IsListening; + + /** Container for all currently active connections on this server. */ + cTCPLinkImplPtrs m_Connections; + + /** Mutex protecting m_Connections againt multithreaded access. */ + cCriticalSection m_CS; + + /** Contains the error code for the failure to listen. Only valid for non-listening instances. */ + int m_ErrorCode; + + /** Contains the error message for the failure to listen. Only valid for non-listening instances. */ + AString m_ErrorMsg; + + /** The SharedPtr to self, so that it can be passed to created links. */ + cServerHandleImplPtr m_SelfPtr; + + + + /** Creates a new instance with the specified callbacks. + Initializes the internals, but doesn't start listening yet. */ + cServerHandleImpl(cNetwork::cListenCallbacksPtr a_ListenCallbacks); + + /** Starts listening on the specified port. + Returns true if successful, false on failure. On failure, sets m_ErrorCode and m_ErrorMsg. */ + bool Listen(UInt16 a_Port); + + /** The callback called by LibEvent upon incoming connection. */ + static void Callback(evconnlistener * a_Listener, evutil_socket_t a_Socket, sockaddr * a_Addr, int a_Len, void * a_Self); + + /** Removes the specified link from m_Connections. + Called by cTCPLinkImpl when the link is terminated. */ + void RemoveLink(const cTCPLinkImpl * a_Link); +}; + + + + + diff --git a/src/OSSupport/StackTrace.cpp b/src/OSSupport/StackTrace.cpp index a56568457..015a53ba0 100644 --- a/src/OSSupport/StackTrace.cpp +++ b/src/OSSupport/StackTrace.cpp @@ -34,7 +34,7 @@ void PrintStackTrace(void) // Use the backtrace() function to get and output the stackTrace: // Code adapted from http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes void * stackTrace[30]; - size_t numItems = backtrace(stackTrace, ARRAYCOUNT(stackTrace)); + int numItems = backtrace(stackTrace, ARRAYCOUNT(stackTrace)); backtrace_symbols_fd(stackTrace, numItems, STDERR_FILENO); #endif } diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp new file mode 100644 index 000000000..b4cefa60c --- /dev/null +++ b/src/OSSupport/TCPLinkImpl.cpp @@ -0,0 +1,331 @@ + +// TCPLinkImpl.cpp + +// Implements the cTCPLinkImpl class implementing the TCP link functionality + +#include "Globals.h" +#include "TCPLinkImpl.h" +#include "NetworkSingleton.h" +#include "ServerHandleImpl.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cTCPLinkImpl: + +cTCPLinkImpl::cTCPLinkImpl(cTCPLink::cCallbacksPtr a_LinkCallbacks): + super(a_LinkCallbacks), + m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), -1, BEV_OPT_CLOSE_ON_FREE)) +{ +} + + + + + +cTCPLinkImpl::cTCPLinkImpl(evutil_socket_t a_Socket, cTCPLink::cCallbacksPtr a_LinkCallbacks, cServerHandleImplPtr a_Server, const sockaddr * a_Address, socklen_t a_AddrLen): + super(a_LinkCallbacks), + m_BufferEvent(bufferevent_socket_new(cNetworkSingleton::Get().GetEventBase(), a_Socket, BEV_OPT_CLOSE_ON_FREE)), + m_Server(a_Server) +{ + // Update the endpoint addresses: + UpdateLocalAddress(); + UpdateAddress(a_Address, a_AddrLen, m_RemoteIP, m_RemotePort); +} + + + + + +cTCPLinkImpl::~cTCPLinkImpl() +{ + bufferevent_free(m_BufferEvent); +} + + + + + +cTCPLinkImplPtr cTCPLinkImpl::Connect(const AString & a_Host, UInt16 a_Port, cTCPLink::cCallbacksPtr a_LinkCallbacks, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks) +{ + ASSERT(a_LinkCallbacks != nullptr); + ASSERT(a_ConnectCallbacks != nullptr); + + // Create a new link: + cTCPLinkImplPtr res{new cTCPLinkImpl(a_LinkCallbacks)}; // Cannot use std::make_shared here, constructor is not accessible + res->m_ConnectCallbacks = a_ConnectCallbacks; + cNetworkSingleton::Get().AddLink(res); + res->m_Callbacks->OnLinkCreated(res); + res->Enable(res); + + // If a_Host is an IP address, schedule a connection immediately: + sockaddr_storage sa; + int salen = static_cast<int>(sizeof(sa)); + if (evutil_parse_sockaddr_port(a_Host.c_str(), reinterpret_cast<sockaddr *>(&sa), &salen) == 0) + { + // Insert the correct port: + if (sa.ss_family == AF_INET6) + { + reinterpret_cast<sockaddr_in6 *>(&sa)->sin6_port = htons(a_Port); + } + else + { + reinterpret_cast<sockaddr_in *>(&sa)->sin_port = htons(a_Port); + } + + // Queue the connect request: + if (bufferevent_socket_connect(res->m_BufferEvent, reinterpret_cast<sockaddr *>(&sa), salen) == 0) + { + // Success + return res; + } + // Failure + cNetworkSingleton::Get().RemoveLink(res.get()); + return nullptr; + } + + // a_Host is a hostname, connect after a lookup: + if (bufferevent_socket_connect_hostname(res->m_BufferEvent, cNetworkSingleton::Get().GetDNSBase(), AF_UNSPEC, a_Host.c_str(), a_Port) == 0) + { + // Success + return res; + } + // Failure + cNetworkSingleton::Get().RemoveLink(res.get()); + return nullptr; +} + + + + + +void cTCPLinkImpl::Enable(cTCPLinkImplPtr a_Self) +{ + // Take hold of a shared copy of self, to keep as long as the callbacks are coming: + m_Self = a_Self; + + // Set the LibEvent callbacks and enable processing: + bufferevent_setcb(m_BufferEvent, ReadCallback, nullptr, EventCallback, this); + bufferevent_enable(m_BufferEvent, EV_READ | EV_WRITE); +} + + + + + +bool cTCPLinkImpl::Send(const void * a_Data, size_t a_Length) +{ + return (bufferevent_write(m_BufferEvent, a_Data, a_Length) == 0); +} + + + + + +void cTCPLinkImpl::Shutdown(void) +{ + #ifdef _WIN32 + shutdown(bufferevent_getfd(m_BufferEvent), SD_SEND); + #else + shutdown(bufferevent_getfd(m_BufferEvent), SHUT_WR); + #endif + bufferevent_disable(m_BufferEvent, EV_WRITE); +} + + + + + +void cTCPLinkImpl::Close(void) +{ + // Disable all events on the socket, but keep it alive: + bufferevent_disable(m_BufferEvent, EV_READ | EV_WRITE); + if (m_Server == nullptr) + { + cNetworkSingleton::Get().RemoveLink(this); + } + else + { + m_Server->RemoveLink(this); + } + m_Self.reset(); +} + + + + + + +void cTCPLinkImpl::ReadCallback(bufferevent * a_BufferEvent, void * a_Self) +{ + ASSERT(a_Self != nullptr); + cTCPLinkImpl * Self = static_cast<cTCPLinkImpl *>(a_Self); + ASSERT(Self->m_Callbacks != nullptr); + + // Read all the incoming data, in 1024-byte chunks: + char data[1024]; + size_t length; + while ((length = bufferevent_read(a_BufferEvent, data, sizeof(data))) > 0) + { + Self->m_Callbacks->OnReceivedData(data, length); + } +} + + + + + +void cTCPLinkImpl::EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self) +{ + ASSERT(a_Self != nullptr); + cTCPLinkImplPtr Self = static_cast<cTCPLinkImpl *>(a_Self)->m_Self; + + // If an error is reported, call the error callback: + if (a_What & BEV_EVENT_ERROR) + { + // Choose the proper callback to call based on whether we were waiting for connection or not: + int err = EVUTIL_SOCKET_ERROR(); + if (Self->m_ConnectCallbacks != nullptr) + { + if (err == 0) + { + // This could be a DNS failure + err = bufferevent_socket_get_dns_error(a_BufferEvent); + } + Self->m_ConnectCallbacks->OnError(err, evutil_socket_error_to_string(err)); + } + else + { + Self->m_Callbacks->OnError(err, evutil_socket_error_to_string(err)); + if (Self->m_Server == nullptr) + { + cNetworkSingleton::Get().RemoveLink(Self.get()); + } + else + { + Self->m_Server->RemoveLink(Self.get()); + } + } + Self->m_Self.reset(); + return; + } + + // Pending connection succeeded, call the connection callback: + if (a_What & BEV_EVENT_CONNECTED) + { + if (Self->m_ConnectCallbacks != nullptr) + { + Self->m_ConnectCallbacks->OnConnected(*Self); + // Reset the connect callbacks so that later errors get reported through the link callbacks: + Self->m_ConnectCallbacks.reset(); + return; + } + Self->UpdateLocalAddress(); + Self->UpdateRemoteAddress(); + } + + // If the connection has been closed, call the link callback and remove the connection: + if (a_What & BEV_EVENT_EOF) + { + Self->m_Callbacks->OnRemoteClosed(); + if (Self->m_Server != nullptr) + { + Self->m_Server->RemoveLink(Self.get()); + } + else + { + cNetworkSingleton::Get().RemoveLink(Self.get()); + } + Self->m_Self.reset(); + return; + } + + // Unknown event, report it: + LOGWARNING("cTCPLinkImpl: Unhandled LibEvent event %d (0x%x)", a_What, a_What); + ASSERT(!"cTCPLinkImpl: Unhandled LibEvent event"); +} + + + + + +void cTCPLinkImpl::UpdateAddress(const sockaddr * a_Address, socklen_t a_AddrLen, AString & a_IP, UInt16 & a_Port) +{ + // Based on the family specified in the address, use the correct datastructure to convert to IP string: + char IP[128]; + switch (a_Address->sa_family) + { + case AF_INET: // IPv4: + { + const sockaddr_in * sin = reinterpret_cast<const sockaddr_in *>(a_Address); + evutil_inet_ntop(AF_INET, &(sin->sin_addr), IP, sizeof(IP)); + a_Port = ntohs(sin->sin_port); + break; + } + case AF_INET6: // IPv6 + { + const sockaddr_in6 * sin = reinterpret_cast<const sockaddr_in6 *>(a_Address); + evutil_inet_ntop(AF_INET6, &(sin->sin6_addr), IP, sizeof(IP)); + a_Port = ntohs(sin->sin6_port); + break; + } + + default: + { + LOGWARNING("%s: Unknown socket address family: %d", __FUNCTION__, a_Address->sa_family); + ASSERT(!"Unknown socket address family"); + break; + } + } + a_IP.assign(IP); +} + + + + + +void cTCPLinkImpl::UpdateLocalAddress(void) +{ + sockaddr_storage sa; + socklen_t salen = static_cast<socklen_t>(sizeof(sa)); + getsockname(bufferevent_getfd(m_BufferEvent), reinterpret_cast<sockaddr *>(&sa), &salen); + UpdateAddress(reinterpret_cast<const sockaddr *>(&sa), salen, m_LocalIP, m_LocalPort); +} + + + + + +void cTCPLinkImpl::UpdateRemoteAddress(void) +{ + sockaddr_storage sa; + socklen_t salen = static_cast<socklen_t>(sizeof(sa)); + getpeername(bufferevent_getfd(m_BufferEvent), reinterpret_cast<sockaddr *>(&sa), &salen); + UpdateAddress(reinterpret_cast<const sockaddr *>(&sa), salen, m_RemoteIP, m_RemotePort); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cNetwork API: + +bool cNetwork::Connect( + const AString & a_Host, + UInt16 a_Port, + cNetwork::cConnectCallbacksPtr a_ConnectCallbacks, + cTCPLink::cCallbacksPtr a_LinkCallbacks +) +{ + // Add a connection request to the queue: + cTCPLinkImplPtr Conn = cTCPLinkImpl::Connect(a_Host, a_Port, a_LinkCallbacks, a_ConnectCallbacks); + return (Conn != nullptr); +} + + + + + diff --git a/src/OSSupport/TCPLinkImpl.h b/src/OSSupport/TCPLinkImpl.h new file mode 100644 index 000000000..735e8ed9d --- /dev/null +++ b/src/OSSupport/TCPLinkImpl.h @@ -0,0 +1,122 @@ + +// TCPLinkImpl.h + +// Declares the cTCPLinkImpl class implementing the TCP link functionality + +// This is an internal header, no-one outside OSSupport should need to include it; use Network.h instead + + + + + +#pragma once + +#include "Network.h" +#include <event2/event.h> +#include <event2/bufferevent.h> + + + + + +// fwd: +class cServerHandleImpl; +typedef SharedPtr<cServerHandleImpl> cServerHandleImplPtr; +class cTCPLinkImpl; +typedef SharedPtr<cTCPLinkImpl> cTCPLinkImplPtr; +typedef std::vector<cTCPLinkImplPtr> cTCPLinkImplPtrs; + + + + + +class cTCPLinkImpl: + public cTCPLink +{ + typedef cTCPLink super; + +public: + /** Creates a new link based on the given socket. + Used for connections accepted in a server using cNetwork::Listen(). + a_Address and a_AddrLen describe the remote peer that has connected. + The link is created disabled, you need to call Enable() to start the regular communication. */ + cTCPLinkImpl(evutil_socket_t a_Socket, cCallbacksPtr a_LinkCallbacks, cServerHandleImplPtr a_Server, const sockaddr * a_Address, socklen_t a_AddrLen); + + /** Destroys the LibEvent handle representing the link. */ + ~cTCPLinkImpl(); + + /** Queues a connection request to the specified host. + a_ConnectCallbacks must be valid. + Returns a link that has the connection request queued, or NULL for failure. */ + static cTCPLinkImplPtr Connect(const AString & a_Host, UInt16 a_Port, cTCPLink::cCallbacksPtr a_LinkCallbacks, cNetwork::cConnectCallbacksPtr a_ConnectCallbacks); + + /** Enables communication over the link. + Links are created with communication disabled, so that creation callbacks can be called first. + This function then enables the regular communication to be reported. + The a_Self parameter is used so that the socket can keep itself alive as long as the callbacks are coming. */ + void Enable(cTCPLinkImplPtr a_Self); + + // cTCPLink overrides: + virtual bool Send(const void * a_Data, size_t a_Length) override; + virtual AString GetLocalIP(void) const override { return m_LocalIP; } + virtual UInt16 GetLocalPort(void) const override { return m_LocalPort; } + virtual AString GetRemoteIP(void) const override { return m_RemoteIP; } + virtual UInt16 GetRemotePort(void) const override { return m_RemotePort; } + virtual void Shutdown(void) override; + virtual void Close(void) override; + +protected: + + /** Callbacks to call when the connection is established. + May be NULL if not used. Only used for outgoing connections (cNetwork::Connect()). */ + cNetwork::cConnectCallbacksPtr m_ConnectCallbacks; + + /** The LibEvent handle representing this connection. */ + bufferevent * m_BufferEvent; + + /** The server handle that has created this link. + Only valid for incoming connections, nullptr for outgoing connections. */ + cServerHandleImplPtr m_Server; + + /** The IP address of the local endpoint. Valid only after the socket has been connected. */ + AString m_LocalIP; + + /** The port of the local endpoint. Valid only after the socket has been connected. */ + UInt16 m_LocalPort; + + /** The IP address of the remote endpoint. Valid only after the socket has been connected. */ + AString m_RemoteIP; + + /** The port of the remote endpoint. Valid only after the socket has been connected. */ + UInt16 m_RemotePort; + + /** SharedPtr to self, used to keep this object alive as long as the callbacks are coming. + Initialized in Enable(), cleared in Close() and EventCallback(RemoteClosed). */ + cTCPLinkImplPtr m_Self; + + + /** Creates a new link to be queued to connect to a specified host:port. + Used for outgoing connections created using cNetwork::Connect(). + To be used only by the Connect() factory function. + The link is created disabled, you need to call Enable() to start the regular communication. */ + cTCPLinkImpl(const cCallbacksPtr a_LinkCallbacks); + + /** Callback that LibEvent calls when there's data available from the remote peer. */ + static void ReadCallback(bufferevent * a_BufferEvent, void * a_Self); + + /** Callback that LibEvent calls when there's a non-data-related event on the socket. */ + static void EventCallback(bufferevent * a_BufferEvent, short a_What, void * a_Self); + + /** Sets a_IP and a_Port to values read from a_Address, based on the correct address family. */ + static void UpdateAddress(const sockaddr * a_Address, socklen_t a_AddrLen, AString & a_IP, UInt16 & a_Port); + + /** Updates m_LocalIP and m_LocalPort based on the metadata read from the socket. */ + void UpdateLocalAddress(void); + + /** Updates m_RemoteIP and m_RemotePort based on the metadata read from the socket. */ + void UpdateRemoteAddress(void); +}; + + + + diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 1e5fe5586..169367949 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -174,10 +174,10 @@ void cProtocol172::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, cha cPacketizer Pkt(*this, 0x24); // Block Action packet Pkt.WriteInt(a_BlockX); - Pkt.WriteShort(a_BlockY); + Pkt.WriteShort(static_cast<short>(a_BlockY)); Pkt.WriteInt(a_BlockZ); - Pkt.WriteByte(a_Byte1); - Pkt.WriteByte(a_Byte2); + Pkt.WriteByte(static_cast<Byte>(a_Byte1)); + Pkt.WriteByte(static_cast<Byte>(a_Byte2)); Pkt.WriteVarInt(a_BlockType); } @@ -190,7 +190,7 @@ void cProtocol172::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x25); // Block Break Animation packet - Pkt.WriteVarInt(a_EntityID); + Pkt.WriteVarInt(static_cast<UInt32>(a_EntityID)); Pkt.WriteInt(a_BlockX); Pkt.WriteInt(a_BlockY); Pkt.WriteInt(a_BlockZ); @@ -204,10 +204,11 @@ void cProtocol172::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY void cProtocol172::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { ASSERT(m_State == 3); // In game mode? + ASSERT((a_BlockY >= 0) && (a_BlockY < 256)); cPacketizer Pkt(*this, 0x23); // Block Change packet Pkt.WriteInt(a_BlockX); - Pkt.WriteByte(a_BlockY); + Pkt.WriteByte(static_cast<Byte>(a_BlockY)); Pkt.WriteInt(a_BlockZ); Pkt.WriteVarInt(a_BlockType); Pkt.WriteByte(a_BlockMeta); @@ -228,8 +229,8 @@ void cProtocol172::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockV Pkt.WriteInt((int)a_Changes.size() * 4); for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr) { - unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12); - unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4); + int Coords = itr->m_RelY | (itr->m_RelZ << 8) | (itr->m_RelX << 12); + int Blocks = static_cast<int>(itr->m_BlockMeta | (itr->m_BlockType << 4)); Pkt.WriteInt((Coords << 16) | Blocks); } // for itr - a_Changes[] } @@ -352,11 +353,13 @@ void cProtocol172::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ) void cProtocol172::SendEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration) { ASSERT(m_State == 3); // In game mode? + ASSERT((a_EffectID >= 0) && (a_EffectID < 256)); + ASSERT((a_Amplifier >= 0) && (a_Amplifier < 256)); cPacketizer Pkt(*this, 0x1D); // Entity Effect packet Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteByte(a_EffectID); - Pkt.WriteByte(a_Amplifier); + Pkt.WriteByte(static_cast<Byte>(a_EffectID)); + Pkt.WriteByte(static_cast<Byte>(a_Amplifier)); Pkt.WriteShort(a_Duration); } @@ -438,9 +441,9 @@ void cProtocol172::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char cPacketizer Pkt(*this, 0x15); // Entity Relative Move packet Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteByte(a_RelX); - Pkt.WriteByte(a_RelY); - Pkt.WriteByte(a_RelZ); + Pkt.WriteChar(a_RelX); + Pkt.WriteChar(a_RelY); + Pkt.WriteChar(a_RelZ); } @@ -453,9 +456,9 @@ void cProtocol172::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, cPacketizer Pkt(*this, 0x17); // Entity Look And Relative Move packet Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteByte(a_RelX); - Pkt.WriteByte(a_RelY); - Pkt.WriteByte(a_RelZ); + Pkt.WriteChar(a_RelX); + Pkt.WriteChar(a_RelY); + Pkt.WriteChar(a_RelZ); Pkt.WriteByteAngle(a_Entity.GetYaw()); Pkt.WriteByteAngle(a_Entity.GetPitch()); } @@ -537,9 +540,9 @@ void cProtocol172::SendHealth(void) cPacketizer Pkt(*this, 0x06); // Update Health packet cPlayer * Player = m_Client->GetPlayer(); - Pkt.WriteFloat((float)Player->GetHealth()); - Pkt.WriteShort(Player->GetFoodLevel()); - Pkt.WriteFloat((float)Player->GetFoodSaturationLevel()); + Pkt.WriteFloat(static_cast<float>(Player->GetHealth())); + Pkt.WriteShort(static_cast<short>(Player->GetFoodLevel())); + Pkt.WriteFloat(static_cast<float>(Player->GetFoodSaturationLevel())); } @@ -584,10 +587,10 @@ void cProtocol172::SendLogin(const cPlayer & a_Player, const cWorld & a_World) cServer * Server = cRoot::Get()->GetServer(); cPacketizer Pkt(*this, 0x01); // Join Game packet Pkt.WriteInt(a_Player.GetUniqueID()); - Pkt.WriteByte((Byte)a_Player.GetEffectiveGameMode() | (Server->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4 - Pkt.WriteChar((char)a_World.GetDimension()); + Pkt.WriteByte(static_cast<Byte>(a_Player.GetEffectiveGameMode()) | (Server->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4 + Pkt.WriteChar(static_cast<char>(a_World.GetDimension())); Pkt.WriteByte(2); // TODO: Difficulty (set to Normal) - Pkt.WriteByte(std::min(Server->GetMaxPlayers(), 60)); + Pkt.WriteByte(static_cast<Byte>(std::min(Server->GetMaxPlayers(), 60))); Pkt.WriteString("default"); // Level type - wtf? } m_LastSentDimension = a_World.GetDimension(); @@ -595,9 +598,9 @@ void cProtocol172::SendLogin(const cPlayer & a_Player, const cWorld & a_World) // Send the spawn position: { cPacketizer Pkt(*this, 0x05); // Spawn Position packet - Pkt.WriteInt((int)a_World.GetSpawnX()); - Pkt.WriteInt((int)a_World.GetSpawnY()); - Pkt.WriteInt((int)a_World.GetSpawnZ()); + Pkt.WriteInt(static_cast<int>(a_World.GetSpawnX())); + Pkt.WriteInt(static_cast<int>(a_World.GetSpawnY())); + Pkt.WriteInt(static_cast<int>(a_World.GetSpawnZ())); } // Send player abilities: @@ -629,11 +632,11 @@ void cProtocol172::SendPaintingSpawn(const cPainting & a_Painting) ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x10); // Spawn Painting packet - Pkt.WriteVarInt(a_Painting.GetUniqueID()); + Pkt.WriteVarInt(static_cast<UInt32>(a_Painting.GetUniqueID())); Pkt.WriteString(a_Painting.GetName().c_str()); - Pkt.WriteInt((int)a_Painting.GetPosX()); - Pkt.WriteInt((int)a_Painting.GetPosY()); - Pkt.WriteInt((int)a_Painting.GetPosZ()); + Pkt.WriteInt(static_cast<int>(a_Painting.GetPosX())); + Pkt.WriteInt(static_cast<int>(a_Painting.GetPosY())); + Pkt.WriteInt(static_cast<int>(a_Painting.GetPosZ())); Pkt.WriteInt(a_Painting.GetDirection()); } @@ -644,19 +647,19 @@ void cProtocol172::SendPaintingSpawn(const cPainting & a_Painting) void cProtocol172::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length, unsigned int m_Scale) { ASSERT(m_State == 3); // In game mode? + ASSERT(a_Length + 3 <= USHRT_MAX); + ASSERT((a_X >= 0) && (a_X < 256)); + ASSERT((a_Y >= 0) && (a_Y < 256)); cPacketizer Pkt(*this, 0x34); - Pkt.WriteVarInt(a_ID); - Pkt.WriteShort (3 + a_Length); + Pkt.WriteVarInt(static_cast<UInt32>(a_ID)); + Pkt.WriteShort (static_cast<short>(3 + a_Length)); Pkt.WriteByte(0); - Pkt.WriteByte(a_X); - Pkt.WriteByte(a_Y); + Pkt.WriteByte(static_cast<Byte>(a_X)); + Pkt.WriteByte(static_cast<Byte>(a_Y)); - for (unsigned int i = 0; i < a_Length; ++i) - { - Pkt.WriteByte(a_Colors[i]); - } + Pkt.WriteBuf(reinterpret_cast<const char *>(a_Colors), a_Length); } @@ -666,18 +669,21 @@ void cProtocol172::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colo void cProtocol172::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators, unsigned int m_Scale) { ASSERT(m_State == 3); // In game mode? + ASSERT(1 + 3 * a_Decorators.size() < USHRT_MAX); cPacketizer Pkt(*this, 0x34); - Pkt.WriteVarInt(a_ID); - Pkt.WriteShort ((short)(1 + (3 * a_Decorators.size()))); + Pkt.WriteVarInt(static_cast<UInt32>(a_ID)); + Pkt.WriteShort (static_cast<short>(1 + (3 * a_Decorators.size()))); Pkt.WriteByte(1); for (cMapDecoratorList::const_iterator it = a_Decorators.begin(); it != a_Decorators.end(); ++it) { - Pkt.WriteByte((it->GetType() << 4) | (it->GetRot() & 0xf)); - Pkt.WriteByte(it->GetPixelX()); - Pkt.WriteByte(it->GetPixelZ()); + ASSERT((it->GetPixelX() >= 0) && (it->GetPixelX() < 256)); + ASSERT((it->GetPixelZ() >= 0) && (it->GetPixelZ() < 256)); + Pkt.WriteByte(static_cast<Byte>((it->GetType() << 4) | static_cast<Byte>(it->GetRot() & 0xf))); + Pkt.WriteByte(static_cast<Byte>(it->GetPixelX())); + Pkt.WriteByte(static_cast<Byte>(it->GetPixelZ())); } } @@ -688,13 +694,14 @@ void cProtocol172::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decor void cProtocol172::SendMapInfo(int a_ID, unsigned int a_Scale) { ASSERT(m_State == 3); // In game mode? + ASSERT((a_Scale >= 0) && (a_Scale < 256)); cPacketizer Pkt(*this, 0x34); - Pkt.WriteVarInt(a_ID); + Pkt.WriteVarInt(static_cast<UInt32>(a_ID)); Pkt.WriteShort (2); Pkt.WriteByte(2); - Pkt.WriteByte(a_Scale); + Pkt.WriteByte(static_cast<Byte>(a_Scale)); } @@ -707,7 +714,7 @@ void cProtocol172::SendPickupSpawn(const cPickup & a_Pickup) { cPacketizer Pkt(*this, 0x0e); // Spawn Object packet - Pkt.WriteVarInt(a_Pickup.GetUniqueID()); + Pkt.WriteVarInt(static_cast<UInt32>(a_Pickup.GetUniqueID())); Pkt.WriteByte(2); // Type = Pickup Pkt.WriteFPInt(a_Pickup.GetPosX()); Pkt.WriteFPInt(a_Pickup.GetPosY()); @@ -763,7 +770,7 @@ void cProtocol172::SendEntityAnimation(const cEntity & a_Entity, char a_Animatio ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x0b); // Animation packet - Pkt.WriteVarInt(a_Entity.GetUniqueID()); + Pkt.WriteVarInt(static_cast<UInt32>(a_Entity.GetUniqueID())); Pkt.WriteChar(a_Animation); } @@ -865,7 +872,7 @@ void cProtocol172::SendPlayerMaxSpeed(void) { Pkt.WriteShort(1); // Modifier count Pkt.WriteInt64(0x662a6b8dda3e4c1c); - Pkt.WriteInt64(0x881396ea6097278d); // UUID of the modifier + Pkt.WriteInt64(static_cast<Int64>(0x881396ea6097278d)); // UUID of the modifier Pkt.WriteDouble(Player->GetSprintingMaxSpeed() - Player->GetNormalMaxSpeed()); Pkt.WriteByte(2); } @@ -960,10 +967,11 @@ void cProtocol172::SendPluginMessage(const AString & a_Channel, const AString & void cProtocol172::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID) { ASSERT(m_State == 3); // In game mode? + ASSERT((a_EffectID >= 0) && (a_EffectID < 256)); cPacketizer Pkt(*this, 0x1e); Pkt.WriteInt(a_Entity.GetUniqueID()); - Pkt.WriteByte(a_EffectID); + Pkt.WriteByte(static_cast<Byte>(a_EffectID)); } @@ -1009,13 +1017,14 @@ void cProtocol172::SendExperience (void) void cProtocol172::SendExperienceOrb(const cExpOrb & a_ExpOrb) { ASSERT(m_State == 3); // In game mode? + ASSERT((a_ExpOrb.GetReward() >= 0) && (a_ExpOrb.GetReward() < SHRT_MAX)); cPacketizer Pkt(*this, 0x11); - Pkt.WriteVarInt(a_ExpOrb.GetUniqueID()); + Pkt.WriteVarInt(static_cast<UInt32>(a_ExpOrb.GetUniqueID())); Pkt.WriteFPInt(a_ExpOrb.GetPosX()); Pkt.WriteFPInt(a_ExpOrb.GetPosY()); Pkt.WriteFPInt(a_ExpOrb.GetPosZ()); - Pkt.WriteShort(a_ExpOrb.GetReward()); + Pkt.WriteShort(static_cast<short>(a_ExpOrb.GetReward())); } @@ -1060,7 +1069,7 @@ void cProtocol172::SendDisplayObjective(const AString & a_Objective, cScoreboard ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x3d); - Pkt.WriteByte((int) a_Display); + Pkt.WriteByte(static_cast<Byte>(a_Display)); Pkt.WriteString(a_Objective); } @@ -1074,11 +1083,11 @@ void cProtocol172::SendSoundEffect(const AString & a_SoundName, double a_X, doub cPacketizer Pkt(*this, 0x29); // Sound Effect packet Pkt.WriteString(a_SoundName); - Pkt.WriteInt((int)(a_X * 8.0)); - Pkt.WriteInt((int)(a_Y * 8.0)); - Pkt.WriteInt((int)(a_Z * 8.0)); + Pkt.WriteInt(static_cast<int>(a_X * 8.0)); + Pkt.WriteInt(static_cast<int>(a_Y * 8.0)); + Pkt.WriteInt(static_cast<int>(a_Z * 8.0)); Pkt.WriteFloat(a_Volume); - Pkt.WriteByte((Byte)(a_Pitch * 63)); + Pkt.WriteByte(static_cast<Byte>(a_Pitch * 63)); } @@ -1088,11 +1097,12 @@ void cProtocol172::SendSoundEffect(const AString & a_SoundName, double a_X, doub void cProtocol172::SendSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) { ASSERT(m_State == 3); // In game mode? + ASSERT((a_SrcY >= 0) && (a_SrcY < 256)); cPacketizer Pkt(*this, 0x28); // Effect packet Pkt.WriteInt(a_EffectID); Pkt.WriteInt(a_SrcX); - Pkt.WriteByte(a_SrcY); + Pkt.WriteByte(static_cast<Byte>(a_SrcY)); Pkt.WriteInt(a_SrcZ); Pkt.WriteInt(a_Data); Pkt.WriteBool(false); @@ -1107,17 +1117,17 @@ void cProtocol172::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock) ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x0e); // Spawn Object packet - Pkt.WriteVarInt(a_FallingBlock.GetUniqueID()); + Pkt.WriteVarInt(static_cast<UInt32>(a_FallingBlock.GetUniqueID())); Pkt.WriteByte(70); // Falling block Pkt.WriteFPInt(a_FallingBlock.GetPosX()); Pkt.WriteFPInt(a_FallingBlock.GetPosY()); Pkt.WriteFPInt(a_FallingBlock.GetPosZ()); Pkt.WriteByteAngle(a_FallingBlock.GetYaw()); Pkt.WriteByteAngle(a_FallingBlock.GetPitch()); - Pkt.WriteInt(((int)a_FallingBlock.GetBlockType()) | (((int)a_FallingBlock.GetBlockMeta()) << 16)); // Or 0x10 - Pkt.WriteShort((short)(a_FallingBlock.GetSpeedX() * 400)); - Pkt.WriteShort((short)(a_FallingBlock.GetSpeedY() * 400)); - Pkt.WriteShort((short)(a_FallingBlock.GetSpeedZ() * 400)); + Pkt.WriteInt((static_cast<int>(a_FallingBlock.GetBlockType()) | ((static_cast<int>(a_FallingBlock.GetBlockMeta()) << 16)))); + Pkt.WriteShort(static_cast<short>(a_FallingBlock.GetSpeedX() * 400)); + Pkt.WriteShort(static_cast<short>(a_FallingBlock.GetSpeedY() * 400)); + Pkt.WriteShort(static_cast<short>(a_FallingBlock.GetSpeedZ() * 400)); } @@ -1129,7 +1139,7 @@ void cProtocol172::SendSpawnMob(const cMonster & a_Mob) ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x0f); // Spawn Mob packet - Pkt.WriteVarInt(a_Mob.GetUniqueID()); + Pkt.WriteVarInt(static_cast<UInt32>(a_Mob.GetUniqueID())); Pkt.WriteByte((Byte)a_Mob.GetMobType()); Pkt.WriteFPInt(a_Mob.GetPosX()); Pkt.WriteFPInt(a_Mob.GetPosY()); @@ -1137,9 +1147,9 @@ void cProtocol172::SendSpawnMob(const cMonster & a_Mob) Pkt.WriteByteAngle(a_Mob.GetPitch()); Pkt.WriteByteAngle(a_Mob.GetHeadYaw()); Pkt.WriteByteAngle(a_Mob.GetYaw()); - Pkt.WriteShort((short)(a_Mob.GetSpeedX() * 400)); - Pkt.WriteShort((short)(a_Mob.GetSpeedY() * 400)); - Pkt.WriteShort((short)(a_Mob.GetSpeedZ() * 400)); + Pkt.WriteShort(static_cast<short>(a_Mob.GetSpeedX() * 400)); + Pkt.WriteShort(static_cast<short>(a_Mob.GetSpeedY() * 400)); + Pkt.WriteShort(static_cast<short>(a_Mob.GetSpeedZ() * 400)); Pkt.WriteEntityMetadata(a_Mob); Pkt.WriteByte(0x7f); // Metadata terminator } @@ -1153,8 +1163,8 @@ void cProtocol172::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0xe); // Spawn Object packet - Pkt.WriteVarInt(a_Entity.GetUniqueID()); - Pkt.WriteByte(a_ObjectType); + Pkt.WriteVarInt(static_cast<UInt32>(a_Entity.GetUniqueID())); + Pkt.WriteChar(a_ObjectType); Pkt.WriteFPInt(a_Entity.GetPosX()); Pkt.WriteFPInt(a_Entity.GetPosY()); Pkt.WriteFPInt(a_Entity.GetPosZ()); @@ -1163,9 +1173,9 @@ void cProtocol172::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, Pkt.WriteInt(a_ObjectData); if (a_ObjectData != 0) { - Pkt.WriteShort((short)(a_Entity.GetSpeedX() * 400)); - Pkt.WriteShort((short)(a_Entity.GetSpeedY() * 400)); - Pkt.WriteShort((short)(a_Entity.GetSpeedZ() * 400)); + Pkt.WriteShort(static_cast<short>(a_Entity.GetSpeedX() * 400)); + Pkt.WriteShort(static_cast<short>(a_Entity.GetSpeedY() * 400)); + Pkt.WriteShort(static_cast<short>(a_Entity.GetSpeedZ() * 400)); } } @@ -1178,8 +1188,8 @@ void cProtocol172::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTyp ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0xe); // Spawn Object packet - Pkt.WriteVarInt(a_Vehicle.GetUniqueID()); - Pkt.WriteByte(a_VehicleType); + Pkt.WriteVarInt(static_cast<UInt32>(a_Vehicle.GetUniqueID())); + Pkt.WriteChar(a_VehicleType); Pkt.WriteFPInt(a_Vehicle.GetPosX()); Pkt.WriteFPInt(a_Vehicle.GetPosY()); Pkt.WriteFPInt(a_Vehicle.GetPosZ()); @@ -1188,9 +1198,9 @@ void cProtocol172::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTyp Pkt.WriteInt(a_VehicleSubType); if (a_VehicleSubType != 0) { - Pkt.WriteShort((short)(a_Vehicle.GetSpeedX() * 400)); - Pkt.WriteShort((short)(a_Vehicle.GetSpeedY() * 400)); - Pkt.WriteShort((short)(a_Vehicle.GetSpeedZ() * 400)); + Pkt.WriteShort(static_cast<short>(a_Vehicle.GetSpeedX() * 400)); + Pkt.WriteShort(static_cast<short>(a_Vehicle.GetSpeedY() * 400)); + Pkt.WriteShort(static_cast<short>(a_Vehicle.GetSpeedZ() * 400)); } } @@ -1211,7 +1221,7 @@ void cProtocol172::SendStatistics(const cStatManager & a_Manager) const AString & StatName = cStatInfo::GetName((eStatistic) i); Pkt.WriteString(StatName); - Pkt.WriteVarInt(Value); + Pkt.WriteVarInt(static_cast<UInt32>(Value)); } } @@ -1224,7 +1234,7 @@ void cProtocol172::SendTabCompletionResults(const AStringVector & a_Results) ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x3a); // Tab-Complete packet - Pkt.WriteVarInt((int)a_Results.size()); + Pkt.WriteVarInt(static_cast<UInt32>(a_Results.size())); for (AStringVector::const_iterator itr = a_Results.begin(), end = a_Results.end(); itr != end; ++itr) { @@ -1309,7 +1319,7 @@ void cProtocol172::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity) cPacketizer Pkt(*this, 0x35); // Update tile entity packet Pkt.WriteInt(a_BlockEntity.GetPosX()); - Pkt.WriteShort(a_BlockEntity.GetPosY()); + Pkt.WriteShort(static_cast<short>(a_BlockEntity.GetPosY())); Pkt.WriteInt(a_BlockEntity.GetPosZ()); Byte Action = 0; @@ -1389,7 +1399,7 @@ void cProtocol172::SendWholeInventory(const cWindow & a_Window) cPacketizer Pkt(*this, 0x30); // Window Items packet Pkt.WriteChar(a_Window.GetWindowID()); - Pkt.WriteShort(a_Window.GetNumSlots()); + Pkt.WriteShort(static_cast<short>(a_Window.GetNumSlots())); cItems Slots; a_Window.GetSlots(*(m_Client->GetPlayer()), Slots); for (cItems::const_iterator itr = Slots.begin(), end = Slots.end(); itr != end; ++itr) @@ -1426,9 +1436,9 @@ void cProtocol172::SendWindowOpen(const cWindow & a_Window) cPacketizer Pkt(*this, 0x2d); Pkt.WriteChar(a_Window.GetWindowID()); - Pkt.WriteChar(a_Window.GetWindowType()); + Pkt.WriteChar(static_cast<char>(a_Window.GetWindowType())); Pkt.WriteString(a_Window.GetWindowTitle()); - Pkt.WriteChar(a_Window.GetNumNonInventorySlots()); + Pkt.WriteChar(static_cast<char>(a_Window.GetNumNonInventorySlots())); Pkt.WriteBool(true); if (a_Window.GetWindowType() == cWindow::wtAnimalChest) { @@ -1505,7 +1515,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, size_t a_Size) break; } cByteBuffer bb(PacketLen + 1); - VERIFY(m_ReceivedData.ReadToByteBuffer(bb, (int)PacketLen)); + VERIFY(m_ReceivedData.ReadToByteBuffer(bb, static_cast<size_t>(PacketLen))); m_ReceivedData.CommitRead(); UInt32 PacketType; @@ -1524,7 +1534,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, size_t a_Size) AString PacketData; bb.ReadAll(PacketData); bb.ResetRead(); - bb.ReadVarInt(PacketType); + bb.ReadVarInt(PacketType); // We have already read the packet type once, it will be there again. ASSERT(PacketData.size() > 0); // We have written an extra NUL, so there had to be at least one byte read PacketData.resize(PacketData.size() - 1); AString PacketDataHex; @@ -1748,21 +1758,30 @@ void cProtocol172::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffe { short EncKeyLength, EncNonceLength; a_ByteBuffer.ReadBEShort(EncKeyLength); + if ((EncKeyLength < 0) || (EncKeyLength > MAX_ENC_LEN)) + { + LOGD("Invalid Encryption Key length: %d. Kicking client.", EncKeyLength); + m_Client->Kick("Invalid EncKeyLength"); + return; + } AString EncKey; - if (!a_ByteBuffer.ReadString(EncKey, EncKeyLength)) + if (!a_ByteBuffer.ReadString(EncKey, static_cast<size_t>(EncKeyLength))) { return; } - a_ByteBuffer.ReadBEShort(EncNonceLength); - AString EncNonce; - if (!a_ByteBuffer.ReadString(EncNonce, EncNonceLength)) + if (!a_ByteBuffer.ReadBEShort(EncNonceLength)) { return; } - if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN)) + if ((EncNonceLength < 0) || (EncNonceLength > MAX_ENC_LEN)) + { + LOGD("Invalid Encryption Nonce length: %d. Kicking client.", EncNonceLength); + m_Client->Kick("Invalid EncNonceLength"); + return; + } + AString EncNonce; + if (!a_ByteBuffer.ReadString(EncNonce, static_cast<size_t>(EncNonceLength))) { - LOGD("Too long encryption"); - m_Client->Kick("Hacked client"); return; } @@ -1851,7 +1870,7 @@ void cProtocol172::HandlePacketAnimation(cByteBuffer & a_ByteBuffer) void cProtocol172::HandlePacketBlockDig(cByteBuffer & a_ByteBuffer) { - HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Status); + HANDLE_READ(a_ByteBuffer, ReadChar, char, Status); HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockX); HANDLE_READ(a_ByteBuffer, ReadByte, Byte, BlockY); HANDLE_READ(a_ByteBuffer, ReadBEInt, int, BlockZ); @@ -2069,7 +2088,7 @@ void cProtocol172::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) if (Length + 1 != (int)a_ByteBuffer.GetReadableSpace()) { LOGD("Invalid plugin message packet, payload length doesn't match packet length (exp %d, got %d)", - (int)a_ByteBuffer.GetReadableSpace() - 1, Length + static_cast<int>(a_ByteBuffer.GetReadableSpace()) - 1, Length ); return; } @@ -2083,7 +2102,7 @@ void cProtocol172::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) // Read the plugin message and relay to clienthandle: AString Data; - if (!a_ByteBuffer.ReadString(Data, Length)) + if (!a_ByteBuffer.ReadString(Data, static_cast<size_t>(Length))) { return; } @@ -2239,6 +2258,7 @@ void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const { if (a_Channel == "MC|AdvCdm") { + size_t BeginningSpace = a_ByteBuffer.GetReadableSpace(); HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Mode); switch (Mode) { @@ -2262,13 +2282,23 @@ void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const return; } } // switch (Mode) + + // Read the remainder of the packet (Vanilla sometimes sends bogus data at the end of the packet; #1692): + size_t BytesRead = BeginningSpace - a_ByteBuffer.GetReadableSpace(); + if (BytesRead < static_cast<size_t>(a_PayloadLength)) + { + LOGD("Protocol 1.7: Skipping garbage data at the end of a vanilla MC|AdvCdm packet, %u bytes", + static_cast<unsigned>(a_PayloadLength - BytesRead) + ); + a_ByteBuffer.SkipRead(static_cast<size_t>(a_PayloadLength) - BytesRead); + } return; } else if (a_Channel == "MC|Brand") { // Read the client's brand: AString Brand; - if (a_ByteBuffer.ReadString(Brand, a_PayloadLength)) + if (a_ByteBuffer.ReadString(Brand, static_cast<size_t>(a_PayloadLength))) { m_Client->SetClientBrand(Brand); } @@ -2287,7 +2317,7 @@ void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const else if (a_Channel == "MC|ItemName") { AString ItemName; - if (a_ByteBuffer.ReadString(ItemName, a_PayloadLength)) + if (a_ByteBuffer.ReadString(ItemName, static_cast<size_t>(a_PayloadLength))) { m_Client->HandleAnvilItemName(ItemName); } @@ -2303,7 +2333,7 @@ void cProtocol172::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const // Read the payload and send it through to the clienthandle: AString Message; - VERIFY(a_ByteBuffer.ReadString(Message, a_PayloadLength)); + VERIFY(a_ByteBuffer.ReadString(Message, static_cast<size_t>(a_PayloadLength))); m_Client->HandlePluginMessage(a_Channel, Message); } @@ -2363,7 +2393,7 @@ bool cProtocol172::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item) // Read the metadata AString Metadata; - if (!a_ByteBuffer.ReadString(Metadata, MetadataLength)) + if (!a_ByteBuffer.ReadString(Metadata, static_cast<size_t>(MetadataLength))) { return false; } @@ -2532,7 +2562,7 @@ void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item) } WriteShort(ItemType); - WriteByte (a_Item.m_ItemCount); + WriteChar (a_Item.m_ItemCount); WriteShort(a_Item.m_ItemDamage); if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty() && (a_Item.m_ItemType != E_ITEM_FIREWORK_ROCKET) && (a_Item.m_ItemType != E_ITEM_FIREWORK_STAR)) @@ -2692,7 +2722,7 @@ void cProtocol172::cPacketizer::WriteBlockEntity(const cBlockEntity & a_BlockEnt void cProtocol172::cPacketizer::WriteByteAngle(double a_Angle) { - WriteByte((char)(255 * a_Angle / 360)); + WriteChar(static_cast<char>(255 * a_Angle / 360)); } @@ -2835,7 +2865,7 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) case mtCreeper: { WriteByte(0x10); - WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : -1); + WriteByte(((const cCreeper &)a_Mob).IsBlowing() ? 1 : 0); WriteByte(0x11); WriteByte(((const cCreeper &)a_Mob).IsCharged() ? 1 : 0); break; @@ -2904,15 +2934,14 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) WriteByte(0x13); WriteByte(Wolf.IsBegging() ? 1 : 0); WriteByte(0x14); - WriteByte(Wolf.GetCollarColor()); + WriteByte(static_cast<Byte>(Wolf.GetCollarColor())); break; } case mtSheep: { WriteByte(0x10); - Byte SheepMetadata = 0; - SheepMetadata = ((const cSheep &)a_Mob).GetFurColor(); + Byte SheepMetadata = static_cast<Byte>(((const cSheep &)a_Mob).GetFurColor() & 0x0f); if (((const cSheep &)a_Mob).IsSheared()) { SheepMetadata |= 0x10; @@ -2949,7 +2978,7 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) case mtWither: { WriteByte(0x54); // Int at index 20 - WriteInt(((const cWither &)a_Mob).GetWitherInvulnerableTicks()); + WriteInt(static_cast<int>(((const cWither &)a_Mob).GetWitherInvulnerableTicks())); WriteByte(0x66); // Float at index 6 WriteFloat((float)(a_Mob.GetHealth())); break; @@ -2958,14 +2987,14 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) case mtSlime: { WriteByte(0x10); - WriteByte(((const cSlime &)a_Mob).GetSize()); + WriteByte(static_cast<Byte>(((const cSlime &)a_Mob).GetSize())); break; } case mtMagmaCube: { WriteByte(0x10); - WriteByte(((const cMagmaCube &)a_Mob).GetSize()); + WriteByte(static_cast<Byte>(((const cMagmaCube &)a_Mob).GetSize())); break; } @@ -3004,7 +3033,7 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) WriteByte(0x50); // Int at index 16 WriteInt(Flags); WriteByte(0x13); // Byte at index 19 - WriteByte(Horse.GetHorseType()); + WriteByte(static_cast<Byte>(Horse.GetHorseType())); WriteByte(0x54); // Int at index 20 int Appearance = 0; Appearance = Horse.GetHorseColor(); @@ -3014,6 +3043,12 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) WriteInt(Horse.GetHorseArmour()); break; } + + default: + { + // No data to send for this mob + break; + } } // switch (a_Mob.GetType()) // Custom name: @@ -3066,7 +3101,7 @@ void cProtocol176::SendPlayerSpawn(const cPlayer & a_Player) { // Called to spawn another player for the client cPacketizer Pkt(*this, 0x0c); // Spawn Player packet - Pkt.WriteVarInt(a_Player.GetUniqueID()); + Pkt.WriteVarInt(static_cast<UInt32>(a_Player.GetUniqueID())); Pkt.WriteString(cMojangAPI::MakeUUIDDashed(a_Player.GetClientHandle()->GetUUID())); if (a_Player.HasCustomName()) { diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index ce580d73e..3c4e049bd 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -207,9 +207,9 @@ void cProtocol180::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockV Pkt.WriteVarInt((UInt32)a_Changes.size()); for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr) { - short Coords = (short) (itr->y | (itr->z << 8) | (itr->x << 12)); + short Coords = (short) (itr->m_RelY | (itr->m_RelZ << 8) | (itr->m_RelX << 12)); Pkt.WriteShort(Coords); - Pkt.WriteVarInt((itr->BlockType & 0xFFF) << 4 | (itr->BlockMeta & 0xF)); + Pkt.WriteVarInt((itr->m_BlockType & 0xFFF) << 4 | (itr->m_BlockMeta & 0xF)); } // for itr - a_Changes[] } @@ -1723,7 +1723,11 @@ void cProtocol180::AddReceivedData(const char * a_Data, size_t a_Size) { // Decompress the data: AString CompressedData; - m_ReceivedData.ReadString(CompressedData, CompressedSize); + if (!m_ReceivedData.ReadString(CompressedData, CompressedSize)) + { + m_Client->Kick("Compression failure"); + return; + } InflateString(CompressedData.data(), CompressedSize, UncompressedData); PacketLen = UncompressedData.size(); } @@ -1765,7 +1769,7 @@ void cProtocol180::AddReceivedData(const char * a_Data, size_t a_Size) AString PacketData; bb.ReadAll(PacketData); bb.ResetRead(); - bb.ReadVarInt(PacketType); + bb.ReadVarInt(PacketType); // We have already read the packet type once, it will be there again ASSERT(PacketData.size() > 0); // We have written an extra NUL, so there had to be at least one byte read PacketData.resize(PacketData.size() - 1); AString PacketDataHex; @@ -2327,6 +2331,16 @@ void cProtocol180::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) if (Channel.substr(0, 3) == "MC|") { HandleVanillaPluginMessage(a_ByteBuffer, Channel); + + // Skip any unread data (vanilla sometimes sends garbage at the end of a packet; #1692): + if (a_ByteBuffer.GetReadableSpace() > 1) + { + LOGD("Protocol 1.8: Skipping garbage data at the end of a vanilla PluginMessage packet, %u bytes", + static_cast<unsigned>(a_ByteBuffer.GetReadableSpace() - 1) + ); + a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace() - 1); + } + return; } diff --git a/src/Root.cpp b/src/Root.cpp index 9f8ffeeff..eaacf3608 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -47,6 +47,7 @@ cRoot::cRoot(void) : m_FurnaceRecipe(nullptr), m_WebAdmin(nullptr), m_PluginManager(nullptr), + m_MojangAPI(nullptr), m_bStop(false), m_bRestart(false) { @@ -135,8 +136,9 @@ void cRoot::Start(void) } LOG("Starting server..."); + m_MojangAPI = new cMojangAPI; bool ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true); - m_MojangAPI.Start(IniFile, ShouldAuthenticate); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init + m_MojangAPI->Start(IniFile, ShouldAuthenticate); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init if (!m_Server->InitServer(IniFile, ShouldAuthenticate)) { IniFile.WriteFile("settings.ini"); @@ -149,7 +151,7 @@ void cRoot::Start(void) LOGD("Loading settings..."); m_RankManager.reset(new cRankManager()); - m_RankManager->Initialize(m_MojangAPI); + m_RankManager->Initialize(*m_MojangAPI); m_CraftingRecipes = new cCraftingRecipes; m_FurnaceRecipe = new cFurnaceRecipe(); @@ -196,7 +198,7 @@ void cRoot::Start(void) } #endif - LOG("Startup complete, took %ld ms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count())); + LOG("Startup complete, took %ldms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count())); #ifdef _WIN32 EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button #endif @@ -213,21 +215,28 @@ void cRoot::Start(void) // Stop the server: m_WebAdmin->Stop(); + LOG("Shutting down server..."); m_Server->Shutdown(); + delete m_MojangAPI; m_MojangAPI = nullptr; + LOGD("Shutting down deadlock detector..."); dd.Stop(); + LOGD("Stopping world threads..."); StopWorlds(); + LOGD("Stopping authenticator..."); m_Authenticator.Stop(); LOGD("Freeing MonsterConfig..."); delete m_MonsterConfig; m_MonsterConfig = nullptr; delete m_WebAdmin; m_WebAdmin = nullptr; + LOGD("Unloading recipes..."); delete m_FurnaceRecipe; m_FurnaceRecipe = nullptr; delete m_CraftingRecipes; m_CraftingRecipes = nullptr; + LOGD("Unloading worlds..."); UnloadWorlds(); @@ -238,6 +247,7 @@ void cRoot::Start(void) LOG("Cleaning up..."); delete m_Server; m_Server = nullptr; + LOG("Shutdown successful!"); } @@ -629,7 +639,7 @@ bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallbac bool cRoot::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback) { - for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end();itr++) + for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) { if (itr->second->DoWithPlayerByUUID(a_PlayerUUID, a_Callback)) { diff --git a/src/Root.h b/src/Root.h index 2c512a5df..fdaf444bd 100644 --- a/src/Root.h +++ b/src/Root.h @@ -86,7 +86,7 @@ public: cWebAdmin * GetWebAdmin (void) { return m_WebAdmin; } // tolua_export cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export cAuthenticator & GetAuthenticator (void) { return m_Authenticator; } - cMojangAPI & GetMojangAPI (void) { return m_MojangAPI; } + cMojangAPI & GetMojangAPI (void) { return *m_MojangAPI; } cRankManager * GetRankManager (void) { return m_RankManager.get(); } /** Queues a console command for execution through the cServer class. @@ -191,7 +191,7 @@ private: cWebAdmin * m_WebAdmin; cPluginManager * m_PluginManager; cAuthenticator m_Authenticator; - cMojangAPI m_MojangAPI; + cMojangAPI * m_MojangAPI; std::unique_ptr<cRankManager> m_RankManager; diff --git a/src/Server.cpp b/src/Server.cpp index 3eaf6e096..4dbe59ac6 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -764,7 +764,7 @@ void cServer::cNotifyWriteThread::Execute(void) while (!m_ShouldTerminate) { cCSLock Lock(m_CS); - while (m_Clients.size() == 0) + while (m_Clients.empty()) { cCSUnlock Unlock(Lock); m_Event.Wait(); diff --git a/src/SetChunkData.cpp b/src/SetChunkData.cpp index e335176a8..5a0bea980 100644 --- a/src/SetChunkData.cpp +++ b/src/SetChunkData.cpp @@ -132,7 +132,7 @@ void cSetChunkData::RemoveInvalidBlockEntities(void) ItemTypeToString(WorldBlockType).c_str(), WorldBlockType ); cBlockEntityList::iterator itr2 = itr; - itr2++; + ++itr2; delete *itr; m_BlockEntities.erase(itr); itr = itr2; diff --git a/src/Simulator/DelayedFluidSimulator.cpp b/src/Simulator/DelayedFluidSimulator.cpp index dc8dffe2d..0973962b6 100644 --- a/src/Simulator/DelayedFluidSimulator.cpp +++ b/src/Simulator/DelayedFluidSimulator.cpp @@ -130,7 +130,7 @@ void cDelayedFluidSimulator::Simulate(float a_Dt) -void cDelayedFluidSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +void cDelayedFluidSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { void * ChunkDataRaw = (m_FluidBlock == E_BLOCK_WATER) ? a_Chunk->GetWaterSimulatorData() : a_Chunk->GetLavaSimulatorData(); cDelayedFluidSimulatorChunkData * ChunkData = (cDelayedFluidSimulatorChunkData *)ChunkDataRaw; diff --git a/src/Simulator/DelayedFluidSimulator.h b/src/Simulator/DelayedFluidSimulator.h index 8a6c26c7a..e3182812d 100644 --- a/src/Simulator/DelayedFluidSimulator.h +++ b/src/Simulator/DelayedFluidSimulator.h @@ -56,7 +56,7 @@ public: // cSimulator overrides: virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; virtual void Simulate(float a_Dt) override; - virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; + virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; virtual cFluidSimulatorData * CreateChunkData(void) override { return new cDelayedFluidSimulatorChunkData(m_TickDelay); } protected: diff --git a/src/Simulator/FireSimulator.cpp b/src/Simulator/FireSimulator.cpp index 75ebefcf7..8456ed11d 100644 --- a/src/Simulator/FireSimulator.cpp +++ b/src/Simulator/FireSimulator.cpp @@ -88,11 +88,11 @@ cFireSimulator::~cFireSimulator() -void cFireSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +void cFireSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { cCoordWithIntList & Data = a_Chunk->GetFireSimulatorData(); - int NumMSecs = (int)a_Dt; + int NumMSecs = static_cast<int>(a_Dt.count()); for (cCoordWithIntList::iterator itr = Data.begin(); itr != Data.end();) { int x = itr->x; diff --git a/src/Simulator/FireSimulator.h b/src/Simulator/FireSimulator.h index 9ccc3ef4f..a40e29565 100644 --- a/src/Simulator/FireSimulator.h +++ b/src/Simulator/FireSimulator.h @@ -23,7 +23,7 @@ public: ~cFireSimulator(); virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used - virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; + virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override; diff --git a/src/Simulator/FloodyFluidSimulator.cpp b/src/Simulator/FloodyFluidSimulator.cpp index 37d58307b..bcd083294 100644 --- a/src/Simulator/FloodyFluidSimulator.cpp +++ b/src/Simulator/FloodyFluidSimulator.cpp @@ -105,7 +105,7 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re } // Spread to the neighbors: if (SpreadFurther && (NewMeta < 8)) - { + { SpreadXZ(a_Chunk, a_RelX, a_RelY, a_RelZ, NewMeta); } // If source creation is on, check for it here: diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp index 8a56287a8..1cc5340dd 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.cpp +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -93,7 +93,7 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, ), SimulatedPlayerToggleableBlocks.end()); - auto & RepeatersDelayList = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_RepeatersDelayList; + auto & RepeatersDelayList = ((cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData())->m_RepeatersDelayList; RepeatersDelayList.erase(std::remove_if(RepeatersDelayList.begin(), RepeatersDelayList.end(), [RelX, a_BlockY, RelZ, Block](const sRepeatersDelayList & itr) { return itr.a_RelBlockPos.Equals(Vector3i(RelX, a_BlockY, RelZ)) && (Block != E_BLOCK_REDSTONE_REPEATER_ON) && (Block != E_BLOCK_REDSTONE_REPEATER_OFF); @@ -145,7 +145,7 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, -void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +void cIncrementalRedstoneSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { m_RedstoneSimulatorChunkData = (cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulatorChunkData *)a_Chunk->GetRedstoneSimulatorData(); if (m_RedstoneSimulatorChunkData == nullptr) @@ -1488,62 +1488,92 @@ bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_RelBlockX, int a_Rel for (const auto & itr : *m_PoweredBlocks) { - if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; } + if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + { + continue; + } switch (a_Meta & 0x3) { case 0x0: { // Flip the coords to check the back of the repeater - if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1)))) { return true; } + if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1)))) + { + return true; + } break; } case 0x1: { - if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ)))) { return true; } + if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ)))) + { + return true; + } break; } case 0x2: { - if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1)))) { return true; } + if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1)))) + { + return true; + } break; } case 0x3: { - if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ)))) { return true; } + if (itr.a_SourcePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ)))) + { + return true; + } break; } } - } + } // for itr - m_PoweredBlocks[] for (const auto & itr : *m_LinkedPoweredBlocks) { - if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; } + if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + { + continue; + } switch (a_Meta & 0x3) { case 0x0: { - if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1)))) { return true; } + if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1)))) + { + return true; + } break; } case 0x1: { - if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ)))) { return true; } + if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ)))) + { + return true; + } break; } case 0x2: { - if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1)))) { return true; } + if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1)))) + { + return true; + } break; } case 0x3: { - if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ)))) { return true; } + if (itr.a_MiddlePos.Equals(AdjustRelativeCoords(Vector3i(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ)))) + { + return true; + } break; } } - } + } // for itr - m_LinkedPoweredBlocks[] return false; // Couldn't find power source behind repeater } @@ -1559,10 +1589,13 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB case 0x0: case 0x2: { - // Check if eastern(right) neighbor is a powered on repeater who is facing us + // Check if eastern (right) neighbor is a powered on repeater who is facing us BLOCKTYPE Block = 0; NIBBLETYPE OtherRepeaterDir = 0; - if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) // Is right neighbor a powered repeater? + if ( + m_Chunk->UnboundedRelGetBlock(a_RelBlockX + 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) && + (Block == E_BLOCK_REDSTONE_REPEATER_ON) + ) { if ((OtherRepeaterDir & 0x03) == 0x3) { @@ -1571,7 +1604,10 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB } // Check if western(left) neighbor is a powered on repeater who is facing us - if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) + if ( + m_Chunk->UnboundedRelGetBlock(a_RelBlockX - 1, a_RelBlockY, a_RelBlockZ, Block, OtherRepeaterDir) && + (Block == E_BLOCK_REDSTONE_REPEATER_ON) + ) { if ((OtherRepeaterDir & 0x03) == 0x1) { @@ -1590,7 +1626,10 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB BLOCKTYPE Block = 0; NIBBLETYPE OtherRepeaterDir = 0; - if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) + if ( + m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ + 1, Block, OtherRepeaterDir) && + (Block == E_BLOCK_REDSTONE_REPEATER_ON) + ) { if ((OtherRepeaterDir & 0x30) == 0x00) { @@ -1599,7 +1638,10 @@ bool cIncrementalRedstoneSimulator::IsRepeaterLocked(int a_RelBlockX, int a_RelB } // Check if northern(up) neighbor is a powered on repeater who is facing us - if (m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, Block, OtherRepeaterDir) && (Block == E_BLOCK_REDSTONE_REPEATER_ON)) + if ( + m_Chunk->UnboundedRelGetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ - 1, Block, OtherRepeaterDir) && + (Block == E_BLOCK_REDSTONE_REPEATER_ON) + ) { if ((OtherRepeaterDir & 0x03) == 0x02) { @@ -1625,7 +1667,10 @@ bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_RelBlockX, int a_RelBl for (const auto & itr : *m_PoweredBlocks) { - if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; } + if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + { + continue; + } int X = a_RelBlockX, Z = a_RelBlockZ; AddFaceDirection(X, a_RelBlockY, Z, Face); @@ -1638,7 +1683,10 @@ bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_RelBlockX, int a_RelBl for (const auto & itr : *m_LinkedPoweredBlocks) { - if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) { continue; } + if (!itr.a_BlockPos.Equals(Vector3i(a_RelBlockX, a_RelBlockY, a_RelBlockZ))) + { + continue; + } int X = a_RelBlockX, Z = a_RelBlockZ; AddFaceDirection(X, a_RelBlockY, Z, Face); @@ -2074,9 +2122,11 @@ void cIncrementalRedstoneSimulator::SetSourceUnpowered(int a_RelSourceX, int a_R for (const auto & itr : BlocksPotentiallyUnpowered) { - if (!AreCoordsPowered(itr.x, itr.y, itr.z)) + auto Neighbour = a_Chunk->GetRelNeighborChunk(itr.x, itr.z); + if (!AreCoordsPowered(itr.x, itr.y, itr.z) && (Neighbour->GetBlock(itr) != E_BLOCK_REDSTONE_REPEATER_ON)) { - SetSourceUnpowered(itr.x, itr.y, itr.z, a_Chunk->GetRelNeighborChunk(itr.x, itr.z)); + // Repeaters time themselves with regards to unpowering; ensure we don't do it for them + SetSourceUnpowered(itr.x, itr.y, itr.z, Neighbour); } } } diff --git a/src/Simulator/IncrementalRedstoneSimulator.h b/src/Simulator/IncrementalRedstoneSimulator.h index 3d2962c08..43f0e89d0 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.h +++ b/src/Simulator/IncrementalRedstoneSimulator.h @@ -32,7 +32,7 @@ public: } virtual void Simulate(float a_Dt) override { UNUSED(a_Dt); } // not used - virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; + virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override { return IsRedstone(a_BlockType); } virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; diff --git a/src/Simulator/NoopRedstoneSimulator.h b/src/Simulator/NoopRedstoneSimulator.h index 4e05529f5..b8c797472 100644 --- a/src/Simulator/NoopRedstoneSimulator.h +++ b/src/Simulator/NoopRedstoneSimulator.h @@ -21,7 +21,7 @@ public: // ~cRedstoneNoopSimulator(); virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used - virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override + virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override { UNUSED(a_Dt); UNUSED(a_ChunkX); diff --git a/src/Simulator/SandSimulator.cpp b/src/Simulator/SandSimulator.cpp index dfbd3e458..497f81999 100644 --- a/src/Simulator/SandSimulator.cpp +++ b/src/Simulator/SandSimulator.cpp @@ -24,7 +24,7 @@ cSandSimulator::cSandSimulator(cWorld & a_World, cIniFile & a_IniFile) : -void cSandSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +void cSandSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { cSandSimulatorChunkData & ChunkData = a_Chunk->GetSandSimulatorData(); if (ChunkData.empty()) diff --git a/src/Simulator/SandSimulator.h b/src/Simulator/SandSimulator.h index 93b1de8e2..8fff659ed 100644 --- a/src/Simulator/SandSimulator.h +++ b/src/Simulator/SandSimulator.h @@ -18,7 +18,7 @@ public: // cSimulator overrides: virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used - virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; + virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override; /// Returns true if a falling-able block can start falling through the specified block type diff --git a/src/Simulator/Simulator.h b/src/Simulator/Simulator.h index f28a07e35..c8066edfd 100644 --- a/src/Simulator/Simulator.h +++ b/src/Simulator/Simulator.h @@ -24,7 +24,7 @@ public: virtual void Simulate(float a_Dt) = 0; /// Called in each tick for each chunk, a_Dt is the time passed since the last tick, in msec; direct access to chunk data available - virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) + virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { UNUSED(a_Dt); UNUSED(a_ChunkX); diff --git a/src/Simulator/SimulatorManager.cpp b/src/Simulator/SimulatorManager.cpp index 918bac7a1..e74642fc0 100644 --- a/src/Simulator/SimulatorManager.cpp +++ b/src/Simulator/SimulatorManager.cpp @@ -42,7 +42,7 @@ void cSimulatorManager::Simulate(float a_Dt) -void cSimulatorManager::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +void cSimulatorManager::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) { // m_Ticks has already been increased in Simulate() for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr) diff --git a/src/Simulator/SimulatorManager.h b/src/Simulator/SimulatorManager.h index 31a709316..b96f6ca84 100644 --- a/src/Simulator/SimulatorManager.h +++ b/src/Simulator/SimulatorManager.h @@ -33,7 +33,7 @@ public: void Simulate(float a_Dt); - void SimulateChunk(float a_DT, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk); + void SimulateChunk(std::chrono::milliseconds a_DT, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk); void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk); diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp index 5febf5d6c..a63525356 100644 --- a/src/StringUtils.cpp +++ b/src/StringUtils.cpp @@ -23,7 +23,7 @@ AString & AppendVPrintf(AString & str, const char * format, va_list args) ASSERT(format != nullptr); char buffer[2048]; - size_t len; + int len; #ifdef va_copy va_list argsCopy; va_copy(argsCopy, args); @@ -34,14 +34,14 @@ AString & AppendVPrintf(AString & str, const char * format, va_list args) // MS CRT provides secure printf that doesn't behave like in the C99 standard if ((len = _vsnprintf_s(buffer, ARRAYCOUNT(buffer), _TRUNCATE, format, argsCopy)) != -1) #else // _MSC_VER - if ((len = vsnprintf(buffer, ARRAYCOUNT(buffer), format, argsCopy)) < ARRAYCOUNT(buffer)) + if ((len = vsnprintf(buffer, ARRAYCOUNT(buffer), format, argsCopy)) < static_cast<int>(ARRAYCOUNT(buffer))) #endif // else _MSC_VER { // The result did fit into the static buffer #ifdef va_copy va_end(argsCopy); #endif - str.append(buffer, len); + str.append(buffer, static_cast<size_t>(len)); return str; } #ifdef va_copy @@ -51,7 +51,6 @@ AString & AppendVPrintf(AString & str, const char * format, va_list args) // The result did not fit into the static buffer, use a dynamic buffer: #ifdef _MSC_VER // for MS CRT, we need to calculate the result length - // MS doesn't have va_copy() and does nod need it at all len = _vscprintf(format, args); if (len == -1) { @@ -63,11 +62,11 @@ AString & AppendVPrintf(AString & str, const char * format, va_list args) #ifdef va_copy va_copy(argsCopy, args); #endif - std::vector<char> Buffer(len + 1); + std::vector<char> Buffer(static_cast<size_t>(len) + 1); #ifdef _MSC_VER - vsprintf_s((char *)&(Buffer.front()), Buffer.size(), format, argsCopy); + vsprintf_s(&(Buffer.front()), Buffer.size(), format, argsCopy); #else // _MSC_VER - vsnprintf((char *)&(Buffer.front()), Buffer.size(), format, argsCopy); + vsnprintf(&(Buffer.front()), Buffer.size(), format, argsCopy); #endif // else _MSC_VER str.append(&(Buffer.front()), Buffer.size() - 1); #ifdef va_copy @@ -85,7 +84,7 @@ AString & Printf(AString & str, const char * format, ...) str.clear(); va_list args; va_start(args, format); - std::string &retval = AppendVPrintf(str, format, args); + std::string & retval = AppendVPrintf(str, format, args); va_end(args); return retval; } @@ -108,11 +107,11 @@ AString Printf(const char * format, ...) -AString & AppendPrintf(AString &str, const char * format, ...) +AString & AppendPrintf(AString & dst, const char * format, ...) { va_list args; va_start(args, format); - std::string &retval = AppendVPrintf(str, format, args); + std::string & retval = AppendVPrintf(dst, format, args); va_end(args); return retval; } @@ -297,7 +296,6 @@ void ReplaceString(AString & iHayStack, const AString & iNeedle, const AString & -// Converts a stream of BE shorts into UTF-8 string; returns a ref to a_UTF8 AString & RawBEToUTF8(const char * a_RawData, size_t a_NumShorts, AString & a_UTF8) { a_UTF8.clear(); @@ -314,22 +312,22 @@ AString & RawBEToUTF8(const char * a_RawData, size_t a_NumShorts, AString & a_UT a_UTF8.push_back((char)(192 + c / 64)); a_UTF8.push_back((char)(128 + c % 64)); } - else if (c - 0xd800u < 0x800) + else if (c - 0xd800 < 0x800) { // Error, silently drop } else if (c < 0x10000) { - a_UTF8.push_back((char)(224 + c / 4096)); - a_UTF8.push_back((char)(128 + c / 64 % 64)); - a_UTF8.push_back((char)(128 + c % 64)); + a_UTF8.push_back(static_cast<char>(224 + c / 4096)); + a_UTF8.push_back(static_cast<char>(128 + (c / 64) % 64)); + a_UTF8.push_back(static_cast<char>(128 + c % 64)); } else if (c < 0x110000) { - a_UTF8.push_back((char)(240 + c / 262144)); - a_UTF8.push_back((char)(128 + c / 4096 % 64)); - a_UTF8.push_back((char)(128 + c / 64 % 64)); - a_UTF8.push_back((char)(128 + c % 64)); + a_UTF8.push_back(static_cast<char>(240 + c / 262144)); + a_UTF8.push_back(static_cast<char>(128 + (c / 4096) % 64)); + a_UTF8.push_back(static_cast<char>(128 + (c / 64) % 64)); + a_UTF8.push_back(static_cast<char>(128 + c % 64)); } else { @@ -382,7 +380,7 @@ Notice from the original file: -static const char trailingBytesForUTF8[256] = +static const Byte trailingBytesForUTF8[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -572,18 +570,18 @@ AString & CreateHexDump(AString & a_Out, const void * a_Data, size_t a_Size, siz int Count = sprintf(line, "%08x:", (unsigned)i); #endif // Remove the terminating nullptr / leftover garbage in line, after the sprintf-ed value - memset(line + Count, 32, sizeof(line) - Count); + memset(line + Count, 32, sizeof(line) - static_cast<size_t>(Count)); p = line + 10; q = p + 2 + a_BytesPerLine * 3 + 1; for (size_t j = 0; j < k; j++) { - unsigned char c = ((unsigned char *)a_Data)[i + j]; + Byte c = (reinterpret_cast<const Byte *>(a_Data))[i + j]; p[0] = HEX(c >> 4); p[1] = HEX(c & 0xf); p[2] = ' '; if (c >= ' ') { - q[0] = (char)c; + q[0] = static_cast<char>(c); } else { @@ -708,7 +706,7 @@ AString URLDecode(const AString & a_String) res.push_back(ch); continue; } - res.push_back((hi << 4) | lo); + res.push_back(static_cast<char>((hi << 4) | lo)); i += 2; } // for i - a_String[] return res; @@ -767,7 +765,8 @@ AString Base64Decode(const AString & a_Base64String) { AString res; size_t i, len = a_Base64String.size(); - int o, c; + size_t o; + int c; res.resize((len * 4) / 3 + 5, 0); // Approximate the upper bound on the result length for (o = 0, i = 0; i < len; i++) { @@ -850,7 +849,7 @@ AString Base64Encode(const AString & a_Input) short GetBEShort(const char * a_Mem) { const Byte * Bytes = (const Byte *)a_Mem; - return (Bytes[0] << 8) | Bytes[1]; + return static_cast<short>((Bytes[0] << 8) | Bytes[1]); } @@ -870,9 +869,9 @@ int GetBEInt(const char * a_Mem) void SetBEInt(char * a_Mem, Int32 a_Value) { a_Mem[0] = a_Value >> 24; - a_Mem[1] = (a_Value >> 16) & 0xff; - a_Mem[2] = (a_Value >> 8) & 0xff; - a_Mem[3] = a_Value & 0xff; + a_Mem[1] = static_cast<char>((a_Value >> 16) & 0xff); + a_Mem[2] = static_cast<char>((a_Value >> 8) & 0xff); + a_Mem[3] = static_cast<char>(a_Value & 0xff); } diff --git a/src/StringUtils.h b/src/StringUtils.h index 159e8ecac..bfe2a41fa 100644 --- a/src/StringUtils.h +++ b/src/StringUtils.h @@ -21,31 +21,40 @@ typedef std::list<AString> AStringList; -/** Add the formated string to the existing data in the string */ -extern AString & AppendVPrintf(AString & str, const char * format, va_list args) FORMATSTRING(2, 0); +/** Add the formated string to the existing data in the string. +Returns a_Dst. */ +extern AString & AppendVPrintf(AString & a_Dst, const char * format, va_list args) FORMATSTRING(2, 0); -/// Output the formatted text into the string -extern AString & Printf (AString & str, const char * format, ...) FORMATSTRING(2, 3); +/** Output the formatted text into the string. +Returns a_Dst. */ +extern AString & Printf (AString & a_Dst, const char * format, ...) FORMATSTRING(2, 3); -/// Output the formatted text into string, return string by value +/** Output the formatted text into string +Returns the formatted string by value. */ extern AString Printf(const char * format, ...) FORMATSTRING(1, 2); -/// Add the formatted string to the existing data in the string -extern AString & AppendPrintf (AString & str, const char * format, ...) FORMATSTRING(2, 3); +/** Add the formatted string to the existing data in the string. +Returns a_Dst */ +extern AString & AppendPrintf (AString & a_Dst, const char * format, ...) FORMATSTRING(2, 3); -/// Split the string at any of the listed delimiters, return as a stringvector +/** Split the string at any of the listed delimiters. +Return the splitted strings as a stringvector. */ extern AStringVector StringSplit(const AString & str, const AString & delim); -/// Split the string at any of the listed delimiters and trim each value, return as a stringvector +/** Split the string at any of the listed delimiters and trim each value. +Returns the splitted strings as a stringvector. */ extern AStringVector StringSplitAndTrim(const AString & str, const AString & delim); -/// Trime whitespace at both ends of the string +/** Trims whitespace at both ends of the string. +Returns a trimmed copy of the original string. */ extern AString TrimString(const AString & str); // tolua_export -/// In-place string conversion to uppercase; returns the same string +/** In-place string conversion to uppercase. +Returns the same string object. */ extern AString & InPlaceUppercase(AString & s); -/// In-place string conversion to lowercase; returns the same string +/** In-place string conversion to lowercase. +Returns the same string object. */ extern AString & InPlaceLowercase(AString & s); /** Returns an upper-cased copy of the string */ @@ -54,28 +63,30 @@ extern AString StrToUpper(const AString & s); /** Returns a lower-cased copy of the string */ extern AString StrToLower(const AString & s); -/// Case-insensitive string comparison; returns 0 if the strings are the same +/** Case-insensitive string comparison. +Returns 0 if the strings are the same, <0 if s1 < s2 and >0 if s1 > s2. */ extern int NoCaseCompare(const AString & s1, const AString & s2); // tolua_export -/// Case-insensitive string comparison that returns a rating of equal-ness between [0 - s1.length()] +/** Case-insensitive string comparison that returns a rating of equal-ness between [0 - s1.length()]. */ extern size_t RateCompareString(const AString & s1, const AString & s2); -/// Replaces *each* occurence of iNeedle in iHayStack with iReplaceWith +/** Replaces *each* occurence of iNeedle in iHayStack with iReplaceWith */ extern void ReplaceString(AString & iHayStack, const AString & iNeedle, const AString & iReplaceWith); // tolua_export -/// Converts a stream of BE shorts into UTF-8 string; returns a ref to a_UTF8 +/** Converts a stream of BE shorts into UTF-8 string; returns a_UTF8. */ extern AString & RawBEToUTF8(const char * a_RawData, size_t a_NumShorts, AString & a_UTF8); -/// Converts a UTF-8 string into a UTF-16 BE string; returns a ref to a_UTF16 +/** Converts a UTF-8 string into a UTF-16 BE string. */ extern AString UTF8ToRawBEUTF16(const char * a_UTF8, size_t a_UTF8Length); -/// Creates a nicely formatted HEX dump of the given memory block. Max a_BytesPerLine is 120 +/** Creates a nicely formatted HEX dump of the given memory block. +Max a_BytesPerLine is 120. */ extern AString & CreateHexDump(AString & a_Out, const void * a_Data, size_t a_Size, size_t a_BytesPerLine); -/// Returns a copy of a_Message with all quotes and backslashes escaped by a backslash +/** Returns a copy of a_Message with all quotes and backslashes escaped by a backslash. */ extern AString EscapeString(const AString & a_Message); // tolua_export -/// Removes all control codes used by MC for colors and styles +/** Removes all control codes used by MC for colors and styles. */ extern AString StripColorCodes(const AString & a_Message); // tolua_export /// URL-Decodes the given string, replacing all "%HH" into the correct characters. Invalid % sequences are left intact diff --git a/src/Vector3.h b/src/Vector3.h index 1e4a1f5d9..1f3f6b955 100644 --- a/src/Vector3.h +++ b/src/Vector3.h @@ -307,6 +307,22 @@ public: return (a_X - x) / (a_OtherEnd.x - x); } + /** Rotates the vector 90 degrees clockwise around the vertical axis. + Note that this is specific to minecraft's axis ordering, which is X+ left, Z+ down. */ + inline void TurnCW(void) + { + std::swap(x, z); + x = -x; + } + + /** Rotates the vector 90 degrees counterclockwise around the vertical axis. + Note that this is specific to minecraft's axis ordering, which is X+ left, Z+ down. */ + inline void TurnCCW(void) + { + std::swap(x, z); + z = -z; + } + /** The max difference between two coords for which the coords are assumed equal. */ static const double EPS; diff --git a/src/World.cpp b/src/World.cpp index 69b39f831..24b1a9b40 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -233,21 +233,19 @@ cWorld::cTickThread::cTickThread(cWorld & a_World) : void cWorld::cTickThread::Execute(void) { auto LastTime = std::chrono::steady_clock::now(); - static const auto msPerTick = std::chrono::milliseconds(50); - auto TickTime = std::chrono::steady_clock::duration(50); + auto TickTime = std::chrono::duration_cast<std::chrono::milliseconds>(cTickTime(1)); while (!m_ShouldTerminate) { auto NowTime = std::chrono::steady_clock::now(); - auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(NowTime - LastTime).count(); - auto LastTickMsec = std::chrono::duration_cast<std::chrono::duration<int>>(TickTime).count(); - m_World.Tick(static_cast<float>(msec), LastTickMsec); - TickTime = std::chrono::steady_clock::now() - NowTime; + auto WaitTime = std::chrono::duration_cast<std::chrono::milliseconds>(NowTime - LastTime); + m_World.Tick(WaitTime, TickTime); + TickTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - NowTime); - if (TickTime < msPerTick) + if (TickTime < cTickTime(1)) { - // Stretch tick time until it's at least msPerTick - std::this_thread::sleep_for(msPerTick - TickTime); + // Stretch tick time until it's at least 1 tick + std::this_thread::sleep_for(cTickTime(1) - TickTime); } LastTime = NowTime; @@ -273,12 +271,17 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin #endif m_Dimension(a_Dimension), m_IsSpawnExplicitlySet(false), + m_SpawnX(0), + m_SpawnY(0), + m_SpawnZ(0), + m_BroadcastDeathMessages(true), + m_BroadcastAchievementMessages(true), m_IsDaylightCycleEnabled(true), - m_WorldAgeSecs(0), - m_TimeOfDaySecs(0), m_WorldAge(0), m_TimeOfDay(0), m_LastTimeUpdate(0), + m_LastUnload(0), + m_LastSave(0), m_SkyDarkness(0), m_GameMode(gmNotSet), m_bEnabledPVP(false), @@ -616,13 +619,10 @@ void cWorld::Start(void) InitialiseGeneratorDefaults(IniFile); InitialiseAndLoadMobSpawningValues(IniFile); - SetTimeOfDay(IniFile.GetValueSetI("General", "TimeInTicks", m_TimeOfDay)); + SetTimeOfDay(IniFile.GetValueSetI("General", "TimeInTicks", GetTimeOfDay())); m_ChunkMap = make_unique<cChunkMap>(this); - 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); @@ -646,10 +646,10 @@ void cWorld::Start(void) m_TickThread.Start(); // Init of the spawn monster time (as they are supposed to have different spawn rate) - m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfHostile, 0)); - m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfPassive, 0)); - m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfAmbient, 0)); - m_LastSpawnMonster.insert(std::map<cMonster::eFamily, Int64>::value_type(cMonster::mfWater, 0)); + m_LastSpawnMonster.insert(std::map<cMonster::eFamily, cTickTimeLong>::value_type(cMonster::mfHostile, cTickTimeLong(0))); + m_LastSpawnMonster.insert(std::map<cMonster::eFamily, cTickTimeLong>::value_type(cMonster::mfPassive, cTickTimeLong(0))); + m_LastSpawnMonster.insert(std::map<cMonster::eFamily, cTickTimeLong>::value_type(cMonster::mfAmbient, cTickTimeLong(0))); + m_LastSpawnMonster.insert(std::map<cMonster::eFamily, cTickTimeLong>::value_type(cMonster::mfWater, cTickTimeLong(0))); m_MapManager.LoadMapData(); @@ -840,7 +840,7 @@ void cWorld::Stop(void) IniFile.SetValueB("Mechanics", "UseChatPrefixes", m_bUseChatPrefixes); IniFile.SetValueB("General", "IsDaylightCycleEnabled", m_IsDaylightCycleEnabled); IniFile.SetValueI("General", "Weather", (int)m_Weather); - IniFile.SetValueI("General", "TimeInTicks", m_TimeOfDay); + IniFile.SetValueI("General", "TimeInTicks", GetTimeOfDay()); IniFile.WriteFile(m_IniFileName); m_TickThread.Stop(); @@ -854,7 +854,7 @@ void cWorld::Stop(void) -void cWorld::Tick(float a_Dt, int a_LastTickDurationMSec) +void cWorld::Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) { // Call the plugins cPluginManager::Get()->CallHookWorldTick(*this, a_Dt, a_LastTickDurationMSec); @@ -870,30 +870,27 @@ void cWorld::Tick(float a_Dt, int a_LastTickDurationMSec) SetChunkData(**itr); } // for itr - SetChunkDataQueue[] - m_WorldAgeSecs += (double)a_Dt / 1000.0; - m_WorldAge = (Int64)(m_WorldAgeSecs * 20.0); + m_WorldAge += a_Dt; if (m_IsDaylightCycleEnabled) { - // We need sub-tick precision here, that's why we store the time in seconds and calculate ticks off of it - m_TimeOfDaySecs += (double)a_Dt / 1000.0; + // We need sub-tick precision here, that's why we store the time in milliseconds and calculate ticks off of it + m_TimeOfDay += a_Dt; // Wrap time of day each 20 minutes (1200 seconds) - if (m_TimeOfDaySecs > 1200.0) + if (m_TimeOfDay > std::chrono::minutes(20)) { - m_TimeOfDaySecs -= 1200.0; + m_TimeOfDay -= std::chrono::minutes(20); } - m_TimeOfDay = static_cast<int>(m_TimeOfDaySecs * 20.0); - // Updates the sky darkness based on current time of day UpdateSkyDarkness(); // Broadcast time update every 40 ticks (2 seconds) - if (m_LastTimeUpdate < m_WorldAge - 40) + if (m_LastTimeUpdate < m_WorldAge - cTickTime(40)) { BroadcastTimeUpdate(); - m_LastTimeUpdate = m_WorldAge; + m_LastTimeUpdate = std::chrono::duration_cast<cTickTimeLong>(m_WorldAge); } } @@ -913,23 +910,23 @@ void cWorld::Tick(float a_Dt, int a_LastTickDurationMSec) m_ChunkMap->Tick(a_Dt); - TickClients(a_Dt); + TickClients(static_cast<float>(a_Dt.count())); TickQueuedBlocks(); TickQueuedTasks(); TickScheduledTasks(); - GetSimulatorManager()->Simulate(a_Dt); + GetSimulatorManager()->Simulate(static_cast<float>(a_Dt.count())); - TickWeather(a_Dt); + TickWeather(static_cast<float>(a_Dt.count())); m_ChunkMap->FastSetQueuedBlocks(); - if (m_WorldAge - m_LastSave > 60 * 5 * 20) // Save each 5 minutes + if (m_WorldAge - m_LastSave > std::chrono::minutes(5)) // Save each 5 minutes { SaveAllChunks(); } - if (m_WorldAge - m_LastUnload > 10 * 20) // Unload every 10 seconds + if (m_WorldAge - m_LastUnload > std::chrono::minutes(5)) // Unload every 10 seconds { UnloadUnusedChunks(); } @@ -975,7 +972,7 @@ void cWorld::TickWeather(float a_Dt) -void cWorld::TickMobs(float a_Dt) +void cWorld::TickMobs(std::chrono::milliseconds a_Dt) { // _X 2013_10_22: This is a quick fix for #283 - the world needs to be locked while ticking mobs cWorld::cLock Lock(*this); @@ -996,7 +993,7 @@ void cWorld::TickMobs(float a_Dt) for (size_t i = 0; i < ARRAYCOUNT(AllFamilies); i++) { cMonster::eFamily Family = AllFamilies[i]; - int SpawnDelay = cMonster::GetSpawnDelay(Family); + cTickTime SpawnDelay = cTickTime(cMonster::GetSpawnDelay(Family)); if ( (m_LastSpawnMonster[Family] > m_WorldAge - SpawnDelay) || // Not reached the needed ticks before the next round MobCensus.IsCapped(Family) @@ -1004,7 +1001,7 @@ void cWorld::TickMobs(float a_Dt) { continue; } - m_LastSpawnMonster[Family] = m_WorldAge; + m_LastSpawnMonster[Family] = std::chrono::duration_cast<cTickTimeLong>(m_WorldAge); cMobSpawner Spawner(Family, m_AllowedMobs); if (Spawner.CanSpawnAnything()) { @@ -1068,7 +1065,7 @@ void cWorld::TickScheduledTasks(void) // Move all the due tasks from m_ScheduledTasks into Tasks: for (auto itr = m_ScheduledTasks.begin(); itr != m_ScheduledTasks.end();) // Cannot use range-basd for, we're modifying the container { - if ((*itr)->m_TargetTick < WorldAge) + if ((*itr)->m_TargetTick < std::chrono::duration_cast<cTickTimeLong>(WorldAge).count()) { auto next = itr; ++next; @@ -1143,7 +1140,7 @@ void cWorld::TickClients(float a_Dt) void cWorld::UpdateSkyDarkness(void) { - int TempTime = (int)m_TimeOfDay; + int TempTime = std::chrono::duration_cast<cTickTime>(m_TimeOfDay).count(); if (TempTime <= TIME_SUNSET) { m_SkyDarkness = 0; @@ -1424,14 +1421,15 @@ void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, NIBBLETYPE a_Sapling { cNoise Noise(m_Generator.GetSeed()); sSetBlockVector Logs, Other; + auto WorldAge = (int)(std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count() & 0xffffffff); switch (a_SaplingMeta & 0x07) { - case E_META_SAPLING_APPLE: GetAppleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; - case E_META_SAPLING_BIRCH: GetBirchTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; - case E_META_SAPLING_CONIFER: GetConiferTreeImage(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; - case E_META_SAPLING_JUNGLE: GetJungleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; - case E_META_SAPLING_ACACIA: GetAcaciaTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; - case E_META_SAPLING_DARK_OAK: GetDarkoakTreeImage(a_X, a_Y, a_Z, Noise, (int)(m_WorldAge & 0xffffffff), Logs, Other); break; + case E_META_SAPLING_APPLE: GetAppleTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; + case E_META_SAPLING_BIRCH: GetBirchTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; + case E_META_SAPLING_CONIFER: GetConiferTreeImage(a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; + case E_META_SAPLING_JUNGLE: GetJungleTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; + case E_META_SAPLING_ACACIA: GetAcaciaTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; + case E_META_SAPLING_DARK_OAK: GetDarkoakTreeImage(a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break; } Other.insert(Other.begin(), Logs.begin(), Logs.end()); Logs.clear(); @@ -1446,7 +1444,7 @@ 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_WorldAge & 0xffffffff), GetBiomeAt(a_X, a_Z), Logs, Other); + GetTreeImageByBiome(a_X, a_Y, a_Z, Noise, (int)(std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count() & 0xffffffff), GetBiomeAt(a_X, a_Z), Logs, Other); Other.insert(Other.begin(), Logs.begin(), Logs.end()); Logs.clear(); GrowTreeImage(Other); @@ -1464,7 +1462,7 @@ void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks) sSetBlockVector b2; for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr) { - if (itr->BlockType == E_BLOCK_LOG) + if (itr->m_BlockType == E_BLOCK_LOG) { b2.push_back(*itr); } @@ -1479,7 +1477,7 @@ void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks) // 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) + switch (itr->m_BlockType) { CASE_TREE_ALLOWED_BLOCKS: { @@ -1927,6 +1925,15 @@ void cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, int a_FuseTicks, +void cWorld::SetBlocks(const sSetBlockVector & a_Blocks) +{ + m_ChunkMap->SetBlocks(a_Blocks); +} + + + + + void cWorld::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType) { m_ChunkMap->ReplaceBlocks(a_Blocks, a_FilterBlockType); @@ -2398,7 +2405,7 @@ void cWorld::BroadcastTimeUpdate(const cClientHandle * a_Exclude) { continue; } - ch->SendTimeUpdate(m_WorldAge, m_TimeOfDay, m_IsDaylightCycleEnabled); + ch->SendTimeUpdate(std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count(), std::chrono::duration_cast<cTickTimeLong>(m_TimeOfDay).count(), m_IsDaylightCycleEnabled); } } @@ -2600,7 +2607,7 @@ bool cWorld::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const void cWorld::UnloadUnusedChunks(void) { - m_LastUnload = m_WorldAge; + m_LastUnload = std::chrono::duration_cast<cTickTimeLong>(m_WorldAge); m_ChunkMap->UnloadUnusedChunks(); } @@ -3076,7 +3083,7 @@ bool cWorld::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunk void cWorld::SaveAllChunks(void) { - m_LastSave = m_WorldAge; + m_LastSave = std::chrono::duration_cast<cTickTimeLong>(m_WorldAge); m_ChunkMap->SaveAllChunks(); } @@ -3105,7 +3112,7 @@ void cWorld::QueueTask(std::unique_ptr<cTask> a_Task) void cWorld::ScheduleTask(int a_DelayTicks, cTask * a_Task) { - Int64 TargetTick = a_DelayTicks + m_WorldAge; + Int64 TargetTick = a_DelayTicks + std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count(); // Insert the task into the list of scheduled tasks, ordered by its target tick cCSLock Lock(m_CSScheduledTasks); diff --git a/src/World.h b/src/World.h index 4f24280a4..e7519dab8 100644 --- a/src/World.h +++ b/src/World.h @@ -157,8 +157,8 @@ public: BroadcastTimeUpdate(); } - virtual Int64 GetWorldAge (void) const override { return m_WorldAge; } - virtual int GetTimeOfDay(void) const override { return m_TimeOfDay; } + virtual Int64 GetWorldAge (void) const override { return std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count(); } + virtual int GetTimeOfDay(void) const override { return std::chrono::duration_cast<cTickTime>(m_TimeOfDay).count(); } void SetTicksUntilWeatherChange(int a_WeatherInterval) { @@ -167,8 +167,7 @@ public: virtual void SetTimeOfDay(int a_TimeOfDay) override { - m_TimeOfDay = a_TimeOfDay; - m_TimeOfDaySecs = (double)a_TimeOfDay / 20.0; + m_TimeOfDay = cTickTime(a_TimeOfDay); UpdateSkyDarkness(); BroadcastTimeUpdate(); } @@ -491,6 +490,11 @@ public: // tolua_end + /** Performs the specified single-block set operations simultaneously, as if SetBlock() was called for each item. + Is more efficient than calling SetBlock() multiple times. + If the chunk for any of the blocks is not loaded, the set operation is ignored silently. */ + void SetBlocks(const sSetBlockVector & a_Blocks); + /** Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType */ void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType); @@ -799,7 +803,7 @@ public: int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const cItem * a_Item, const Vector3d * a_Speed = nullptr); // tolua_export /** Returns a random number from the m_TickRand in range [0 .. a_Range]. To be used only in the tick thread! */ - int GetTickRandomNumber(unsigned a_Range) { return (int)(m_TickRand.randInt(a_Range)); } + int GetTickRandomNumber(int a_Range) { return (int)(m_TickRand.randInt(a_Range)); } /** Appends all usernames starting with a_Text (case-insensitive) into Results */ void TabCompleteUserName(const AString & a_Text, AStringVector & a_Results); @@ -909,14 +913,13 @@ private: bool m_BroadcastAchievementMessages; bool m_IsDaylightCycleEnabled; - double m_WorldAgeSecs; // World age, in seconds. Is only incremented, cannot be set by plugins. - double m_TimeOfDaySecs; // Time of day in seconds. Can be adjusted. Is wrapped to zero each day. - Int64 m_WorldAge; // World age in ticks, calculated off of m_WorldAgeSecs - int m_TimeOfDay; // Time in ticks, calculated off of m_TimeOfDaySecs - Int64 m_LastTimeUpdate; // The tick in which the last time update has been sent. - Int64 m_LastUnload; // The last WorldAge (in ticks) in which unloading was triggerred - Int64 m_LastSave; // The last WorldAge (in ticks) in which save-all was triggerred - std::map<cMonster::eFamily, Int64> m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned (for each megatype of monster) // MG TODO : find a way to optimize without creating unmaintenability (if mob IDs are becoming unrowed) + // std::chrono::milliseconds is guaranteed to be good for 292 years by the standard. + std::chrono::milliseconds m_WorldAge; + std::chrono::milliseconds m_TimeOfDay; + cTickTimeLong m_LastTimeUpdate; // The tick in which the last time update has been sent. + cTickTimeLong m_LastUnload; // The last WorldAge (in ticks) in which unloading was triggerred + cTickTimeLong m_LastSave; // The last WorldAge (in ticks) in which save-all was triggerred + std::map<cMonster::eFamily, cTickTimeLong> m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned (for each megatype of monster) // MG TODO : find a way to optimize without creating unmaintenability (if mob IDs are becoming unrowed) NIBBLETYPE m_SkyDarkness; @@ -1046,13 +1049,13 @@ private: cWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_OverworldName = ""); virtual ~cWorld(); - void Tick(float a_Dt, int a_LastTickDurationMSec); + void Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec); /** Handles the weather in each tick */ void TickWeather(float a_Dt); /** Handles the mob spawning/moving/destroying each tick */ - void TickMobs(float a_Dt); + void TickMobs(std::chrono::milliseconds a_Dt); /** Executes all tasks queued onto the tick thread */ void TickQueuedTasks(void); diff --git a/src/WorldStorage/FastNBT.cpp b/src/WorldStorage/FastNBT.cpp index ed8e8bb14..033a07601 100644 --- a/src/WorldStorage/FastNBT.cpp +++ b/src/WorldStorage/FastNBT.cpp @@ -10,6 +10,13 @@ +/** If a list being loaded has more than this number of items, it's considered corrupted. */ +static const int MAX_LIST_ITEMS = 10000; + + + + + // The number of NBT tags that are reserved when an NBT parsing is started. // You can override this by using a cmdline define #ifndef NBT_RESERVE_SIZE @@ -102,7 +109,7 @@ bool cParsedNBT::ReadCompound(void) ASSERT(m_Tags.size() > 0); // Reads the latest tag as a compound - int ParentIdx = (int)m_Tags.size() - 1; + size_t ParentIdx = m_Tags.size() - 1; int PrevSibling = -1; for (;;) { @@ -113,10 +120,10 @@ bool cParsedNBT::ReadCompound(void) { break; } - m_Tags.push_back(cFastNBTTag(TagType, ParentIdx, PrevSibling)); + m_Tags.push_back(cFastNBTTag(TagType, static_cast<int>(ParentIdx), PrevSibling)); if (PrevSibling >= 0) { - m_Tags[PrevSibling].m_NextSibling = (int)m_Tags.size() - 1; + m_Tags[static_cast<size_t>(PrevSibling)].m_NextSibling = (int)m_Tags.size() - 1; } else { @@ -142,26 +149,27 @@ bool cParsedNBT::ReadList(eTagType a_ChildrenType) NEEDBYTES(4); int Count = GetBEInt(m_Data + m_Pos); m_Pos += 4; - if (Count < 0) + if ((Count < 0) || (Count > MAX_LIST_ITEMS)) { return false; } // Read items: - int ParentIdx = (int)m_Tags.size() - 1; + ASSERT(m_Tags.size() > 0); + size_t ParentIdx = m_Tags.size() - 1; int PrevSibling = -1; for (int i = 0; i < Count; i++) { - m_Tags.push_back(cFastNBTTag(a_ChildrenType, ParentIdx, PrevSibling)); + m_Tags.push_back(cFastNBTTag(a_ChildrenType, static_cast<int>(ParentIdx), PrevSibling)); if (PrevSibling >= 0) { - m_Tags[PrevSibling].m_NextSibling = (int)m_Tags.size() - 1; + m_Tags[static_cast<size_t>(PrevSibling)].m_NextSibling = static_cast<int>(m_Tags.size()) - 1; } else { - m_Tags[ParentIdx].m_FirstChild = (int)m_Tags.size() - 1; + m_Tags[ParentIdx].m_FirstChild = static_cast<int>(m_Tags.size()) - 1; } - PrevSibling = (int)m_Tags.size() - 1; + PrevSibling = static_cast<int>(m_Tags.size()) - 1; RETURN_FALSE_IF_FALSE(ReadTag()); } // for (i) m_Tags[ParentIdx].m_LastChild = PrevSibling; @@ -210,16 +218,16 @@ bool cParsedNBT::ReadTag(void) return false; } NEEDBYTES(len); - Tag.m_DataLength = len; + Tag.m_DataLength = static_cast<size_t>(len); Tag.m_DataStart = m_Pos; - m_Pos += len; + m_Pos += static_cast<size_t>(len); return true; } case TAG_List: { NEEDBYTES(1); - eTagType ItemType = (eTagType)m_Data[m_Pos]; + eTagType ItemType = static_cast<eTagType>(m_Data[m_Pos]); m_Pos++; RETURN_FALSE_IF_FALSE(ReadList(ItemType)); return true; @@ -243,9 +251,9 @@ bool cParsedNBT::ReadTag(void) } len *= 4; NEEDBYTES(len); - Tag.m_DataLength = len; + Tag.m_DataLength = static_cast<size_t>(len); Tag.m_DataStart = m_Pos; - m_Pos += len; + m_Pos += static_cast<size_t>(len); return true; } @@ -269,7 +277,7 @@ int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLen { return -1; } - if (m_Tags[a_Tag].m_Type != TAG_Compound) + if (m_Tags[static_cast<size_t>(a_Tag)].m_Type != TAG_Compound) { return -1; } @@ -278,11 +286,11 @@ int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLen { a_NameLength = strlen(a_Name); } - for (int Child = m_Tags[a_Tag].m_FirstChild; Child != -1; Child = m_Tags[Child].m_NextSibling) + for (int Child = m_Tags[static_cast<size_t>(a_Tag)].m_FirstChild; Child != -1; Child = m_Tags[static_cast<size_t>(Child)].m_NextSibling) { if ( - (m_Tags[Child].m_NameLength == a_NameLength) && - (memcmp(m_Data + m_Tags[Child].m_NameStart, a_Name, a_NameLength) == 0) + (m_Tags[static_cast<size_t>(Child)].m_NameLength == a_NameLength) && + (memcmp(m_Data + m_Tags[static_cast<size_t>(Child)].m_NameStart, a_Name, a_NameLength) == 0) ) { return Child; @@ -406,7 +414,7 @@ void cFastNBTWriter::EndList(void) ASSERT(m_Stack[m_CurrentStack].m_Type == TAG_List); // Update the list count: - SetBEInt((char *)(m_Result.c_str() + m_Stack[m_CurrentStack].m_Pos), m_Stack[m_CurrentStack].m_Count); + SetBEInt(const_cast<char *>(m_Result.c_str() + m_Stack[m_CurrentStack].m_Pos), m_Stack[m_CurrentStack].m_Count); --m_CurrentStack; } @@ -418,7 +426,7 @@ void cFastNBTWriter::EndList(void) void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value) { TagCommon(a_Name, TAG_Byte); - m_Result.push_back(a_Value); + m_Result.push_back(static_cast<char>(a_Value)); } @@ -428,8 +436,8 @@ void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value) void cFastNBTWriter::AddShort(const AString & a_Name, Int16 a_Value) { TagCommon(a_Name, TAG_Short); - Int16 Value = htons(a_Value); - m_Result.append((const char *)&Value, 2); + UInt16 Value = htons(a_Value); + m_Result.append(reinterpret_cast<const char *>(&Value), 2); } @@ -439,8 +447,8 @@ void cFastNBTWriter::AddShort(const AString & a_Name, Int16 a_Value) void cFastNBTWriter::AddInt(const AString & a_Name, Int32 a_Value) { TagCommon(a_Name, TAG_Int); - Int32 Value = htonl(a_Value); - m_Result.append((const char *)&Value, 4); + UInt32 Value = htonl(a_Value); + m_Result.append(reinterpret_cast<const char *>(&Value), 4); } @@ -450,8 +458,8 @@ void cFastNBTWriter::AddInt(const AString & a_Name, Int32 a_Value) void cFastNBTWriter::AddLong(const AString & a_Name, Int64 a_Value) { TagCommon(a_Name, TAG_Long); - Int64 Value = HostToNetwork8(&a_Value); - m_Result.append((const char *)&Value, 8); + UInt64 Value = HostToNetwork8(&a_Value); + m_Result.append(reinterpret_cast<const char *>(&Value), 8); } @@ -461,8 +469,8 @@ void cFastNBTWriter::AddLong(const AString & a_Name, Int64 a_Value) void cFastNBTWriter::AddFloat(const AString & a_Name, float a_Value) { TagCommon(a_Name, TAG_Float); - Int32 Value = HostToNetwork4(&a_Value); - m_Result.append((const char *)&Value, 4); + UInt32 Value = HostToNetwork4(&a_Value); + m_Result.append(reinterpret_cast<const char *>(&Value), 4); } @@ -472,8 +480,8 @@ void cFastNBTWriter::AddFloat(const AString & a_Name, float a_Value) void cFastNBTWriter::AddDouble(const AString & a_Name, double a_Value) { TagCommon(a_Name, TAG_Double); - Int64 Value = HostToNetwork8(&a_Value); - m_Result.append((const char *)&Value, 8); + UInt64 Value = HostToNetwork8(&a_Value); + m_Result.append(reinterpret_cast<const char *>(&Value), 8); } @@ -483,8 +491,8 @@ void cFastNBTWriter::AddDouble(const AString & a_Name, double a_Value) void cFastNBTWriter::AddString(const AString & a_Name, const AString & a_Value) { TagCommon(a_Name, TAG_String); - Int16 len = htons((short)(a_Value.size())); - m_Result.append((const char *)&len, 2); + UInt16 len = htons(static_cast<short>(a_Value.size())); + m_Result.append(reinterpret_cast<const char *>(&len), 2); m_Result.append(a_Value.c_str(), a_Value.size()); } @@ -495,8 +503,8 @@ void cFastNBTWriter::AddString(const AString & a_Name, const AString & a_Value) void cFastNBTWriter::AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements) { TagCommon(a_Name, TAG_ByteArray); - u_long len = htonl((u_long)a_NumElements); - m_Result.append((const char *)&len, 4); + u_long len = htonl(static_cast<u_long>(a_NumElements)); + m_Result.append(reinterpret_cast<const char *>(&len), 4); m_Result.append(a_Value, a_NumElements); } @@ -507,18 +515,18 @@ void cFastNBTWriter::AddByteArray(const AString & a_Name, const char * a_Value, void cFastNBTWriter::AddIntArray(const AString & a_Name, const int * a_Value, size_t a_NumElements) { TagCommon(a_Name, TAG_IntArray); - u_long len = htonl((u_long)a_NumElements); + u_long len = htonl(static_cast<u_long>(a_NumElements)); size_t cap = m_Result.capacity(); size_t size = m_Result.length(); if ((cap - size) < (4 + a_NumElements * 4)) { m_Result.reserve(size + 4 + (a_NumElements * 4)); } - m_Result.append((const char *)&len, 4); + m_Result.append(reinterpret_cast<const char *>(&len), 4); for (size_t i = 0; i < a_NumElements; i++) { - int Element = htonl(a_Value[i]); - m_Result.append((const char *)&Element, 4); + UInt32 Element = htonl(a_Value[i]); + m_Result.append(reinterpret_cast<const char *>(&Element), 4); } } @@ -538,8 +546,8 @@ void cFastNBTWriter::Finish(void) void cFastNBTWriter::WriteString(const char * a_Data, UInt16 a_Length) { - Int16 Len = htons(a_Length); - m_Result.append((const char *)&Len, 2); + UInt16 Len = htons(a_Length); + m_Result.append(reinterpret_cast<const char *>(&Len), 2); m_Result.append(a_Data, a_Length); } diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 432e122b5..c87397542 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -495,12 +495,14 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster) case mtEnderman: EntityClass = "Enderman"; break; case mtGhast: EntityClass = "Ghast"; break; case mtGiant: EntityClass = "Giant"; break; + case mtGuardian: EntityClass = "Guardian"; break; case mtHorse: EntityClass = "Horse"; break; case mtIronGolem: EntityClass = "VillagerGolem"; break; case mtMagmaCube: EntityClass = "LavaSlime"; break; case mtMooshroom: EntityClass = "MushroomCow"; break; case mtOcelot: EntityClass = "Ozelot"; break; case mtPig: EntityClass = "Pig"; break; + case mtRabbit: EntityClass = "Rabbit"; break; case mtSheep: EntityClass = "Sheep"; break; case mtSilverfish: EntityClass = "Silverfish"; break; case mtSkeleton: EntityClass = "Skeleton"; break; @@ -627,10 +629,12 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster) case mtEnderDragon: case mtGhast: case mtGiant: + case mtGuardian: case mtIronGolem: case mtMooshroom: case mtOcelot: case mtPig: + case mtRabbit: case mtSilverfish: case mtSnowGolem: case mtSpider: diff --git a/src/WorldStorage/SchematicFileSerializer.cpp b/src/WorldStorage/SchematicFileSerializer.cpp index 64f4cb00d..fb881e290 100644 --- a/src/WorldStorage/SchematicFileSerializer.cpp +++ b/src/WorldStorage/SchematicFileSerializer.cpp @@ -184,7 +184,9 @@ bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cP { LOG("Dimensions are missing from the schematic file (%d, %d, %d), (%d, %d, %d)", TSizeX, TSizeY, TSizeZ, - a_NBT.GetType(TSizeX), a_NBT.GetType(TSizeY), a_NBT.GetType(TSizeZ) + (TSizeX >= 0) ? a_NBT.GetType(TSizeX) : -1, + (TSizeY >= 0) ? a_NBT.GetType(TSizeY) : -1, + (TSizeZ >= 0) ? a_NBT.GetType(TSizeZ) : -1 ); return false; } diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index af65db700..a76e9461a 100755 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -1410,6 +1410,10 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a { LoadGiantFromNBT(a_Entities, a_NBT, a_EntityTagIdx); } + else if (strncmp(a_IDTag, "Guardian", a_IDTagLength) == 0) + { + LoadGuardianFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } else if (strncmp(a_IDTag, "Horse", a_IDTagLength) == 0) { LoadHorseFromNBT(a_Entities, a_NBT, a_EntityTagIdx); @@ -1438,6 +1442,10 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a { LoadPigFromNBT(a_Entities, a_NBT, a_EntityTagIdx); } + else if (strncmp(a_IDTag, "Rabbit", a_IDTagLength) == 0) + { + LoadRabbitFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } else if (strncmp(a_IDTag, "Sheep", a_IDTagLength) == 0) { LoadSheepFromNBT(a_Entities, a_NBT, a_EntityTagIdx); @@ -2197,6 +2205,26 @@ void cWSSAnvil::LoadGiantFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ +void cWSSAnvil::LoadGuardianFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::unique_ptr<cGuardian> Monster(new cGuardian()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + void cWSSAnvil::LoadHorseFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "Type"); @@ -2339,6 +2367,26 @@ void cWSSAnvil::LoadPigFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB +void cWSSAnvil::LoadRabbitFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + std::unique_ptr<cRabbit> Monster(new cRabbit()); + if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) + { + return; + } + + a_Entities.push_back(Monster.release()); +} + + + + + void cWSSAnvil::LoadSheepFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { int ColorIdx = a_NBT.FindChildByName(a_TagIdx, "Color"); @@ -2897,9 +2945,9 @@ bool cWSSAnvil::GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int { return false; } - a_X = a_NBT.GetInt(x); - a_Y = a_NBT.GetInt(y); - a_Z = a_NBT.GetInt(z); + a_X = Clamp(a_NBT.GetInt(x), -40000000, 40000000); // World is limited to 30M blocks in XZ, we clamp to 40M + a_Y = Clamp(a_NBT.GetInt(y), -10000, 10000); // Y is limited to 0 .. 255, we clamp to 10K + a_Z = Clamp(a_NBT.GetInt(z), -40000000, 40000000); return true; } diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index 974ba932e..362796614 100755 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -191,12 +191,14 @@ protected: void LoadEndermanFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadGhastFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadGiantFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadGuardianFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadHorseFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadIronGolemFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadMagmaCubeFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadMooshroomFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadOcelotFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadPigFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadRabbitFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadSheepFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadSilverfishFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadSkeletonFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); |