]> git.localhorst.tv Git - blank.git/commitdiff
decrease packet frequency
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Thu, 5 Nov 2015 13:20:13 +0000 (14:20 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Thu, 5 Nov 2015 13:20:13 +0000 (14:20 +0100)
src/net/CongestionControl.hpp
src/net/Packet.hpp
src/net/net.cpp
src/server/ClientConnection.hpp
src/server/net.cpp

index 7d4bacacb39a55d1e34dd5aae73367f0af678c06..76f98f61d08173189952b33274398068bfb0af35 100644 (file)
@@ -23,6 +23,8 @@ public:
        Mode GetMode() const noexcept { return mode; }
        /// according to current mode, drop this many unimportant packets
        unsigned int SuggestedPacketSkip() const noexcept { return (1 << mode) - 1; }
+       /// according to current mode, pause between large uncritical packets for this many ticks
+       unsigned int SuggestedPacketHold() const noexcept { return (1 << (mode + 1)) - 1; }
 
        /// packet loss as factor
        float PacketLoss() const noexcept { return packet_loss; }
@@ -45,6 +47,7 @@ private:
 
        void UpdateRTT(std::uint16_t) noexcept;
        bool SamplePacket(std::uint16_t) const noexcept;
+       std::size_t SampleIndex(std::uint16_t) const noexcept;
 
        void UpdateStats() noexcept;
 
@@ -64,7 +67,6 @@ private:
        float packet_loss;
 
        Uint32 stamps[16];
-       std::size_t stamp_cursor;
        std::uint16_t stamp_last;
        float rtt;
 
index 97b76c8a3416b5b83f0cfd83015a506e5becfdf4..7dede0d87d61d0cdb9e1d142fd6694350fe9d025 100644 (file)
@@ -206,6 +206,10 @@ struct Packet {
                static constexpr std::size_t MAX_LEN = MAX_PAYLOAD_LEN;
                static constexpr std::size_t MAX_DATA_LEN = MAX_LEN - 12;
 
+               static constexpr std::size_t GetSize(std::size_t data_len) noexcept {
+                       return data_len + 12;
+               }
+
                void WriteTransmissionId(std::uint32_t) noexcept;
                void ReadTransmissionId(std::uint32_t &) const noexcept;
                void WriteDataOffset(std::uint32_t) noexcept;
index 2e202e0da8037d20fc8b668e4324af42ac314c06..7de901e31f58b7f842461e2a2414e677db8ceca5 100644 (file)
@@ -41,7 +41,6 @@ CongestionControl::CongestionControl()
 , packets_lost(0)
 , packets_received(0)
 , packet_loss(0.0f)
-, stamp_cursor(15)
 , stamp_last(0)
 , rtt(64.0f)
 , next_sample(1000)
@@ -50,11 +49,11 @@ CongestionControl::CongestionControl()
 , tx_kbps(0.0f)
 , rx_kbps(0.0f)
 , mode(GOOD)
-// rtt > 100ms or packet loss > 5% is BAD
-, bad_rtt(100.0f)
+// rtt > 75ms or packet loss > 5% is BAD
+, bad_rtt(75.0f)
 , bad_loss(0.05f)
-// rtt > 250ms or packet loss > 15% is UGLY
-, ugly_rtt(250.0f)
+// rtt > 150ms or packet loss > 15% is UGLY
+, ugly_rtt(150.0f)
 , ugly_loss(0.15f)
 , mode_keep_time(1000) {
        Uint32 now = SDL_GetTicks();
@@ -71,8 +70,7 @@ void CongestionControl::PacketSent(uint16_t seq) noexcept {
        if (!SamplePacket(seq)) {
                return;
        }
-       stamp_cursor = (stamp_cursor + 1) % 16;
-       stamps[stamp_cursor] = SDL_GetTicks();
+       stamps[SampleIndex(seq)] = SDL_GetTicks();
        stamp_last = seq;
 }
 
@@ -97,22 +95,25 @@ void CongestionControl::UpdatePacketLoss() noexcept {
        }
 }
 
-void CongestionControl::UpdateRTT(std::uint16_t seq) noexcept {
+void CongestionControl::UpdateRTT(uint16_t seq) noexcept {
        if (!SamplePacket(seq)) return;
-       int16_t diff = int16_t(seq) - int16_t(stamp_last);
-       diff /= sample_skip;
-       if (diff > 0 || diff < -15) {
-               // packet outside observed time frame
+       int16_t diff = int16_t(stamp_last) - int16_t(seq);
+       if (diff < 0 || diff > int(15 * sample_skip)) {
+               // packet outside observed frame
                return;
        }
-       int cur_rtt = SDL_GetTicks() - stamps[(stamp_cursor + diff + 16) % 16];
+       int cur_rtt = SDL_GetTicks() - stamps[SampleIndex(seq)];
        rtt += (cur_rtt - rtt) * 0.1f;
 }
 
-bool CongestionControl::SamplePacket(std::uint16_t seq) const noexcept {
+bool CongestionControl::SamplePacket(uint16_t seq) const noexcept {
        return seq % sample_skip == 0;
 }
 
+size_t CongestionControl::SampleIndex(uint16_t seq) const noexcept {
+       return (seq / sample_skip) % 16;
+}
+
 void CongestionControl::PacketIn(const UDPpacket &pack) noexcept {
        rx_bytes += pack.len + packet_overhead;
        UpdateStats();
@@ -197,7 +198,9 @@ CongestionControl::Mode CongestionControl::Conditions() const noexcept {
 Connection::Connection(const IPaddress &addr)
 : handler(nullptr)
 , addr(addr)
-, send_timer(500)
+// make sure a packet is sent at least every 50ms since packets contains
+// acks that the remote end will use to measure RTT
+, send_timer(50)
 , recv_timer(10000)
 , ctrl_out{ 0, 0xFFFF, 0xFFFFFFFF }
 , ctrl_in{ 0, 0xFFFF, 0xFFFFFFFF }
index 16beccf7aecb100471185aca85cd684023e2d6c8..bd42241d9974897a8210afc3479e500c3e6e1c53 100644 (file)
@@ -46,7 +46,7 @@ public:
        }
        /// send the previously prepared packet
        std::uint16_t Send();
-       /// send the previously prepared packet of non-default length
+       /// send the previously prepared packet of given payload length
        std::uint16_t Send(std::size_t len);
 
        void AttachPlayer(Player &);
@@ -85,12 +85,13 @@ private:
        void On(const Packet::PlayerUpdate &) override;
        void On(const Packet::Message &) override;
 
+       void CheckEntities();
        bool CanSpawn(const Entity &) const noexcept;
        bool CanDespawn(const Entity &) const noexcept;
 
        void SendSpawn(SpawnStatus &);
        void SendDespawn(SpawnStatus &);
-       /// true if updates are pushed to the client this frame
+       /// true if entity updates are pushed to the client this frame
        bool SendingUpdates() const noexcept;
        void QueueUpdate(SpawnStatus &);
        void SendUpdates();
@@ -118,6 +119,7 @@ private:
        ChunkTransmitter transmitter;
        std::deque<glm::ivec3> chunk_queue;
        glm::ivec3 old_base;
+       unsigned int chunk_blocks_skipped;
 
 };
 
index 38266d79f0f2887f82222b2dc4896e6bc29188b8..47342ffad130b0a2edc6337797fa458eee9863ec 100644 (file)
@@ -162,7 +162,7 @@ void ChunkTransmitter::SendData(size_t i) {
        if (data_packets[i] == -1) {
                ++confirm_wait;
        }
-       data_packets[i] = conn.Send();
+       data_packets[i] = conn.Send(Packet::ChunkData::GetSize(len));
 }
 
 void ChunkTransmitter::Release() {
@@ -188,7 +188,8 @@ ClientConnection::ClientConnection(Server &server, const IPaddress &addr)
 , old_actions(0)
 , transmitter(*this)
 , chunk_queue()
-, old_base() {
+, old_base()
+, chunk_blocks_skipped(0) {
        conn.SetHandler(this);
 }
 
@@ -202,58 +203,60 @@ void ClientConnection::Update(int dt) {
                return;
        }
        if (HasPlayer()) {
-               // sync entities
-               auto global_iter = server.GetWorld().Entities().begin();
-               auto global_end = server.GetWorld().Entities().end();
-               auto local_iter = spawns.begin();
-               auto local_end = spawns.end();
-
-               while (global_iter != global_end && local_iter != local_end) {
-                       if (global_iter->ID() == local_iter->entity->ID()) {
-                               // they're the same
-                               if (CanDespawn(*global_iter)) {
-                                       SendDespawn(*local_iter);
-                               } else if (SendingUpdates()) {
-                                       // update
-                                       QueueUpdate(*local_iter);
-                               }
-                               ++global_iter;
-                               ++local_iter;
-                       } else if (global_iter->ID() < local_iter->entity->ID()) {
-                               // global entity was inserted
-                               if (CanSpawn(*global_iter)) {
-                                       auto spawned = spawns.emplace(local_iter, *global_iter);
-                                       SendSpawn(*spawned);
-                               }
-                               ++global_iter;
-                       } else {
-                               // global entity was removed
+               CheckPlayerFix();
+               CheckChunkQueue();
+               CheckEntities();
+               SendUpdates();
+       }
+       if (conn.ShouldPing()) {
+               conn.SendPing(server.GetPacket(), server.GetSocket());
+       }
+}
+
+void ClientConnection::CheckEntities() {
+       auto global_iter = server.GetWorld().Entities().begin();
+       auto global_end = server.GetWorld().Entities().end();
+       auto local_iter = spawns.begin();
+       auto local_end = spawns.end();
+
+       while (global_iter != global_end && local_iter != local_end) {
+               if (global_iter->ID() == local_iter->entity->ID()) {
+                       // they're the same
+                       if (CanDespawn(*global_iter)) {
                                SendDespawn(*local_iter);
-                               ++local_iter;
+                       } else if (SendingUpdates()) {
+                               // update
+                               QueueUpdate(*local_iter);
                        }
-               }
-
-               // leftover spawns
-               while (global_iter != global_end) {
+                       ++global_iter;
+                       ++local_iter;
+               } else if (global_iter->ID() < local_iter->entity->ID()) {
+                       // global entity was inserted
                        if (CanSpawn(*global_iter)) {
-                               spawns.emplace_back(*global_iter);
-                               SendSpawn(spawns.back());
+                               auto spawned = spawns.emplace(local_iter, *global_iter);
+                               SendSpawn(*spawned);
                        }
                        ++global_iter;
-               }
-
-               // leftover despawns
-               while (local_iter != local_end) {
+               } else {
+                       // global entity was removed
                        SendDespawn(*local_iter);
                        ++local_iter;
                }
-               SendUpdates();
+       }
 
-               CheckPlayerFix();
-               CheckChunkQueue();
+       // leftover spawns
+       while (global_iter != global_end) {
+               if (CanSpawn(*global_iter)) {
+                       spawns.emplace_back(*global_iter);
+                       SendSpawn(spawns.back());
+               }
+               ++global_iter;
        }
-       if (conn.ShouldPing()) {
-               conn.SendPing(server.GetPacket(), server.GetSocket());
+
+       // leftover despawns
+       while (local_iter != local_end) {
+               SendDespawn(*local_iter);
+               ++local_iter;
        }
 }
 
@@ -311,7 +314,7 @@ void ClientConnection::SendDespawn(SpawnStatus &status) {
 }
 
 bool ClientConnection::SendingUpdates() const noexcept {
-       return entity_updates_skipped >= NetStat().SuggestedPacketSkip();
+       return entity_updates_skipped >= NetStat().SuggestedPacketHold();
 }
 
 void ClientConnection::QueueUpdate(SpawnStatus &status) {
@@ -399,13 +402,14 @@ void ClientConnection::CheckChunkQueue() {
                old_base = PlayerChunks().Base();
                sort(chunk_queue.begin(), chunk_queue.end(), QueueCompare(old_base));
        }
-       // if we have packet skip enabled and just pushed an entity
-       // update, don't also send chunk data
-       if (NetStat().SuggestedPacketSkip() > 0 && entity_updates_skipped == 0) {
+       // don't push entity updates and chunk data in the same tick
+       if (chunk_blocks_skipped >= NetStat().SuggestedPacketHold() && !SendingUpdates()) {
+               ++chunk_blocks_skipped;
                return;
        }
        if (transmitter.Transmitting()) {
                transmitter.Transmit();
+               chunk_blocks_skipped = 0;
                return;
        }
        if (transmitter.Idle()) {
@@ -418,6 +422,7 @@ void ClientConnection::CheckChunkQueue() {
                                Chunk *chunk = PlayerChunks().Get(pos);
                                if (chunk) {
                                        transmitter.Send(*chunk);
+                                       chunk_blocks_skipped = 0;
                                        return;
                                } else {
                                        chunk_queue.push_back(pos);