From 8507332e2d0c54aec4045fb6f0021bdc3bd57750 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Mon, 14 Sep 2015 17:44:35 +0200 Subject: [PATCH] add packet for merging player state back to client also, part of the merge procedure is already in place --- doc/protocol | 38 ++++++++++++++++++------- src/client/InteractiveState.hpp | 15 ++++++++++ src/client/MasterState.hpp | 1 + src/client/client.cpp | 49 +++++++++++++++++++++++++++++++-- src/net/ClientConnection.hpp | 2 ++ src/net/ConnectionHandler.hpp | 1 + src/net/Packet.hpp | 10 +++++++ src/net/net.cpp | 32 +++++++++++++++++++++ 8 files changed, 136 insertions(+), 12 deletions(-) diff --git a/doc/protocol b/doc/protocol index 432a3e9..749e099 100644 --- a/doc/protocol +++ b/doc/protocol @@ -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 diff --git a/src/client/InteractiveState.hpp b/src/client/InteractiveState.hpp index 9b1ce52..91a5944 100644 --- a/src/client/InteractiveState.hpp +++ b/src/client/InteractiveState.hpp @@ -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 + 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 player_hist; + }; } diff --git a/src/client/MasterState.hpp b/src/client/MasterState.hpp index 61069dc..aaea6a9 100644 --- a/src/client/MasterState.hpp +++ b/src/client/MasterState.hpp @@ -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 diff --git a/src/client/client.cpp b/src/client/client.cpp index 8a3d1c8..4a48b45 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -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); +} + } } diff --git a/src/net/ClientConnection.hpp b/src/net/ClientConnection.hpp index c375409..7fb797b 100644 --- a/src/net/ClientConnection.hpp +++ b/src/net/ClientConnection.hpp @@ -62,6 +62,8 @@ private: void SendDespawn(SpawnStatus &); void SendUpdate(SpawnStatus &); + void CheckPlayerFix(); + private: Server &server; Connection conn; diff --git a/src/net/ConnectionHandler.hpp b/src/net/ConnectionHandler.hpp index 56538c6..d0172fd 100644 --- a/src/net/ConnectionHandler.hpp +++ b/src/net/ConnectionHandler.hpp @@ -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 &) { } }; diff --git a/src/net/Packet.hpp b/src/net/Packet.hpp index 232d0be..c4a8ae3 100644 --- a/src/net/Packet.hpp +++ b/src/net/Packet.hpp @@ -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 PayloadType As() { diff --git a/src/net/net.cpp b/src/net/net.cpp index 13ea4d3..3f4bf74 100644 --- a/src/net/net.cpp +++ b/src/net/net.cpp @@ -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(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(udp_pack.data); @@ -674,6 +703,9 @@ void ConnectionHandler::Handle(const UDPpacket &udp_pack) { case Packet::EntityUpdate::TYPE: On(Packet::As(udp_pack)); break; + case Packet::PlayerCorrection::TYPE: + On(Packet::As(udp_pack)); + break; default: // drop unknown or unhandled packets break; -- 2.39.2