#ifndef MSP_GAME_MESSAGES_H_
#define MSP_GAME_MESSAGES_H_
+#include <msp/geometry/quaternion.h>
+#include <msp/linal/vector.h>
+
namespace Msp::Game {
namespace Messages {
std::string value;
};
+struct SpawnEntity
+{
+ std::uint16_t type;
+ std::uint16_t setup;
+ LinAl::Vector<float, 3> position;
+ Geometry::Quaternion<float> rotation;
+ LinAl::Vector<float, 3> scale;
+};
+
struct StageActivated
{
std::uint16_t name;
add<StageActivated>(&StageActivated::name);
}
+
+StageProtocol::StageProtocol()
+{
+ using namespace Messages;
+
+ using Vector3f = LinAl::Vector<float, 3>;
+ using Quaternion = Geometry::Quaternion<float>;
+ add<Vector3f, float, float, float>(&Vector3f::x, &Vector3f::y, &Vector3f::z);
+ add<Quaternion>(&Quaternion::a, &Quaternion::b, &Quaternion::c, &Quaternion::d);
+
+ add<SpawnEntity>(&SpawnEntity::type, &SpawnEntity::setup, &SpawnEntity::position, &SpawnEntity::rotation, &SpawnEntity::scale);
+}
+
} // namespace Msp::Game
CoreProtocol();
};
+
+class StageProtocol: public Net::Protocol
+{
+public:
+ StageProtocol();
+};
+
} // namespace Msp::Game
#endif
--- /dev/null
+#include "replicator.h"
+#include "entity.h"
+#include "networking.h"
+#include "spawner.h"
+#include "transform.h"
+#include "zygote.h"
+
+using namespace std;
+
+namespace Msp::Game {
+
+Replicator::Replicator(Stage &s, Networking &n):
+ System(s),
+ observer(stage.get_event_bus()),
+ networking(n)
+{
+ 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);
+}
+
+void Replicator::add_spawner(Spawner &spawner, const string &type)
+{
+ bool exists = false;
+ for(Spawner *s: spawners)
+ {
+ if(s==&spawner)
+ exists = true;
+ else if(s->can_spawn(type))
+ throw key_error(type);
+ }
+
+ if(!exists)
+ spawners.push_back(&spawner);
+}
+
+void Replicator::add_target_player(unsigned id)
+{
+ players.push_back(id);
+ for(const ReplicatedEntity &e: entities)
+ send_spawn(e, id);
+}
+
+void Replicator::component_created(const Events::ComponentCreated &event)
+{
+ if(Handle<Zygote> zygote = dynamic_handle_cast<Zygote>(event.component))
+ {
+ Handle<Entity> entity = zygote->get_entity();
+ Handle<Transform> transform = entity->get_transform();
+ auto i = lower_bound_member(entities, transform, &ReplicatedEntity::transform);
+ i = entities.emplace(i, entity, transform, zygote);
+ send_spawn(*i, -1);
+ }
+}
+
+void Replicator::send_spawn(const ReplicatedEntity &re, int target)
+{
+ const SpawnInfo &info = re.zygote->get_spawn_info();
+ Messages::SpawnEntity message;
+ message.type = networking.intern_string(info.type_name);
+ message.setup = networking.intern_string(info.setup_name);
+ const TransformValues &tf = re.transform->get_values();
+ message.position = tf.position;
+ message.rotation = tf.rotation;
+ message.scale = tf.scale;
+ if(target>=0)
+ networking.send(target, message);
+ else
+ networking.send(message);
+}
+
+void Replicator::receive(const Messages::SpawnEntity &message)
+{
+ const string &type_name = networking.get_string(message.type);
+ auto i = ranges::find_if(spawners, [&type_name](Spawner *s){ return s->can_spawn(type_name); });
+ if(i==spawners.end())
+ return; // TODO report the error somehow
+
+ const string &setup_name = networking.get_string(message.setup);
+ (*i)->spawn(type_name, setup_name, TransformValues(message.position, message.rotation, message.scale));
+}
+
+} // namespace Msp::Game
--- /dev/null
+#ifndef MSP_GAME_REPLICATOR_H_
+#define MSP_GAME_REPLICATOR_H_
+
+#include <vector>
+#include <msp/net/receiver.h>
+#include "messages.h"
+#include "protocol.h"
+#include "system.h"
+
+namespace Msp::Game {
+
+class Networking;
+class Spawner;
+class Transform;
+class Zygote;
+
+class MSPGAME_API Replicator: public System, private Net::PacketReceiver<Messages::SpawnEntity>
+{
+private:
+ struct ReplicatedEntity
+ {
+ Handle<Entity> entity;
+ Handle<Transform> transform;
+ Handle<Zygote> zygote;
+ };
+
+ EventObserver observer;
+ Networking &networking;
+ Net::DynamicDispatcher dispatcher;
+ std::vector<Spawner *> spawners;
+ std::vector<ReplicatedEntity> entities;
+ std::vector<unsigned> players;
+
+public:
+ Replicator(Stage &, Networking &);
+
+ void add_spawner(Spawner &, const std::string &);
+ void add_target_player(unsigned);
+
+ void tick(Time::TimeDelta) override { }
+
+private:
+ void component_created(const Events::ComponentCreated &);
+ void send_spawn(const ReplicatedEntity &, int);
+
+ void receive(const Messages::SpawnEntity &) override;
+};
+
+} // namespace Msp::Game
+
+#endif
--- /dev/null
+#include "spawner.h"
+#include "entity.h"
+#include "replicator.h"
+#include "system.h"
+#include "transform.h"
+#include "zygote.h"
+
+using namespace std;
+
+namespace Msp::Game {
+
+Spawner::Spawner(Stage &s, Replicator *r, Handle<Entity> p, SpawningHandler &h):
+ resources(s.get_resources()),
+ reflector(s.get_reflector()),
+ replicator(r),
+ parent(p),
+ handler(h)
+{ }
+
+void Spawner::add_to_replicator(const string &type_name)
+{
+ if(replicator)
+ replicator->add_spawner(*this, type_name);
+}
+
+bool Spawner::can_spawn(const string &type_name) const
+{
+ return ranges::any_of(spawnable_types, [&type_name](const SpawnableType &t){ return t.type.get_name()==type_name; });
+}
+
+void Spawner::spawn(const string &type_name, const string &setup_name, const TransformValues &tf)
+{
+ auto i = ranges::find_if(spawnable_types, [&type_name](const SpawnableType &t){ return t.type.get_name()==type_name; });
+ if(i==spawnable_types.end())
+ throw key_error(type_name);
+
+ spawn(*i, setup_name, tf);
+}
+
+void Spawner::spawn(const SpawnableType &type, const string &setup_name, const TransformValues &tf)
+{
+ auto i = ranges::find_if(spawn_infos, [&type, &setup_name](const auto &s){
+ return s->type_name==type.type.get_name() && s->setup_name==setup_name;
+ });
+ if(i==spawn_infos.end())
+ i = spawn_infos.emplace(spawn_infos.end(), make_unique<SpawnInfo>(this, type.type.get_name(), setup_name));
+
+ Owned<Entity> entity = (this->*type.create_func)(setup_name, tf);
+ if(Handle<Zygote> zygote = entity->get_component<Zygote>())
+ zygote->set_spawn_info(**i);
+ handler.spawned(move(entity));
+}
+
+} // namespace Msp::Game
--- /dev/null
+#ifndef MSP_GAME_SPAWNER_H_
+#define MSP_GAME_SPAWNER_H_
+
+#include <algorithm>
+#include "messages.h"
+#include "owned.h"
+
+namespace Msp::Game {
+
+class Replicator;
+class Spawner;
+struct TransformValues;
+
+struct SpawnInfo
+{
+ Spawner *spawner;
+ std::string type_name;
+ std::string setup_name;
+};
+
+
+class MSPGAME_API SpawningHandler
+{
+public:
+ virtual void spawned(Owned<Entity>) = 0;
+};
+
+
+class MSPGAME_API Spawner
+{
+private:
+ struct SpawnableType
+ {
+ const Reflection::ClassBase &type;
+ Owned<Entity> (Spawner::*create_func)(const std::string &, const TransformValues &) const;
+ };
+
+ DataFile::Collection &resources;
+ Reflection::Reflector &reflector;
+ Replicator *replicator = nullptr;
+ Handle<Entity> parent;
+ SpawningHandler &handler;
+ std::vector<SpawnableType> spawnable_types;
+ std::vector<std::unique_ptr<SpawnInfo>> spawn_infos;
+
+public:
+ Spawner(Stage &, Replicator *, Handle<Entity>, SpawningHandler &);
+
+ template<typename T>
+ requires std::is_base_of_v<Entity, T>
+ void add_spawnable_type();
+
+private:
+ void add_to_replicator(const std::string &);
+
+public:
+ bool can_spawn(const std::string &) const;
+
+ template<typename T>
+ requires std::is_base_of_v<Entity, T>
+ void spawn(const std::string &, const TransformValues &);
+
+ void spawn(const std::string &, const std::string &, const TransformValues &);
+
+private:
+ void spawn(const SpawnableType &, const std::string &, const TransformValues &);
+
+ template<typename T>
+ Owned<Entity> create(const std::string &, const TransformValues &) const;
+};
+
+
+template<typename T>
+ requires std::is_base_of_v<Entity, T>
+void Spawner::add_spawnable_type()
+{
+ const Reflection::ClassBase &type = reflector.get_or_create_class<T>();
+ /* There's assumed to be only a few types per spawner, so sorting is not
+ necessary */
+ spawnable_types.emplace_back(type, &Spawner::create<T>);
+ add_to_replicator(type.get_name());
+}
+
+template<typename T>
+ requires std::is_base_of_v<Entity, T>
+void Spawner::spawn(const std::string &sname, const TransformValues &tf)
+{
+ const Reflection::ClassBase &type = reflector.get_or_create_class<T>();
+ auto i = std::ranges::find_if(spawnable_types, [&type](const SpawnableType &t){ return &t.type==&type; });
+ if(i==spawnable_types.end())
+ throw key_error(type.get_name());
+
+ spawn(*i, sname, tf);
+}
+
+template<typename T>
+Owned<Entity> Spawner::create(const std::string &setup_name, const TransformValues &tf) const
+{
+ const typename T::Setup &setup = resources.get<typename T::Setup>(setup_name);
+ return Owned<T>(parent, setup, tf);
+}
+
+} // namespace Msp::Game
+
+#endif
--- /dev/null
+#include "zygote.h"
+#include <msp/core/except.h>
+
+namespace Msp::Game {
+
+void Zygote::set_spawn_info(const SpawnInfo &s)
+{
+ if(spawn_info)
+ throw already_called("spawn_info already set");
+ spawn_info = &s;
+}
+
+const SpawnInfo &Zygote::get_spawn_info() const
+{
+ if(!spawn_info)
+ throw invalid_state("no spawn_info");
+ return *spawn_info;
+}
+
+} // namespace Msp::Game
--- /dev/null
+#ifndef MSP_GAME_ZYGOTE_H_
+#define MSP_GAME_ZYGOTE_H_
+
+#include "component.h"
+
+namespace Msp::Game {
+
+struct SpawnInfo;
+
+class Zygote: public Component
+{
+private:
+ const SpawnInfo *spawn_info = nullptr;
+
+public:
+ Zygote(Handle<Entity> e): Component(e) { }
+
+ void set_spawn_info(const SpawnInfo &);
+ const SpawnInfo &get_spawn_info() const;
+};
+
+} // namespace Msp::Game
+
+#endif