]> git.tdb.fi Git - libs/game.git/commitdiff
Implement queueing for remote calls
authorMikko Rasa <tdb@tdb.fi>
Sun, 21 Jan 2024 18:10:50 +0000 (20:10 +0200)
committerMikko Rasa <tdb@tdb.fi>
Sun, 21 Jan 2024 18:10:50 +0000 (20:10 +0200)
This allows received calls to be dispatched in a controlled order during
a frame.

source/game/landscape.h
source/game/remotecall.cpp [new file with mode: 0644]
source/game/remotecall.h
source/game/replicator.cpp
source/game/replicator.h

index 8463b1434e273363b89c021d8d94efba6a4a6eda..4151e42eeda5d6b2916f767368274f0a34aa588c 100644 (file)
@@ -52,7 +52,7 @@ private:
        };
 
        Replicator *replicator = nullptr;
-       ReplicatedCall<Messages::LandscapeData, TerrainBlock> data_call;
+       ReplicatedCall<Messages::LandscapeData, TerrainBlock, DIRECT> data_call;
        Owned<Entity> subroot;
        Spawner spawner;
        BlockSetup block_setup;
diff --git a/source/game/remotecall.cpp b/source/game/remotecall.cpp
new file mode 100644 (file)
index 0000000..1c6c6d6
--- /dev/null
@@ -0,0 +1,20 @@
+#include "remotecall.h"
+#include <msp/debug/demangle.h>
+#include <msp/strings/format.h>
+#include "replicator.h"
+
+namespace Msp::Game {
+
+not_dispatched::not_dispatched(const std::type_info &ti):
+       logic_error(format("Remote call %s not dispatched", Debug::demangle(ti.name())))
+{ }
+
+
+RemoteCallBase::RemoteCallBase(Replicator *r):
+       replicator(r)
+{
+       if(replicator)
+               replicator->add_call(*this);
+}
+
+} // namespace Msp::Game
index a78f6126f37601cf97af6a11ac695fc7793574cd..21e51de1c354a9f8ad5487994b48b311086c4e57 100644 (file)
 
 namespace Msp::Game {
 
+enum DispatchType
+{
+       DIRECT,
+       QUEUED
+};
+
 template<typename P>
 concept HasEntityIdField = requires(P p) { { (p.entity_id) } -> std::same_as<std::uint32_t &>; };
 
 template<typename P, typename E>
 concept ValidRemoteCall = std::is_same_v<E, void> || (std::is_base_of_v<Entity, E> && HasEntityIdField<P>);
 
+class MSPGAME_API not_dispatched: public std::logic_error
+{
+public:
+       not_dispatched(const std::type_info &);
+};
+
+
+class MSPGAME_API RemoteCallBase
+{
+protected:
+       Replicator *replicator = nullptr;
+
+       RemoteCallBase(Replicator *replicator);
+
+public:
+       virtual void verify_dispatched() const = 0;
+};
+
+
+template<typename P, typename E, DispatchType D>
+struct RemoteCallQueue;
+
+template<typename P, typename E>
+struct RemoteCallQueue<P, E, DIRECT> { };
 
 template<typename P, typename E>
+struct RemoteCallQueue<P, E, QUEUED>
+{
+       struct QueuedPacket
+       {
+               Handle<E> entity;
+               P packet;
+       };
+
+       std::vector<QueuedPacket> queue;
+};
+
+template<typename P>
+struct RemoteCallQueue<P, void, QUEUED>
+{
+       std::vector<P> queue;
+};
+
+
+template<typename P, typename E, DispatchType D>
        requires ValidRemoteCall<P, E>
-class RemoteCall: protected Net::PacketReceiver<P>
+class RemoteCall: protected RemoteCallBase, private RemoteCallQueue<P, E, D>, protected Net::PacketReceiver<P>
 {
 protected:
        using Function = std::conditional_t<std::is_same_v<E, void>,
                std::function<void(const P &)>, std::function<void(Handle<E>, const P &)>>;
 
-       Replicator *replicator = nullptr;
+private:
        Function func;
 
+protected:
        RemoteCall(Replicator *, Function);
 
+       template<typename... Args>
+       void invoke(Args &&... a);
+
        void prepare(Handle<E>, P &) const;
        void receive(P &&) override;
+       void verify_dispatched() const override;
+public:
+       void dispatch();
 };
 
 
-template<typename P, typename E = void>
-class ReplicatedCall: public RemoteCall<P, E>
+template<typename P, typename E = void, DispatchType D = QUEUED>
+class ReplicatedCall: public RemoteCall<P, E, D>
 {
 public:
-       ReplicatedCall(Replicator *r, RemoteCall<P, E>::Function f): RemoteCall<P, E>(r, std::move(f)) { }
+       ReplicatedCall(Replicator *r, RemoteCall<P, E, D>::Function f): RemoteCall<P, E, D>(r, std::move(f)) { }
 
        void operator()(Handle<E>, P &);
        void operator()(unsigned, Handle<E>, P &);
 };
 
-template<typename P>
-class ReplicatedCall<P, void>: public RemoteCall<P, void>
+template<typename P, DispatchType D>
+class ReplicatedCall<P, void, D>: public RemoteCall<P, void, D>
 {
 public:
-       ReplicatedCall(Replicator *r, RemoteCall<P, void>::Function f): RemoteCall<P, void>(r, std::move(f)) { }
+       ReplicatedCall(Replicator *r, RemoteCall<P, void, D>::Function f): RemoteCall<P, void, D>(r, std::move(f)) { }
 
        void operator()(const P &);
        void operator()(unsigned, const P &);
 };
 
 
-template<typename P, typename E = void>
-class RequestCall: public RemoteCall<P, E>
+template<typename P, typename E = void, DispatchType D = QUEUED>
+class RequestCall: public RemoteCall<P, E, D>
 {
 public:
-       RequestCall(Replicator *r, RemoteCall<P, E>::Function f): RemoteCall<P, E>(r, std::move(f)) { }
+       RequestCall(Replicator *r, RemoteCall<P, E, D>::Function f): RemoteCall<P, E, D>(r, std::move(f)) { }
 
        void operator()(Handle<E>, P &);
 };
 
-template<typename P>
-class RequestCall<P, void>: public RemoteCall<P, void>
+template<typename P, DispatchType D>
+class RequestCall<P, void, D>: public RemoteCall<P, void, D>
 {
 public:
-       RequestCall(Replicator *r, RemoteCall<P, void>::Function f): RemoteCall<P, void>(r, std::move(f)) { }
+       RequestCall(Replicator *r, RemoteCall<P, void, D>::Function f): RemoteCall<P, void, D>(r, std::move(f)) { }
 
        void operator()(P &);
 };
 
 
-template<typename P, typename E>
+template<typename P, typename E, DispatchType D>
        requires ValidRemoteCall<P, E>
-RemoteCall<P, E>::RemoteCall(Replicator *r, Function f):
-       replicator(r),
+RemoteCall<P, E, D>::RemoteCall(Replicator *r, Function f):
+       RemoteCallBase(r),
        func(std::move(f))
 {
        if(replicator)
                replicator->add_receiver(*this);
 }
 
-template<typename P, typename E>
+template<typename P, typename E, DispatchType D>
        requires ValidRemoteCall<P, E>
-void RemoteCall<P, E>::prepare(Handle<E> entity, P &packet) const
+template<typename... Args>
+void RemoteCall<P, E, D>::invoke(Args &&... args)
+{
+       if constexpr(D==QUEUED)
+               this->queue.emplace_back(std::forward<Args>(args)...);
+       else
+               func(std::forward<Args>(args)...);
+}
+
+template<typename P, typename E, DispatchType D>
+       requires ValidRemoteCall<P, E>
+void RemoteCall<P, E, D>::prepare(Handle<E> entity, P &packet) const
 {
        Handle<Zygote> zygote = entity->template get_component<Zygote>();
        if(!zygote)
@@ -103,12 +170,12 @@ void RemoteCall<P, E>::prepare(Handle<E> entity, P &packet) const
        packet.entity_id = entity_id;
 }
 
-template<typename P, typename E>
+template<typename P, typename E, DispatchType D>
        requires ValidRemoteCall<P, E>
-void RemoteCall<P, E>::receive(P &&packet)
+void RemoteCall<P, E, D>::receive(P &&packet)
 {
        if constexpr(std::is_same_v<E, void>)
-               this->func(packet);
+               invoke(packet);
        else
        {
                Handle<Entity> entity = this->replicator->find_entity(packet.entity_id, this->replicator->is_server());
@@ -119,13 +186,39 @@ void RemoteCall<P, E>::receive(P &&packet)
                if(!cast_entity)
                        return;
 
-               this->func(cast_entity, packet);
+               invoke(cast_entity, packet);
        }
 }
 
+template<typename P, typename E, DispatchType D>
+       requires ValidRemoteCall<P, E>
+void RemoteCall<P, E, D>::verify_dispatched() const
+{
+       if constexpr(D==QUEUED)
+               if(!this->queue.empty())
+                       throw not_dispatched(typeid(P));
+}
 
-template<typename P, typename E>
-void ReplicatedCall<P, E>::operator()(Handle<E> entity, P &packet)
+template<typename P, typename E, DispatchType D>
+       requires ValidRemoteCall<P, E>
+void RemoteCall<P, E, D>::dispatch()
+{
+       if constexpr(D==QUEUED)
+       {
+               for(const auto &p: this->queue)
+               {
+                       if constexpr(std::is_same_v<E, void>)
+                               func(p);
+                       else
+                               func(p.entity, p.packet);
+               }
+               this->queue.clear();
+       }
+}
+
+
+template<typename P, typename E, DispatchType D>
+void ReplicatedCall<P, E, D>::operator()(Handle<E> entity, P &packet)
 {
        if(this->replicator)
        {
@@ -134,11 +227,11 @@ void ReplicatedCall<P, E>::operator()(Handle<E> entity, P &packet)
                this->prepare(entity, packet);
                this->replicator->send(entity, packet);
        }
-       this->func(entity, packet);
+       this->invoke(entity, packet);
 }
 
-template<typename P, typename E>
-void ReplicatedCall<P, E>::operator()(unsigned target, Handle<E> entity, P &packet)
+template<typename P, typename E, DispatchType D>
+void ReplicatedCall<P, E, D>::operator()(unsigned target, Handle<E> entity, P &packet)
 {
        if(this->replicator)
        {
@@ -148,8 +241,8 @@ void ReplicatedCall<P, E>::operator()(unsigned target, Handle<E> entity, P &pack
 }
 
 
-template<typename P>
-void ReplicatedCall<P, void>::operator()(const P &packet)
+template<typename P, DispatchType D>
+void ReplicatedCall<P, void, D>::operator()(const P &packet)
 {
        if(this->replicator)
        {
@@ -157,19 +250,19 @@ void ReplicatedCall<P, void>::operator()(const P &packet)
                        throw invalid_state("ReplicatedCall<P>::operator()");
                this->replicator->send(packet);
        }
-       this->func(packet);
+       this->invoke(packet);
 }
 
-template<typename P>
-void ReplicatedCall<P, void>::operator()(unsigned target, const P &packet)
+template<typename P, DispatchType D>
+void ReplicatedCall<P, void, D>::operator()(unsigned target, const P &packet)
 {
        if(this->replicator)
                this->replicator->send(target, packet);
 }
 
 
-template<typename P, typename E>
-void RequestCall<P, E>::operator()(Handle<E> entity, P &packet)
+template<typename P, typename E, DispatchType D>
+void RequestCall<P, E, D>::operator()(Handle<E> entity, P &packet)
 {
        if(this->replicator && !this->replicator->is_server())
        {
@@ -177,17 +270,17 @@ void RequestCall<P, E>::operator()(Handle<E> entity, P &packet)
                this->replicator->send(entity, packet);
        }
        else
-               this->func(entity, packet);
+               this->invoke(entity, packet);
 }
 
 
-template<typename P>
-void RequestCall<P, void>::operator()(P &packet)
+template<typename P, DispatchType D>
+void RequestCall<P, void, D>::operator()(P &packet)
 {
        if(this->replicator && !this->replicator->is_server())
                this->replicator->send(packet);
        else
-               this->func(packet);
+               this->invoke(packet);
 }
 
 } // namespace Msp::Game
index db39728bf8299e2c375e338884dfe054c3a3d80a..08fae30d9e7775afacbaa80d5ebe09c267dcdd1d 100644 (file)
@@ -59,6 +59,11 @@ void Replicator::set_protocol(const Net::Protocol &p)
        add_receiver<Messages::GrantPossession>(*this);
 }
 
+void Replicator::add_call(RemoteCallBase &c)
+{
+       calls.push_back(&c);
+}
+
 Handle<Entity> Replicator::find_entity(uint32_t id, bool check_access) const
 {
        auto i = find_if(entities, [id](const ReplicatedEntity &e){ return e.zygote->get_entity_id()==id; });
@@ -90,6 +95,11 @@ void Replicator::tick(Time::TimeDelta)
 
 void Replicator::deferred_tick()
 {
+#if DEBUG
+       for(const RemoteCallBase *c: calls)
+               c->verify_dispatched();
+#endif
+
        if(is_server())
        {
                // TODO may send spawns one frame late, depending on execution order between systems
index 6cd5e83e176a548307a15dd3748de698a9bf3512..5823366b998740d32590f12fd7eb05672dcbe42a 100644 (file)
@@ -44,6 +44,7 @@ private:
        Networking &networking;
        Net::DynamicDispatcher dispatcher;
        const Net::Protocol *protocol = nullptr;
+       std::vector<RemoteCallBase *> calls;
        std::vector<Spawner *> spawners;
        std::vector<ReplicatedEntity> entities;
        std::vector<std::uint32_t> players;
@@ -65,6 +66,8 @@ private:
        void set_protocol(const Net::Protocol &);
 
 public:
+       void add_call(RemoteCallBase &);
+
        template<typename P>
        void add_receiver(Net::PacketReceiver<P> &);