Handle<Entity> get_entity() const { return entity; }
};
+template<typename T>
+class BufferedComponent: public Component
+{
+public:
+ using Data = T;
+
+protected:
+ T data[2];
+ uint8_t read_index = 0;
+ uint8_t write_index = 0;
+ bool written = false;
+
+ BufferedComponent(Handle<Entity> e): Component(e) { }
+
+ const T &read() const { return data[read_index]; }
+ T &write();
+
+public:
+ virtual void prepare_tick() { write_index = 1-read_index; written = false; }
+ virtual void commit_tick() { if(written) read_index = write_index; }
+};
+
+template<typename T>
+T &BufferedComponent<T>::write()
+{
+ if(!written && write_index!=read_index)
+ {
+ data[write_index] = data[read_index];
+ written = true;
+ }
+ return data[write_index];
+}
+
} // namespace Msp::Game
#endif
#include "system.h"
+using namespace std;
+
namespace Msp::Game {
+thread_local System *System::active = nullptr;
+
+void System::begin_tick()
+{
+ if(active)
+ throw logic_error("System::active != nullptr");
+ active = this;
+
+ for(const Dependency &d: dependencies)
+ if(d.prepare)
+ d.prepare(stage);
+}
+
+void System::end_tick()
+{
+ for(const Dependency &d: dependencies)
+ if(d.commit)
+ d.commit(stage);
+
+ if(active==this)
+ active = nullptr;
+}
+
void System::deferred_tick()
{
for(const auto &f: deferred_queue)
#include <functional>
#include <msp/time/timedelta.h>
+#include "component.h"
+#include "reflection.h"
+#include "stage.h"
namespace Msp::Game {
-class Stage;
-
class System
{
+public:
+ enum DependencyFlags
+ {
+ NO_DEPENDENCY = 0,
+ READ_OLD = 1,
+ READ_FRESH = 3,
+ WRITE = 4,
+ UPDATE = READ_OLD | WRITE,
+ CHAINED_UPDATE = READ_FRESH | WRITE
+ };
+
+ struct Dependency
+ {
+ DependencyFlags flags = NO_DEPENDENCY;
+ const Reflection::ClassBase &type;
+ void (*prepare)(Stage &) = nullptr;
+ void (*commit)(Stage &) = nullptr;
+
+ Dependency(const Reflection::ClassBase &t): type(t) { }
+ };
+
+ class Activator: public NonCopyable
+ {
+ private:
+ System &system;
+
+ public:
+ Activator(System &s): system(s) { system.begin_tick(); }
+ ~Activator() { system.end_tick(); }
+ };
+
protected:
Stage &stage;
std::vector<std::function<void()>> deferred_queue;
+ std::vector<Dependency> dependencies;
+
+ static thread_local System *active;
System(Stage &s): stage(s) { }
public:
Stage &get_stage() const { return stage; }
+protected:
+ template<typename T>
+ void declare_dependency(DependencyFlags);
+
+public:
+ void begin_tick();
virtual void tick(Time::TimeDelta) = 0;
+ void end_tick();
virtual void deferred_tick();
protected:
void defer(F &&f) { deferred_queue.emplace_back(std::forward<F>(f)); }
};
+
+template<typename T>
+inline void System::declare_dependency(DependencyFlags flags)
+{
+ if(!std::is_base_of_v<Component, T>)
+ throw std::invalid_argument("System::declare_dependency");
+
+ const Reflection::ClassBase &type = stage.get_reflector().get_or_create_class<T>();
+ auto i = find_if(dependencies, [&type](const Dependency &d){ return &d.type==&type; });
+
+ if(i!=dependencies.end())
+ flags = static_cast<DependencyFlags>(flags|i->flags);
+
+ Dependency &dep = (i!=dependencies.end() ? *i : dependencies.emplace_back(type));
+ dep.flags = flags;
+ if constexpr(requires(T &c) { typename T::Data; c.prepare_tick(); c.commit_tick(); })
+ if(flags&WRITE)
+ {
+ dep.prepare = +[](Stage &s){ s.iterate_objects<T>([](T &c){ c.prepare_tick(); }); };
+ dep.commit = +[](Stage &s){ s.iterate_objects<T>([](T &c){ c.commit_tick(); }); };
+ }
+}
+
} // namespace Msp::Game
#endif