diff options
| -rw-r--r-- | src/crepe/Component.h | 17 | ||||
| -rw-r--r-- | src/crepe/api/BehaviorScript.h | 1 | ||||
| -rw-r--r-- | src/crepe/api/Engine.cpp | 7 | ||||
| -rw-r--r-- | src/crepe/api/Engine.h | 14 | ||||
| -rw-r--r-- | src/crepe/api/GameObject.h | 3 | ||||
| -rw-r--r-- | src/crepe/api/ParticleEmitter.cpp | 15 | ||||
| -rw-r--r-- | src/crepe/api/ParticleEmitter.h | 6 | ||||
| -rw-r--r-- | src/crepe/api/Script.hpp | 11 | ||||
| -rw-r--r-- | src/crepe/api/Transform.h | 1 | ||||
| -rw-r--r-- | src/crepe/manager/ComponentManager.h | 24 | ||||
| -rw-r--r-- | src/crepe/manager/ReplayManager.cpp | 7 | ||||
| -rw-r--r-- | src/crepe/manager/ReplayManager.h | 52 | ||||
| -rw-r--r-- | src/crepe/manager/SystemManager.h | 24 | ||||
| -rw-r--r-- | src/crepe/system/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/crepe/system/EventSystem.h | 7 | ||||
| -rw-r--r-- | src/crepe/system/ReplaySystem.h | 15 | ||||
| -rw-r--r-- | src/example/replay.cpp | 4 | 
17 files changed, 185 insertions, 24 deletions
| diff --git a/src/crepe/Component.h b/src/crepe/Component.h index fc0268c..52e06d5 100644 --- a/src/crepe/Component.h +++ b/src/crepe/Component.h @@ -39,10 +39,27 @@ protected:  	virtual Component & operator=(Component &&) = delete;  protected: +	/** +	 * \name ReplayManager (Memento) functions +	 * \{ +	 */ +	/** +	 * \brief Save a snapshot of this component's state +	 * \note This function should only be implemented on components that should be saved/restored +	 * by ReplayManager. +	 * \returns Unique pointer to a deep copy of this component +	 */  	virtual std::unique_ptr<Component> save() const; +	//! Copy constructor (used by \c save())  	Component(const Component &) = default; +	/** +	 * \brief Restore this component from a snapshot +	 * \param snapshot Data to fill this component with (as returned by \c save()) +	 */  	virtual void restore(const Component & snapshot); +	//! Copy assignment operator (used by \c restore())  	virtual Component & operator=(const Component &); +	//! \}  public:  	virtual ~Component() = default; diff --git a/src/crepe/api/BehaviorScript.h b/src/crepe/api/BehaviorScript.h index 02d588a..3909b96 100644 --- a/src/crepe/api/BehaviorScript.h +++ b/src/crepe/api/BehaviorScript.h @@ -9,7 +9,6 @@  namespace crepe {  class ScriptSystem; -class Mediator;  class ComponentManager;  class Script; diff --git a/src/crepe/api/Engine.cpp b/src/crepe/api/Engine.cpp index 7ae89b9..e8b7fd6 100644 --- a/src/crepe/api/Engine.cpp +++ b/src/crepe/api/Engine.cpp @@ -5,12 +5,12 @@  using namespace crepe;  using namespace std; -void Engine::start() { +int Engine::main() noexcept {  	try {  		this->setup();  	} catch (const exception & e) {  		Log::logf(Log::Level::ERROR, "Uncaught exception in setup: {}\n", e.what()); -		return; +		return EXIT_FAILURE;  	}  	try { @@ -19,10 +19,11 @@ void Engine::start() {  		Log::logf(Log::Level::ERROR, "Uncaught exception in main loop: {}\n", e.what());  		this->event_manager.trigger_event<ShutDownEvent>();  	} + +	return EXIT_SUCCESS;  }  void Engine::setup() { -	this->game_running = true;  	this->loop_timer.start();  	this->scene_manager.load_next_scene(); diff --git a/src/crepe/api/Engine.h b/src/crepe/api/Engine.h index 5421d60..efe7853 100644 --- a/src/crepe/api/Engine.h +++ b/src/crepe/api/Engine.h @@ -20,13 +20,16 @@ namespace crepe {   */  class Engine {  public: -	void start(); -  	/** -	 * \brief Add a new concrete scene to the scene manager +	 * \brief Engine entrypoint +	 * +	 * This function is called by the game programmer after registering all scenes  	 * -	 * \tparam T  Type of concrete scene +	 * \returns process exit code  	 */ +	int main() noexcept; + +	//! \copydoc SceneManager::add_scene  	template <typename T>  	void add_scene(); @@ -44,7 +47,8 @@ private:  	 */  	void loop(); -	bool game_running = false; +	//! Game loop condition +	bool game_running = true;  private:  	//! Global context diff --git a/src/crepe/api/GameObject.h b/src/crepe/api/GameObject.h index 6203f81..0dabed1 100644 --- a/src/crepe/api/GameObject.h +++ b/src/crepe/api/GameObject.h @@ -39,8 +39,9 @@ private:  public:  	//! The id of the GameObject  	const game_object_id_t id; - +	//! This entity's transform  	Transform & transform; +	//! This entity's metadata  	Metadata & metadata;  public: diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp index 90b77a0..fd69e26 100644 --- a/src/crepe/api/ParticleEmitter.cpp +++ b/src/crepe/api/ParticleEmitter.cpp @@ -1,6 +1,7 @@  #include "ParticleEmitter.h"  using namespace crepe; +using namespace std;  ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Data & data)  	: Component(game_object_id), @@ -9,3 +10,17 @@ ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Data & d  		this->data.particles.emplace_back();  	}  } + +unique_ptr<Component> ParticleEmitter::save() const { +	return unique_ptr<Component>{new ParticleEmitter(*this)}; +} + +void ParticleEmitter::restore(const Component & snapshot) { +	*this = static_cast<const ParticleEmitter &>(snapshot); +} + +ParticleEmitter & ParticleEmitter::operator=(const ParticleEmitter & other) { +	data.particles = other.data.particles; +	return *this; +} + diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h index b83fd61..5f563de 100644 --- a/src/crepe/api/ParticleEmitter.h +++ b/src/crepe/api/ParticleEmitter.h @@ -80,6 +80,12 @@ public:  public:  	//! Configuration data for particle emission settings.  	Data data; + +protected: +	virtual std::unique_ptr<Component> save() const; +	ParticleEmitter(const ParticleEmitter &) = default; +	virtual void restore(const Component & snapshot); +	virtual ParticleEmitter & operator=(const ParticleEmitter &);  };  } // namespace crepe diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp index 2553fd1..b42a6df 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -40,10 +40,17 @@ void Script::subscribe_internal(const EventHandler<EventType> & callback,  	EventManager & mgr = this->mediator->event_manager;  	subscription_t listener = mgr.subscribe<EventType>(  		[this, callback](const EventType & data) -> bool { +			// check if (parent) BehaviorScript component is active  			bool & active = this->active;  			if (!active) return false; -			ReplayManager & replay = this->mediator->replay_manager; -			if (replay.get_state() == ReplayManager::PLAYING) return false; + +			// check if replay manager is playing (if initialized) +			try { +				ReplayManager & replay = this->mediator->replay_manager; +				if (replay.get_state() == ReplayManager::PLAYING) return false; +			} catch (const std::runtime_error &) {} + +			// call user-provided callback  			return callback(data);  		},  		channel); diff --git a/src/crepe/api/Transform.h b/src/crepe/api/Transform.h index bbd23e0..a6f3486 100644 --- a/src/crepe/api/Transform.h +++ b/src/crepe/api/Transform.h @@ -41,7 +41,6 @@ protected:  	Transform(const Transform &) = default;  	virtual void restore(const Component & snapshot);  	virtual Transform & operator=(const Transform &) = default; -  };  } // namespace crepe diff --git a/src/crepe/manager/ComponentManager.h b/src/crepe/manager/ComponentManager.h index 457a196..c3a5b4a 100644 --- a/src/crepe/manager/ComponentManager.h +++ b/src/crepe/manager/ComponentManager.h @@ -142,19 +142,39 @@ public:  	template <typename T>  	RefVector<T> get_components_by_tag(const std::string & tag) const; +	//! Snapshot of single component (including path in \c components)  	struct SnapshotComponent { +		//! \c components path  		std::type_index type; +		//! \c components path  		game_object_id_t id; +		//! \c components path  		size_t index; +		//! Actual component snapshot  		std::unique_ptr<Component> component;  	}; +	//! Snapshot of the entire component manager state  	struct Snapshot { +		//! All components +		std::vector<SnapshotComponent> components;  		// TODO: some kind of hash code that ensures components exist in all the same places as  		// this snapshot -		std::vector<SnapshotComponent> components;  	}; +	/** +	 * \name ReplayManager (Memento) functions +	 * \{ +	 */ +	/** +	 * \brief Save a snapshot of the component manager state +	 * \returns Deep copy of the component manager's internal state +	 */  	Snapshot save(); -	void restore(const Snapshot &); +	/** +	 * \brief Restore component manager from a snapshot +	 * \param snapshot Snapshot to restore from (as returned by \c save()) +	 */ +	void restore(const Snapshot & snapshot); +	//! \}  private:  	/** diff --git a/src/crepe/manager/ReplayManager.cpp b/src/crepe/manager/ReplayManager.cpp index ab8a5a0..db6acb0 100644 --- a/src/crepe/manager/ReplayManager.cpp +++ b/src/crepe/manager/ReplayManager.cpp @@ -38,6 +38,9 @@ void ReplayManager::release(recording_t handle) {  }  void ReplayManager::frame_record() { +	if (this->state != RECORDING) +		throw runtime_error("ReplayManager: frame_step called while not playing"); +  	ComponentManager & components = this->mediator.component_manager;  	Recording & recording = this->recording; @@ -46,6 +49,9 @@ void ReplayManager::frame_record() {  }  bool ReplayManager::frame_step() { +	if (this->state != PLAYING) +		throw runtime_error("ReplayManager: frame_step called while not playing"); +  	ComponentManager & components = this->mediator.component_manager;  	Recording & recording = this->recording; @@ -58,6 +64,7 @@ bool ReplayManager::frame_step() {  	// end of recording  	recording.frame = 0;  	this->state = IDLE; +	this->recording.clear();  	return true;  } diff --git a/src/crepe/manager/ReplayManager.h b/src/crepe/manager/ReplayManager.h index 7be18f3..d3af879 100644 --- a/src/crepe/manager/ReplayManager.h +++ b/src/crepe/manager/ReplayManager.h @@ -8,13 +8,14 @@  namespace crepe { -class ReplaySystem; -  typedef size_t recording_t;  /**   * \brief Replay manager   * + * The replay manager is responsible for creating, storing and restoring ComponentManager + * snapshots. Sequential snapshots can be recorded and replayed in combination with + * ReplaySystem.   */  class ReplayManager : public Manager {  	// TODO: Delete recordings at end of scene @@ -22,31 +23,70 @@ public:  	ReplayManager(Mediator & mediator);  public: +	//! Start a new recording  	void record_start(); +	/** +	 * \brief End the latest recording started by \c record_start() +	 * \returns Handle to recording +	 */  	recording_t record_end(); +	/** +	 * \brief Play a recording +	 * \param handle Handle to recording (as returned by \c record_end()) +	 */  	void play(recording_t handle); +	/** +	 * \brief Delete a recording from memory +	 * \param handle Handle to recording (as returned by \c record_end()) +	 */  	void release(recording_t handle);  public: +	//! Internal state  	enum State { -		IDLE, -		RECORDING, -		PLAYING, +		IDLE, //!< Not doing anything +		RECORDING, //!< Currently recording +		PLAYING, //!< Currently playing back a recording  	}; +	//! Get current internal state  	State get_state() const;  public: +	/** +	 * \brief Record a single frame to the current recording +	 * +	 * This function is called by ReplaySystem after the game programmer has called \c +	 * record_start() +	 */  	void frame_record(); +	/** +	 * \brief Play the next frame of the current recording +	 * +	 * \returns `true` if the recording is finished playing +	 * \returns `false` if there are more frames +	 * +	 * This function also automatically resets the internal state from PLAYING to IDLE at the end +	 * of a recording. +	 */  	bool frame_step();  private: +	/** +	 * \brief Recording data +	 */  	struct Recording { +		//! Current frame being shown  		size_t frame = 0; +		//! All frames in recording  		std::vector<ComponentManager::Snapshot> frames;  	}; +	//! Internal state  	State state = IDLE; -	OptionalRef<Recording> recording; +	//! Current recording handle  	recording_t id = -1; +	//! Current recording data +	OptionalRef<Recording> recording; +	//! Recording storage  	std::unordered_map<recording_t, std::unique_ptr<Recording>> memory;  }; diff --git a/src/crepe/manager/SystemManager.h b/src/crepe/manager/SystemManager.h index 6cf7f2b..a47961b 100644 --- a/src/crepe/manager/SystemManager.h +++ b/src/crepe/manager/SystemManager.h @@ -1,11 +1,21 @@  #pragma once +#include <typeindex> +#include <unordered_map> +#include <memory> +  #include "../system/System.h"  #include "Manager.h"  namespace crepe { +/** + * \brief Collection of all systems + * + * This manager aggregates all systems and provides utility functions to retrieve references to + * and update systems. + */  class SystemManager : public Manager {  public:  	SystemManager(Mediator &); @@ -50,9 +60,23 @@ public:  	T & get_system();  public: +	/** +	 * \brief SystemManager snapshot +	 * +	 * The SystemManager snapshot only stores which systems are active +	 */  	typedef std::unordered_map<std::type_index, bool> Snapshot; +	/** +	 * \brief Save a snapshot of the systems' state +	 * \returns Copy of each system's active property +	 */  	Snapshot save(); +	/** +	 * \brief Restore system active state from a snapshot +	 * \param snapshot Snapshot to restore from (as returned by \c save()) +	 */  	void restore(const Snapshot & snapshot); +	//! Disable all systems  	void disable_all();  }; diff --git a/src/crepe/system/CMakeLists.txt b/src/crepe/system/CMakeLists.txt index 3473876..52369d0 100644 --- a/src/crepe/system/CMakeLists.txt +++ b/src/crepe/system/CMakeLists.txt @@ -23,5 +23,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  	AnimatorSystem.h  	InputSystem.h  	EventSystem.h +	ReplaySystem.h  	AISystem.h  ) diff --git a/src/crepe/system/EventSystem.h b/src/crepe/system/EventSystem.h index a179d00..ff3ca4e 100644 --- a/src/crepe/system/EventSystem.h +++ b/src/crepe/system/EventSystem.h @@ -4,10 +4,17 @@  namespace crepe { +/** + * \brief EventManager dispatch helper system + */  class EventSystem : public System {  public:  	using System::System; +	/** +	 * \brief Dispatch queued events +	 * \see EventManager::dispatch_events +	 */  	void fixed_update() override;  }; diff --git a/src/crepe/system/ReplaySystem.h b/src/crepe/system/ReplaySystem.h index 919c554..8ba60d5 100644 --- a/src/crepe/system/ReplaySystem.h +++ b/src/crepe/system/ReplaySystem.h @@ -7,6 +7,11 @@  namespace crepe { +/** + * \brief ReplayManager helper system + * + * This system records and replays recordings using ReplayManager. + */  class ReplaySystem : public System {  public:  	using System::System; @@ -14,15 +19,25 @@ public:  	void fixed_update() override;  private: +	//! Last ReplayManager state  	ReplayManager::State last_state = ReplayManager::IDLE; +	/** +	 * \brief Playback snapshot +	 * +	 * When starting playback, the component state is saved and most systems are disabled. This +	 * struct stores the engine state before ReplayManager::play is called. +	 */  	struct Snapshot {  		ComponentManager::Snapshot components;  		SystemManager::Snapshot systems;  	}; +	//! Before playback snapshot  	Snapshot playback; +	//! Snapshot state and disable systems during playback  	void playback_begin(); +	//! Restore state from before \c playback_begin()  	void playback_end();  }; diff --git a/src/example/replay.cpp b/src/example/replay.cpp index 7faf6cb..130c0d3 100644 --- a/src/example/replay.cpp +++ b/src/example/replay.cpp @@ -83,7 +83,5 @@ int main(int argc, char * argv[]) {  	Engine engine;  	engine.add_scene<TestScene>(); -	engine.start(); - -	return 0; +	return engine.main();  } |