diff options
| -rw-r--r-- | src/crepe/Component.cpp | 9 | ||||
| -rw-r--r-- | src/crepe/Component.h | 13 | ||||
| -rw-r--r-- | src/crepe/api/GameObject.cpp | 10 | ||||
| -rw-r--r-- | src/crepe/api/GameObject.h | 13 | ||||
| -rw-r--r-- | src/crepe/api/LoopManager.cpp | 2 | ||||
| -rw-r--r-- | src/crepe/api/Transform.cpp | 10 | ||||
| -rw-r--r-- | src/crepe/api/Transform.h | 7 | ||||
| -rw-r--r-- | src/crepe/manager/ComponentManager.cpp | 26 | ||||
| -rw-r--r-- | src/crepe/manager/ComponentManager.h | 9 | ||||
| -rw-r--r-- | src/crepe/manager/Mediator.h | 2 | ||||
| -rw-r--r-- | src/crepe/manager/ReplayManager.cpp | 25 | ||||
| -rw-r--r-- | src/crepe/manager/ReplayManager.h | 24 | ||||
| -rw-r--r-- | src/crepe/system/ReplaySystem.cpp | 70 | ||||
| -rw-r--r-- | src/crepe/system/ReplaySystem.h | 11 | ||||
| -rw-r--r-- | src/test/ECSTest.cpp | 14 | 
15 files changed, 209 insertions, 36 deletions
| diff --git a/src/crepe/Component.cpp b/src/crepe/Component.cpp index 141e1a8..8086492 100644 --- a/src/crepe/Component.cpp +++ b/src/crepe/Component.cpp @@ -1,6 +1,7 @@  #include "Component.h"  using namespace crepe; +using namespace std;  Component::Component(game_object_id_t id) : game_object_id(id) {} @@ -8,3 +9,11 @@ Component & Component::operator=(const Component &) {  	return *this;  } +unique_ptr<Component> Component::save() const { +	return unique_ptr<Component>(new Component(*this)); +} + +void Component::restore(const Component & snapshot) { +	*this = snapshot; +} + diff --git a/src/crepe/Component.h b/src/crepe/Component.h index 47c5c34..fc0268c 100644 --- a/src/crepe/Component.h +++ b/src/crepe/Component.h @@ -1,5 +1,7 @@  #pragma once +#include <memory> +  #include "types.h"  namespace crepe { @@ -32,15 +34,16 @@ protected:  	//! Only ComponentManager can create components  	friend class ComponentManager; -	// create snapshot -	Component(const Component &) = default; -	// restore snapshot -	virtual Component & operator=(const Component &); -  	// components are never moved  	Component(Component &&) = delete;  	virtual Component & operator=(Component &&) = delete; +protected: +	virtual std::unique_ptr<Component> save() const; +	Component(const Component &) = default; +	virtual void restore(const Component & snapshot); +	virtual Component & operator=(const Component &); +  public:  	virtual ~Component() = default; diff --git a/src/crepe/api/GameObject.cpp b/src/crepe/api/GameObject.cpp index ea9c425..68b074e 100644 --- a/src/crepe/api/GameObject.cpp +++ b/src/crepe/api/GameObject.cpp @@ -11,13 +11,9 @@ GameObject::GameObject(Mediator & mediator, game_object_id_t id,  					   const std::string & name, const std::string & tag,  					   const vec2 & position, double rotation, double scale)  	: id(id), -	  mediator(mediator) { - -	// Add Transform and Metadata components -	ComponentManager & mgr = this->mediator.component_manager; -	mgr.add_component<Transform>(this->id, position, rotation, scale); -	mgr.add_component<Metadata>(this->id, name, tag); -} +	  mediator(mediator), +		transform(mediator.component_manager->add_component<Transform>(this->id, position, rotation, scale)), +		metadata(mediator.component_manager->add_component<Metadata>(this->id, name, tag)) { }  void GameObject::set_parent(const GameObject & parent) {  	ComponentManager & mgr = this->mediator.component_manager; diff --git a/src/crepe/api/GameObject.h b/src/crepe/api/GameObject.h index a311c21..6203f81 100644 --- a/src/crepe/api/GameObject.h +++ b/src/crepe/api/GameObject.h @@ -7,6 +7,8 @@  namespace crepe {  class Mediator; +class Transform; +class Metadata;  /**   * \brief Represents a GameObject @@ -35,6 +37,13 @@ private:  	friend class ComponentManager;  public: +	//! The id of the GameObject +	const game_object_id_t id; + +	Transform & transform; +	Metadata & metadata; + +public:  	/**  	 * \brief Set the parent of this GameObject  	 * @@ -68,10 +77,6 @@ public:  	 */  	void set_persistent(bool persistent = true); -public: -	//! The id of the GameObject -	const game_object_id_t id; -  protected:  	Mediator & mediator;  }; diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index 3511bca..2855455 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -25,6 +25,8 @@ LoopManager::LoopManager() {  	this->load_system<InputSystem>();  	this->load_system<EventSystem>();  	this->load_system<AudioSystem>(); + +	this->mediator.loop_manager = *this;  }  void LoopManager::start() { diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp index a85b792..5fc886b 100644 --- a/src/crepe/api/Transform.cpp +++ b/src/crepe/api/Transform.cpp @@ -3,6 +3,7 @@  #include "Transform.h"  using namespace crepe; +using namespace std;  Transform::Transform(game_object_id_t id, const vec2 & point, double rotation, double scale)  	: Component(id), @@ -11,3 +12,12 @@ Transform::Transform(game_object_id_t id, const vec2 & point, double rotation, d  	  scale(scale) {  	dbg_trace();  } + +unique_ptr<Component> Transform::save() const { +	return unique_ptr<Component>{new Transform(*this)}; +} + +void Transform::restore(const Component & snapshot) { +	*this = static_cast<const Transform &>(snapshot); +} + diff --git a/src/crepe/api/Transform.h b/src/crepe/api/Transform.h index 7ee6d65..bbd23e0 100644 --- a/src/crepe/api/Transform.h +++ b/src/crepe/api/Transform.h @@ -35,6 +35,13 @@ protected:  	virtual int get_instances_max() const { return 1; }  	//! ComponentManager instantiates all components  	friend class ComponentManager; + +protected: +	virtual std::unique_ptr<Component> save() const; +	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.cpp b/src/crepe/manager/ComponentManager.cpp index 24ba0d7..5f5c050 100644 --- a/src/crepe/manager/ComponentManager.cpp +++ b/src/crepe/manager/ComponentManager.cpp @@ -72,3 +72,29 @@ set<game_object_id_t> ComponentManager::get_objects_by_tag(const string & tag) c  	return this->get_objects_by_predicate<Metadata>(  		[tag](const Metadata & data) { return data.tag == tag; });  } + +ComponentManager::Snapshot ComponentManager::save() { +	Snapshot snapshot{}; +	for (const auto & [type, by_id_index] : this->components) { +		for (game_object_id_t id = 0; id < by_id_index.size(); id++) { +			const auto & components = by_id_index[id]; +			for (size_t index = 0; index < components.size(); index++) { +				const Component & component = *components[index]; +				snapshot.components.push_back(SnapshotComponent{ +					.type = type, +					.id = id, +					.index = index, +					.component = component.save(), +				}); +			} +		} +	} +	return snapshot; +} + +void ComponentManager::restore(const Snapshot & snapshot) { +	for (const SnapshotComponent & info : snapshot.components) { +		this->components[info.type][info.id][info.index]->restore(*info.component); +	} +} + diff --git a/src/crepe/manager/ComponentManager.h b/src/crepe/manager/ComponentManager.h index dd7c154..457a196 100644 --- a/src/crepe/manager/ComponentManager.h +++ b/src/crepe/manager/ComponentManager.h @@ -143,10 +143,15 @@ public:  	RefVector<T> get_components_by_tag(const std::string & tag) const;  	struct SnapshotComponent { -		Component component; +		std::type_index type; +		game_object_id_t id; +		size_t index; +		std::unique_ptr<Component> component;  	};  	struct Snapshot { -		 +		// TODO: some kind of hash code that ensures components exist in all the same places as +		// this snapshot +		std::vector<SnapshotComponent> components;  	};  	Snapshot save();  	void restore(const Snapshot &); diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h index eef4432..f5864e7 100644 --- a/src/crepe/manager/Mediator.h +++ b/src/crepe/manager/Mediator.h @@ -14,6 +14,7 @@ class ResourceManager;  class SDLContext;  class LoopTimer;  class ReplayManager; +class LoopManager;  /**   * Struct to pass references to classes that would otherwise need to be singletons down to @@ -36,6 +37,7 @@ struct Mediator {  	OptionalRef<ResourceManager> resource_manager;  	OptionalRef<LoopTimer> timer;  	OptionalRef<ReplayManager> replay_manager; +	OptionalRef<LoopManager> loop_manager;  };  } // namespace crepe diff --git a/src/crepe/manager/ReplayManager.cpp b/src/crepe/manager/ReplayManager.cpp index 82c2275..81ff114 100644 --- a/src/crepe/manager/ReplayManager.cpp +++ b/src/crepe/manager/ReplayManager.cpp @@ -1,4 +1,4 @@ -#include "../util/Log.h" +#include <format>  #include "ReplayManager.h"  #include "Manager.h" @@ -11,26 +11,29 @@ ReplayManager::ReplayManager(Mediator & mediator) : Manager(mediator) {  }  void ReplayManager::record_start() { -	if (this->recording) this->release(this->current_recording); -	this->current_recording++; -	this->recording = true; +	if (this->state == RECORDING) this->release(this->id); +	this->id++; +	this->memory[this->id] = make_unique<Recording>(); +	this->recording = *this->memory.at(this->id); +	this->state = RECORDING;  }  recording_t ReplayManager::record_end() { -	this->recording = false; -	return this->current_recording; +	this->state = IDLE; +	return this->id;  }  void ReplayManager::play(recording_t handle) {  	if (!this->memory.contains(handle))  		throw out_of_range(format("ReplayManager: no recording for handle {}", handle)); -	Recording & recording = *this->memory.at(handle); - -	dbg_log("TODO: magic"); +	this->recording = *this->memory.at(handle); +	this->recording->frame = 0; +	this->state = PLAYING;  }  void ReplayManager::release(recording_t handle) { -	dbg_log("release"); - +	if (!this->memory.contains(handle)) +		return; +	this->memory.erase(handle);  } diff --git a/src/crepe/manager/ReplayManager.h b/src/crepe/manager/ReplayManager.h index c50196c..672d093 100644 --- a/src/crepe/manager/ReplayManager.h +++ b/src/crepe/manager/ReplayManager.h @@ -1,13 +1,14 @@  #pragma once +#include <unordered_map> +  #include "Manager.h"  #include "ComponentManager.h" -#include <unordered_map> +#include "util/OptionalRef.h"  namespace crepe {  class ReplaySystem; -class Memento;  typedef size_t recording_t; @@ -20,11 +21,20 @@ protected:  	void record_frame();  private: -	typedef std::vector<ComponentManager::Snapshot> Recording; - -	bool recording = false; -	recording_t current_recording = -1; - +	struct Recording { +		size_t frame = 0; +		std::vector<ComponentManager::Snapshot> frames; +	}; + +	enum State { +		IDLE, +		RECORDING, +		PLAYING, +	}; + +	State state = IDLE; +	OptionalRef<Recording> recording; +	recording_t id = -1;  	std::unordered_map<recording_t, std::unique_ptr<Recording>> memory;  public: diff --git a/src/crepe/system/ReplaySystem.cpp b/src/crepe/system/ReplaySystem.cpp index 3aabb58..85595a2 100644 --- a/src/crepe/system/ReplaySystem.cpp +++ b/src/crepe/system/ReplaySystem.cpp @@ -1,7 +1,77 @@ +#include "system/ScriptSystem.h" + +#include "../manager/ReplayManager.h" +  #include "ReplaySystem.h" +#include "../api/LoopManager.h" +  using namespace crepe; +using namespace std;  void ReplaySystem::fixed_update() { +	ReplayManager & replay = this->mediator.replay_manager; + +	switch (replay.state) { +		case ReplayManager::IDLE: break; +		case ReplayManager::RECORDING: { +			this->update_recording(); +			break; +		} +		case ReplayManager::PLAYING: { +			this->update_playing(); +			break; +		} +	} + +	this->last_state = replay.state; +} + +void ReplaySystem::update_recording() { +	ReplayManager & replay = this->mediator.replay_manager; +	ComponentManager & components = this->mediator.component_manager; + +	ReplayManager::Recording & recording = replay.recording; +	recording.frames.push_back(components.save()); +	recording.frame++; +} + +void ReplaySystem::update_playing() { +	ReplayManager & replay = this->mediator.replay_manager; + +	if (this->last_state != ReplayManager::PLAYING) { +		this->playback_begin(); +	} + +	ReplayManager::Recording & recording = replay.recording; + +	if (recording.frames.size() == recording.frame) { +		this->playback_end(); +		return; +	} + +	ComponentManager & components = this->mediator.component_manager; +	ComponentManager::Snapshot & frame = recording.frames.at(recording.frame); + +	components.restore(frame); +	recording.frame++; +} + +void ReplaySystem::playback_begin() { +	LoopManager & loop_manager = this->mediator.loop_manager; +	// TODO: store system active state +	// TODO: disable most systems +	// TODO: store components snapshot +} + +void ReplaySystem::playback_end() { +	ReplayManager & replay = this->mediator.replay_manager; + +	replay.state = ReplayManager::IDLE; + +	LoopManager & loop_manager = this->mediator.loop_manager; + +	// TODO: restore system active state snapshot +	// TODO: restore components snapshot  } diff --git a/src/crepe/system/ReplaySystem.h b/src/crepe/system/ReplaySystem.h index 15ef3fc..fb40176 100644 --- a/src/crepe/system/ReplaySystem.h +++ b/src/crepe/system/ReplaySystem.h @@ -1,5 +1,7 @@  #pragma once +#include "../manager/ReplayManager.h" +  #include "System.h"  namespace crepe { @@ -9,6 +11,15 @@ public:  	using System::System;  	void fixed_update() override; + +private: +	ReplayManager::State last_state = ReplayManager::IDLE; +	void update_recording(); +	void update_playing(); + +	std::unordered_map<std::type_index, bool> system_active_snapshot; +	void playback_begin(); +	void playback_end();  };  } diff --git a/src/test/ECSTest.cpp b/src/test/ECSTest.cpp index af2b7b0..8f86a91 100644 --- a/src/test/ECSTest.cpp +++ b/src/test/ECSTest.cpp @@ -466,3 +466,17 @@ TEST_F(ECSTest, ComponentsByTag) {  		EXPECT_EQ(objects.size(), 3);  	}  } + +TEST_F(ECSTest, Snapshot) { +	GameObject foo = mgr.new_object("foo"); + +	foo.transform.position = {1, 1}; + +	ComponentManager::Snapshot snapshot = mgr.save(); + +	foo.transform.position = {0, 0}; + +	mgr.restore(snapshot); + +	EXPECT_EQ(foo.transform.position, (vec2{1, 1})); +} |