--- /dev/null
+#include "accessguard.h"
+
+namespace Msp::Game {
+
+thread_local AccessGuard *AccessGuard::instance = nullptr;
+
+AccessGuard::AccessGuard()
+{
+ if(!instance)
+ instance = this;
+}
+
+AccessGuard::AccessGuard(AccessGuard &&other):
+ flags(std::move(other.flags))
+{
+ if(&other==instance)
+ instance = this;
+}
+
+AccessGuard &AccessGuard::operator=(AccessGuard &&other)
+{
+ flags = std::move(other.flags);
+ if(&other==instance)
+ instance = this;
+ return *this;
+}
+
+AccessGuard::~AccessGuard()
+{
+ if(this==instance)
+ instance = nullptr;
+}
+
+AccessGuard &AccessGuard::get_instance()
+{
+ if(!instance)
+ throw std::logic_error("no AccessGuard instance");
+ return *instance;
+}
+
+void AccessGuard::block_all()
+{
+ default_flag = BLOCKED;
+ for(uint8_t &f: flags)
+ f = BLOCKED;
+}
+
+void AccessGuard::unblock_all()
+{
+ default_flag = UNBLOCKED;
+ for(uint8_t &f: flags)
+ f = UNBLOCKED;
+}
+
+unsigned AccessGuard::get_next_index()
+{
+ static unsigned next_index = 0;
+ return next_index++;
+}
+
+} // namespace Msp::Game
--- /dev/null
+#ifndef MSP_GAME_ACCESSGUARD_H_
+#define MSP_GAME_ACCESSGUARD_H_
+
+#include <cstdint>
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <msp/core/noncopyable.h>
+
+namespace Msp::Game {
+
+class invalid_access: public std::logic_error
+{
+public:
+ invalid_access(const std::string &w): logic_error(w) { }
+};
+
+
+class AccessGuard
+{
+public:
+ struct Create { static std::string describe() { return "create"; } };
+ struct Destroy { static std::string describe() { return "destroy"; } };
+
+ template<typename T = void>
+ struct BlockForScope: NonCopyable
+ {
+ BlockForScope() { get_instance().block<T>(); }
+ ~BlockForScope() { get_instance().unblock<T>(); }
+ };
+
+private:
+ const uint8_t UNBLOCKED = 0;
+ const uint8_t BLOCKED = 1;
+
+ std::uint8_t default_flag = UNBLOCKED;
+ std::vector<std::uint8_t> flags;
+
+ static thread_local AccessGuard *instance;
+
+public:
+ AccessGuard();
+ AccessGuard(AccessGuard &&);
+ AccessGuard &operator=(AccessGuard &&);
+ ~AccessGuard();
+
+ static AccessGuard &get_instance();
+
+ void block_all();
+ void unblock_all();
+
+private:
+ static unsigned get_next_index();
+
+ template<typename T>
+ static unsigned get_index();
+
+ template<typename T>
+ std::uint8_t &get();
+
+public:
+ template<typename T>
+ void block() { get<T>() = BLOCKED; }
+
+ template<typename T>
+ void unblock() { get<T>() = UNBLOCKED; }
+
+ template<typename T>
+ void check() { if(get<T>()!=UNBLOCKED) throw invalid_access(T::describe()); }
+};
+
+
+template<typename T>
+inline unsigned AccessGuard::get_index()
+{
+ static unsigned index = get_next_index();
+ return index;
+}
+
+template<typename T>
+inline std::uint8_t &AccessGuard::get()
+{
+ unsigned index = get_index<T>();
+ if(flags.size()<=index)
+ flags.resize(index+1, default_flag);
+ return flags[index];
+}
+
+template<>
+inline void AccessGuard::block<void>()
+{ block_all(); }
+
+template<>
+inline void AccessGuard::unblock<void>()
+{ unblock_all(); }
+
+} // namespace Msp::Game
+
+#endif
Director::Director(DataFile::Collection &r):
resources(r),
event_source(event_bus)
-{ }
+{
+#if DEBUG
+ access_guard.emplace();
+#endif
+}
// Hide ~unique_ptr<Stage> from the header
Director::~Director()
#define MSP_GAME_DIRECTOR_H_
#include <memory>
+#include <optional>
#include <vector>
#include <msp/datafile/collection.h>
#include <msp/time/timedelta.h>
#include <msp/time/timestamp.h>
+#include "accessguard.h"
#include "eventbus.h"
#include "events.h"
#include "eventsource.h"
using EventSource = Game::EventSource<Events::StageCreated, Events::StageActivated, Events::StageDeactivated>;
private:
+ std::optional<AccessGuard> access_guard;
DataFile::Collection &resources;
EventBus event_bus;
EventSource event_source;
#define MSP_GAME_OWNED_H_
#include <stdexcept>
+#include "accessguard.h"
#include "events.h"
#include "handle.h"
#include "stage.h"
template<typename P, typename... Args>
Owned<T>::Owned(Handle<P> parent, Args &&... args)
{
+#ifdef DEBUG
+ AccessGuard::get_instance().check<AccessGuard::Create>();
+#endif
+
if(!parent)
throw std::invalid_argument("Owned::Owned");
if(!obj)
return;
+#ifdef DEBUG
+ AccessGuard::get_instance().check<AccessGuard::Destroy>();
+#endif
+
Stage &stage = get_stage(*obj);
if constexpr(std::is_base_of_v<Component, T>)
#include "stage.h"
+#include "accessguard.h"
#include "camera.h"
#include "root.h"
#include "system.h"
void Stage::tick(Time::TimeDelta dt)
{
- for(const auto &s: systems)
- s->tick(dt);
+ {
+#ifdef DEBUG
+ AccessGuard::BlockForScope _block;;
+#endif
+ for(const auto &s: systems)
+ s->tick(dt);
+ }
+
for(const auto &s: systems)
s->deferred_tick();
}