diff options
author | Tiger Wang <ziwei.tiger@outlook.com> | 2021-02-06 19:37:03 +0100 |
---|---|---|
committer | Tiger Wang <ziwei.tiger@outlook.com> | 2021-02-07 23:36:15 +0100 |
commit | be2bf999c280d6892b38272d3f783b3462f0b745 (patch) | |
tree | 6567e7f011e50dd8e6f65f7dd933a1d7dc34ea93 /src/Entities/Player.cpp | |
parent | Adds playerlist header and footer broadcasting (1.8-1.13) (diff) | |
download | cuberite-be2bf999c280d6892b38272d3f783b3462f0b745.tar cuberite-be2bf999c280d6892b38272d3f783b3462f0b745.tar.gz cuberite-be2bf999c280d6892b38272d3f783b3462f0b745.tar.bz2 cuberite-be2bf999c280d6892b38272d3f783b3462f0b745.tar.lz cuberite-be2bf999c280d6892b38272d3f783b3462f0b745.tar.xz cuberite-be2bf999c280d6892b38272d3f783b3462f0b745.tar.zst cuberite-be2bf999c280d6892b38272d3f783b3462f0b745.zip |
Diffstat (limited to 'src/Entities/Player.cpp')
-rw-r--r-- | src/Entities/Player.cpp | 843 |
1 files changed, 424 insertions, 419 deletions
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index d20795643..e3994e88c 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -179,47 +179,6 @@ cPlayer::cPlayer(const cClientHandlePtr & a_Client) : -void cPlayer::AddKnownItem(const cItem & a_Item) -{ - if (a_Item.m_ItemType < 0) - { - return; - } - - auto Response = m_KnownItems.insert(a_Item.CopyOne()); - if (!Response.second) - { - // The item was already known, bail out: - return; - } - - // Process the recipes that got unlocked by this newly-known item: - auto Recipes = cRoot::Get()->GetCraftingRecipes()->FindNewRecipesForItem(a_Item, m_KnownItems); - for (const auto & RecipeId : Recipes) - { - AddKnownRecipe(RecipeId); - } -} - - - - - -void cPlayer::AddKnownRecipe(UInt32 a_RecipeId) -{ - auto Response = m_KnownRecipes.insert(a_RecipeId); - if (!Response.second) - { - // The recipe was already known, bail out: - return; - } - m_ClientHandle->SendUnlockRecipe(a_RecipeId); -} - - - - - cPlayer::~cPlayer(void) { LOGD("Deleting cPlayer \"%s\" at %p, ID %d", GetName().c_str(), static_cast<void *>(this), GetUniqueID()); @@ -238,302 +197,6 @@ cPlayer::~cPlayer(void) -void cPlayer::OnAddToWorld(cWorld & a_World) -{ - Super::OnAddToWorld(a_World); - - // Update world name tracking: - m_CurrentWorldName = m_World->GetName(); - - // Fix to stop the player falling through the world, until we get serversided collision detection: - FreezeInternal(GetPosition(), false); - - // Set capabilities based on new world: - SetCapabilities(); - - // Send contents of the inventory window: - m_ClientHandle->SendWholeInventory(*m_CurrentWindow); - - // Send health (the respawn packet, which understandably resets health, is also used for world travel...): - m_ClientHandle->SendHealth(); - - // Send experience, similar story with the respawn packet: - m_ClientHandle->SendExperience(); - - // Send hotbar active slot (also reset by respawn): - m_ClientHandle->SendHeldItemChange(m_Inventory.GetEquippedSlotNum()); - - // Update player team: - UpdateTeam(); - - // Send scoreboard data: - m_World->GetScoreBoard().SendTo(*m_ClientHandle); - - // Update the view distance: - m_ClientHandle->SetViewDistance(m_ClientHandle->GetRequestedViewDistance()); - - // Send current weather of target world: - m_ClientHandle->SendWeather(a_World.GetWeather()); - - // Send time: - m_ClientHandle->SendTimeUpdate(a_World.GetWorldAge(), a_World.GetTimeOfDay(), a_World.IsDaylightCycleEnabled()); - - // Finally, deliver the notification hook: - cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*this); -} - - - - - -void cPlayer::OnRemoveFromWorld(cWorld & a_World) -{ - Super::OnRemoveFromWorld(a_World); - - // Remove any references to this player pointer by windows in the old world: - CloseWindow(false); - - // Remove the client handle from the world: - m_World->RemoveClientFromChunks(m_ClientHandle.get()); - - if (m_ClientHandle->IsDestroyed()) // Note: checking IsWorldChangeScheduled not appropriate here since we can disconnecting while having a scheduled warp - { - // Disconnecting, do the necessary cleanup. - // This isn't in the destructor to avoid crashing accessing destroyed objects during shutdown. - - if (!cRoot::Get()->GetPluginManager()->CallHookPlayerDestroyed(*this)) - { - cRoot::Get()->BroadcastChatLeave(Printf("%s has left the game", GetName().c_str())); - LOGINFO("Player %s has left the game", GetName().c_str()); - } - - // Remove ourself from everyone's lists: - cRoot::Get()->BroadcastPlayerListsRemovePlayer(*this); - - // Atomically decrement player count (in world thread): - cRoot::Get()->GetServer()->PlayerDestroyed(); - - // We're just disconnecting. The remaining code deals with going through portals, so bail: - return; - } - - const auto DestinationDimension = m_WorldChangeInfo.m_NewWorld->GetDimension(); - - // Award relevant achievements: - if (DestinationDimension == dimEnd) - { - AwardAchievement(Statistic::AchTheEnd); - } - else if (DestinationDimension == dimNether) - { - AwardAchievement(Statistic::AchPortal); - } - - // Clear sent chunk lists from the clienthandle: - m_ClientHandle->RemoveFromWorld(); - - // The clienthandle caches the coords of the chunk we're standing at. Invalidate this. - m_ClientHandle->InvalidateCachedSentChunk(); - - // Clientside warp start: - m_ClientHandle->SendRespawn(DestinationDimension, false); -} - - - - - -void cPlayer::SpawnOn(cClientHandle & a_Client) -{ - if (!m_bVisible || (m_ClientHandle.get() == (&a_Client))) - { - return; - } - - LOGD("Spawing %s on %s", GetName().c_str(), a_Client.GetUsername().c_str()); - - a_Client.SendPlayerSpawn(*this); - a_Client.SendEntityHeadLook(*this); - a_Client.SendEntityEquipment(*this, 0, m_Inventory.GetEquippedItem()); - a_Client.SendEntityEquipment(*this, 1, m_Inventory.GetEquippedBoots()); - a_Client.SendEntityEquipment(*this, 2, m_Inventory.GetEquippedLeggings()); - a_Client.SendEntityEquipment(*this, 3, m_Inventory.GetEquippedChestplate()); - a_Client.SendEntityEquipment(*this, 4, m_Inventory.GetEquippedHelmet()); -} - - - - - -void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) -{ - m_ClientHandle->Tick(a_Dt.count()); - - if (m_ClientHandle->IsDestroyed()) - { - Destroy(); - return; - } - - if (!m_ClientHandle->IsPlaying()) - { - // We're not yet in the game, ignore everything: - return; - } - - m_Stats.AddValue(Statistic::PlayOneMinute); - m_Stats.AddValue(Statistic::TimeSinceDeath); - - if (IsCrouched()) - { - m_Stats.AddValue(Statistic::SneakTime); - } - - // Handle the player detach, when the player is in spectator mode - if ( - (IsGameModeSpectator()) && - (m_AttachedTo != nullptr) && - ( - (m_AttachedTo->IsDestroyed()) || // Watching entity destruction - (m_AttachedTo->GetHealth() <= 0) || // Watching entity dead - (IsCrouched()) // Or the player wants to be detached - ) - ) - { - Detach(); - } - - if (!a_Chunk.IsValid()) - { - // Players are ticked even if the parent chunk is invalid. - // We've processed as much as we can, bail: - return; - } - - ASSERT((GetParentChunk() != nullptr) && (GetParentChunk()->IsValid())); - ASSERT(a_Chunk.IsValid()); - - // Handle a frozen player: - TickFreezeCode(); - - if ( - m_IsFrozen || // Don't do Tick updates if frozen - IsWorldChangeScheduled() // If we're about to change worlds (e.g. respawn), abort processing all world interactions (GH #3939) - ) - { - return; - } - - Super::Tick(a_Dt, a_Chunk); - - // Handle charging the bow: - if (m_IsChargingBow) - { - m_BowCharge += 1; - } - - BroadcastMovementUpdate(m_ClientHandle.get()); - - if (m_Health > 0) // make sure player is alive - { - m_World->CollectPickupsByPlayer(*this); - - if ((m_EatingFinishTick >= 0) && (m_EatingFinishTick <= m_World->GetWorldAge())) - { - FinishEating(); - } - - HandleFood(); - } - - if (m_IsFishing) - { - HandleFloater(); - } - - // Update items (e.g. Maps) - m_Inventory.UpdateItems(); - - // Send Player List (Once per m_LastPlayerListTime/1000 ms) - if (m_LastPlayerListTime + PLAYER_LIST_TIME_MS <= std::chrono::steady_clock::now()) - { - m_World->BroadcastPlayerListUpdatePing(*this); - m_LastPlayerListTime = std::chrono::steady_clock::now(); - } - - if (m_TicksUntilNextSave == 0) - { - SaveToDisk(); - m_TicksUntilNextSave = PLAYER_INVENTORY_SAVE_INTERVAL; - } - else - { - m_TicksUntilNextSave--; - } -} - - - - - -void cPlayer::TickFreezeCode() -{ - if (m_IsFrozen) - { - if ((!m_IsManuallyFrozen) && (GetClientHandle()->IsPlayerChunkSent())) - { - // If the player was automatically frozen, unfreeze if the chunk the player is inside is loaded and sent - Unfreeze(); - - // Pull the player out of any solids that might have loaded on them. - PREPARE_REL_AND_CHUNK(GetPosition(), *(GetParentChunk())); - if (RelSuccess) - { - int NewY = Rel.y; - if (NewY < 0) - { - NewY = 0; - } - while (NewY < cChunkDef::Height - 2) - { - // If we find a position with enough space for the player - if ( - !cBlockInfo::IsSolid(Chunk->GetBlock(Rel.x, NewY, Rel.z)) && - !cBlockInfo::IsSolid(Chunk->GetBlock(Rel.x, NewY + 1, Rel.z)) - ) - { - // If the found position is not the same as the original - if (NewY != Rel.y) - { - SetPosition(GetPosition().x, NewY, GetPosition().z); - GetClientHandle()->SendPlayerPosition(); - } - break; - } - ++NewY; - } - } - } - else if (GetWorld()->GetWorldAge() % 100 == 0) - { - // 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 - FreezeInternal(GetPosition(), m_IsManuallyFrozen); - } - } - else - { - if (!GetClientHandle()->IsPlayerChunkSent() || (!GetParentChunk()->IsValid())) - { - FreezeInternal(GetPosition(), false); - } - } -} - - - - - int cPlayer::CalcLevelFromXp(int a_XpTotal) { // level 0 to 15 @@ -1092,69 +755,6 @@ void cPlayer::SetFlying(bool a_IsFlying) -void cPlayer::ApplyArmorDamage(int a_DamageBlocked) -{ - short ArmorDamage = static_cast<short>(std::max(a_DamageBlocked / 4, 1)); - - for (int i = 0; i < 4; i++) - { - UseItem(cInventory::invArmorOffset + i, ArmorDamage); - } -} - - - - - -bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) -{ - if ((a_TDI.DamageType != dtInVoid) && (a_TDI.DamageType != dtPlugin)) - { - if (IsGameModeCreative() || IsGameModeSpectator()) - { - // No damage / health in creative or spectator mode if not void or plugin damage - return false; - } - } - - if ((a_TDI.Attacker != nullptr) && (a_TDI.Attacker->IsPlayer())) - { - cPlayer * Attacker = static_cast<cPlayer *>(a_TDI.Attacker); - - if ((m_Team != nullptr) && (m_Team == Attacker->m_Team)) - { - if (!m_Team->AllowsFriendlyFire()) - { - // Friendly fire is disabled - return false; - } - } - } - - if (Super::DoTakeDamage(a_TDI)) - { - // Any kind of damage adds food exhaustion - AddFoodExhaustion(0.3f); - m_ClientHandle->SendHealth(); - - // Tell the wolves - if (a_TDI.Attacker != nullptr) - { - if (a_TDI.Attacker->IsPawn()) - { - NotifyNearbyWolves(static_cast<cPawn*>(a_TDI.Attacker), true); - } - } - m_Stats.AddValue(Statistic::DamageTaken, FloorC<cStatManager::StatValue>(a_TDI.FinalDamage * 10 + 0.5)); - return true; - } - return false; -} - - - - - void cPlayer::NotifyNearbyWolves(cPawn * a_Opponent, bool a_IsPlayerInvolved) { ASSERT(a_Opponent != nullptr); @@ -1888,22 +1488,6 @@ void cPlayer::ForceSetSpeed(const Vector3d & a_Speed) -void cPlayer::DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) -{ - if (m_IsFrozen) - { - // Do not set speed to a frozen client - return; - } - Super::DoSetSpeed(a_SpeedX, a_SpeedY, a_SpeedZ); - // Send the speed to the client so he actualy moves - m_ClientHandle->SendEntityVelocity(*this); -} - - - - - void cPlayer::SetVisible(bool a_bVisible) { // Need to Check if the player or other players are in gamemode spectator, but will break compatibility @@ -3211,15 +2795,436 @@ bool cPlayer::CanInstantlyMine(BLOCKTYPE a_Block) -float cPlayer::GetExplosionExposureRate(Vector3d a_ExplosionPosition, float a_ExlosionPower) +void cPlayer::AddKnownItem(const cItem & a_Item) +{ + if (a_Item.m_ItemType < 0) + { + return; + } + + auto Response = m_KnownItems.insert(a_Item.CopyOne()); + if (!Response.second) + { + // The item was already known, bail out: + return; + } + + // Process the recipes that got unlocked by this newly-known item: + auto Recipes = cRoot::Get()->GetCraftingRecipes()->FindNewRecipesForItem(a_Item, m_KnownItems); + for (const auto & RecipeId : Recipes) + { + AddKnownRecipe(RecipeId); + } +} + + + + + +void cPlayer::AddKnownRecipe(UInt32 a_RecipeId) +{ + auto Response = m_KnownRecipes.insert(a_RecipeId); + if (!Response.second) + { + // The recipe was already known, bail out: + return; + } + m_ClientHandle->SendUnlockRecipe(a_RecipeId); +} + + + + + +void cPlayer::TickFreezeCode() +{ + if (m_IsFrozen) + { + if ((!m_IsManuallyFrozen) && (GetClientHandle()->IsPlayerChunkSent())) + { + // If the player was automatically frozen, unfreeze if the chunk the player is inside is loaded and sent + Unfreeze(); + + // Pull the player out of any solids that might have loaded on them. + PREPARE_REL_AND_CHUNK(GetPosition(), *(GetParentChunk())); + if (RelSuccess) + { + int NewY = Rel.y; + if (NewY < 0) + { + NewY = 0; + } + while (NewY < cChunkDef::Height - 2) + { + // If we find a position with enough space for the player + if ( + !cBlockInfo::IsSolid(Chunk->GetBlock(Rel.x, NewY, Rel.z)) && + !cBlockInfo::IsSolid(Chunk->GetBlock(Rel.x, NewY + 1, Rel.z)) + ) + { + // If the found position is not the same as the original + if (NewY != Rel.y) + { + SetPosition(GetPosition().x, NewY, GetPosition().z); + GetClientHandle()->SendPlayerPosition(); + } + break; + } + ++NewY; + } + } + } + else if (GetWorld()->GetWorldAge() % 100 == 0) + { + // 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 + FreezeInternal(GetPosition(), m_IsManuallyFrozen); + } + } + else + { + if (!GetClientHandle()->IsPlayerChunkSent() || (!GetParentChunk()->IsValid())) + { + FreezeInternal(GetPosition(), false); + } + } +} + + + + + +void cPlayer::ApplyArmorDamage(int a_DamageBlocked) +{ + short ArmorDamage = static_cast<short>(std::max(a_DamageBlocked / 4, 1)); + + for (int i = 0; i < 4; i++) + { + UseItem(cInventory::invArmorOffset + i, ArmorDamage); + } +} + + + + + +void cPlayer::BroadcastMovementUpdate(const cClientHandle * a_Exclude) +{ + if (!m_IsFrozen && m_Speed.SqrLength() > 0.001) + { + // If the player is not frozen, has a non-zero speed, + // send the speed to the client so he is forced to move so: + m_ClientHandle->SendEntityVelocity(*this); + } + + // Since we do no physics processing for players, speed will otherwise never decrease: + m_Speed.Set(0, 0, 0); + + Super::BroadcastMovementUpdate(a_Exclude); +} + + + + + +bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + // Filters out damage for creative mode / friendly fire. + + if ((a_TDI.DamageType != dtInVoid) && (a_TDI.DamageType != dtPlugin)) + { + if (IsGameModeCreative() || IsGameModeSpectator()) + { + // No damage / health in creative or spectator mode if not void or plugin damage + return false; + } + } + + if ((a_TDI.Attacker != nullptr) && (a_TDI.Attacker->IsPlayer())) + { + cPlayer * Attacker = static_cast<cPlayer *>(a_TDI.Attacker); + + if ((m_Team != nullptr) && (m_Team == Attacker->m_Team)) + { + if (!m_Team->AllowsFriendlyFire()) + { + // Friendly fire is disabled + return false; + } + } + } + + if (Super::DoTakeDamage(a_TDI)) + { + // Any kind of damage adds food exhaustion + AddFoodExhaustion(0.3f); + m_ClientHandle->SendHealth(); + + // Tell the wolves + if (a_TDI.Attacker != nullptr) + { + if (a_TDI.Attacker->IsPawn()) + { + NotifyNearbyWolves(static_cast<cPawn*>(a_TDI.Attacker), true); + } + } + m_Stats.AddValue(Statistic::DamageTaken, FloorC<cStatManager::StatValue>(a_TDI.FinalDamage * 10 + 0.5)); + return true; + } + return false; +} + + + + + +float cPlayer::GetEnchantmentBlastKnockbackReduction() { if ( IsGameModeSpectator() || (IsGameModeCreative() && !IsOnGround()) ) { - return 0; // No impact from explosion + return 1; // No impact from explosion } - return Super::GetExplosionExposureRate(a_ExplosionPosition, a_ExlosionPower) / 30.0f; + return Super::GetEnchantmentBlastKnockbackReduction(); +} + + + + + +void cPlayer::OnAddToWorld(cWorld & a_World) +{ + Super::OnAddToWorld(a_World); + + // Update world name tracking: + m_CurrentWorldName = m_World->GetName(); + + // Fix to stop the player falling through the world, until we get serversided collision detection: + FreezeInternal(GetPosition(), false); + + // Set capabilities based on new world: + SetCapabilities(); + + // Send contents of the inventory window: + m_ClientHandle->SendWholeInventory(*m_CurrentWindow); + + // Send health (the respawn packet, which understandably resets health, is also used for world travel...): + m_ClientHandle->SendHealth(); + + // Send experience, similar story with the respawn packet: + m_ClientHandle->SendExperience(); + + // Send hotbar active slot (also reset by respawn): + m_ClientHandle->SendHeldItemChange(m_Inventory.GetEquippedSlotNum()); + + // Update player team: + UpdateTeam(); + + // Send scoreboard data: + m_World->GetScoreBoard().SendTo(*m_ClientHandle); + + // Update the view distance: + m_ClientHandle->SetViewDistance(m_ClientHandle->GetRequestedViewDistance()); + + // Send current weather of target world: + m_ClientHandle->SendWeather(a_World.GetWeather()); + + // Send time: + m_ClientHandle->SendTimeUpdate(a_World.GetWorldAge(), a_World.GetTimeOfDay(), a_World.IsDaylightCycleEnabled()); + + // Finally, deliver the notification hook: + cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*this); +} + + + + + +void cPlayer::OnRemoveFromWorld(cWorld & a_World) +{ + Super::OnRemoveFromWorld(a_World); + + // Remove any references to this player pointer by windows in the old world: + CloseWindow(false); + + // Remove the client handle from the world: + m_World->RemoveClientFromChunks(m_ClientHandle.get()); + + if (m_ClientHandle->IsDestroyed()) // Note: checking IsWorldChangeScheduled not appropriate here since we can disconnecting while having a scheduled warp + { + // Disconnecting, do the necessary cleanup. + // This isn't in the destructor to avoid crashing accessing destroyed objects during shutdown. + + if (!cRoot::Get()->GetPluginManager()->CallHookPlayerDestroyed(*this)) + { + cRoot::Get()->BroadcastChatLeave(Printf("%s has left the game", GetName().c_str())); + LOGINFO("Player %s has left the game", GetName().c_str()); + } + + // Remove ourself from everyone's lists: + cRoot::Get()->BroadcastPlayerListsRemovePlayer(*this); + + // Atomically decrement player count (in world thread): + cRoot::Get()->GetServer()->PlayerDestroyed(); + + // We're just disconnecting. The remaining code deals with going through portals, so bail: + return; + } + + const auto DestinationDimension = m_WorldChangeInfo.m_NewWorld->GetDimension(); + + // Award relevant achievements: + if (DestinationDimension == dimEnd) + { + AwardAchievement(Statistic::AchTheEnd); + } + else if (DestinationDimension == dimNether) + { + AwardAchievement(Statistic::AchPortal); + } + + // Clear sent chunk lists from the clienthandle: + m_ClientHandle->RemoveFromWorld(); + + // The clienthandle caches the coords of the chunk we're standing at. Invalidate this. + m_ClientHandle->InvalidateCachedSentChunk(); + + // Clientside warp start: + m_ClientHandle->SendRespawn(DestinationDimension, false); +} + + + + + +void cPlayer::SpawnOn(cClientHandle & a_Client) +{ + if (!m_bVisible || (m_ClientHandle.get() == (&a_Client))) + { + return; + } + + LOGD("Spawing %s on %s", GetName().c_str(), a_Client.GetUsername().c_str()); + + a_Client.SendPlayerSpawn(*this); + a_Client.SendEntityHeadLook(*this); + a_Client.SendEntityEquipment(*this, 0, m_Inventory.GetEquippedItem()); + a_Client.SendEntityEquipment(*this, 1, m_Inventory.GetEquippedBoots()); + a_Client.SendEntityEquipment(*this, 2, m_Inventory.GetEquippedLeggings()); + a_Client.SendEntityEquipment(*this, 3, m_Inventory.GetEquippedChestplate()); + a_Client.SendEntityEquipment(*this, 4, m_Inventory.GetEquippedHelmet()); +} + + + + + +void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + m_ClientHandle->Tick(a_Dt.count()); + + if (m_ClientHandle->IsDestroyed()) + { + Destroy(); + return; + } + + if (!m_ClientHandle->IsPlaying()) + { + // We're not yet in the game, ignore everything: + return; + } + + m_Stats.AddValue(Statistic::PlayOneMinute); + m_Stats.AddValue(Statistic::TimeSinceDeath); + + if (IsCrouched()) + { + m_Stats.AddValue(Statistic::SneakTime); + } + + // Handle the player detach, when the player is in spectator mode + if ( + (IsGameModeSpectator()) && + (m_AttachedTo != nullptr) && + ( + (m_AttachedTo->IsDestroyed()) || // Watching entity destruction + (m_AttachedTo->GetHealth() <= 0) || // Watching entity dead + (IsCrouched()) // Or the player wants to be detached + ) + ) + { + Detach(); + } + + if (!a_Chunk.IsValid()) + { + // Players are ticked even if the parent chunk is invalid. + // We've processed as much as we can, bail: + return; + } + + ASSERT((GetParentChunk() != nullptr) && (GetParentChunk()->IsValid())); + ASSERT(a_Chunk.IsValid()); + + // Handle a frozen player: + TickFreezeCode(); + + if ( + m_IsFrozen || // Don't do Tick updates if frozen + IsWorldChangeScheduled() // If we're about to change worlds (e.g. respawn), abort processing all world interactions (GH #3939) + ) + { + return; + } + + Super::Tick(a_Dt, a_Chunk); + + // Handle charging the bow: + if (m_IsChargingBow) + { + m_BowCharge += 1; + } + + BroadcastMovementUpdate(m_ClientHandle.get()); + + if (m_Health > 0) // make sure player is alive + { + m_World->CollectPickupsByPlayer(*this); + + if ((m_EatingFinishTick >= 0) && (m_EatingFinishTick <= m_World->GetWorldAge())) + { + FinishEating(); + } + + HandleFood(); + } + + if (m_IsFishing) + { + HandleFloater(); + } + + // Update items (e.g. Maps) + m_Inventory.UpdateItems(); + + // Send Player List (Once per m_LastPlayerListTime/1000 ms) + if (m_LastPlayerListTime + PLAYER_LIST_TIME_MS <= std::chrono::steady_clock::now()) + { + m_World->BroadcastPlayerListUpdatePing(*this); + m_LastPlayerListTime = std::chrono::steady_clock::now(); + } + + if (m_TicksUntilNextSave == 0) + { + SaveToDisk(); + m_TicksUntilNextSave = PLAYER_INVENTORY_SAVE_INTERVAL; + } + else + { + m_TicksUntilNextSave--; + } } |