From 55ba39ca0e2d4aed9c0c1b3e030727728ea0a02f Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Wed, 17 Mar 2021 23:18:02 +0000 Subject: [PATCH] Don't send ping updates one packet at a time * Use the batch update feature of the packet. * Lengthen interval between time and ping update packets (ref. http://github.com/cuberite/cuberite/issues/4082#issuecomment-348675321). --- src/Blocks/BroadcastInterface.h | 2 +- src/Broadcaster.cpp | 6 +++--- src/ChunkMap.h | 2 +- src/ClientHandle.cpp | 4 ++-- src/ClientHandle.h | 2 +- src/Entities/Player.cpp | 12 ----------- src/Entities/Player.h | 2 -- src/Protocol/Protocol.h | 2 +- src/Protocol/Protocol_1_8.cpp | 14 +++++++++---- src/Protocol/Protocol_1_8.h | 2 +- src/World.cpp | 36 +++++++++++++++++++++++---------- src/World.h | 20 ++++++++++++++---- 12 files changed, 61 insertions(+), 43 deletions(-) diff --git a/src/Blocks/BroadcastInterface.h b/src/Blocks/BroadcastInterface.h index 552686cf8..ab756fe88 100644 --- a/src/Blocks/BroadcastInterface.h +++ b/src/Blocks/BroadcastInterface.h @@ -53,7 +53,7 @@ public: virtual void BroadcastPlayerListRemovePlayer (const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr) = 0; virtual void BroadcastPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName, const cClientHandle * a_Exclude = nullptr) = 0; virtual void BroadcastPlayerListUpdateGameMode (const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr) = 0; - virtual void BroadcastPlayerListUpdatePing (const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr) = 0; + virtual void BroadcastPlayerListUpdatePing () = 0; virtual void BroadcastRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID, const cClientHandle * a_Exclude = nullptr) = 0; virtual void BroadcastScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) = 0; virtual void BroadcastScoreUpdate (const AString & a_Objective, const AString & a_PlayerName, cObjective::Score a_Score, Byte a_Mode) = 0; diff --git a/src/Broadcaster.cpp b/src/Broadcaster.cpp index 1e9f9b876..7cc3deaaa 100644 --- a/src/Broadcaster.cpp +++ b/src/Broadcaster.cpp @@ -488,11 +488,11 @@ void cWorld::BroadcastPlayerListUpdateGameMode(const cPlayer & a_Player, const c -void cWorld::BroadcastPlayerListUpdatePing(const cPlayer & a_Player, const cClientHandle * a_Exclude) +void cWorld::BroadcastPlayerListUpdatePing() { - ForClientsInWorld(*this, a_Exclude, [&](cClientHandle & a_Client) + ForClientsInWorld(*this, nullptr, [&](cClientHandle & a_Client) { - a_Client.SendPlayerListUpdatePing(a_Player); + a_Client.SendPlayerListUpdatePing(); } ); } diff --git a/src/ChunkMap.h b/src/ChunkMap.h index bafadc818..99a1d764d 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -378,7 +378,7 @@ public: void ChunkValidated(void); // Called by chunks that have become valid /** Returns the CS for locking the chunkmap; only cWorld::cLock may use this function! */ - cCriticalSection & GetCS(void) { return m_CSChunks; } + cCriticalSection & GetCS(void) const { return m_CSChunks; } /** Increments (a_AlwaysTicked == true) or decrements (false) the m_AlwaysTicked counter for the specified chunk. If the m_AlwaysTicked counter is greater than zero, the chunk is ticked in the tick-thread regardless of diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 62613bac7..5309ef672 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -2715,9 +2715,9 @@ void cClientHandle::SendPlayerListUpdateGameMode(const cPlayer & a_Player) -void cClientHandle::SendPlayerListUpdatePing(const cPlayer & a_Player) +void cClientHandle::SendPlayerListUpdatePing() { - m_Protocol->SendPlayerListUpdatePing(a_Player); + m_Protocol->SendPlayerListUpdatePing(); } diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 9f1113669..c73a8c873 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -193,7 +193,7 @@ public: // tolua_export void SendPlayerListRemovePlayer (const cPlayer & a_Player); void SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName); void SendPlayerListUpdateGameMode (const cPlayer & a_Player); - void SendPlayerListUpdatePing (const cPlayer & a_Player); + void SendPlayerListUpdatePing (); void SendPlayerMaxSpeed (void); ///< Informs the client of the maximum player speed (1.6.1+) void SendPlayerMoveLook (void); void SendPlayerPosition (void); diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index b6997f5f1..e431e4cc5 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -34,9 +34,6 @@ // 6000 ticks or 5 minutes #define PLAYER_INVENTORY_SAVE_INTERVAL 6000 -// 1000 = once per second -#define PLAYER_LIST_TIME_MS std::chrono::milliseconds(1000) - namespace { @@ -131,8 +128,6 @@ cPlayer::cPlayer(const cClientHandlePtr & a_Client) : SetMaxHealth(MAX_HEALTH); m_Health = MAX_HEALTH; - m_LastPlayerListTime = std::chrono::steady_clock::now(); - cWorld * World = nullptr; if (!LoadFromDisk(World)) { @@ -3205,13 +3200,6 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) // 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(); diff --git a/src/Entities/Player.h b/src/Entities/Player.h index f7d54340e..86d4a2a07 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -656,8 +656,6 @@ private: /** The item being dragged by the cursor while in a UI window */ cItem m_DraggingItem; - std::chrono::steady_clock::time_point m_LastPlayerListTime; - cClientHandlePtr m_ClientHandle; cSlotNums m_InventoryPaintSlots; diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 743a73aba..3c70cdb55 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -410,7 +410,7 @@ public: virtual void SendPlayerListHeaderFooter (const cCompositeChat & a_Header, const cCompositeChat & a_Footer) = 0; virtual void SendPlayerListRemovePlayer (const cPlayer & a_Player) = 0; virtual void SendPlayerListUpdateGameMode (const cPlayer & a_Player) = 0; - virtual void SendPlayerListUpdatePing (const cPlayer & a_Player) = 0; + virtual void SendPlayerListUpdatePing () = 0; virtual void SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName) = 0; virtual void SendPlayerMaxSpeed (void) = 0; ///< Informs the client of the maximum player speed (1.6.1+) virtual void SendPlayerMoveLook (void) = 0; diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp index 654146e14..79f75fcc5 100644 --- a/src/Protocol/Protocol_1_8.cpp +++ b/src/Protocol/Protocol_1_8.cpp @@ -1111,15 +1111,21 @@ void cProtocol_1_8_0::SendPlayerListUpdateGameMode(const cPlayer & a_Player) -void cProtocol_1_8_0::SendPlayerListUpdatePing(const cPlayer & a_Player) +void cProtocol_1_8_0::SendPlayerListUpdatePing() { ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, pktPlayerList); Pkt.WriteVarInt32(2); - Pkt.WriteVarInt32(1); - Pkt.WriteUUID(a_Player.GetUUID()); - Pkt.WriteVarInt32(static_cast(a_Player.GetClientHandle()->GetPing())); + + const auto World = m_Client->GetPlayer()->GetWorld(); + Pkt.WriteVarInt32(static_cast(World->GetPlayerCount())); + World->ForEachPlayer([&Pkt](cPlayer & a_Player) + { + Pkt.WriteUUID(a_Player.GetUUID()); + Pkt.WriteVarInt32(static_cast(a_Player.GetClientHandle()->GetPing())); + return false; + }); } diff --git a/src/Protocol/Protocol_1_8.h b/src/Protocol/Protocol_1_8.h index 9a849e82e..ae6d09417 100644 --- a/src/Protocol/Protocol_1_8.h +++ b/src/Protocol/Protocol_1_8.h @@ -94,7 +94,7 @@ public: virtual void SendPlayerListRemovePlayer (const cPlayer & a_Player) override; virtual void SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName) override; virtual void SendPlayerListUpdateGameMode (const cPlayer & a_Player) override; - virtual void SendPlayerListUpdatePing (const cPlayer & a_Player) override; + virtual void SendPlayerListUpdatePing () override; virtual void SendPlayerMaxSpeed (void) override; virtual void SendPlayerMoveLook (void) override; virtual void SendPlayerPosition (void) override; diff --git a/src/World.cpp b/src/World.cpp index 3e00e2fd5..e0f0b6c1c 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -91,7 +91,7 @@ namespace World //////////////////////////////////////////////////////////////////////////////// // cWorld::cLock: -cWorld::cLock::cLock(cWorld & a_World) : +cWorld::cLock::cLock(const cWorld & a_World) : Super(&(a_World.m_ChunkMap.GetCS())) { } @@ -168,7 +168,7 @@ cWorld::cWorld( m_IsDaylightCycleEnabled(true), m_WorldAge(0), m_TimeOfDay(0), - m_LastTimeUpdate(0), + m_WorldTickAge(0), m_LastChunkCheck(0), m_LastSave(0), m_SkyDarkness(0), @@ -959,33 +959,38 @@ void cWorld::Stop(cDeadlockDetect & a_DeadlockDetect) void cWorld::Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) { - // Call the plugins + // Notify the plugins: cPluginManager::Get()->CallHookWorldTick(*this, a_Dt, a_LastTickDurationMSec); m_WorldAge += a_Dt; + m_WorldTickAge += 1; if (m_IsDaylightCycleEnabled) { - // We need sub-tick precision here, that's why we store the time in milliseconds and calculate ticks off of it m_TimeOfDay += a_Dt; - // Wrap time of day each 20 minutes (1200 seconds) + // Wrap time of day every 20 minutes (1200 seconds): if (m_TimeOfDay > std::chrono::minutes(20)) { m_TimeOfDay -= std::chrono::minutes(20); } - // Updates the sky darkness based on current time of day + // Updates the sky darkness based on current time of day: UpdateSkyDarkness(); - // Broadcast time update every 40 ticks (2 seconds) - if (m_LastTimeUpdate < m_WorldAge - cTickTime(40)) + // Broadcast time update every 64 ticks (3.2 seconds): + if ((m_WorldTickAge % 64) == 0) { BroadcastTimeUpdate(); - m_LastTimeUpdate = std::chrono::duration_cast(m_WorldAge); } } + // Broadcast player list pings every 256 ticks (12.8 seconds): + if ((m_WorldTickAge % 256) == 0) + { + BroadcastPlayerListUpdatePing(); + } + TickQueuedChunkDataSets(); TickQueuedBlocks(); m_ChunkMap.Tick(a_Dt); @@ -993,11 +998,10 @@ void cWorld::Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_La TickQueuedEntityAdditions(); m_MapManager.TickMaps(); TickQueuedTasks(); + TickWeather(static_cast(a_Dt.count())); GetSimulatorManager()->Simulate(static_cast(a_Dt.count())); - TickWeather(static_cast(a_Dt.count())); - if (m_WorldAge - m_LastChunkCheck > std::chrono::seconds(10)) { // Unload every 10 seconds @@ -2535,6 +2539,16 @@ bool cWorld::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback a_Ca +size_t cWorld::GetPlayerCount() const +{ + cLock Lock(*this); + return m_Players.size(); +} + + + + + bool cWorld::DoWithEntityByID(UInt32 a_UniqueID, cEntityCallback a_Callback) { // First check the entities-to-add: diff --git a/src/World.h b/src/World.h index 4857a4655..b94bbacdb 100644 --- a/src/World.h +++ b/src/World.h @@ -78,7 +78,7 @@ public: { using Super = cCSLock; public: - cLock(cWorld & a_World); + cLock(const cWorld & a_World); }; @@ -212,7 +212,7 @@ public: virtual void BroadcastPlayerListRemovePlayer (const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr) override; virtual void BroadcastPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName, const cClientHandle * a_Exclude = nullptr) override; virtual void BroadcastPlayerListUpdateGameMode (const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr) override; - virtual void BroadcastPlayerListUpdatePing (const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr) override; + virtual void BroadcastPlayerListUpdatePing () override; virtual void BroadcastRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID, const cClientHandle * a_Exclude = nullptr) override; virtual void BroadcastScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; virtual void BroadcastScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override; @@ -303,6 +303,9 @@ 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) override; // Exported in ManualBindings.cpp + /** Returns the number of players currently in this world. */ + size_t GetPlayerCount() const; + /** Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found and callback returned false. */ bool DoWithEntityByID(UInt32 a_UniqueID, cEntityCallback a_Callback); // Exported in ManualBindings.cpp @@ -1054,11 +1057,20 @@ private: bool m_IsDaylightCycleEnabled; /** The age of the world. - Monotonic, always increasing each game tick, persistent across server restart. */ + Monotonic, always increasing each game tick, persistent across server restart. + We need sub-tick precision here, that's why we store the time in milliseconds and calculate ticks off of it. */ std::chrono::milliseconds m_WorldAge; + /** The duration of one Minecraft day that has elapsed. + Wraps every 20 minutes. + We need sub-tick precision here, that's why we store the time in milliseconds and calculate ticks off of it. */ std::chrono::milliseconds m_TimeOfDay; - cTickTimeLong m_LastTimeUpdate; // The tick in which the last time update has been sent. + + /** The age of the world, 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_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::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)