]> git.tdb.fi Git - libs/game.git/commitdiff
Add facilities to designate entities as player-controlled
authorMikko Rasa <tdb@tdb.fi>
Tue, 9 Jan 2024 21:49:59 +0000 (23:49 +0200)
committerMikko Rasa <tdb@tdb.fi>
Tue, 9 Jan 2024 21:50:04 +0000 (23:50 +0200)
source/game/events.h
source/game/messages.h
source/game/possessed.cpp [new file with mode: 0644]
source/game/possessed.h [new file with mode: 0644]
source/game/protocol.cpp
source/game/replicator.cpp
source/game/replicator.h

index 7636a1008c6a394a85f86dbfd8cdd3842c4d8776..436537e08f33300987ab2af3abaeeb617c3a6916 100644 (file)
@@ -77,6 +77,12 @@ struct RemotePlayerEntered
        unsigned id;
 };
 
+struct PossessionChanged
+{
+       Handle<Entity> entity;
+       unsigned player_id;
+};
+
 struct RemotePlayerExited
 {
        unsigned id;
index c576ae6c6f0e612ac2eef24aed76f7e44ae61bc6..4b962152c44beb6fb0a1956ad412da4616233fa9 100644 (file)
@@ -48,6 +48,12 @@ struct SpawnEntity
        LinAl::Vector<float, 3> scale;
 };
 
+struct GrantPossession
+{
+       std::uint32_t entity_id;
+       std::uint16_t player_id;
+};
+
 struct StageActivated
 {
        std::uint16_t name;
diff --git a/source/game/possessed.cpp b/source/game/possessed.cpp
new file mode 100644 (file)
index 0000000..cc52e1c
--- /dev/null
@@ -0,0 +1,17 @@
+#include "possessed.h"
+
+namespace Msp::Game {
+
+void Possessed::set_player_id(unsigned p)
+{
+       if(p!=player_id)
+               changed = true;
+       player_id = p;
+}
+
+void Possessed::clear_changed()
+{
+       changed = false;
+}
+
+} // namespace Msp::Game
diff --git a/source/game/possessed.h b/source/game/possessed.h
new file mode 100644 (file)
index 0000000..54350da
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef MSP_GAME_POSSESSED_H_
+#define MSP_GAME_POSSESSED_H_
+
+#include "component.h"
+#include "mspgame_api.h"
+
+namespace Msp::Game {
+
+class MSPGAME_API Possessed: public Component
+{
+private:
+       unsigned player_id = 0;
+       bool changed = false;
+
+public:
+       Possessed(Handle<Entity> e): Component(e) { }
+
+       void set_player_id(unsigned);
+       unsigned get_player_id() const { return player_id; }
+       bool has_changed() const { return changed; }
+       void clear_changed();
+};
+
+} // namespace Msp::Game
+
+#endif
index 9b81f516ae364b5326388da47839928ded266197..32242a9f0729631e3a9d7fbbdd214f0eb0e8beb2 100644 (file)
@@ -26,6 +26,7 @@ StageProtocol::StageProtocol()
        add<HeightmapPoint>(&HeightmapPoint::elevation, &HeightmapPoint::material);
 
        add<SpawnEntity>(&SpawnEntity::id, &SpawnEntity::type, &SpawnEntity::setup, &SpawnEntity::position, &SpawnEntity::rotation, &SpawnEntity::scale);
+       add<GrantPossession>(&GrantPossession::entity_id, &GrantPossession::player_id);
        add<LandscapeData>(&LandscapeData::entity_id, &LandscapeData::heightmap);
 }
 
index 94bdc272a9e252a496ddbf30d4997a6ca1485fc1..9fd6eb52d76e6be8afad858d996f51ac9edd08de 100644 (file)
@@ -1,5 +1,6 @@
 #include "replicator.h"
 #include <msp/core/raii.h>
+#include "possessed.h"
 #include "spawner.h"
 #include "transform.h"
 #include "zygote.h"
@@ -10,6 +11,7 @@ namespace Msp::Game {
 
 Replicator::Replicator(Stage &s, Networking &n):
        System(s),
+       event_source(stage.get_event_bus()),
        observer(stage.get_event_bus()),
        networking(n),
        protocol(networking.set_receiver<StageProtocol>(&dispatcher))
@@ -17,6 +19,7 @@ Replicator::Replicator(Stage &s, Networking &n):
        observer.observe<Events::ComponentCreated>([this](const auto &e){ component_created(e); });
 
        add_receiver<Messages::SpawnEntity>(*this);
+       add_receiver<Messages::GrantPossession>(*this);
 }
 
 void Replicator::add_spawner(Spawner &spawner, const string &type)
@@ -47,6 +50,23 @@ Handle<Entity> Replicator::find_entity(uint32_t id) const
        return (i!=entities.end() ? i->entity : nullptr);
 }
 
+void Replicator::tick(Time::TimeDelta)
+{
+       stage.iterate_objects<Possessed>([this](Possessed &p){
+               if(p.has_changed())
+               {
+                       auto i = lower_bound_member(entities, p.get_entity()->get_transform(), &ReplicatedEntity::transform);
+                       if(i!=entities.end() && i->possessed.get()==&p)
+                       {
+                               event_source.emit<Events::PossessionChanged>(i->entity, p.get_player_id());
+                               if(i->visible_to_players && is_server())
+                                       send_possession(*i, -1);
+                       }
+                       p.clear_changed();
+               }
+       });
+}
+
 void Replicator::deferred_tick()
 {
        for(ReplicatedEntity &e: entities)
@@ -63,14 +83,22 @@ void Replicator::component_created(const Events::ComponentCreated &event)
        {
                Handle<Entity> entity = zygote->get_entity();
                Handle<Transform> transform = entity->get_transform();
+               Handle<Possessed> possessed = entity->get_component<Possessed>();
                auto i = lower_bound_member(entities, transform, &ReplicatedEntity::transform);
-               entities.emplace(i, entity, transform, zygote);
+               entities.emplace(i, entity, transform, zygote, possessed);
 
                if(networking.is_server())
                        zygote->set_entity_id(next_entity_id++);
                else if(received_entity_id!=Zygote::NO_ID)
                        zygote->set_entity_id(received_entity_id);
        }
+       else if(Handle<Possessed> possessed = dynamic_handle_cast<Possessed>(event.component))
+       {
+               Handle<Transform> transform = possessed->get_entity()->get_transform();
+               auto i = lower_bound_member(entities, transform, &ReplicatedEntity::transform);
+               if(i!=entities.end() && i->transform==transform)
+                       i->possessed = possessed;
+       }
 }
 
 void Replicator::send_spawn(const ReplicatedEntity &re, int target)
@@ -97,6 +125,27 @@ void Replicator::send_spawn(const ReplicatedEntity &re, int target)
        }
 
        info.spawner->notify_spawn_sent(re.entity, target);
+
+       if(re.possessed && re.possessed->get_player_id())
+               send_possession(re, target);
+}
+
+void Replicator::send_possession(const ReplicatedEntity &re, int target)
+{
+       if(target<0 && players.empty())
+               return;
+
+       Messages::GrantPossession grant;
+       grant.entity_id = re.zygote->get_entity_id();
+       grant.player_id = re.possessed->get_player_id();
+
+       if(target>=0)
+               networking.send(target, grant);
+       else
+       {
+               for(unsigned p: players)
+                       networking.send(p, grant);
+       }
 }
 
 void Replicator::receive(const Messages::SpawnEntity &message)
@@ -111,4 +160,12 @@ void Replicator::receive(const Messages::SpawnEntity &message)
        (*i)->spawn(type_name, setup_name, TransformValues(message.position, message.rotation, message.scale));
 }
 
+void Replicator::receive(const Messages::GrantPossession &message)
+{
+       Handle<Entity> entity = find_entity(message.entity_id);
+       if(entity)
+               if(Handle<Possessed> possessed = entity->get_component<Possessed>())
+                       possessed->set_player_id(message.player_id);
+}
+
 } // namespace Msp::Game
index 6dc42e85a048791668b54fbcd8c2a890ae530a80..23c5b6636d7adb848c8eceadea9cda8b9f1959b9 100644 (file)
 
 namespace Msp::Game {
 
+class Possessed;
 class Spawner;
 class Transform;
 
-class MSPGAME_API Replicator: public System, private Net::PacketReceiver<Messages::SpawnEntity>
+class MSPGAME_API Replicator: public System, private Net::PacketReceiver<Messages::SpawnEntity>,
+       private Net::PacketReceiver<Messages::GrantPossession>
 {
+public:
+       using EventSource = Game::EventSource<Events::PossessionChanged>;
+
 private:
        struct ReplicatedEntity
        {
                Handle<Entity> entity;
                Handle<Transform> transform;
                Handle<Zygote> zygote;
+               Handle<Possessed> possessed;
                bool visible_to_players = false;
        };
 
+       EventSource event_source;
        EventObserver observer;
        Networking &networking;
        Net::DynamicDispatcher dispatcher;
@@ -41,6 +48,7 @@ public:
 
        void add_spawner(Spawner &, const std::string &);
        void add_target_player(unsigned);
+       bool is_server() const { return networking.is_server(); }
 
        template<typename P>
        void add_receiver(Net::PacketReceiver<P> &);
@@ -56,14 +64,16 @@ public:
 
        Handle<Entity> find_entity(std::uint32_t) const;
 
-       void tick(Time::TimeDelta) override { }
+       void tick(Time::TimeDelta) override;
        void deferred_tick() override;
 
 private:
        void component_created(const Events::ComponentCreated &);
        void send_spawn(const ReplicatedEntity &, int);
+       void send_possession(const ReplicatedEntity &, int);
 
        void receive(const Messages::SpawnEntity &) override;
+       void receive(const Messages::GrantPossession &) override;
 };
 
 template<typename P>