+#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