From 68f47f2824989b21ff9a480a367a6d0a41804f41 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Fri, 11 Sep 2015 16:59:35 +0200 Subject: [PATCH] better control over entity update transmission --- src/ai/Spawner.cpp | 3 ++- src/ai/ai.cpp | 5 ++++ src/client/MasterState.hpp | 15 +++++++++++ src/client/client.cpp | 50 ++++++++++++++++++++++++++++-------- src/net/Client.hpp | 5 +++- src/net/ClientConnection.hpp | 3 +++ src/net/Packet.hpp | 4 +++ src/net/net.cpp | 25 +++++++++++++++--- src/world/World.cpp | 20 +++++++++++++++ src/world/World.hpp | 3 +++ 10 files changed, 116 insertions(+), 17 deletions(-) diff --git a/src/ai/Spawner.cpp b/src/ai/Spawner.cpp index 13a8f52..eaaa1f8 100644 --- a/src/ai/Spawner.cpp +++ b/src/ai/Spawner.cpp @@ -120,7 +120,6 @@ void Spawner::Spawn(Entity &reference, const glm::ivec3 &chunk, const glm::vec3 rot.z *= (random.Next() % 1024); Entity &e = world.AddEntity(); - e.Name("spawned"); e.Position(chunk, pos); e.Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } }); e.WorldCollidable(true); @@ -129,8 +128,10 @@ void Spawner::Spawn(Entity &reference, const glm::ivec3 &chunk, const glm::vec3 Controller *ctrl; if (random()) { ctrl = new RandomWalk(e, random.Next()); + e.Name("spawned walker"); } else { ctrl = new Chaser(world, e, reference); + e.Name("spawned chaser"); } controllers.emplace_back(ctrl); } diff --git a/src/ai/ai.cpp b/src/ai/ai.cpp index a1abfdc..d50aee5 100644 --- a/src/ai/ai.cpp +++ b/src/ai/ai.cpp @@ -28,6 +28,11 @@ Chaser::~Chaser() { } void Chaser::Update(int dt) { + if (Target().Dead()) { + Controlled().Kill(); + return; + } + glm::vec3 diff(Target().AbsoluteDifference(Controlled())); float dist = length(diff); if (dist < std::numeric_limits::epsilon()) { diff --git a/src/client/MasterState.hpp b/src/client/MasterState.hpp index 8eeadf0..61069dc 100644 --- a/src/client/MasterState.hpp +++ b/src/client/MasterState.hpp @@ -7,6 +7,7 @@ #include "../net/Client.hpp" #include "../net/ConnectionHandler.hpp" +#include #include @@ -55,6 +56,13 @@ public: void On(const Packet::DespawnEntity &) override; void On(const Packet::EntityUpdate &) override; +private: + /// flag entity as updated by given packet + /// returns false if the update should be ignored + bool UpdateEntity(std::uint32_t id, std::uint16_t seq); + /// drop update information or given entity + void ClearEntity(std::uint32_t id); + private: Environment &env; World::Config world_conf; @@ -67,6 +75,13 @@ private: int login_packet; + struct UpdateStatus { + std::uint16_t last_packet; + int last_update; + }; + std::map update_status; + IntervalTimer update_timer; + }; } diff --git a/src/client/client.cpp b/src/client/client.cpp index 96c30f5..757bc3a 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -137,8 +137,11 @@ MasterState::MasterState( , state() , client(cc) , init_state(*this) -, login_packet(-1) { +, login_packet(-1) +, update_status() +, update_timer(16) { client.GetConnection().SetHandler(this); + update_timer.Start(); } void MasterState::Quit() { @@ -161,6 +164,7 @@ void MasterState::Handle(const SDL_Event &event) { void MasterState::Update(int dt) { + update_timer.Update(dt); client.Handle(); client.Update(dt); } @@ -194,6 +198,8 @@ void MasterState::On(const Packet::Join &pack) { } else { // joining game cout << "joined game \"" << world_conf.name << '"' << endl; + // server received our login + login_packet = -1; } uint32_t player_id; @@ -225,20 +231,16 @@ void MasterState::On(const Packet::SpawnEntity &pack) { } uint32_t entity_id; pack.ReadEntityID(entity_id); - Entity *entity = state->GetWorld().AddEntity(entity_id); - if (!entity) { - cout << "entity ID inconsistency" << endl; - Quit(); - return; - } - pack.ReadEntity(*entity); + Entity &entity = state->GetWorld().ForceAddEntity(entity_id); + UpdateEntity(entity_id, pack.Seq()); + pack.ReadEntity(entity); uint32_t skel_id; pack.ReadSkeletonID(skel_id); CompositeModel *skel = state->GetSkeletons().ByID(skel_id); if (skel) { - skel->Instantiate(entity->GetModel()); + skel->Instantiate(entity.GetModel()); } - cout << "spawned entity " << entity->Name() << " at " << entity->AbsolutePosition() << endl; + cout << "spawned entity " << entity.Name() << " at " << entity.AbsolutePosition() << endl; } void MasterState::On(const Packet::DespawnEntity &pack) { @@ -249,6 +251,7 @@ void MasterState::On(const Packet::DespawnEntity &pack) { } uint32_t entity_id; pack.ReadEntityID(entity_id); + ClearEntity(entity_id); for (Entity &entity : state->GetWorld().Entities()) { if (entity.ID() == entity_id) { entity.Kill(); @@ -283,10 +286,35 @@ void MasterState::On(const Packet::EntityUpdate &pack) { return; } if (world_iter->ID() == entity_id) { - pack.ReadEntity(*world_iter, i); + if (UpdateEntity(entity_id, pack.Seq())) { + pack.ReadEntity(*world_iter, i); + } } } } +bool MasterState::UpdateEntity(uint32_t entity_id, uint16_t seq) { + auto entry = update_status.find(entity_id); + if (entry == update_status.end()) { + update_status.emplace(entity_id, UpdateStatus{ seq, update_timer.Elapsed() }); + return true; + } + + int pack_diff = int16_t(seq) - int16_t(entry->second.last_packet); + int time_diff = update_timer.Elapsed() - entry->second.last_update; + entry->second.last_update = update_timer.Elapsed(); + + if (pack_diff > 0 || time_diff > 1500) { + entry->second.last_packet = seq; + return true; + } else { + return false; + } +} + +void MasterState::ClearEntity(uint32_t entity_id) { + update_status.erase(entity_id); +} + } } diff --git a/src/net/Client.hpp b/src/net/Client.hpp index 2848aed..ee848c0 100644 --- a/src/net/Client.hpp +++ b/src/net/Client.hpp @@ -2,6 +2,7 @@ #define BLANK_NET_CLIENT_HPP_ #include "Connection.hpp" +#include "../app/IntervalTimer.hpp" #include #include @@ -33,7 +34,8 @@ public: std::uint16_t SendPing(); std::uint16_t SendLogin(const std::string &); std::uint16_t SendPart(); - std::uint16_t SendPlayerUpdate(const Entity &); + // this may not send the update at all, in which case it returns -1 + int SendPlayerUpdate(const Entity &); private: void HandlePacket(const UDPpacket &); @@ -42,6 +44,7 @@ private: Connection conn; UDPsocket client_sock; UDPpacket client_pack; + IntervalTimer update_timer; }; diff --git a/src/net/ClientConnection.hpp b/src/net/ClientConnection.hpp index fb6420a..c375409 100644 --- a/src/net/ClientConnection.hpp +++ b/src/net/ClientConnection.hpp @@ -3,6 +3,7 @@ #include "Connection.hpp" #include "ConnectionHandler.hpp" +#include "../app/IntervalTimer.hpp" #include #include @@ -67,6 +68,8 @@ private: Entity *player; std::list spawns; unsigned int confirm_wait; + std::uint16_t player_update_pack; + IntervalTimer player_update_timer; }; diff --git a/src/net/Packet.hpp b/src/net/Packet.hpp index 76cd966..4742a45 100644 --- a/src/net/Packet.hpp +++ b/src/net/Packet.hpp @@ -53,6 +53,10 @@ struct Packet { std::size_t length; std::uint8_t *data; + std::uint16_t Seq() const noexcept { + return reinterpret_cast(data - sizeof(Header))->header.ctrl.seq; + } + template void Write(const T &, size_t off) noexcept; template diff --git a/src/net/net.cpp b/src/net/net.cpp index e6af690..4e6a63a 100644 --- a/src/net/net.cpp +++ b/src/net/net.cpp @@ -51,11 +51,13 @@ IPaddress client_resolve(const char *host, Uint16 port) { Client::Client(const Config &conf) : conn(client_resolve(conf.host.c_str(), conf.port)) , client_sock(client_bind(0)) -, client_pack{ -1, nullptr, 0 } { +, client_pack{ -1, nullptr, 0 } +, update_timer(16) { client_pack.data = new Uint8[sizeof(Packet)]; client_pack.maxlen = sizeof(Packet); // establish connection SendPing(); + update_timer.Start(); } Client::~Client() { @@ -91,6 +93,7 @@ void Client::HandlePacket(const UDPpacket &udp_pack) { } void Client::Update(int dt) { + update_timer.Update(dt); conn.Update(dt); if (conn.ShouldPing()) { SendPing(); @@ -107,7 +110,9 @@ uint16_t Client::SendLogin(const string &name) { return conn.Send(client_pack, client_sock); } -uint16_t Client::SendPlayerUpdate(const Entity &player) { +int Client::SendPlayerUpdate(const Entity &player) { + // don't send all too many updates + if (!update_timer.Hit()) return -1; auto pack = Packet::Make(client_pack); pack.WritePlayer(player); return conn.Send(client_pack, client_sock); @@ -124,7 +129,9 @@ ClientConnection::ClientConnection(Server &server, const IPaddress &addr) , conn(addr) , player(nullptr) , spawns() -, confirm_wait(0) { +, confirm_wait(0) +, player_update_pack(0) +, player_update_timer(1500) { conn.SetHandler(this); } @@ -308,6 +315,10 @@ void ClientConnection::On(const Packet::Login &pack) { response.WritePlayer(*new_player); response.WriteWorldName(server.GetWorld().Name()); conn.Send(server.GetPacket(), server.GetSocket()); + // set up update tracking + player_update_pack = pack.Seq(); + player_update_timer.Reset(); + player_update_timer.Start(); } else { // aw no :( cout << "rejected login from player \"" << name << '"' << endl; @@ -323,7 +334,13 @@ void ClientConnection::On(const Packet::Part &) { void ClientConnection::On(const Packet::PlayerUpdate &pack) { if (!HasPlayer()) return; - pack.ReadPlayer(Player()); + int pack_diff = int16_t(pack.Seq()) - int16_t(player_update_pack); + bool overdue = player_update_timer.HitOnce(); + player_update_timer.Reset(); + if (pack_diff > 0 || overdue) { + player_update_pack = pack.Seq(); + pack.ReadPlayer(Player()); + } } diff --git a/src/world/World.cpp b/src/world/World.cpp index 3ff7da3..505cbbb 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -114,6 +114,26 @@ Entity *World::AddEntity(std::uint32_t id) { return &*entity; } +Entity &World::ForceAddEntity(std::uint32_t id) { + if (entities.empty() || entities.back().ID() < id) { + entities.emplace_back(); + entities.back().ID(id); + return entities.back(); + } + + auto position = entities.begin(); + auto end = entities.end(); + while (position != end && position->ID() < id) { + ++position; + } + if (position != end && position->ID() == id) { + return *position; + } + auto entity = entities.emplace(position); + entity->ID(id); + return *entity; +} + namespace { diff --git a/src/world/World.hpp b/src/world/World.hpp index 8d82250..cd63161 100644 --- a/src/world/World.hpp +++ b/src/world/World.hpp @@ -78,6 +78,9 @@ public: /// add entity with given ID /// returns nullptr if the ID is already taken Entity *AddEntity(std::uint32_t id); + /// add entity with given ID + /// returs an existing entity if ID is already taken + Entity &ForceAddEntity(std::uint32_t id); const std::vector &Players() const noexcept { return players; } std::list &Entities() noexcept { return entities; } -- 2.39.2