]> git.tdb.fi Git - libs/game.git/commitdiff
Add a mechanism for replicating function calls to clients
authorMikko Rasa <tdb@tdb.fi>
Sat, 17 Jun 2023 21:53:36 +0000 (00:53 +0300)
committerMikko Rasa <tdb@tdb.fi>
Sat, 17 Jun 2023 21:53:36 +0000 (00:53 +0300)
source/game/remotecall.h [new file with mode: 0644]
source/game/replicator.cpp
source/game/replicator.h

diff --git a/source/game/remotecall.h b/source/game/remotecall.h
new file mode 100644 (file)
index 0000000..730f533
--- /dev/null
@@ -0,0 +1,143 @@
+#ifndef MSP_GAME_REMOTECALL_H_
+#define MSP_GAME_REMOTECALL_H_
+
+#include <functional>
+#include <stdexcept>
+#include <msp/net/receiver.h>
+#include "entity.h"
+#include "replicator.h"
+#include "zygote.h"
+
+namespace Msp::Game {
+
+template<typename P>
+class ReplicatedCall: private Net::PacketReceiver<P>
+{
+public:
+       using Function = std::function<void(const P &)>;
+
+private:
+       Replicator *replicator = nullptr;
+       Function func;
+
+public:
+       ReplicatedCall(Replicator *, Function);
+
+       void operator()(P &);
+       void operator()(unsigned, P &);
+
+private:
+       void receive(const P &) override;
+};
+
+
+template<typename P, typename E = Entity>
+       requires std::is_base_of_v<Entity, E>
+class ReplicatedEntityCall: private Net::PacketReceiver<P>
+{
+public:
+       using Function = std::function<void(Handle<E>, const P &)>;
+
+private:
+       Replicator *replicator = nullptr;
+       Function func;
+
+public:
+       ReplicatedEntityCall(Replicator *, Function);
+
+       void operator()(Handle<Entity>, P &);
+       void operator()(unsigned, Handle<Entity>, P &);
+
+private:
+       void prepare(Handle<Entity>, P &);
+
+public:
+       void receive(const P &) override;
+};
+
+
+template<typename P>
+ReplicatedCall<P>::ReplicatedCall(Replicator *r, std::function<void(const P &)> f):
+       replicator(r),
+       func(std::move(f))
+{
+       if(replicator)
+               replicator->add_receiver<P>(*this);
+}
+
+template<typename P>
+void ReplicatedCall<P>::operator()(P &packet)
+{
+       if(replicator)
+               replicator->send(packet);
+       func(packet);
+}
+
+template<typename P>
+void ReplicatedCall<P>::receive(const P &packet)
+{
+       func(packet);
+}
+
+
+template<typename P, typename E>
+ReplicatedEntityCall<P, E>::ReplicatedEntityCall(Replicator *r, Function f):
+       replicator(r),
+       func(std::move(f))
+{
+       if(replicator)
+               replicator->add_receiver<P>(*this);
+}
+
+template<typename P, typename E>
+void ReplicatedEntityCall<P, E>::operator()(Handle<Entity> entity, P &packet)
+{
+       if(replicator)
+       {
+               prepare(entity, packet);
+               replicator->send(entity, packet);
+       }
+       func(entity, packet);
+}
+
+template<typename P, typename E>
+void ReplicatedEntityCall<P, E>::operator()(unsigned target, Handle<Entity> entity, P &packet)
+{
+       if(replicator)
+       {
+               prepare(entity, packet);
+               replicator->send(target, packet);
+       }
+}
+
+template<typename P, typename E>
+void ReplicatedEntityCall<P, E>::prepare(Handle<Entity> entity, P &packet)
+{
+       Handle<Zygote> zygote = entity->get_component<Zygote>();
+       if(!zygote)
+               throw std::invalid_argument("ReplicatedEntityCall::operator()");
+
+       std::uint32_t entity_id = zygote->get_entity_id();
+       if(entity_id==Zygote::NO_ID)
+               throw std::invalid_argument("ReplicatedEntityCall::operator()");
+
+       packet.entity_id = entity_id;
+}
+
+template<typename P, typename E>
+void ReplicatedEntityCall<P, E>::receive(const P &packet)
+{
+       Handle<Entity> entity = replicator->find_entity(packet.entity_id);
+       if(!entity)
+               return;  // TODO report the error somehow
+
+       Handle<E> cast_entity = dynamic_handle_cast<E>(entity);
+       if(!cast_entity)
+               return;
+
+       func(cast_entity, packet);
+}
+
+} // namespace Msp::Game
+
+#endif
index 2962bc72ff18c574f593c724c98912ebc61b4413..5fbc08ee28d46ba6f85bf637428fd596815da002 100644 (file)
@@ -1,6 +1,4 @@
 #include "replicator.h"
-#include "entity.h"
-#include "networking.h"
 #include <msp/core/raii.h>
 #include "spawner.h"
 #include "transform.h"
@@ -13,12 +11,12 @@ namespace Msp::Game {
 Replicator::Replicator(Stage &s, Networking &n):
        System(s),
        observer(stage.get_event_bus()),
-       networking(n)
+       networking(n),
+       protocol(networking.set_receiver<StageProtocol>(&dispatcher))
 {
        observer.observe<Events::ComponentCreated>([this](const auto &e){ component_created(e); });
 
-       const Net::Protocol &protocol = networking.set_receiver<StageProtocol>(&dispatcher);
-       dispatcher.add_receiver<Messages::SpawnEntity>(protocol.get_packet_id<Messages::SpawnEntity>(), *this);
+       add_receiver<Messages::SpawnEntity>(*this);
 }
 
 void Replicator::add_spawner(Spawner &spawner, const string &type)
@@ -43,6 +41,12 @@ void Replicator::add_target_player(unsigned id)
                send_spawn(e, id);
 }
 
+Handle<Entity> Replicator::find_entity(uint32_t id) const
+{
+       auto i = find_if(entities, [id](const ReplicatedEntity &e){ return e.zygote->get_entity_id()==id; });
+       return (i!=entities.end() ? i->entity : nullptr);
+}
+
 void Replicator::deferred_tick()
 {
        for(ReplicatedEntity &e: entities)
index 176ec66d4d95f82fc5216b8fbaa2ac90f8f6d7c3..6dc42e85a048791668b54fbcd8c2a890ae530a80 100644 (file)
@@ -3,14 +3,15 @@
 
 #include <vector>
 #include <msp/net/receiver.h>
+#include "entity.h"
 #include "messages.h"
+#include "networking.h"
 #include "protocol.h"
 #include "system.h"
 #include "zygote.h"
 
 namespace Msp::Game {
 
-class Networking;
 class Spawner;
 class Transform;
 
@@ -28,6 +29,7 @@ private:
        EventObserver observer;
        Networking &networking;
        Net::DynamicDispatcher dispatcher;
+       const Net::Protocol &protocol;
        std::vector<Spawner *> spawners;
        std::vector<ReplicatedEntity> entities;
        std::vector<unsigned> players;
@@ -40,6 +42,20 @@ public:
        void add_spawner(Spawner &, const std::string &);
        void add_target_player(unsigned);
 
+       template<typename P>
+       void add_receiver(Net::PacketReceiver<P> &);
+
+       template<typename P>
+       void send(const P &);
+
+       template<typename P>
+       void send(Handle<Entity>, const P &);
+
+       template<typename P>
+       void send(unsigned, const P &);
+
+       Handle<Entity> find_entity(std::uint32_t) const;
+
        void tick(Time::TimeDelta) override { }
        void deferred_tick() override;
 
@@ -50,6 +66,37 @@ private:
        void receive(const Messages::SpawnEntity &) override;
 };
 
+template<typename P>
+void Replicator::add_receiver(Net::PacketReceiver<P> &recv)
+{
+       dispatcher.add_receiver<P>(protocol.get_packet_id<P>(), recv);
+}
+
+template<typename P>
+void Replicator::send(const P &packet)
+{
+       for(unsigned p: players)
+               networking.send(p, packet);
+}
+
+template<typename P>
+void Replicator::send(Handle<Entity> entity, const P &packet)
+{
+       Handle<Transform> transform = entity->get_transform();
+       auto i = lower_bound_member(entities, transform, &ReplicatedEntity::transform);
+       if(i==entities.end() || i->transform!=transform)
+               return;
+
+       if(i->visible_to_players)
+               send(packet);
+}
+
+template<typename P>
+void Replicator::send(unsigned target, const P &packet)
+{
+       networking.send(target, packet);
+}
+
 } // namespace Msp::Game
 
 #endif