]> 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)
 
+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
 =======
@@ -56,8 +65,7 @@ it's changing worlds.
 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
@@ -82,8 +90,7 @@ Sent by clients to notify the server of their changes to the player.
 
 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
 
 
@@ -96,8 +103,7 @@ Code: 5
 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
@@ -125,7 +131,19 @@ Contained entities must be ordered by ascending entity ID.
 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 "../world/EntityState.hpp"
 #include "../world/World.hpp"
 
+#include <list>
+
 
 namespace blank {
 
@@ -35,6 +38,9 @@ public:
        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;
@@ -45,6 +51,15 @@ private:
        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::PlayerCorrection &) override;
 
 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()
-, 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);
@@ -111,7 +112,7 @@ void InteractiveState::Update(int dt) {
        Entity &player = *interface.GetPlayer().entity;
 
        if (update_timer.Hit()) {
-               master.GetClient().SendPlayerUpdate(player);
+               PushPlayerUpdate(player);
        }
 
        glm::mat4 trans = player.Transform(player.ChunkCoords());
@@ -122,6 +123,37 @@ void InteractiveState::Update(int dt) {
        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()));
@@ -322,5 +354,18 @@ void MasterState::ClearEntity(uint32_t 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 CheckPlayerFix();
+
 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::PlayerCorrection &) { }
 
 };
 
index 232d0be9900d880995c90e77339491ea1194319e..c4a8ae350299ff111abfcd47cf5228e09679eb9e 100644 (file)
@@ -139,6 +139,16 @@ struct Packet {
                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() {
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::PlayerCorrection::MAX_LEN;
 
 namespace {
 
@@ -187,6 +188,8 @@ void ClientConnection::Update(int dt) {
                        SendDespawn(*local_iter);
                        ++local_iter;
                }
+
+               CheckPlayerFix();
        }
        if (conn.ShouldPing()) {
                conn.SendPing(server.GetPacket(), server.GetSocket());
@@ -249,6 +252,14 @@ void ClientConnection::SendUpdate(SpawnStatus &status) {
        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;
@@ -491,6 +502,8 @@ const char *Packet::Type2String(uint8_t t) noexcept {
                        return "DespawnEntity";
                case EntityUpdate::TYPE:
                        return "EntityUpdate";
+               case PlayerCorrection::TYPE:
+                       return "PlayerCorrection";
                default:
                        return "Unknown";
        }
@@ -646,6 +659,22 @@ void Packet::EntityUpdate::ReadEntityState(EntityState &state, uint32_t num) con
        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);
@@ -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::PlayerCorrection::TYPE:
+                       On(Packet::As<Packet::PlayerCorrection>(udp_pack));
+                       break;
                default:
                        // drop unknown or unhandled packets
                        break;