]> git.localhorst.tv Git - blank.git/commitdiff
better control over entity update transmission
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Fri, 11 Sep 2015 14:59:35 +0000 (16:59 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Fri, 11 Sep 2015 15:07:36 +0000 (17:07 +0200)
src/ai/Spawner.cpp
src/ai/ai.cpp
src/client/MasterState.hpp
src/client/client.cpp
src/net/Client.hpp
src/net/ClientConnection.hpp
src/net/Packet.hpp
src/net/net.cpp
src/world/World.cpp
src/world/World.hpp

index 13a8f52e6f44eb8be438faee0553d185da1af06f..eaaa1f8a03f30b4d1df690d6a1da0fffc0bcd954 100644 (file)
@@ -120,7 +120,6 @@ void Spawner::Spawn(Entity &reference, const glm::ivec3 &chunk, const glm::vec3
        rot.z *= (random.Next<unsigned short>() % 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<std::uint64_t>());
+               e.Name("spawned walker");
        } else {
                ctrl = new Chaser(world, e, reference);
+               e.Name("spawned chaser");
        }
        controllers.emplace_back(ctrl);
 }
index a1abfdc3cf73444102ed63e6210a00b7e2566f1e..d50aee5b6219aa06f170bc86b3f03eb0fb1cf7e3 100644 (file)
@@ -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<float>::epsilon()) {
index 8eeadf051811596a8f3db3b47306b6b537925f62..61069dc5aa7136eb8be750bce26ece226ebef708 100644 (file)
@@ -7,6 +7,7 @@
 #include "../net/Client.hpp"
 #include "../net/ConnectionHandler.hpp"
 
+#include <map>
 #include <memory>
 
 
@@ -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<std::uint32_t, UpdateStatus> update_status;
+       IntervalTimer update_timer;
+
 };
 
 }
index 96c30f5b1d15d8ca3229e7a701cfa1211b60a21d..757bc3a265ab6cfd883a9f9fbdfc45020c73ae80 100644 (file)
@@ -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);
+}
+
 }
 }
index 2848aedb342a37ebcac5ebd8c7052089c0696397..ee848c0d74729db9976cf382ccfe8ad1d83b512d 100644 (file)
@@ -2,6 +2,7 @@
 #define BLANK_NET_CLIENT_HPP_
 
 #include "Connection.hpp"
+#include "../app/IntervalTimer.hpp"
 
 #include <string>
 #include <SDL_net.h>
@@ -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;
 
 };
 
index fb6420acfed074959df27449741d3cb80cb5526c..c37540907851f7cd8c1f9cc8cf4dfca3ff193941 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "Connection.hpp"
 #include "ConnectionHandler.hpp"
+#include "../app/IntervalTimer.hpp"
 
 #include <list>
 #include <SDL_net.h>
@@ -67,6 +68,8 @@ private:
        Entity *player;
        std::list<SpawnStatus> spawns;
        unsigned int confirm_wait;
+       std::uint16_t player_update_pack;
+       IntervalTimer player_update_timer;
 
 };
 
index 76cd966bed681aec73e22f8c9a87276eb1cc4569..4742a4525d679a44333c2d27b7eb88a306793cdc 100644 (file)
@@ -53,6 +53,10 @@ struct Packet {
                std::size_t length;
                std::uint8_t *data;
 
+               std::uint16_t Seq() const noexcept {
+                       return reinterpret_cast<const Packet *>(data - sizeof(Header))->header.ctrl.seq;
+               }
+
                template<class T>
                void Write(const T &, size_t off) noexcept;
                template<class T>
index e6af690aeacb36f16ad0c289843b3df456833ffe..4e6a63a9ad07a31330e004de4171f813aa848c40 100644 (file)
@@ -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<Packet::PlayerUpdate>(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());
+       }
 }
 
 
index 3ff7da341e3e52f2d6989a2bf67969f896d51b98..505cbbb70f3838c0b89e0a27d5d18a2d41b752df 100644 (file)
@@ -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 {
 
index 8d822501bd07735d8c2448f9ec6250989d16e9aa..cd631610a4367d261a0744aa0e78c7c1c6c29844 100644 (file)
@@ -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<Player> &Players() const noexcept { return players; }
        std::list<Entity> &Entities() noexcept { return entities; }