]> git.localhorst.tv Git - blank.git/commitdiff
add packet for merging player state back to client
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Mon, 14 Sep 2015 15:44:35 +0000 (17:44 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Mon, 14 Sep 2015 15:45:49 +0000 (17:45 +0200)
also, part of the merge procedure is already in place

doc/protocol
src/client/InteractiveState.hpp
src/client/MasterState.hpp
src/client/client.cpp
src/net/ClientConnection.hpp
src/net/ConnectionHandler.hpp
src/net/Packet.hpp
src/net/net.cpp

index 432a3e9413d6604e75808424ab35bb29fd7bdf96..749e0995c775aa490294caff56c7bda6f8aa202b 100644 (file)
@@ -17,6 +17,15 @@ with native LE (or BE if the server and all clients are on that,
 but that's by accident and will break if conversion code is ever
 added)
 
 but that's by accident and will break if conversion code is ever
 added)
 
+Common Types
+------------
+
+Name          Size  Type
+vec3          12    3x 32bit float
+vec3i         12    3x 32bit signed int
+quat          16    4x 32bit float
+entity state  64    vec3i, vec3, vec3, quat, vec3
+
 
 Packets
 =======
 
 Packets
 =======
@@ -56,8 +65,7 @@ it's changing worlds.
 Code: 2
 Payload:
         0 entity ID of the player, 32bit unsigned int
 Code: 2
 Payload:
         0 entity ID of the player, 32bit unsigned int
-        4 chunk coords of the player, 3x 32bit signed int
-       16 pos/vel/rot/ang of the player, 13x 32bit float
+        4 entity state of the player
        68 name of the world the server's currently running
           max 32 byte UTF-8 string
 Length: 68-100
        68 name of the world the server's currently running
           max 32 byte UTF-8 string
 Length: 68-100
@@ -82,8 +90,7 @@ Sent by clients to notify the server of their changes to the player.
 
 Code: 4
 Payload:
 
 Code: 4
 Payload:
-        0 chunk coords of the player, 3x 32bit signed int
-       12 pos/vel/rot/ang of the player, 13x 32bit float
+        0 entity state of the player as seen by the client
 Length: 64
 
 
 Length: 64
 
 
@@ -96,8 +103,7 @@ Code: 5
 Payload:
          0 entity ID, 32bit unsigned int
          4 entity's skeleton ID, 32bit unsigned int
 Payload:
          0 entity ID, 32bit unsigned int
          4 entity's skeleton ID, 32bit unsigned int
-         8 chunk coords of the entity, 3x 32bit signed int
-        20 pos/vel/rot/ang of the entity, 13x 32bit float
+         8 entity state
         72 bounding box of the entity, 6x 32bit float
         96 flags, 32bit bitfield with boolean values
            1: world collision
         72 bounding box of the entity, 6x 32bit float
         96 flags, 32bit bitfield with boolean values
            1: world collision
@@ -125,7 +131,19 @@ Contained entities must be ordered by ascending entity ID.
 Code: 7
 Payload:
         0 number of entities, 32bit int, 1-7
 Code: 7
 Payload:
         0 number of entities, 32bit int, 1-7
-        4 chunk coords of the entity, 3x 32bit signed int
-       16 pos/vel/rot/ang of the entity, 13x 32bit float
-       68 next entity...
-Length: 4 + multiple of 64, max 452
+        4 entity ID, 32bit unsigned int
+        8 entity state
+       72 next entity...
+Length: 4 + multiple of 68, max 452
+
+
+Player Correction
+-----------------
+
+Sent by the server to tell a client that its prediction is way off.
+
+Code: 8
+Payload:
+        0 sequence number of the offending packet, 16bit unsigned int
+        2 entity state of the player's entity on the server
+Length: 66
index 9b1ce52236309d23751286b037e2d80ead35701f..91a5944181b2979b075ddce9b80af3d2657a5a48 100644 (file)
@@ -8,8 +8,11 @@
 #include "../ui/Interface.hpp"
 #include "../world/BlockTypeRegistry.hpp"
 #include "../world/ChunkRenderer.hpp"
 #include "../ui/Interface.hpp"
 #include "../world/BlockTypeRegistry.hpp"
 #include "../world/ChunkRenderer.hpp"
+#include "../world/EntityState.hpp"
 #include "../world/World.hpp"
 
 #include "../world/World.hpp"
 
+#include <list>
+
 
 namespace blank {
 
 
 namespace blank {
 
@@ -35,6 +38,9 @@ public:
        void Update(int dt) override;
        void Render(Viewport &) override;
 
        void Update(int dt) override;
        void Render(Viewport &) override;
 
+       void PushPlayerUpdate(const Entity &);
+       void MergePlayerCorrection(std::uint16_t, const EntityState &);
+
 private:
        MasterState &master;
        BlockTypeRegistry block_types;
 private:
        MasterState &master;
        BlockTypeRegistry block_types;
@@ -45,6 +51,15 @@ private:
        Skeletons skeletons;
        IntervalTimer update_timer;
 
        Skeletons skeletons;
        IntervalTimer update_timer;
 
+       struct PlayerHistory {
+               EntityState state;
+               int timestamp;
+               std::uint16_t packet;
+               PlayerHistory(EntityState s, int t, std::uint16_t p)
+               : state(s), timestamp(t), packet(p) { }
+       };
+       std::list<PlayerHistory> player_hist;
+
 };
 
 }
 };
 
 }
index 61069dc5aa7136eb8be750bce26ece226ebef708..aaea6a99b442aa152b209d0f6953261746025b50 100644 (file)
@@ -55,6 +55,7 @@ public:
        void On(const Packet::SpawnEntity &) override;
        void On(const Packet::DespawnEntity &) override;
        void On(const Packet::EntityUpdate &) override;
        void On(const Packet::SpawnEntity &) override;
        void On(const Packet::DespawnEntity &) override;
        void On(const Packet::EntityUpdate &) override;
+       void On(const Packet::PlayerCorrection &) override;
 
 private:
        /// flag entity as updated by given packet
 
 private:
        /// flag entity as updated by given packet
index 8a3d1c89c98e36a17c645852172f3d92d9cfb0eb..4a48b45afc91eb5179f9a6d038c88b19244f213d 100644 (file)
@@ -56,7 +56,8 @@ InteractiveState::InteractiveState(MasterState &master, uint32_t player_id)
 )
 , chunk_renderer(*interface.GetPlayer().chunks)
 , skeletons()
 )
 , chunk_renderer(*interface.GetPlayer().chunks)
 , skeletons()
-, update_timer(16) {
+, update_timer(16)
+, player_hist() {
        TextureIndex tex_index;
        master.GetEnv().loader.LoadBlockTypes("default", block_types, tex_index);
        chunk_renderer.LoadTextures(master.GetEnv().loader, tex_index);
        TextureIndex tex_index;
        master.GetEnv().loader.LoadBlockTypes("default", block_types, tex_index);
        chunk_renderer.LoadTextures(master.GetEnv().loader, tex_index);
@@ -111,7 +112,7 @@ void InteractiveState::Update(int dt) {
        Entity &player = *interface.GetPlayer().entity;
 
        if (update_timer.Hit()) {
        Entity &player = *interface.GetPlayer().entity;
 
        if (update_timer.Hit()) {
-               master.GetClient().SendPlayerUpdate(player);
+               PushPlayerUpdate(player);
        }
 
        glm::mat4 trans = player.Transform(player.ChunkCoords());
        }
 
        glm::mat4 trans = player.Transform(player.ChunkCoords());
@@ -122,6 +123,37 @@ void InteractiveState::Update(int dt) {
        master.GetEnv().audio.Orientation(dir, up);
 }
 
        master.GetEnv().audio.Orientation(dir, up);
 }
 
+void InteractiveState::PushPlayerUpdate(const Entity &player) {
+       std::uint16_t packet = master.GetClient().SendPlayerUpdate(player);
+       if (player_hist.size() < 16) {
+               player_hist.emplace_back(player.GetState(), update_timer.Elapsed(), packet);
+       } else {
+               auto entry = player_hist.begin();
+               entry->state = player.GetState();
+               entry->timestamp = update_timer.Elapsed();
+               entry->packet = packet;
+               player_hist.splice(player_hist.end(), player_hist, entry);
+       }
+}
+
+void InteractiveState::MergePlayerCorrection(uint16_t seq, const EntityState &corrected_state) {
+       if (player_hist.empty()) return;
+
+       auto entry = player_hist.begin();
+       auto end = player_hist.end();
+
+       // drop anything older than the fix
+       while (entry != end) {
+               int pack_diff = int16_t(seq) - int16_t(entry->packet);
+               if (pack_diff < 0) {
+                       entry = player_hist.erase(entry);
+               } else {
+                       break;
+               }
+       }
+       if (entry == end) return;
+}
+
 void InteractiveState::Render(Viewport &viewport) {
        Entity &player = *interface.GetPlayer().entity;
        viewport.WorldPosition(player.Transform(player.ChunkCoords()));
 void InteractiveState::Render(Viewport &viewport) {
        Entity &player = *interface.GetPlayer().entity;
        viewport.WorldPosition(player.Transform(player.ChunkCoords()));
@@ -322,5 +354,18 @@ void MasterState::ClearEntity(uint32_t entity_id) {
        update_status.erase(entity_id);
 }
 
        update_status.erase(entity_id);
 }
 
+void MasterState::On(const Packet::PlayerCorrection &pack) {
+       if (!state) {
+               cout << "got player correction without a player :S" << endl;
+               Quit();
+               return;
+       }
+       uint16_t pack_seq;
+       EntityState corrected_state;
+       pack.ReadPacketSeq(pack_seq);
+       pack.ReadPlayerState(corrected_state);
+       state->MergePlayerCorrection(pack_seq, corrected_state);
+}
+
 }
 }
 }
 }
index c37540907851f7cd8c1f9cc8cf4dfca3ff193941..7fb797bea1d89870034ffcb4ef23803f83b69def 100644 (file)
@@ -62,6 +62,8 @@ private:
        void SendDespawn(SpawnStatus &);
        void SendUpdate(SpawnStatus &);
 
        void SendDespawn(SpawnStatus &);
        void SendUpdate(SpawnStatus &);
 
+       void CheckPlayerFix();
+
 private:
        Server &server;
        Connection conn;
 private:
        Server &server;
        Connection conn;
index 56538c6178505d0ee35c764bcfac4b684e1723b4..d0172fd2f87eeb07c30c1a43334abea0f02a6ca8 100644 (file)
@@ -29,6 +29,7 @@ private:
        virtual void On(const Packet::SpawnEntity &) { }
        virtual void On(const Packet::DespawnEntity &) { }
        virtual void On(const Packet::EntityUpdate &) { }
        virtual void On(const Packet::SpawnEntity &) { }
        virtual void On(const Packet::DespawnEntity &) { }
        virtual void On(const Packet::EntityUpdate &) { }
+       virtual void On(const Packet::PlayerCorrection &) { }
 
 };
 
 
 };
 
index 232d0be9900d880995c90e77339491ea1194319e..c4a8ae350299ff111abfcd47cf5228e09679eb9e 100644 (file)
@@ -139,6 +139,16 @@ struct Packet {
                void ReadEntityState(EntityState &, std::uint32_t) const noexcept;
        };
 
                void ReadEntityState(EntityState &, std::uint32_t) const noexcept;
        };
 
+       struct PlayerCorrection : public Payload {
+               static constexpr std::uint8_t TYPE = 8;
+               static constexpr std::size_t MAX_LEN = 66;
+
+               void WritePacketSeq(std::uint16_t) noexcept;
+               void ReadPacketSeq(std::uint16_t &) const noexcept;
+               void WritePlayer(const Entity &) noexcept;
+               void ReadPlayerState(EntityState &) const noexcept;
+       };
+
 
        template<class PayloadType>
        PayloadType As() {
 
        template<class PayloadType>
        PayloadType As() {
index 13ea4d372784c1f177e404b63394a32d325c5be5..3f4bf7453eab78a0ac79b7f045026ec10114a8f0 100644 (file)
@@ -29,6 +29,7 @@ constexpr size_t Packet::PlayerUpdate::MAX_LEN;
 constexpr size_t Packet::SpawnEntity::MAX_LEN;
 constexpr size_t Packet::DespawnEntity::MAX_LEN;
 constexpr size_t Packet::EntityUpdate::MAX_LEN;
 constexpr size_t Packet::SpawnEntity::MAX_LEN;
 constexpr size_t Packet::DespawnEntity::MAX_LEN;
 constexpr size_t Packet::EntityUpdate::MAX_LEN;
+constexpr size_t Packet::PlayerCorrection::MAX_LEN;
 
 namespace {
 
 
 namespace {
 
@@ -187,6 +188,8 @@ void ClientConnection::Update(int dt) {
                        SendDespawn(*local_iter);
                        ++local_iter;
                }
                        SendDespawn(*local_iter);
                        ++local_iter;
                }
+
+               CheckPlayerFix();
        }
        if (conn.ShouldPing()) {
                conn.SendPing(server.GetPacket(), server.GetSocket());
        }
        if (conn.ShouldPing()) {
                conn.SendPing(server.GetPacket(), server.GetSocket());
@@ -249,6 +252,14 @@ void ClientConnection::SendUpdate(SpawnStatus &status) {
        conn.Send(server.GetPacket(), server.GetSocket());
 }
 
        conn.Send(server.GetPacket(), server.GetSocket());
 }
 
+void ClientConnection::CheckPlayerFix() {
+       // check always succeeds for now ;)
+       auto pack = Packet::Make<Packet::PlayerCorrection>(server.GetPacket());
+       pack.WritePacketSeq(player_update_pack);
+       pack.WritePlayer(Player());
+       conn.Send(server.GetPacket(), server.GetSocket());
+}
+
 void ClientConnection::AttachPlayer(Entity &new_player) {
        DetachPlayer();
        player = &new_player;
 void ClientConnection::AttachPlayer(Entity &new_player) {
        DetachPlayer();
        player = &new_player;
@@ -491,6 +502,8 @@ const char *Packet::Type2String(uint8_t t) noexcept {
                        return "DespawnEntity";
                case EntityUpdate::TYPE:
                        return "EntityUpdate";
                        return "DespawnEntity";
                case EntityUpdate::TYPE:
                        return "EntityUpdate";
+               case PlayerCorrection::TYPE:
+                       return "PlayerCorrection";
                default:
                        return "Unknown";
        }
                default:
                        return "Unknown";
        }
@@ -646,6 +659,22 @@ void Packet::EntityUpdate::ReadEntityState(EntityState &state, uint32_t num) con
        Read(state, off + 4);
 }
 
        Read(state, off + 4);
 }
 
+void Packet::PlayerCorrection::WritePacketSeq(std::uint16_t s) noexcept {
+       Write(s, 0);
+}
+
+void Packet::PlayerCorrection::ReadPacketSeq(std::uint16_t &s) const noexcept {
+       Read(s, 0);
+}
+
+void Packet::PlayerCorrection::WritePlayer(const Entity &player) noexcept {
+       Write(player.GetState(), 2);
+}
+
+void Packet::PlayerCorrection::ReadPlayerState(EntityState &state) const noexcept {
+       Read(state, 2);
+}
+
 
 void ConnectionHandler::Handle(const UDPpacket &udp_pack) {
        const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
 
 void ConnectionHandler::Handle(const UDPpacket &udp_pack) {
        const Packet &pack = *reinterpret_cast<const Packet *>(udp_pack.data);
@@ -674,6 +703,9 @@ void ConnectionHandler::Handle(const UDPpacket &udp_pack) {
                case Packet::EntityUpdate::TYPE:
                        On(Packet::As<Packet::EntityUpdate>(udp_pack));
                        break;
                case Packet::EntityUpdate::TYPE:
                        On(Packet::As<Packet::EntityUpdate>(udp_pack));
                        break;
+               case Packet::PlayerCorrection::TYPE:
+                       On(Packet::As<Packet::PlayerCorrection>(udp_pack));
+                       break;
                default:
                        // drop unknown or unhandled packets
                        break;
                default:
                        // drop unknown or unhandled packets
                        break;