mirror of
https://github.com/cuberite/cuberite.git
synced 2025-01-08 11:57:39 +08:00
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.
This commit is contained in:
parent
a2a2226179
commit
4cd49d7eca
@ -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.",
|
||||
|
@ -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",
|
||||
},
|
||||
|
@ -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);
|
||||
|
@ -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<lua_Number>(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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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<cTickTimeLong>(m_WorldDate).count(), IsDaylightCycleEnabled());
|
||||
a_Client.SendTimeUpdate(GetWorldAge(), GetWorldDate(), IsDaylightCycleEnabled());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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<int> m_TicksSinceLastPacket;
|
||||
|
||||
|
@ -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<long long>(a_WorldAge)
|
||||
a_WorldName.c_str(), static_cast<long long>(a_WorldAge.count())
|
||||
);
|
||||
ListTrackedCSs();
|
||||
ASSERT(!"Deadlock detected");
|
||||
abort();
|
||||
std::abort();
|
||||
}
|
||||
|
||||
|
||||
@ -182,7 +181,3 @@ void cDeadlockDetect::ListTrackedCSs(void)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 C = int, typename T>
|
||||
typename std::enable_if<std::is_arithmetic<T>::value, C>::type FloorC(T a_Value)
|
||||
{
|
||||
return static_cast<C>(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 C = int, typename T>
|
||||
typename std::enable_if<std::is_arithmetic<T>::value, C>::type CeilC(T a_Value)
|
||||
{
|
||||
@ -356,9 +356,17 @@ typename std::enable_if<std::is_arithmetic<T>::value, C>::type CeilC(T a_Value)
|
||||
|
||||
|
||||
|
||||
// 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>;
|
||||
// A time duration representing a Minecraft tick (50 ms), capable of storing at least 32'767 ticks.
|
||||
using cTickTime = std::chrono::duration<signed int, std::ratio_multiply<std::chrono::milliseconds::period, std::ratio<50>>>;
|
||||
|
||||
// A time duration representing a Minecraft tick (50 ms), capable of storing at least a 64 bit signed duration.
|
||||
using cTickTimeLong = std::chrono::duration<signed long long int, cTickTime::period>;
|
||||
|
||||
/** 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<std::byte>;
|
||||
using ContiguousByteBufferView = std::basic_string_view<std::byte>;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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<float>(M_PI) / 12000.f);
|
||||
const auto ProportionOfDay = a_Chunk.GetWorld()->GetTimeOfDay().count() * (static_cast<float>(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);
|
||||
|
@ -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<std::chrono::milliseconds>(cTickTime(1));
|
||||
auto TickTime = std::chrono::duration_cast<std::chrono::milliseconds>(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::milliseconds>(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<cTickTime>(m_WorldDate % 20min).count();
|
||||
return std::chrono::duration_cast<cTickTime>(m_WorldDate % 20min);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Int64 cWorld::GetWorldAge(void) const
|
||||
cTickTimeLong cWorld::GetWorldAge(void) const
|
||||
{
|
||||
return std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count();
|
||||
return std::chrono::duration_cast<cTickTimeLong>(m_WorldAge);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cWorld::SetTimeOfDay(int a_TimeOfDay)
|
||||
cTickTimeLong cWorld::GetWorldDate() const
|
||||
{
|
||||
return std::chrono::duration_cast<cTickTimeLong>(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<int>(m_Weather));
|
||||
IniFile.SetValueI("General", "TimeInTicks", GetTimeOfDay());
|
||||
IniFile.SetValueI("General", "TimeInTicks", GetWorldDate().count());
|
||||
IniFile.SetValueI("General", "WorldAgeMS", static_cast<Int64>(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<cTickTimeLong>(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<cTickTimeLong>(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<int>(std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count() & 0xffffffff);
|
||||
auto WorldAge = static_cast<int>(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<int>(std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count() & 0xffffffff);
|
||||
auto seq = static_cast<int>(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<cTickTimeLong>(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<cTickTimeLong>(m_WorldAge);
|
||||
m_LastSave = m_WorldAge;
|
||||
m_ChunkMap.SaveAllChunks();
|
||||
}
|
||||
}
|
||||
@ -2696,9 +2711,9 @@ void cWorld::QueueTask(std::function<void(cWorld &)> a_Task)
|
||||
|
||||
|
||||
|
||||
void cWorld::ScheduleTask(int a_DelayTicks, std::function<void (cWorld &)> a_Task)
|
||||
void cWorld::ScheduleTask(const cTickTime a_DelayTicks, std::function<void (cWorld &)> a_Task)
|
||||
{
|
||||
Int64 TargetTick = a_DelayTicks + std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count();
|
||||
const auto TargetTick = a_DelayTicks + m_WorldAge;
|
||||
|
||||
// Insert the task into the list of scheduled tasks
|
||||
{
|
||||
|
22
src/World.h
22
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<void(cWorld &)> 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<void(cWorld &)> a_Task);
|
||||
void ScheduleTask(cTickTime a_DelayTicks, std::function<void(cWorld &)> 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<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;
|
||||
@ -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<std::pair<Int64, std::function<void(cWorld &)>>> m_Tasks;
|
||||
std::vector<std::pair<std::chrono::milliseconds, std::function<void(cWorld &)>>> m_Tasks;
|
||||
|
||||
/** Guards m_EntitiesToAdd */
|
||||
cCriticalSection m_CSEntitiesToAdd;
|
||||
|
@ -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);
|
||||
|
@ -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", "");
|
||||
|
Loading…
Reference in New Issue
Block a user