From 4cd49d7eca5f8fd53eb98577a1f218a5086704bb Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Mon, 5 Apr 2021 01:38:43 +0100 Subject: Fix sending incorrect date values on world change Yak shave: make more things use cTickTime. Fix a couple of incorrect modulo-on-millisecond-value by making them use WorldTickAge. --- Server/Plugins/APIDump/APIDesc.lua | 22 ----- Server/Plugins/APIDump/Classes/BlockEntities.lua | 4 - src/Bindings/ManualBindings.cpp | 31 ++++++ src/Bindings/ManualBindings_World.cpp | 104 ++++++++++++++++++++- src/Bindings/PluginManager.cpp | 2 +- src/BlockEntities/BeaconEntity.cpp | 6 +- src/BlockEntities/BrewingstandEntity.cpp | 4 +- src/BlockEntities/FurnaceEntity.cpp | 4 +- src/BlockEntities/HopperEntity.cpp | 27 +++--- src/BlockEntities/HopperEntity.h | 13 ++- src/BlockEntities/MobSpawnerEntity.cpp | 6 +- src/Blocks/BlockBed.cpp | 4 +- src/Blocks/BlockButton.h | 2 +- src/Blocks/BlockPiston.cpp | 4 +- src/Blocks/WorldInterface.h | 6 +- src/Broadcaster.cpp | 2 +- src/ClientHandle.cpp | 10 +- src/ClientHandle.h | 5 +- src/DeadlockDetect.cpp | 23 ++--- src/DeadlockDetect.h | 12 +-- src/Entities/Boat.cpp | 4 +- src/Entities/Entity.cpp | 4 +- src/Entities/Player.cpp | 14 +-- src/Entities/Player.h | 7 +- src/Globals.h | 18 +++- src/MobCensus.cpp | 1 - src/Mobs/CaveSpider.cpp | 2 +- src/Mobs/Monster.cpp | 28 ++---- src/Mobs/Monster.h | 9 +- src/Protocol/Protocol.h | 2 +- src/Protocol/Protocol_1_8.cpp | 17 ++-- src/Protocol/Protocol_1_8.h | 2 +- .../DaylightSensorHandler.h | 2 +- src/World.cpp | 91 ++++++++++-------- src/World.h | 22 +++-- src/WorldStorage/NBTChunkSerializer.cpp | 2 +- src/WorldStorage/WSSAnvil.cpp | 4 +- 37 files changed, 322 insertions(+), 198 deletions(-) diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua index 3fba7a37b..bffc40928 100644 --- a/Server/Plugins/APIDump/APIDesc.lua +++ b/Server/Plugins/APIDump/APIDesc.lua @@ -9138,24 +9138,6 @@ a_Player:OpenWindow(Window); }, Notes = "Returns the relative walk speed of this mob. Standard is 1.0", }, - GetSpawnDelay = - { - IsStatic = true, - Params = - { - { - Name = "MobFamily", - Type = "cMonster#eFamily", - }, - }, - Returns = - { - { - Type = "number", - }, - }, - Notes = "Returns the spawn delay - the number of game ticks between spawn attempts - for the specified mob family.", - }, HasCustomName = { Returns = @@ -11392,10 +11374,6 @@ a_Player:OpenWindow(Window); }, Constants = { - EATING_TICKS = - { - Notes = "Number of ticks required for consuming an item.", - }, MAX_FOOD_LEVEL = { Notes = "The maximum food level value. When the food level is at this value, the player cannot eat.", diff --git a/Server/Plugins/APIDump/Classes/BlockEntities.lua b/Server/Plugins/APIDump/Classes/BlockEntities.lua index debefda7a..91e874f34 100644 --- a/Server/Plugins/APIDump/Classes/BlockEntities.lua +++ b/Server/Plugins/APIDump/Classes/BlockEntities.lua @@ -1076,10 +1076,6 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(), { Notes = "Width (X) of the internal {{cItemGrid}} representing the hopper contents.", }, - TICKS_PER_TRANSFER = - { - Notes = "Number of ticks between when the hopper transfers items.", - }, }, Inherits = "cBlockEntityWithItems", }, diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 20364100f..cd5e69b22 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -2338,6 +2338,36 @@ static int tolua_cClientHandle_SendPluginMessage(lua_State * L) +static int tolua_cClientHandle_SendTimeUpdate(lua_State * L) +{ + cLuaState S(L); + if ( + !S.CheckParamSelf("cClientHandle") || + !S.CheckParamNumber(2, 3) || + !S.CheckParamBool(4) || + !S.CheckParamEnd(5) + ) + { + return 0; + } + + cClientHandle * Client; + cTickTimeLong::rep WorldAge, WorldDate; + bool DoDayLightCycle; + S.GetStackValues(1, Client, WorldAge, WorldDate, DoDayLightCycle); + if (Client == nullptr) + { + return S.ApiParamError("Invalid 'self'"); + } + + Client->SendTimeUpdate(cTickTimeLong(WorldAge), cTickTimeLong(WorldDate), DoDayLightCycle); + return 0; +} + + + + + static int tolua_cClientHandle_GetForgeMods(lua_State * L) { cLuaState S(L); @@ -4361,6 +4391,7 @@ void cManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "GetForgeMods", tolua_cClientHandle_GetForgeMods); tolua_function(tolua_S, "SendPluginMessage", tolua_cClientHandle_SendPluginMessage); + tolua_function(tolua_S, "SendTimeUpdate", tolua_cClientHandle_SendTimeUpdate); tolua_function(tolua_S, "GetUUID", tolua_cClientHandle_GetUUID); tolua_function(tolua_S, "GenerateOfflineUUID", tolua_cClientHandle_GenerateOfflineUUID); tolua_function(tolua_S, "IsUUIDOnline", tolua_cClientHandle_IsUUIDOnline); diff --git a/src/Bindings/ManualBindings_World.cpp b/src/Bindings/ManualBindings_World.cpp index db797483d..6de6f10bd 100644 --- a/src/Bindings/ManualBindings_World.cpp +++ b/src/Bindings/ManualBindings_World.cpp @@ -1266,6 +1266,70 @@ static int tolua_cWorld_GetSignLines(lua_State * tolua_S) +static int tolua_cWorld_GetTimeOfDay(lua_State * tolua_S) +{ + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamSelf("cWorld") || + !L.CheckParamEnd(2) + ) + { + return 0; + } + + // Get params: + cWorld * Self = nullptr; + L.GetStackValues(1, Self); + if (Self == nullptr) + { + return L.ApiParamError("Invalid 'self'"); + } + + // Call the function: + const auto Time = Self->GetTimeOfDay(); + + // Push the returned value: + L.Push(Time.count()); + return 1; +} + + + + + +static int tolua_cWorld_GetWorldAge(lua_State * tolua_S) +{ + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamSelf("cWorld") || + !L.CheckParamEnd(2) + ) + { + return 0; + } + + // Get params: + cWorld * Self = nullptr; + L.GetStackValues(1, Self); + if (Self == nullptr) + { + return L.ApiParamError("Invalid 'self'"); + } + + // Call the function: + const auto Time = Self->GetWorldAge(); + + // Push the returned value: + L.Push(static_cast(Time.count())); + return 1; +} + + + + + static int tolua_cWorld_PrepareChunk(lua_State * tolua_S) { /* Function signature: @@ -1459,6 +1523,37 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S) +static int tolua_cWorld_SetTimeOfDay(lua_State * tolua_S) +{ + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamSelf("cWorld") || + !L.CheckParamNumber(2) || + !L.CheckParamEnd(3) + ) + { + return 0; + } + + // Get params: + cWorld * Self = nullptr; + cTickTime::rep Time; + L.GetStackValues(1, Self, Time); + if (Self == nullptr) + { + return L.ApiParamError("Invalid 'self'"); + } + + // Call the function: + Self->SetTimeOfDay(cTickTime(Time)); + return 0; +} + + + + + static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) { // Function signature: @@ -1490,7 +1585,7 @@ static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not store the callback parameter"); } - World->ScheduleTask(NumTicks, [Task](cWorld & a_World) + World->ScheduleTask(cTickTime(NumTicks), [Task](cWorld & a_World) { Task->Call(&a_World); } @@ -1624,17 +1719,16 @@ void cManualBindings::BindWorld(lua_State * tolua_S) tolua_function(tolua_S, "GetBlockSkyLight", tolua_cWorld_GetBlockSkyLight); tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cWorld_GetBlockTypeMeta); tolua_function(tolua_S, "GetSignLines", tolua_cWorld_GetSignLines); + tolua_function(tolua_S, "GetTimeOfDay", tolua_cWorld_GetTimeOfDay); + tolua_function(tolua_S, "GetWorldAge", tolua_cWorld_GetWorldAge); tolua_function(tolua_S, "PrepareChunk", tolua_cWorld_PrepareChunk); tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask); tolua_function(tolua_S, "ScheduleTask", tolua_cWorld_ScheduleTask); tolua_function(tolua_S, "SetBlock", tolua_cWorld_SetBlock); tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines); + tolua_function(tolua_S, "SetTimeOfDay", tolua_cWorld_SetTimeOfDay); tolua_function(tolua_S, "SpawnSplitExperienceOrbs", tolua_cWorld_SpawnSplitExperienceOrbs); tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight); tolua_endmodule(tolua_S); tolua_endmodule(tolua_S); } - - - - diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 59761ead9..d790562dc 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -541,7 +541,7 @@ bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVec if (world != nullptr) { worldName = world->GetName(); - worldAge = world->GetWorldAge(); + worldAge = world->GetWorldAge().count(); } else { diff --git a/src/BlockEntities/BeaconEntity.cpp b/src/BlockEntities/BeaconEntity.cpp index e59d31f52..6a7916dd1 100644 --- a/src/BlockEntities/BeaconEntity.cpp +++ b/src/BlockEntities/BeaconEntity.cpp @@ -296,8 +296,10 @@ void cBeaconEntity::SendTo(cClientHandle & a_Client) bool cBeaconEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { - // Update the beacon every 4 seconds - if ((GetWorld()->GetWorldAge() % 80) == 0) + using namespace std::chrono_literals; + + // Update the beacon every 4 seconds: + if ((GetWorld()->GetWorldTickAge() % 4s) == 0s) { UpdateBeacon(); GiveEffects(); diff --git a/src/BlockEntities/BrewingstandEntity.cpp b/src/BlockEntities/BrewingstandEntity.cpp index 44a077bc0..bdac1b327 100644 --- a/src/BlockEntities/BrewingstandEntity.cpp +++ b/src/BlockEntities/BrewingstandEntity.cpp @@ -288,8 +288,8 @@ void cBrewingstandEntity::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) void cBrewingstandEntity::UpdateProgressBars(bool a_ForceUpdate) { - /** Sending an update every 3th tick, using a higher value lets look the progressbar ugly */ - if (!a_ForceUpdate && (m_World->GetWorldAge() % 3 != 0)) + // Send an update every 3rd tick, using a higher value makes the progressbar look ugly: + if (!a_ForceUpdate && ((m_World->GetWorldTickAge() % 3_tick) != 0_tick)) { return; } diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp index 6b2e7bbac..d36b19791 100644 --- a/src/BlockEntities/FurnaceEntity.cpp +++ b/src/BlockEntities/FurnaceEntity.cpp @@ -388,8 +388,8 @@ bool cFurnaceEntity::CanCookInputToOutput(void) const void cFurnaceEntity::UpdateProgressBars(bool a_ForceUpdate) { - // In order to preserve bandwidth, an update is sent only every 10th tick - if (!a_ForceUpdate && (m_World->GetWorldAge() % 10 != 0)) + // In order to preserve bandwidth, an update is sent only every 10th tick: + if (!a_ForceUpdate && ((m_World->GetWorldTickAge() % 10_tick) != 0_tick)) { return; } diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp index 9c9f229e4..58a5fd565 100644 --- a/src/BlockEntities/HopperEntity.cpp +++ b/src/BlockEntities/HopperEntity.cpp @@ -17,6 +17,13 @@ +// How many ticks at minimum between two item transfers to or from the hopper. +#define TICKS_PER_TRANSFER 8_tick + + + + + cHopperEntity::cHopperEntity(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Vector3i a_Pos, cWorld * a_World): Super(a_BlockType, a_BlockMeta, a_Pos, ContentsWidth, ContentsHeight, a_World), m_LastMoveItemsInTick(0), @@ -76,14 +83,14 @@ void cHopperEntity::CopyFrom(const cBlockEntity & a_Src) bool cHopperEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); - Int64 CurrentTick = a_Chunk.GetWorld()->GetWorldAge(); bool isDirty = false; if (!m_Locked) { - isDirty = MoveItemsIn (a_Chunk, CurrentTick) || isDirty; - isDirty = MovePickupsIn(a_Chunk, CurrentTick) || isDirty; - isDirty = MoveItemsOut (a_Chunk, CurrentTick) || isDirty; + const auto CurrentTick = a_Chunk.GetWorld()->GetWorldAge(); + isDirty = MoveItemsIn(a_Chunk, CurrentTick) || isDirty; + isDirty = MovePickupsIn(a_Chunk) || isDirty; + isDirty = MoveItemsOut(a_Chunk, CurrentTick) || isDirty; } return isDirty; } @@ -147,7 +154,7 @@ void cHopperEntity::OpenNewWindow(void) -bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick) +bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, const cTickTimeLong a_CurrentTick) { if (m_Pos.y >= cChunkDef::Height) { @@ -155,7 +162,7 @@ bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick) return false; } - if (a_CurrentTick - m_LastMoveItemsInTick < TICKS_PER_TRANSFER) + if ((a_CurrentTick - m_LastMoveItemsInTick) < TICKS_PER_TRANSFER) { // Too early after the previous transfer return false; @@ -201,10 +208,8 @@ bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick) -bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick) +bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk) { - UNUSED(a_CurrentTick); - class cHopperPickupSearchCallback { public: @@ -290,9 +295,9 @@ bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick) -bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick) +bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, const cTickTimeLong a_CurrentTick) { - if (a_CurrentTick - m_LastMoveItemsOutTick < TICKS_PER_TRANSFER) + if ((a_CurrentTick - m_LastMoveItemsOutTick) < TICKS_PER_TRANSFER) { // Too early after the previous transfer return false; diff --git a/src/BlockEntities/HopperEntity.h b/src/BlockEntities/HopperEntity.h index 366eb8e9e..8eb0196a3 100644 --- a/src/BlockEntities/HopperEntity.h +++ b/src/BlockEntities/HopperEntity.h @@ -30,8 +30,7 @@ public: enum { ContentsHeight = 1, - ContentsWidth = 5, - TICKS_PER_TRANSFER = 8, ///< How many ticks at minimum between two item transfers to or from the hopper + ContentsWidth = 5 } ; // tolua_end @@ -48,8 +47,8 @@ public: protected: - Int64 m_LastMoveItemsInTick; - Int64 m_LastMoveItemsOutTick; + cTickTimeLong m_LastMoveItemsInTick; + cTickTimeLong m_LastMoveItemsOutTick; // cBlockEntity overrides: virtual void CopyFrom(const cBlockEntity & a_Src) override; @@ -61,13 +60,13 @@ protected: void OpenNewWindow(void); /** Moves items from the container above it into this hopper. Returns true if the contents have changed. */ - bool MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick); + bool MoveItemsIn(cChunk & a_Chunk, cTickTimeLong a_CurrentTick); /** Moves pickups from above this hopper into it. Returns true if the contents have changed. */ - bool MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick); + bool MovePickupsIn(cChunk & a_Chunk); /** Moves items out from this hopper into the destination. Returns true if the contents have changed. */ - bool MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick); + bool MoveItemsOut(cChunk & a_Chunk, cTickTimeLong a_CurrentTick); /** Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed. */ bool MoveItemsFromChest(cChunk & a_Chunk); diff --git a/src/BlockEntities/MobSpawnerEntity.cpp b/src/BlockEntities/MobSpawnerEntity.cpp index 8e5585b89..5778fb706 100644 --- a/src/BlockEntities/MobSpawnerEntity.cpp +++ b/src/BlockEntities/MobSpawnerEntity.cpp @@ -92,8 +92,10 @@ void cMobSpawnerEntity::UpdateActiveState(void) bool cMobSpawnerEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { - // Update the active flag every 5 seconds - if ((m_World->GetWorldAge() % 100) == 0) + using namespace std::chrono_literals; + + // Update the active flag every 5 seconds: + if ((m_World->GetWorldTickAge() % 5s) == 0s) { UpdateActiveState(); } diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp index 531f88345..4fe22dffd 100644 --- a/src/Blocks/BlockBed.cpp +++ b/src/Blocks/BlockBed.cpp @@ -75,7 +75,7 @@ bool cBlockBedHandler::OnUse( // Sleeping is allowed only during night and thunderstorms: if ( - !(((a_WorldInterface.GetTimeOfDay() > 12541) && (a_WorldInterface.GetTimeOfDay() < 23458)) || + !(((a_WorldInterface.GetTimeOfDay() > 12541_tick) && (a_WorldInterface.GetTimeOfDay() < 23458_tick)) || (a_Player.GetWorld()->GetWeather() == wThunderstorm)) ) // Source: https://minecraft.gamepedia.com/Bed#Sleeping { @@ -146,7 +146,7 @@ bool cBlockBedHandler::OnUse( return false; } ); - a_WorldInterface.SetTimeOfDay(0); + a_WorldInterface.SetTimeOfDay(0_tick); a_ChunkInterface.SetBlockMeta(a_BlockPos, Meta & 0x0b); // Clear the "occupied" bit of the bed's block } return true; diff --git a/src/Blocks/BlockButton.h b/src/Blocks/BlockButton.h index 985c1fff9..0c942cb49 100644 --- a/src/Blocks/BlockButton.h +++ b/src/Blocks/BlockButton.h @@ -193,7 +193,7 @@ private: The given block type is checked when the task is executed to ensure the position still contains a button. */ static void QueueButtonRelease(cWorld & a_ButtonWorld, const Vector3i a_Position, const BLOCKTYPE a_BlockType) { - const auto TickDelay = (a_BlockType == E_BLOCK_STONE_BUTTON) ? 20 : 30; + const auto TickDelay = (a_BlockType == E_BLOCK_STONE_BUTTON) ? 20_tick : 30_tick; a_ButtonWorld.ScheduleTask( TickDelay, [a_Position, a_BlockType](cWorld & a_World) diff --git a/src/Blocks/BlockPiston.cpp b/src/Blocks/BlockPiston.cpp index d9deedfc8..31f15a467 100644 --- a/src/Blocks/BlockPiston.cpp +++ b/src/Blocks/BlockPiston.cpp @@ -56,7 +56,7 @@ void cBlockPistonHandler::ExtendPiston(Vector3i a_BlockPos, cWorld & a_World) // However, we don't confuse animation with the underlying state of the world, so emulate by delaying 1 tick // (Probably why vanilla has so many dupe glitches with sand and pistons lolol) - a_World.ScheduleTask(1, [a_BlockPos](cWorld & World) + a_World.ScheduleTask(1_tick, [a_BlockPos](cWorld & World) { BLOCKTYPE pistonBlock; NIBBLETYPE pistonMeta; @@ -108,7 +108,7 @@ void cBlockPistonHandler::RetractPiston(Vector3i a_BlockPos, cWorld & a_World) a_World.BroadcastBlockAction(a_BlockPos, PistonRetractAction, pistonMeta, pistonBlock); } - a_World.ScheduleTask(1, [a_BlockPos](cWorld & World) + a_World.ScheduleTask(1_tick, [a_BlockPos](cWorld & World) { BLOCKTYPE pistonBlock; NIBBLETYPE pistonMeta; diff --git a/src/Blocks/WorldInterface.h b/src/Blocks/WorldInterface.h index 4ae3f33c9..b610bd9ac 100644 --- a/src/Blocks/WorldInterface.h +++ b/src/Blocks/WorldInterface.h @@ -21,8 +21,8 @@ class cWorldInterface public: virtual ~cWorldInterface() {} - virtual int GetTimeOfDay(void) const = 0; - virtual Int64 GetWorldAge(void) const = 0; + virtual cTickTime GetTimeOfDay(void) const = 0; + virtual cTickTimeLong GetWorldAge(void) const = 0; virtual eDimension GetDimension(void) const = 0; @@ -70,7 +70,7 @@ public: If any chunk in the box is missing, ignores the entities in that chunk silently. */ virtual bool ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback a_Callback) = 0; - virtual void SetTimeOfDay(int a_TimeOfDay) = 0; + virtual void SetTimeOfDay(cTickTime a_TimeOfDay) = 0; /** Returns true if it is raining or storming at the specified location. This takes into account biomes. */ virtual bool IsWeatherWetAt(int a_BlockX, int a_BlockZ) = 0; diff --git a/src/Broadcaster.cpp b/src/Broadcaster.cpp index 445bb6d89..09b834471 100644 --- a/src/Broadcaster.cpp +++ b/src/Broadcaster.cpp @@ -596,7 +596,7 @@ void cWorld::BroadcastTimeUpdate(const cClientHandle * a_Exclude) { ForClientsInWorld(*this, a_Exclude, [&](cClientHandle & a_Client) { - a_Client.SendTimeUpdate(GetWorldAge(), std::chrono::duration_cast(m_WorldDate).count(), IsDaylightCycleEnabled()); + a_Client.SendTimeUpdate(GetWorldAge(), GetWorldDate(), IsDaylightCycleEnabled()); } ); } diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index cd45068ee..f26ed4971 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -538,6 +538,8 @@ void cClientHandle::UnloadOutOfRangeChunks(void) m_Player->GetWorld()->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this); SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ); } + + m_LastUnloadCheck = m_Player->GetWorld()->GetWorldAge(); } @@ -1981,6 +1983,8 @@ bool cClientHandle::CheckBlockInteractionsRate(void) void cClientHandle::Tick(float a_Dt) { + using namespace std::chrono_literals; + // anticheat fastbreak if (m_HasStartedDigging) { @@ -2069,8 +2073,8 @@ void cClientHandle::Tick(float a_Dt) } } - // Unload all chunks that are out of the view distance (every 5 seconds) - if ((m_Player->GetWorld()->GetWorldAge() % 100) == 0) + // Unload all chunks that are out of the view distance (every 5 seconds): + if ((m_Player->GetWorld()->GetWorldAge() - m_LastUnloadCheck) > 5s) { UnloadOutOfRangeChunks(); } @@ -3012,7 +3016,7 @@ void cClientHandle::SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_ -void cClientHandle::SendTimeUpdate(Int64 a_WorldAge, Int64 a_WorldDate, bool a_DoDaylightCycle) +void cClientHandle::SendTimeUpdate(const cTickTimeLong a_WorldAge, const cTickTimeLong a_WorldDate, const bool a_DoDaylightCycle) { m_Protocol->SendTimeUpdate(a_WorldAge, a_WorldDate, a_DoDaylightCycle); } diff --git a/src/ClientHandle.h b/src/ClientHandle.h index a72692266..384963a2d 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -216,7 +216,7 @@ public: // tolua_export void SendTabCompletionResults (const AStringVector & a_Results); void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ); void SendTitleTimes (int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks); // tolua_export - void SendTimeUpdate (Int64 a_WorldAge, Int64 a_WorldDate, bool a_DoDaylightCycle); // tolua_export + void SendTimeUpdate (cTickTimeLong a_WorldAge, cTickTimeLong a_WorldDate, bool a_DoDaylightCycle); void SendUnleashEntity (const cEntity & a_Entity); void SendUnloadChunk (int a_ChunkX, int a_ChunkZ); void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity); @@ -478,6 +478,9 @@ private: int m_LastStreamedChunkX; int m_LastStreamedChunkZ; + /** The last time UnloadOutOfRangeChunks was called. */ + cTickTimeLong m_LastUnloadCheck; + /** Number of ticks since the last network packet was received (increased in Tick(), reset in OnReceivedData()) */ std::atomic m_TicksSinceLastPacket; diff --git a/src/DeadlockDetect.cpp b/src/DeadlockDetect.cpp index 3f47589d8..fc426d529 100644 --- a/src/DeadlockDetect.cpp +++ b/src/DeadlockDetect.cpp @@ -103,11 +103,10 @@ void cDeadlockDetect::Execute(void) { // Check the world ages: cRoot::Get()->ForEachWorld([=](cWorld & a_World) - { - CheckWorldAge(a_World.GetName(), a_World.GetWorldAge()); - return false; - } - ); + { + CheckWorldAge(a_World.GetName(), a_World.GetWorldAge()); + return false; + }); std::this_thread::sleep_for(std::chrono::milliseconds(CYCLE_MILLISECONDS)); } // while (should run) @@ -117,7 +116,7 @@ void cDeadlockDetect::Execute(void) -void cDeadlockDetect::SetWorldAge(const AString & a_WorldName, Int64 a_Age) +void cDeadlockDetect::SetWorldAge(const AString & a_WorldName, const cTickTimeLong a_Age) { m_WorldAges[a_WorldName].m_Age = a_Age; m_WorldAges[a_WorldName].m_NumCyclesSame = 0; @@ -127,7 +126,7 @@ void cDeadlockDetect::SetWorldAge(const AString & a_WorldName, Int64 a_Age) -void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age) +void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, const cTickTimeLong a_Age) { WorldAges::iterator itr = m_WorldAges.find(a_WorldName); if (itr == m_WorldAges.end()) @@ -157,14 +156,14 @@ void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age) -void cDeadlockDetect::DeadlockDetected(const AString & a_WorldName, Int64 a_WorldAge) +void cDeadlockDetect::DeadlockDetected(const AString & a_WorldName, const cTickTimeLong a_WorldAge) { LOGERROR("Deadlock detected: world %s has been stuck at age %lld. Aborting the server.", - a_WorldName.c_str(), static_cast(a_WorldAge) + a_WorldName.c_str(), static_cast(a_WorldAge.count()) ); ListTrackedCSs(); ASSERT(!"Deadlock detected"); - abort(); + std::abort(); } @@ -182,7 +181,3 @@ void cDeadlockDetect::ListTrackedCSs(void) ); } } - - - - diff --git a/src/DeadlockDetect.h b/src/DeadlockDetect.h index 98ce13df3..0016313ba 100644 --- a/src/DeadlockDetect.h +++ b/src/DeadlockDetect.h @@ -46,7 +46,7 @@ protected: struct sWorldAge { /** Last m_WorldAge that has been detected in this world */ - Int64 m_Age; + cTickTimeLong m_Age; /** Number of cycles for which the age has been the same */ int m_NumCyclesSame; @@ -71,16 +71,16 @@ protected: // cIsThread overrides: virtual void Execute(void) override; - /** Sets the initial world age */ - void SetWorldAge(const AString & a_WorldName, Int64 a_Age); + /** Sets the initial world age. */ + void SetWorldAge(const AString & a_WorldName, cTickTimeLong a_Age); - /** Checks if the world's age has changed, updates the world's stats; calls DeadlockDetected() if deadlock detected */ - void CheckWorldAge(const AString & a_WorldName, Int64 a_Age); + /** Checks if the world's age has changed, updates the world's stats; calls DeadlockDetected() if deadlock detected. */ + void CheckWorldAge(const AString & a_WorldName, cTickTimeLong a_Age); /** Called when a deadlock is detected in a world. Aborts the server. a_WorldName is the name of the world whose age has triggered the detection. a_WorldAge is the age (in ticks) in which the world is stuck. */ - [[noreturn]] void DeadlockDetected(const AString & a_WorldName, Int64 a_WorldAge); + [[noreturn]] void DeadlockDetected(const AString & a_WorldName, cTickTimeLong a_WorldAge); /** Outputs a listing of the tracked CSs, together with their name and state. */ void ListTrackedCSs(); diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp index b47771a6e..9d804052d 100644 --- a/src/Entities/Boat.cpp +++ b/src/Entities/Boat.cpp @@ -46,8 +46,8 @@ void cBoat::BroadcastMovementUpdate(const cClientHandle * a_Exclude) // Cannot use super::BroadcastMovementUpdate here, broadcasting position when not // expected by the client breaks things. See https://github.com/cuberite/cuberite/pull/4488 - // Process packet sending every two ticks - if (GetWorld()->GetWorldAge() % 2 != 0) + // Process packet sending every two ticks: + if ((GetWorld()->GetWorldTickAge() % 2_tick) != 0_tick) { return; } diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index a6f7dd58c..9fc3f80cf 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -1884,8 +1884,8 @@ void cEntity::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude) { - // Process packet sending every two ticks - if (GetWorld()->GetWorldAge() % 2 != 0) + // Process packet sending every two ticks: + if ((GetWorld()->GetWorldTickAge() % 2_tick) != 0_tick) { return; } diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index d9c51fcef..91882ad6a 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -59,8 +59,8 @@ const int cPlayer::MAX_HEALTH = 20; const int cPlayer::MAX_FOOD_LEVEL = 20; -/** Number of ticks it takes to eat an item */ -const int cPlayer::EATING_TICKS = 30; +// Number of ticks it takes to eat an item. +#define EATING_TICKS 30_tick @@ -530,7 +530,7 @@ void cPlayer::StartEating(void) void cPlayer::FinishEating(void) { // Reset the timer: - m_EatingFinishTick = -1; + m_EatingFinishTick = -1_tick; // Send the packets: m_ClientHandle->SendEntityStatus(*this, esPlayerEatingAccepted); @@ -553,7 +553,7 @@ void cPlayer::FinishEating(void) void cPlayer::AbortEating(void) { - m_EatingFinishTick = -1; + m_EatingFinishTick = -1_tick; m_World->BroadcastEntityMetadata(*this); } @@ -2929,7 +2929,7 @@ void cPlayer::TickFreezeCode() } } } - else if (GetWorld()->GetWorldAge() % 100 == 0) + else if ((GetWorld()->GetWorldTickAge() % 100_tick) == 0_tick) { // Despite the client side freeze, the player may be able to move a little by // Jumping or canceling flight. Re-freeze every now and then @@ -3115,7 +3115,7 @@ void cPlayer::OnAddToWorld(cWorld & a_World) m_ClientHandle->SendWeather(a_World.GetWeather()); // Send time: - m_ClientHandle->SendTimeUpdate(a_World.GetWorldAge(), a_World.GetTimeOfDay(), a_World.IsDaylightCycleEnabled()); + m_ClientHandle->SendTimeUpdate(a_World.GetWorldAge(), a_World.GetWorldDate(), a_World.IsDaylightCycleEnabled()); // Finally, deliver the notification hook: cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*this); @@ -3298,7 +3298,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { m_World->CollectPickupsByPlayer(*this); - if ((m_EatingFinishTick >= 0) && (m_EatingFinishTick <= m_World->GetWorldAge())) + if ((m_EatingFinishTick >= 0_tick) && (m_EatingFinishTick <= m_World->GetWorldAge())) { FinishEating(); } diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 65dbfaed7..40797fa34 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -86,9 +86,6 @@ public: static const int MAX_FOOD_LEVEL; - /** Number of ticks it takes to eat an item */ - static const int EATING_TICKS; - // tolua_end CLASS_PROTODEF(cPlayer) @@ -371,7 +368,7 @@ public: void AddFoodExhaustion(double a_Exhaustion); /** Returns true if the player is currently in the process of eating the currently equipped item */ - bool IsEating(void) const { return (m_EatingFinishTick >= 0); } + bool IsEating(void) const { return m_EatingFinishTick >= 0_tick; } /** Returns true if the player is currently flying */ bool IsFlying(void) const { return m_IsFlying; } @@ -734,7 +731,7 @@ private: bool m_IsVisible; /** The world tick in which eating will be finished. -1 if not eating */ - Int64 m_EatingFinishTick; + cTickTimeLong m_EatingFinishTick; /** Player Xp level */ int m_LifetimeTotalXp; diff --git a/src/Globals.h b/src/Globals.h index d5af4fa11..dc0bdc3ae 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -338,14 +338,14 @@ T Clamp(T a_Value, T a_Min, T a_Max) -/** Floors a value, then casts it to C (an int by default) */ +/** Floors a value, then casts it to C (an int by default). */ template typename std::enable_if::value, C>::type FloorC(T a_Value) { return static_cast(std::floor(a_Value)); } -/** Ceils a value, then casts it to C (an int by default) */ +/** Ceils a value, then casts it to C (an int by default). */ template typename std::enable_if::value, C>::type CeilC(T a_Value) { @@ -356,9 +356,17 @@ typename std::enable_if::value, C>::type CeilC(T a_Value) -// a tick is 50 ms -using cTickTime = std::chrono::duration>>; -using cTickTimeLong = std::chrono::duration; +// A time duration representing a Minecraft tick (50 ms), capable of storing at least 32'767 ticks. +using cTickTime = std::chrono::duration>>; + +// A time duration representing a Minecraft tick (50 ms), capable of storing at least a 64 bit signed duration. +using cTickTimeLong = std::chrono::duration; + +/** Converts a literal to a tick time. */ +constexpr cTickTimeLong operator ""_tick(const unsigned long long a_Ticks) +{ + return cTickTimeLong(a_Ticks); +} using ContiguousByteBuffer = std::basic_string; using ContiguousByteBufferView = std::basic_string_view; diff --git a/src/MobCensus.cpp b/src/MobCensus.cpp index 8f5fdde75..d6db2f9e5 100644 --- a/src/MobCensus.cpp +++ b/src/MobCensus.cpp @@ -39,7 +39,6 @@ int cMobCensus::GetCapMultiplier(cMonster::eFamily a_MobFamily) case cMonster::mfAmbient: return 16; case cMonster::mfWater: return 5; case cMonster::mfNoSpawn: - case cMonster::mfUnhandled: { break; } diff --git a/src/Mobs/CaveSpider.cpp b/src/Mobs/CaveSpider.cpp index ad3dda09e..7e0dd30c5 100644 --- a/src/Mobs/CaveSpider.cpp +++ b/src/Mobs/CaveSpider.cpp @@ -25,7 +25,7 @@ void cCaveSpider::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) return; } - m_EMPersonality = (GetWorld()->GetTimeOfDay() < (12000 + 1000)) ? PASSIVE : AGGRESSIVE; + m_EMPersonality = (GetWorld()->GetTimeOfDay() < 13000_tick) ? PASSIVE : AGGRESSIVE; } diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 09c0974a0..74cceff91 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -1150,34 +1150,26 @@ cMonster::eFamily cMonster::FamilyFromType(eMonsterType a_Type) case mtZombieHorse: return mfPassive; case mtZombiePigman: return mfHostile; case mtZombieVillager: return mfHostile; - - default: - { - ASSERT(!"Unhandled mob type"); - return mfUnhandled; - } + case mtInvalidType: break; } + UNREACHABLE("Unhandled mob type"); } -int cMonster::GetSpawnDelay(cMonster::eFamily a_MobFamily) +cTickTime cMonster::GetSpawnDelay(cMonster::eFamily a_MobFamily) { switch (a_MobFamily) { - case mfHostile: return 40; - case mfPassive: return 40; - case mfAmbient: return 40; - case mfWater: return 400; - case mfNoSpawn: return -1; - default: - { - ASSERT(!"Unhandled mob family"); - return -1; - } + case mfHostile: return 40_tick; + case mfPassive: return 40_tick; + case mfAmbient: return 40_tick; + case mfWater: return 400_tick; + case mfNoSpawn: return -1_tick; } + UNREACHABLE("Unhandled mob family"); } @@ -1654,7 +1646,7 @@ bool cMonster::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk) if ( (Chunk->GetBlock(Rel) != E_BLOCK_SOULSAND) && // Not on soulsand - (GetWorld()->GetTimeOfDay() < 12000 + 1000) && // Daytime + (GetWorld()->GetTimeOfDay() < 13000_tick) && // Daytime Chunk->IsWeatherSunnyAt(Rel.x, Rel.z) && // Not raining !IsInWater() // Isn't swimming ) diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index 830919c22..aca32e0ef 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -32,8 +32,7 @@ public: mfAmbient = 2, // Bats mfWater = 3, // Squid, Guardian - mfNoSpawn, - mfUnhandled, // Nothing. Be sure this is the last and the others are in order + mfNoSpawn } ; // tolua_end @@ -187,11 +186,11 @@ public: /** Returns the mob family based on the type */ static eFamily FamilyFromType(eMonsterType a_MobType); - /** Returns the spawn delay (number of game ticks between spawn attempts) for the given mob family */ - static int GetSpawnDelay(cMonster::eFamily a_MobFamily); - // tolua_end + /** Returns the spawn delay (number of game ticks between spawn attempts) for the given mob family */ + static cTickTime GetSpawnDelay(cMonster::eFamily a_MobFamily); + /** Translates the MobType enum to the vanilla nbt name */ static AString MobTypeToVanillaNBT(eMonsterType a_MobType); diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 71fc37d04..26a411909 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -438,7 +438,7 @@ public: virtual void SendTabCompletionResults (const AStringVector & a_Results) = 0; virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; virtual void SendTitleTimes (int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) = 0; - virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_WorldDate, bool a_DoDaylightCycle) = 0; + virtual void SendTimeUpdate (cTickTimeLong a_WorldAge, cTickTimeLong a_WorldDate, bool a_DoDaylightCycle) = 0; virtual void SendUnleashEntity (const cEntity & a_Entity) = 0; virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0; virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) = 0; diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp index 7d4dfb85d..3c377ce21 100644 --- a/src/Protocol/Protocol_1_8.cpp +++ b/src/Protocol/Protocol_1_8.cpp @@ -1566,20 +1566,23 @@ void cProtocol_1_8_0::SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int -void cProtocol_1_8_0::SendTimeUpdate(Int64 a_WorldAge, Int64 a_WorldDate, bool a_DoDaylightCycle) +void cProtocol_1_8_0::SendTimeUpdate(const cTickTimeLong a_WorldAge, const cTickTimeLong a_WorldDate, const bool a_DoDaylightCycle) { ASSERT(m_State == 3); // In game mode? - if (!a_DoDaylightCycle) + cPacketizer Pkt(*this, pktTimeUpdate); + Pkt.WriteBEInt64(a_WorldAge.count()); + + if (a_DoDaylightCycle) + { + Pkt.WriteBEInt64(a_WorldDate.count()); + } + else { // Negating the date stops time from advancing on the client // (the std::min construction is to handle the case where the date is exactly zero): - a_WorldDate = std::min(-a_WorldDate, -1LL); + Pkt.WriteBEInt64(std::min(-a_WorldDate.count(), -1LL)); } - - cPacketizer Pkt(*this, pktTimeUpdate); - Pkt.WriteBEInt64(a_WorldAge); - Pkt.WriteBEInt64(a_WorldDate); } diff --git a/src/Protocol/Protocol_1_8.h b/src/Protocol/Protocol_1_8.h index d899dc067..26fc6e8a8 100644 --- a/src/Protocol/Protocol_1_8.h +++ b/src/Protocol/Protocol_1_8.h @@ -119,7 +119,7 @@ public: virtual void SendTabCompletionResults (const AStringVector & a_Results) override; virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override; virtual void SendTitleTimes (int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks) override; - virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_WorldDate, bool a_DoDaylightCycle) override; + virtual void SendTimeUpdate (cTickTimeLong a_WorldAge, cTickTimeLong a_WorldDate, bool a_DoDaylightCycle) override; virtual void SendUnleashEntity (const cEntity & a_Entity) override; virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override; virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override; diff --git a/src/Simulator/IncrementalRedstoneSimulator/DaylightSensorHandler.h b/src/Simulator/IncrementalRedstoneSimulator/DaylightSensorHandler.h index 51a5fcb3b..75e6651da 100644 --- a/src/Simulator/IncrementalRedstoneSimulator/DaylightSensorHandler.h +++ b/src/Simulator/IncrementalRedstoneSimulator/DaylightSensorHandler.h @@ -18,7 +18,7 @@ namespace DaylightSensorHandler } // The [0, 1) proportion of the current day that has elapsed. - const auto ProportionOfDay = a_Chunk.GetWorld()->GetTimeOfDay() * (static_cast(M_PI) / 12000.f); + const auto ProportionOfDay = a_Chunk.GetWorld()->GetTimeOfDay().count() * (static_cast(M_PI) / 12000.f); // The curved value of darkened skylight, with outputs somewhat similar to Vanilla. const auto RawOutput = a_Chunk.GetSkyLightAltered(a_Position) * (0.6f * std::sin(ProportionOfDay) + 0.5f); diff --git a/src/World.cpp b/src/World.cpp index 0a03110e2..271a295de 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -108,7 +108,7 @@ cWorld::cTickThread::cTickThread(cWorld & a_World) : void cWorld::cTickThread::Execute(void) { auto LastTime = std::chrono::steady_clock::now(); - auto TickTime = std::chrono::duration_cast(cTickTime(1)); + auto TickTime = std::chrono::duration_cast(1_tick); while (!m_ShouldTerminate) { @@ -117,10 +117,10 @@ void cWorld::cTickThread::Execute(void) m_World.Tick(WaitTime, TickTime); TickTime = std::chrono::duration_cast(std::chrono::steady_clock::now() - NowTime); - if (TickTime < cTickTime(1)) + if (TickTime < 1_tick) { - // Stretch tick time until it's at least 1 tick - std::this_thread::sleep_for(cTickTime(1) - TickTime); + // Stretch tick time until it's at least 1 tick: + std::this_thread::sleep_for(1_tick - TickTime); } LastTime = NowTime; @@ -404,7 +404,7 @@ cWorld::cWorld( cComposableGenerator::InitializeGeneratorDefaults(IniFile, m_Dimension); InitializeAndLoadMobSpawningValues(IniFile); - SetTimeOfDay(IniFile.GetValueSetI("General", "TimeInTicks", GetTimeOfDay())); + m_WorldDate = cTickTime(IniFile.GetValueSetI("General", "TimeInTicks", GetWorldDate().count())); // preallocate some memory for ticking blocks so we don't need to allocate that often m_BlockTickQueue.reserve(1000); @@ -434,10 +434,10 @@ cWorld::cWorld( } // Init of the spawn monster time (as they are supposed to have different spawn rate) - m_LastSpawnMonster.emplace(cMonster::mfHostile, cTickTimeLong(0)); - m_LastSpawnMonster.emplace(cMonster::mfPassive, cTickTimeLong(0)); - m_LastSpawnMonster.emplace(cMonster::mfAmbient, cTickTimeLong(0)); - m_LastSpawnMonster.emplace(cMonster::mfWater, cTickTimeLong(0)); + m_LastSpawnMonster.emplace(cMonster::mfHostile, 0_tick); + m_LastSpawnMonster.emplace(cMonster::mfPassive, 0_tick); + m_LastSpawnMonster.emplace(cMonster::mfAmbient, 0_tick); + m_LastSpawnMonster.emplace(cMonster::mfWater, 0_tick); } @@ -475,31 +475,49 @@ void cWorld::CastThunderbolt(Vector3i a_Block) -int cWorld::GetTimeOfDay(void) const +cTickTime cWorld::GetTimeOfDay(void) const { using namespace std::chrono_literals; - return std::chrono::duration_cast(m_WorldDate % 20min).count(); + return std::chrono::duration_cast(m_WorldDate % 20min); } -Int64 cWorld::GetWorldAge(void) const +cTickTimeLong cWorld::GetWorldAge(void) const { - return std::chrono::duration_cast(m_WorldAge).count(); + return std::chrono::duration_cast(m_WorldAge); } -void cWorld::SetTimeOfDay(int a_TimeOfDay) +cTickTimeLong cWorld::GetWorldDate() const +{ + return std::chrono::duration_cast(m_WorldDate); +} + + + + + +cTickTimeLong cWorld::GetWorldTickAge() const +{ + return m_WorldTickAge; +} + + + + + +void cWorld::SetTimeOfDay(const cTickTime a_TimeOfDay) { using namespace std::chrono_literals; - m_WorldDate = (m_WorldDate / 20min) * 20min + cTickTime(a_TimeOfDay); + m_WorldDate = (m_WorldDate / 20min) * 20min + a_TimeOfDay; UpdateSkyDarkness(); BroadcastTimeUpdate(); } @@ -955,7 +973,7 @@ void cWorld::Stop(cDeadlockDetect & a_DeadlockDetect) IniFile.SetValueB("Mechanics", "UseChatPrefixes", m_bUseChatPrefixes); IniFile.SetValueB("General", "IsDaylightCycleEnabled", m_IsDaylightCycleEnabled); IniFile.SetValueI("General", "Weather", static_cast(m_Weather)); - IniFile.SetValueI("General", "TimeInTicks", GetTimeOfDay()); + IniFile.SetValueI("General", "TimeInTicks", GetWorldDate().count()); IniFile.SetValueI("General", "WorldAgeMS", static_cast(m_WorldAge.count())); IniFile.WriteFile(m_IniFileName); @@ -988,7 +1006,7 @@ void cWorld::Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_La cPluginManager::Get()->CallHookWorldTick(*this, a_Dt, a_LastTickDurationMSec); m_WorldAge += a_Dt; - m_WorldTickAge += 1; + m_WorldTickAge++; if (m_IsDaylightCycleEnabled) { @@ -998,14 +1016,14 @@ void cWorld::Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_La UpdateSkyDarkness(); // Broadcast time update every 64 ticks (3.2 seconds): - if ((m_WorldTickAge % 64) == 0) + if ((m_WorldTickAge % 64_tick) == 0_tick) { BroadcastTimeUpdate(); } } // Broadcast player list pings every 256 ticks (12.8 seconds): - if ((m_WorldTickAge % 256) == 0) + if ((m_WorldTickAge % 256_tick) == 0_tick) { BroadcastPlayerListUpdatePing(); } @@ -1098,15 +1116,14 @@ void cWorld::TickMobs(std::chrono::milliseconds a_Dt) for (size_t i = 0; i < ARRAYCOUNT(AllFamilies); i++) { cMonster::eFamily Family = AllFamilies[i]; - cTickTime SpawnDelay = cTickTime(cMonster::GetSpawnDelay(Family)); if ( - (m_LastSpawnMonster[Family] > m_WorldAge - SpawnDelay) || // Not reached the needed ticks before the next round + (m_LastSpawnMonster[Family] > (m_WorldTickAge - cMonster::GetSpawnDelay(Family))) || // Not reached the needed ticks before the next round MobCensus.IsCapped(Family) ) { continue; } - m_LastSpawnMonster[Family] = std::chrono::duration_cast(m_WorldAge); + m_LastSpawnMonster[Family] = m_WorldTickAge; cMobSpawner Spawner(Family, m_AllowedMobs); if (Spawner.CanSpawnAnything()) { @@ -1258,11 +1275,9 @@ void cWorld::TickQueuedTasks(void) // Partition everything to be executed by returning false to move to end of list if time reached auto MoveBeginIterator = std::partition(m_Tasks.begin(), m_Tasks.end(), [this](const decltype(m_Tasks)::value_type & a_Task) - { - const auto WorldAgeTicks = std::chrono::duration_cast(m_WorldAge).count(); - return (a_Task.first >= WorldAgeTicks); - } - ); + { + return a_Task.first >= m_WorldAge; + }); // Cut all the due tasks from m_Tasks into Tasks: Tasks.insert( @@ -1286,11 +1301,11 @@ void cWorld::TickQueuedTasks(void) void cWorld::UpdateSkyDarkness(void) { - const int TIME_SUNSET = 12000; - const int TIME_NIGHT_START = 13187; - const int TIME_NIGHT_END = 22812; - const int TIME_SUNRISE = 23999; - const int TIME_SPAWN_DIVISOR = 148; + const auto TIME_SUNSET = 12000_tick; + const auto TIME_NIGHT_START = 13187_tick; + const auto TIME_NIGHT_END = 22812_tick; + const auto TIME_SUNRISE = 23999_tick; + const auto TIME_SPAWN_DIVISOR = 148_tick; const auto TempTime = GetTimeOfDay(); if (TempTime <= TIME_SUNSET) @@ -1448,7 +1463,7 @@ bool cWorld::GrowTreeFromSapling(Vector3i a_BlockPos) { cNoise Noise(m_Generator.GetSeed()); sSetBlockVector Logs, Other; - auto WorldAge = static_cast(std::chrono::duration_cast(m_WorldAge).count() & 0xffffffff); + auto WorldAge = static_cast(m_WorldTickAge.count() & 0xffffffff); auto SaplingMeta = GetBlockMeta(a_BlockPos); switch (SaplingMeta & 0x07) { @@ -1577,7 +1592,7 @@ bool cWorld::GrowTreeByBiome(const Vector3i a_BlockPos) { cNoise Noise(m_Generator.GetSeed()); sSetBlockVector Logs, Other; - auto seq = static_cast(std::chrono::duration_cast(m_WorldAge).count() & 0xffffffff); + auto seq = static_cast(m_WorldTickAge.count() & 0xffffffff); GetTreeImageByBiome(a_BlockPos, Noise, seq, GetBiomeAt(a_BlockPos.x, a_BlockPos.z), Logs, Other); Other.insert(Other.begin(), Logs.begin(), Logs.end()); Logs.clear(); @@ -2192,7 +2207,7 @@ bool cWorld::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const void cWorld::UnloadUnusedChunks(void) { - m_LastChunkCheck = std::chrono::duration_cast(m_WorldAge); + m_LastChunkCheck = m_WorldAge; m_ChunkMap.UnloadUnusedChunks(); } @@ -2668,7 +2683,7 @@ void cWorld::SaveAllChunks(void) { if (IsSavingEnabled()) { - m_LastSave = std::chrono::duration_cast(m_WorldAge); + m_LastSave = m_WorldAge; m_ChunkMap.SaveAllChunks(); } } @@ -2696,9 +2711,9 @@ void cWorld::QueueTask(std::function a_Task) -void cWorld::ScheduleTask(int a_DelayTicks, std::function a_Task) +void cWorld::ScheduleTask(const cTickTime a_DelayTicks, std::function a_Task) { - Int64 TargetTick = a_DelayTicks + std::chrono::duration_cast(m_WorldAge).count(); + const auto TargetTick = a_DelayTicks + m_WorldAge; // Insert the task into the list of scheduled tasks { diff --git a/src/World.h b/src/World.h index feed6a216..9c488c645 100644 --- a/src/World.h +++ b/src/World.h @@ -106,16 +106,11 @@ public: BroadcastTimeUpdate(); } - virtual int GetTimeOfDay(void) const override; - virtual Int64 GetWorldAge(void) const override; - void SetTicksUntilWeatherChange(int a_WeatherInterval) { m_WeatherInterval = a_WeatherInterval; } - virtual void SetTimeOfDay(int a_TimeOfDay) override; - /** Returns the default weather interval for the specific weather type. Returns -1 for any unknown weather. */ int GetDefaultWeatherInterval(eWeather a_Weather) const; @@ -150,6 +145,13 @@ public: // tolua_end + virtual cTickTime GetTimeOfDay(void) const override; + virtual cTickTimeLong GetWorldAge(void) const override; + cTickTimeLong GetWorldDate() const; + cTickTimeLong GetWorldTickAge() const; + + virtual void SetTimeOfDay(cTickTime a_TimeOfDay) override; + /** Retrieves the world height at the specified coords; returns false if chunk not loaded / generated */ bool TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height); // Exported in ManualBindings.cpp @@ -754,7 +756,7 @@ public: void QueueTask(std::function a_Task); // Exported in ManualBindings.cpp /** Queues a lambda task onto the tick thread, with the specified delay. */ - void ScheduleTask(int a_DelayTicks, std::function a_Task); + void ScheduleTask(cTickTime a_DelayTicks, std::function a_Task); /** Returns the number of chunks loaded */ size_t GetNumChunks() const; // tolua_export @@ -993,10 +995,10 @@ private: /** The time since this world began, in ticks. Monotonic, but does not persist across restarts. Used for less important but heavy tasks that run periodically. These tasks don't need to follow wallclock time, and slowing their rate down if TPS drops is desirable. */ - unsigned long long m_WorldTickAge; + cTickTimeLong m_WorldTickAge; - cTickTimeLong m_LastChunkCheck; // The last WorldAge (in ticks) in which unloading and possibly saving was triggered - cTickTimeLong m_LastSave; // The last WorldAge (in ticks) in which save-all was triggerred + std::chrono::milliseconds m_LastChunkCheck; // The last WorldAge in which unloading and possibly saving was triggered. + std::chrono::milliseconds m_LastSave; // The last WorldAge in which save-all was triggerred. std::map 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; @@ -1093,7 +1095,7 @@ private: cCriticalSection m_CSTasks; /** Tasks that have been queued onto the tick thread, possibly to be executed at target tick in the future; guarded by m_CSTasks */ - std::vector>> m_Tasks; + std::vector>> m_Tasks; /** Guards m_EntitiesToAdd */ cCriticalSection m_CSEntitiesToAdd; diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 7ce85616c..e96acccef 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -1252,7 +1252,7 @@ void NBTChunkSerializer::Serialize(const cWorld & aWorld, cChunkCoords aCoords, } // Save the world age to the chunk data. Required by vanilla and mcedit. - aWriter.AddLong("LastUpdate", aWorld.GetWorldAge()); + aWriter.AddLong("LastUpdate", aWorld.GetWorldAge().count()); // Store the flag that the chunk has all the ores, trees, dungeons etc. Cuberite chunks are always complete. aWriter.AddByte("TerrainPopulated", 1); diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index dae1ca825..18c4f13b6 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -109,8 +109,8 @@ cWSSAnvil::cWSSAnvil(cWorld * a_World, int a_CompressionFactor) : Writer.AddInt("SpawnY", FloorC(a_World->GetSpawnY())); Writer.AddInt("SpawnZ", FloorC(a_World->GetSpawnZ())); Writer.AddInt("version", 19133); - Writer.AddLong("DayTime", a_World->GetTimeOfDay()); - Writer.AddLong("Time", a_World->GetWorldAge()); + Writer.AddLong("DayTime", a_World->GetWorldDate().count()); + Writer.AddLong("Time", a_World->GetWorldAge().count()); Writer.AddLong("SizeOnDisk", 0); Writer.AddString("generatorName", "default"); Writer.AddString("generatorOptions", ""); -- cgit v1.2.3