diff --git a/Coral.vcxproj b/Coral.vcxproj
index f9b674a0..2a1baef8 100644
--- a/Coral.vcxproj
+++ b/Coral.vcxproj
@@ -302,6 +302,10 @@
+
+ true
+ true
+
@@ -1025,7 +1029,6 @@
-
diff --git a/Include/EditorSystems/AssetEditorSystems/AssetEditorSystem.h b/Include/EditorSystems/AssetEditorSystems/AssetEditorSystem.h
index 6f565c0d..db958120 100644
--- a/Include/EditorSystems/AssetEditorSystems/AssetEditorSystem.h
+++ b/Include/EditorSystems/AssetEditorSystems/AssetEditorSystem.h
@@ -1,563 +1,208 @@
-#include "Utilities/Time.h"
-#ifdef EDITOR
#pragma once
+#ifdef EDITOR
#include "EditorSystems/EditorSystem.h"
+#include
+
+#include "Utilities/Time.h"
#include "Assets/Asset.h"
#include "Assets/Core/AssetSaveInfo.h"
-#include "Assets/Core/AssetLoadInfo.h"
-#include "Core/AssetManager.h"
#include "Core/Editor.h"
-#include "Core/Input.h"
#include "Meta/MetaType.h"
-#include "Utilities/view_istream.h"
-#include "Utilities/DoUndo.h"
-#include "Utilities/StringFunctions.h"
namespace CE
{
- /*
- Do not derive from this, derive from the templated class below.
- */
- class AssetEditorSystemInterface
+ class AssetEditorMementoStack
{
public:
+ AssetEditorMementoStack() = default;
+ AssetEditorMementoStack(const AssetEditorMementoStack&) = delete;
+ AssetEditorMementoStack(AssetEditorMementoStack&&) noexcept = default;
- /*
- For documentation on any of these functions, see the overriden functions in
- AssetEditorSystem
- */
+ AssetEditorMementoStack& operator=(const AssetEditorMementoStack&) = delete;
+ AssetEditorMementoStack& operator=(AssetEditorMementoStack&&) noexcept = default;
- virtual AssetSaveInfo SaveToMemory() = 0;
- virtual void SaveToFile() = 0;
- virtual TypeId GetAssetTypeId() const = 0;
+ ~AssetEditorMementoStack() = default;
- protected:
- friend class Editor;
- struct MementoAction
+ struct SimilarityToFile
{
- void Do();
- void Undo();
- void RefreshTheAssetEditor();
+ bool mDoesStateMatchFile{};
+ std::filesystem::file_time_type mFileWriteTimeLastTimeWeChecked{};
+ };
+ struct Action
+ {
std::string mState{};
- bool mDoIsNeeded{};
-
- // If the engine was refreshed,
- // we should re-serialize this
- // state in order to take into
- // account any changes made to
- // any assets.
bool mRequiresReserialization{};
+ SimilarityToFile mSimilarityToFile{};
+ };
- std::string mNameOfAssetEditor{};
+ void Do(std::shared_ptr action);
- // Mutable because the result is cached, but it does not influence the state
- // of the asset.
- mutable bool mIsSameAsFile{};
- mutable std::filesystem::file_time_type mTimeWeCheckedIfIsSameAsFile{};
- };
+ bool TryUndo();
+ bool TryRedo();
- using MementoStack = DoUndo::DoUndoStackBase;
+ void ClearRedo();
- void SetMementoStack(MementoStack&& stack) { mMementoStack = std::move(stack); };
- virtual MementoStack&& ExtractMementoStack() = 0;
+ std::shared_ptr GetMostRecentState() const;
- MementoStack mMementoStack{};
+ std::vector> mActions{};
+ size_t mNumOfActionsDone{};
};
-
- /*
- An EditorSystem specialized for editing assets. Deriving from this
- will link the typename to your derived AssetEditor class, which means
- that clicking on the Asset in the contentbrowser will create an instance
- of your system.
- */
- template
- class AssetEditorSystem :
- public EditorSystem,
- public AssetEditorSystemInterface
+ class AssetEditorSystemBase :
+ public EditorSystem
{
public:
- AssetEditorSystem(T&& asset);
- ~AssetEditorSystem() override = default;
+ AssetEditorSystemBase(const Asset& asset);
- const T& GetAsset() const { return mAsset; }
- T& GetAsset() { return mAsset; }
+ ~AssetEditorSystemBase();
/*
AssetEditorSystems generally work on a copy of the original asset,
in order to prevent your changes leading to unexpected behaviour
elsewhere. This function return the orginal asset, if it exists.
*/
- WeakAssetHandle TryGetOriginalAsset() const;
+ WeakAssetHandle TryGetOriginalAsset() const;
+
+ std::optional GetDestinationFile() const;
/*
Saves the asset to file at the end of the frame. Will do a
'restart' of the engine that is completely hidden from the
users, but this restart makes sure your changes are correctly
applied throughout the engine.
-
- If no file location is provided, the file location of the
- original asset will be used.
*/
- void SaveToFile() final;
+ void SaveToFile();
/*
Saves the asset to memory. The resulting AssetSaveInfo can
be used to construct a copy of the asset.
-
- Note that calling AssetSaveInfo::SaveToFile will not trigger
- the engine to restart, which means your changes may not be
- applied correctly, if at all. Prefer AssetEditorSystem::SaveToFile
- if you intend to save this asset to a file.
*/
- [[nodiscard]] AssetSaveInfo SaveToMemory() final;
+ AssetSaveInfo SaveToMemory();
bool IsSavedToFile() const;
- //********************************//
- // Virtual functions //
- //********************************//
+ AssetEditorMementoStack ExtractStack();
- void Tick(float deltaTime) override;
+ void InsertMementoStack(AssetEditorMementoStack stack);
protected:
bool Begin(ImGuiWindowFlags flags) override;
+ void End() override;
+
+ void ShowSaveButton();
+
private:
+ friend ReflectAccess;
+ static MetaType Reflect();
+ REFLECT_AT_START_UP(AssetEditorSystemBase);
+
+ virtual const Asset& GetAsset() const = 0;
+ virtual Asset& GetAsset() = 0;
+
/*
Often the changes aren't directly made to the asset; for example you will be
editing a world, moving some entities, deleting some others, but you are not
- directly modifing the asset, instead storing the changes inside of the system.
+ directly modifing the asset, instead storing the changes inside the system.
This function alerts you that any changes you may have made must be applied to
the asset now.
*/
- virtual void ApplyChangesToAsset() {};
+ virtual void ApplyChangesToAsset() {}
- protected:
- void ShowSaveButton();
-
- T mAsset;
+ virtual std::unique_ptr> ConstructAsset(AssetLoadInfo& loadInfo) = 0;
- std::filesystem::path mPathToSaveAssetTo{};
-
- private:
- TypeId GetAssetTypeId() const final { return MakeTypeId(); };
-
- friend ReflectAccess;
- static MetaType Reflect()
- {
- // Bind the asset to our editor
- Editor::RegisterAssetEditorSystem();
- return MetaType{ MetaType::T>{}, Format("AssetEditorSystem<{}>", MakeTypeName()), MetaType::Base{} };
- }
-
- /*
- * Checks if any changes have been made to our asset, and saves them to the do/undo stack.
- * This allows each action to be undone by reverting to an older version.
- *
- * Is by default called on a cooldown, there is generally no need to call this yourself.
- */
void CheckForDifferences();
- void CompleteDifferenceCheckCycle();
-
- MementoStack&& ExtractMementoStack() override;
-
/*
* Some assets are different every time we load them. entt::registry for example may completely
* shuffle all the entities around as it wishes. So we load and save the asset to account for this.
*/
- static std::string Reserialize(std::string_view serialized);
+ std::string Reserialize(std::string_view serialized);
-
- /**
- * \brief Checking for differences involves saving/loading the asset a few times. We spread this out
- * over several frames to reduce the impact of the frame drops.
- */
- struct DifferenceCheckState
- {
- enum class Stage
- {
- SaveToMemory,
- ReloadFromMemory,
- ResaveToMemory,
- Compare,
- CheckIfSavedToFile,
- NUM_OF_STAGES,
- FirstStage = SaveToMemory
- };
- MementoAction mAction{};
- Stage mStage{};
- std::optional mTemporarilyDeserializedAsset{};
- };
- DifferenceCheckState mDifferenceCheckState{};
- Cooldown mUpdateStateCooldown{ .2f };
-
- // Used for checking if our asset has unsaved changes. Kept in memory for performance reasons.
- // May not always be up to date, it's updated when needed.
- struct AssetOnFile
- {
- std::string mReserializedAsset{};
-
- // If changes are made to the asset file, this mAssetAsSeenOnFile is updated.
- std::filesystem::file_time_type mWriteTimeAtTimeOfReserializing{};
- };
- mutable AssetOnFile mAssetOnFile{};
- };
+ AssetEditorMementoStack mMementoStack{};
- namespace Internal
- {
- std::string GetSystemNameBasedOnAssetName(std::string_view assetName);
- std::string GetAssetNameBasedOnSystemName(std::string_view systemName);
- }
+ static constexpr float sDifferenceCheckCooldown = 1.0f;
+ Timer mDifferenceCheckTimer{};
- inline void AssetEditorSystemInterface::MementoAction::Do()
- {
- if (!mDoIsNeeded)
+ struct CachedFile
{
- return;
- }
-
- RefreshTheAssetEditor();
- }
-
- inline void AssetEditorSystemInterface::MementoAction::Undo()
- {
- mDoIsNeeded = true;
- RefreshTheAssetEditor();
- }
-
- inline void AssetEditorSystemInterface::MementoAction::RefreshTheAssetEditor()
- {
- // The refreshing will return the asset editor to the
- // top state in the stack
- Editor::Get().Refresh({ 0, {}, mNameOfAssetEditor });
- }
-
- template
- AssetEditorSystem::AssetEditorSystem(T&& asset) :
- EditorSystem(Internal::GetSystemNameBasedOnAssetName(asset.GetName())),
- mAsset(std::move(asset)),
- mPathToSaveAssetTo([this]() -> std::filesystem::path
- {
- WeakAssetHandle originalAsset = TryGetOriginalAsset();
-
- if (originalAsset != nullptr)
- {
- return originalAsset.GetFileOfOrigin().value_or(std::filesystem::path{});
- }
- return {};
- }
- ())
- {
- }
+ std::filesystem::file_time_type mLastWriteTime{};
+ std::string mFileContents{};
+ };
+ CachedFile mCachedFile{};
- template
- WeakAssetHandle AssetEditorSystem::TryGetOriginalAsset() const
- {
- return AssetManager::Get().TryGetWeakAsset(mAsset.GetName());
- }
+ std::future> mActionToAdd{};
- template
- void AssetEditorSystem::SaveToFile()
- {
- if (mPathToSaveAssetTo.empty())
- {
- LOG(LogEditor, Warning, "Could not save asset {} - The file location was empty. This is caused by there not being an original asset, and mPathToSaveAssetTo was never updated.",
- mAsset.GetName());
- return;
- }
-
- Editor::Get().Refresh(
- {
- Editor::RefreshRequest::Volatile,
-
- // A shared pointer, because it makes move semantics with lambdas so much simpler
- [assetSaveInfo = std::make_shared(SaveToMemory()), saveTo = mPathToSaveAssetTo]
- {
- assetSaveInfo->SaveToFile(saveTo);
- }
- });
- }
+ std::mutex mAssetMutex{};
+ };
+ /*
+ An EditorSystem specialized for editing assets. Deriving from this
+ will link the typename to your derived AssetEditor class, which means
+ that clicking on the Asset in the contentbrowser will create an instance
+ of your system.
+ */
template
- AssetSaveInfo AssetEditorSystem::SaveToMemory()
+ class AssetEditorSystem :
+ public AssetEditorSystemBase
{
- ApplyChangesToAsset();
-
- std::optional importerInfo{};
-
- if (const WeakAssetHandle originalAsset = TryGetOriginalAsset(); originalAsset != nullptr)
- {
- importerInfo = originalAsset.GetMetaData().GetImporterInfo();
-
- if (importerInfo.has_value())
- {
- importerInfo->mWereEditsMadeAfterImporting |= !IsSavedToFile();
- }
- }
+ public:
+ AssetEditorSystem(T&& asset);
+ ~AssetEditorSystem() override = default;
- AssetSaveInfo saveInfo = mAsset.Save(std::move(importerInfo));
- return saveInfo;
- }
+ //********************************//
+ // Virtual functions //
+ //********************************//
- template
- bool AssetEditorSystem::IsSavedToFile() const
- {
- const MementoAction* const topAction = mMementoStack.PeekTop();
- return topAction == nullptr ? true : topAction->mIsSameAsFile;
- }
+ void Tick(float deltaTime) override;
- template
- void AssetEditorSystem::Tick(float deltaTime)
- {
- EditorSystem::Tick(deltaTime);
+ protected:
+ T mAsset;
- const bool checkForDifferences = mUpdateStateCooldown.IsReady(deltaTime);
+ private:
+ const T& GetAsset() const final { return mAsset; }
+ T& GetAsset() final { return mAsset; }
- if (!Input::Get().HasFocus())
- {
- return;
- }
+ friend ReflectAccess;
+ static MetaType Reflect();
- if (Input::Get().IsKeyboardKeyHeld(Input::KeyboardKey::LeftControl)
- || Input::Get().IsKeyboardKeyHeld(Input::KeyboardKey::RightControl))
- {
- if (mMementoStack.GetNumOfActionsDone() > 1
- && mMementoStack.CanUndo()
- && Input::Get().WasKeyboardKeyPressed(Input::KeyboardKey::Z))
- {
- mMementoStack.Undo();
- mUpdateStateCooldown.mAmountOfTimePassed = 0.0f;
- mDifferenceCheckState.mStage = DifferenceCheckState::Stage::FirstStage;
- return;
- }
-
- if (mMementoStack.CanRedo()
- && Input::Get().WasKeyboardKeyPressed(Input::KeyboardKey::Y))
- {
- mMementoStack.Redo();
- mUpdateStateCooldown.mAmountOfTimePassed = 0.0f;
- mDifferenceCheckState.mStage = DifferenceCheckState::Stage::FirstStage;
- return;
- }
-
- if (Input::Get().WasKeyboardKeyPressed(Input::KeyboardKey::S))
- {
- SaveToFile();
- }
- }
-
- if (checkForDifferences)
- {
- CheckForDifferences();
- }
- }
+ std::unique_ptr> ConstructAsset(AssetLoadInfo& loadInfo) final;
+ };
- template
- bool AssetEditorSystem::Begin(ImGuiWindowFlags flags)
+ namespace Internal
{
- return EditorSystem::Begin(flags | (IsSavedToFile() ? 0 : ImGuiWindowFlags_UnsavedDocument));
+ std::string GetSystemNameBasedOnAssetName(std::string_view assetName);
+ std::string GetAssetNameBasedOnSystemName(std::string_view systemName);
}
template
- void AssetEditorSystem::ShowSaveButton()
- {
- if (ImGui::Button(ICON_FA_FLOPPY_O))
- {
- // If you're looking at this because
- // you want to run logic before saving, look at ApplyChangesToAsset
- SaveToFile();
- }
- }
-
- template
- void AssetEditorSystem::CheckForDifferences()
+ AssetEditorSystem::AssetEditorSystem(T&& asset) :
+ AssetEditorSystemBase(asset),
+ mAsset(std::move(asset))
{
- mUpdateStateCooldown.mAmountOfTimePassed = 0.0f;
-
- switch (mDifferenceCheckState.mStage)
- {
- case DifferenceCheckState::Stage::SaveToMemory:
- {
- mDifferenceCheckState.mAction.mNameOfAssetEditor = GetName();
- mDifferenceCheckState.mAction.mState = SaveToMemory().ToString();
-
- if (mMementoStack.PeekTop() != nullptr
- && mDifferenceCheckState.mAction.mState == mMementoStack.PeekTop()->mState)
- {
- mUpdateStateCooldown.mAmountOfTimePassed = -mUpdateStateCooldown.mCooldown * (static_cast(DifferenceCheckState::Stage::NUM_OF_STAGES) - 1.0f);
- mDifferenceCheckState.mStage = DifferenceCheckState::Stage::CheckIfSavedToFile;
- return;
- }
-
- break;
- }
- case DifferenceCheckState::Stage::ReloadFromMemory:
- {
- std::optional loadInfo = AssetLoadInfo::LoadFromStream(std::make_unique(mDifferenceCheckState.mAction.mState));
-
- if (!loadInfo.has_value())
- {
- LOG(LogEditor, Error, "Failed to load metadata, metadata was invalid somehow?");
- mDifferenceCheckState.mStage = DifferenceCheckState::Stage::CheckIfSavedToFile;
- return;
- }
-
- mDifferenceCheckState.mTemporarilyDeserializedAsset.emplace(*loadInfo);
- break;
- }
- case DifferenceCheckState::Stage::ResaveToMemory:
- {
- mDifferenceCheckState.mAction.mState = mDifferenceCheckState.mTemporarilyDeserializedAsset->Save().ToString();
- break;
- }
- case DifferenceCheckState::Stage::Compare:
- {
- MementoAction* const topAction = mMementoStack.PeekTop();
-
- if (topAction == nullptr)
- {
- // If this is the first action, it is likely
- // going to match the file exactly.
- // We check in more detail in the next stage.
- mDifferenceCheckState.mAction.mIsSameAsFile = true;
-
- mMementoStack.Do(std::move(mDifferenceCheckState.mAction));
- break;
- }
-
- if (topAction->mRequiresReserialization)
- {
- topAction->mState = Reserialize(topAction->mState);
- topAction->mRequiresReserialization = false;
- }
-
- if (topAction->mState != mDifferenceCheckState.mAction.mState)
- {
- LOG(LogEditor, Verbose, "Change detected for {}", GetName());
-
- // A change was made, it's unlikely going to match
- // the file. We check in more detail in the next stage.
- mDifferenceCheckState.mAction.mIsSameAsFile = false;
-
- mMementoStack.Do(std::move(mDifferenceCheckState.mAction));
- }
-
- break;
- }
- case DifferenceCheckState::Stage::CheckIfSavedToFile:
- {
- MementoAction* const topAction = mMementoStack.PeekTop();
-
- if (topAction == nullptr)
- {
- break;
- }
-
- if (!std::filesystem::exists(mPathToSaveAssetTo))
- {
- topAction->mIsSameAsFile = false;
- break;
- }
-
- const std::filesystem::file_time_type lastWriteTime = std::filesystem::last_write_time(mPathToSaveAssetTo);
-
- if (topAction->mTimeWeCheckedIfIsSameAsFile == lastWriteTime)
- {
- break;
- }
-
- topAction->mTimeWeCheckedIfIsSameAsFile = lastWriteTime;
-
- if (mAssetOnFile.mWriteTimeAtTimeOfReserializing != lastWriteTime)
- {
- // Otherwise we load and save the file.
- LOG(LogEditor, Verbose, "Loading asset {} from file to check if it's unsaved...", mAsset.GetName());
-
- std::optional loadInfo = AssetLoadInfo::LoadFromFile(mPathToSaveAssetTo);
-
- if (!loadInfo.has_value())
- {
- LOG(LogEditor, Error, "Could not load asset from file {}", mPathToSaveAssetTo.string());
- break;
- }
-
- T assetAsSeenOnFile{ *loadInfo };
- mAssetOnFile.mReserializedAsset = Reserialize(assetAsSeenOnFile.Save().ToString());
- mAssetOnFile.mWriteTimeAtTimeOfReserializing = lastWriteTime;
- }
-
- topAction->mIsSameAsFile = mAssetOnFile.mReserializedAsset == topAction->mState;
-
- break;
- }
- case DifferenceCheckState::Stage::NUM_OF_STAGES:;
- }
-
- mDifferenceCheckState.mStage = static_cast(static_cast(mDifferenceCheckState.mStage) + 1);
-
- if (mDifferenceCheckState.mStage == DifferenceCheckState::Stage::NUM_OF_STAGES)
- {
- mDifferenceCheckState.mStage = DifferenceCheckState::Stage::FirstStage;
- }
}
template
- void AssetEditorSystem::CompleteDifferenceCheckCycle()
+ void AssetEditorSystem::Tick(float deltaTime)
{
- for (int i = 0; i < static_cast(DifferenceCheckState::Stage::NUM_OF_STAGES) * 2; i++)
- {
- CheckForDifferences();
- }
+ AssetEditorSystemBase::Tick(deltaTime);
}
template
- AssetEditorSystemInterface::MementoStack&& AssetEditorSystem::ExtractMementoStack()
+ MetaType AssetEditorSystem::Reflect()
{
- // It's possible an action was commited in the last .5f seconds, the change
- // has not been registered by the do-undo stack and would be ignored.
- if (mMementoStack.PeekTop() == nullptr
- || (mUpdateStateCooldown.mAmountOfTimePassed != 0.0f || mDifferenceCheckState.mStage != DifferenceCheckState::Stage::FirstStage))
- {
- CompleteDifferenceCheckCycle();
- }
-
- for (std::unique_ptr& action : mMementoStack.GetAllStoredActions())
- {
- action->mRequiresReserialization = true;
- }
-
- return std::move(mMementoStack);
+ // Bind the asset to our editor
+ Editor::RegisterAssetEditorSystem();
+ return MetaType{ MetaType::T>{}, Format("AssetEditorSystem<{}>", MakeTypeName()), MetaType::Base{} };
}
template
- std::string AssetEditorSystem::Reserialize(std::string_view serialized)
- {
- std::optional loadInfo = AssetLoadInfo::LoadFromStream(std::make_unique(serialized));
-
- if (!loadInfo.has_value())
- {
- LOG(LogEditor, Error, "Failed to load metadata, metadata was invalid somehow?");
- return {};
- }
-
- T deserialized{ *loadInfo };
- AssetSaveInfo reserialized = deserialized.Save();
- return reserialized.ToString();
- }
-
- inline std::string Internal::GetSystemNameBasedOnAssetName(const std::string_view assetName)
- {
- return std::string{ assetName };
- }
-
- inline std::string Internal::GetAssetNameBasedOnSystemName(const std::string_view systemName)
+ std::unique_ptr> AssetEditorSystem::ConstructAsset(AssetLoadInfo& loadInfo)
{
- return std::string{ systemName };
+ return MakeUniqueInPlace(loadInfo);
}
}
diff --git a/Include/EditorSystems/AssetEditorSystems/ScriptEditorSystem.h b/Include/EditorSystems/AssetEditorSystems/ScriptEditorSystem.h
index 6ce5c2d2..a90f6cef 100644
--- a/Include/EditorSystems/AssetEditorSystems/ScriptEditorSystem.h
+++ b/Include/EditorSystems/AssetEditorSystems/ScriptEditorSystem.h
@@ -1,3 +1,4 @@
+#include "Core/Input.h"
#ifdef EDITOR
#pragma once
#include "EditorSystems/AssetEditorSystems/AssetEditorSystem.h"
diff --git a/Include/EditorSystems/EditorSystem.h b/Include/EditorSystems/EditorSystem.h
index cca54a73..f893ab0f 100644
--- a/Include/EditorSystems/EditorSystem.h
+++ b/Include/EditorSystems/EditorSystem.h
@@ -57,12 +57,7 @@ namespace CE
*/
virtual bool Begin(ImGuiWindowFlags flags = {});
- /*
- For consistency, if our Begin() is a call to a field function, it makes sense if
- our call to End() is also a field function, although as you can see the behaviour
- does not differ from ImGui::End().
- */
- void End() const { ImGui::End(); }
+ virtual void End() { ImGui::End(); }
private:
friend ReflectAccess;
diff --git a/Include/EditorSystems/ImporterSystem.h b/Include/EditorSystems/ImporterSystem.h
index 1f8c7114..1c975f4f 100644
--- a/Include/EditorSystems/ImporterSystem.h
+++ b/Include/EditorSystems/ImporterSystem.h
@@ -84,7 +84,7 @@ namespace CE
uint32 ShowDuplicateAssetsErrors();
- uint32 ShowErrorsToWarnAboutDiscardChanges();
+ uint32 ShowErrorsToWarnAboutOverwritingChanges();
uint32 ShowReadOnlyErrors();
@@ -112,6 +112,7 @@ namespace CE
static inline bool sExcludeDuplicates{};
static inline bool sIgnoreReadOnly = true;
+ static inline bool sOverWriteChanges{};
friend ReflectAccess;
static MetaType Reflect();
diff --git a/Include/Utilities/DoUndo.h b/Include/Utilities/DoUndo.h
deleted file mode 100644
index ba81e5fb..00000000
--- a/Include/Utilities/DoUndo.h
+++ /dev/null
@@ -1,145 +0,0 @@
-#pragma once
-#include
-#include
-
-namespace CE
-{
- namespace DoUndo
- {
- class Action
- {
- public:
- virtual ~Action() = default;
-
- virtual void Do() = 0;
- virtual void Undo() = 0;
- };
-
- template
- class DoUndoStackBase
- {
- public:
- DoUndoStackBase() = default;
-
- DoUndoStackBase(DoUndoStackBase&&) noexcept = default;
- DoUndoStackBase(const DoUndoStackBase&) = delete;
-
- DoUndoStackBase& operator=(DoUndoStackBase&&) noexcept = default;
- DoUndoStackBase& operator=(const DoUndoStackBase&) = delete;
-
- template
- ActionType& Do(Args&& ...args)
- {
- ClearRedo();
-
- auto action = std::make_unique(std::forward(args)...);
- ActionType& returnValue = *action;
-
- mActionsTaken.push_back(std::move(action));
- mNumOfActionsDone++;
-
- DoTopActionAgain();
-
- LOG(LogEditor, Verbose, "Added {} to DoUndoStack", typeid(ActionType).name());
-
- return returnValue;
- }
-
- void DoTopActionAgain()
- {
- T* mostRecent = PeekTop();
-
- if (mostRecent != nullptr)
- {
- mostRecent->Do();
- mTimeLastActionAdded = std::chrono::high_resolution_clock::now();
- }
- }
-
- bool CanUndo() const { return PeekTop() != nullptr; }
- bool CanRedo() const { return mNumOfActionsDone < mActionsTaken.size(); }
-
- void Undo()
- {
- T* mostRecent = PeekTop();
-
- if (mostRecent != nullptr)
- {
- LOG(LogEditor, Verbose, "Undoing action");
- mNumOfActionsDone--;
- mostRecent->Undo();
- }
- }
-
- void Redo()
- {
- if (CanRedo())
- {
- LOG(LogEditor, Verbose, "Redoing action");
- mNumOfActionsDone++;
- DoTopActionAgain();
- }
- }
-
- void Clear()
- {
- mActionsTaken.clear();
- mNumOfActionsDone = 0;
- }
-
- void ClearRedo()
- {
- mActionsTaken.resize(mNumOfActionsDone);
- }
-
- const T* PeekTop() const
- {
- if (mNumOfActionsDone == 0)
- {
- return nullptr;
- }
- ASSERT(mNumOfActionsDone <= mActionsTaken.size());
-
- return mActionsTaken[mNumOfActionsDone - 1].get();
- }
-
- T* PeekTop()
- {
- return const_cast(const_cast*>(this)->PeekTop());
- }
-
- static inline constexpr float sJustNowTreshold = 1.0f;
-
- float NumOfSecondsSinceLastActionAdded() const
- {
- const auto now = std::chrono::high_resolution_clock::now();
- return (std::chrono::duration_cast>(now - mTimeLastActionAdded)).count();
- }
-
- T* WhatDidWeJustDo()
- {
- T* top = PeekTop();
-
- if (top != nullptr
- && NumOfSecondsSinceLastActionAdded() <= sJustNowTreshold)
- {
- return top;
- }
- return nullptr;
- }
-
- size_t GetNumOfActionsDone() const { return mNumOfActionsDone; }
-
- // Some of these actions may have been undone already!
- std::span> GetAllStoredActions() { return mActionsTaken; }
- std::span> GetAllStoredActions() const { return mActionsTaken; }
-
- private:
- std::vector> mActionsTaken{};
- std::chrono::high_resolution_clock::time_point mTimeLastActionAdded{};
- size_t mNumOfActionsDone{};
- };
-
- using DoUndoStack = DoUndoStackBase;
- }
-}
\ No newline at end of file
diff --git a/Include/Utilities/Reflect/ReflectFieldType.h b/Include/Utilities/Reflect/ReflectFieldType.h
index cdd002b6..3ffce39c 100644
--- a/Include/Utilities/Reflect/ReflectFieldType.h
+++ b/Include/Utilities/Reflect/ReflectFieldType.h
@@ -23,7 +23,7 @@ namespace CE
return;
}
- MetaProps& equalFuncProps = type.AddFunc(std::equal_to(), OperatorType::equal).GetProperties();
+ MetaProps& equalFuncProps = type.AddFunc([](const T& lhs, const T& rhs) { return lhs == rhs; }, OperatorType::equal).GetProperties();
if (type.GetProperties().Has(Props::sIsScriptableTag))
{
diff --git a/Source/Core/Editor.cpp b/Source/Core/Editor.cpp
index 548d893e..de254055 100644
--- a/Source/Core/Editor.cpp
+++ b/Source/Core/Editor.cpp
@@ -15,7 +15,6 @@
#include "Meta/MetaProps.h"
#include "EditorSystems/AssetEditorSystems/AssetEditorSystem.h"
#include "Utilities/view_istream.h"
-#include "GSON/GSONBinary.h"
#include "Utilities/DrawDebugHelpers.h"
#include "Utilities/NameLookUp.h"
@@ -328,7 +327,7 @@ void CE::Editor::FullFillRefreshRequests()
std::string mNameOfSystem{};
// Empty if this was not an asset editor,
- std::optional mAssetEditorRestoreData{};
+ std::optional mAssetEditorRestoreData{};
};
std::vector restorationData{};
@@ -366,7 +365,7 @@ system->GetName());
std::ostringstream savedStateStream{};
system->SaveState(savedStateStream);
- if (AssetEditorSystemInterface* assetEditor = dynamic_cast(system.get());
+ if (AssetEditorSystemBase* assetEditor = dynamic_cast(system.get());
assetEditor != nullptr)
{
if (combinedFlags & RefreshRequest::SaveAssetsToFile)
@@ -374,7 +373,7 @@ system->GetName());
assetEditor->SaveToFile();
}
- restoreInfo.mAssetEditorRestoreData = assetEditor->ExtractMementoStack();
+ restoreInfo.mAssetEditorRestoreData = assetEditor->ExtractStack();
}
DestroySystem(system->GetName());
@@ -418,10 +417,15 @@ system->GetName());
EditorSystem* system{};
if (restoreData.mAssetEditorRestoreData.has_value())
{
- AssetEditorSystemInterface::MementoStack& stack = *restoreData.mAssetEditorRestoreData;
+ AssetEditorMementoStack& stack = *restoreData.mAssetEditorRestoreData;
- const AssetEditorSystemInterface::MementoAction* topAction = stack.PeekTop();
- ASSERT(topAction != nullptr);
+ std::shared_ptr topAction = stack.GetMostRecentState();
+
+ if (topAction == nullptr)
+ {
+ LOG(LogEditor, Error, "Failed to restore to asset editor, topAction was nullptr");
+ continue;
+ }
std::optional loadInfo = AssetLoadInfo::LoadFromStream(std::make_unique(topAction->mState));
@@ -438,11 +442,11 @@ system->GetName());
if (system != nullptr
&& system->GetName() == restoreData.mNameOfSystem)
{
- AssetEditorSystemInterface* systemAsAssetEditor = dynamic_cast(system);
+ AssetEditorSystemBase* systemAsAssetEditor = dynamic_cast(system);
if (systemAsAssetEditor != nullptr)
{
- systemAsAssetEditor->SetMementoStack(std::move(stack));
+ systemAsAssetEditor->InsertMementoStack(std::move(stack));
}
else
{
diff --git a/Source/Core/Engine.cpp b/Source/Core/Engine.cpp
index 217ff4df..c75dc652 100644
--- a/Source/Core/Engine.cpp
+++ b/Source/Core/Engine.cpp
@@ -176,23 +176,21 @@ void CE::Engine::Run([[maybe_unused]] Name starterLevel)
device.NewFrame();
input.NewFrame();
- if (device.GetDisplaySize().x <= 0
- || device.GetDisplaySize().y <= 0)
+ if (device.GetDisplaySize().x > 0
+ && device.GetDisplaySize().y > 0)
{
- continue;
- }
-
-#ifdef EDITOR
- editor.Tick(deltaTime);
-#else
- world->Tick(deltaTime);
+ #ifdef EDITOR
+ editor.Tick(deltaTime);
+ #else
+ world->Tick(deltaTime);
- if (world->HasRequestedEndPlay())
- {
- break;
+ if (world->HasRequestedEndPlay())
+ {
+ break;
+ }
+ world->Render(Device::Get().GetWindowPosition());
+ #endif // EDITOR
}
- world->Render(Device::Get().GetWindowPosition());
-#endif // EDITOR
renderer.RunCommandQueues();
device.EndFrame();
diff --git a/Source/EditorSystems/AssetEditorSystems/AssetEditorSystem.cpp b/Source/EditorSystems/AssetEditorSystems/AssetEditorSystem.cpp
new file mode 100644
index 00000000..b0ff89ed
--- /dev/null
+++ b/Source/EditorSystems/AssetEditorSystems/AssetEditorSystem.cpp
@@ -0,0 +1,361 @@
+#include "Precomp.h"
+#include "EditorSystems/AssetEditorSystems/AssetEditorSystem.h"
+
+#include "Assets/Core/AssetLoadInfo.h"
+#include "Core/Input.h"
+#include "Core/ThreadPool.h"
+
+void CE::AssetEditorMementoStack::Do(std::shared_ptr action)
+{
+ ClearRedo();
+
+ mActions.push_back(std::move(action));
+ mNumOfActionsDone++;
+}
+
+bool CE::AssetEditorMementoStack::TryUndo()
+{
+ const bool canUndo = mNumOfActionsDone > 1;
+ mNumOfActionsDone -= canUndo;
+ return canUndo;
+}
+
+bool CE::AssetEditorMementoStack::TryRedo()
+{
+ const bool canRedo = mNumOfActionsDone < mActions.size();
+ mNumOfActionsDone += canRedo;
+ return canRedo;
+}
+
+void CE::AssetEditorMementoStack::ClearRedo()
+{
+ mActions.resize(mNumOfActionsDone);
+}
+
+std::shared_ptr CE::AssetEditorMementoStack::GetMostRecentState() const
+{
+ if (mNumOfActionsDone == 0)
+ {
+ return nullptr;
+ }
+ ASSERT(mNumOfActionsDone <= mActions.size());
+
+ return mActions[mNumOfActionsDone - 1];
+}
+
+CE::AssetEditorSystemBase::AssetEditorSystemBase(const Asset& asset) : // Don't hold onto this ref, it'll get invalidated
+ EditorSystem(Internal::GetSystemNameBasedOnAssetName(asset.GetName()))
+{
+}
+
+CE::AssetEditorSystemBase::~AssetEditorSystemBase()
+{
+ if (mActionToAdd.valid())
+ {
+ mActionToAdd.get();
+ }
+}
+
+CE::WeakAssetHandle CE::AssetEditorSystemBase::TryGetOriginalAsset() const
+{
+ return AssetManager::Get().TryGetWeakAsset(GetAsset().GetName());
+}
+
+std::optional CE::AssetEditorSystemBase::GetDestinationFile() const
+{
+ const WeakAssetHandle original = TryGetOriginalAsset();
+
+ if (original == nullptr)
+ {
+ return std::nullopt;
+ }
+ return original.GetFileOfOrigin();
+}
+
+void CE::AssetEditorSystemBase::SaveToFile()
+{
+ const std::optional dest = GetDestinationFile();
+
+ if (!dest.has_value())
+ {
+ LOG(LogEditor, Error, "Could not save asset {} - no destination file path",
+ GetAsset().GetName());
+ return;
+ }
+
+ Editor::Get().Refresh(
+ {
+ Editor::RefreshRequest::Volatile,
+
+ // A shared pointer, because it makes move semantics with lambdas so much simpler
+ [assetSaveInfo = std::make_shared(SaveToMemory()), saveTo = *dest]
+ {
+ assetSaveInfo->SaveToFile(saveTo);
+ }
+ });
+}
+
+CE::AssetSaveInfo CE::AssetEditorSystemBase::SaveToMemory()
+{
+ ApplyChangesToAsset();
+
+ std::optional importerInfo{};
+
+ if (const WeakAssetHandle originalAsset = TryGetOriginalAsset(); originalAsset != nullptr)
+ {
+ importerInfo = originalAsset.GetMetaData().GetImporterInfo();
+
+ if (importerInfo.has_value())
+ {
+ importerInfo->mWereEditsMadeAfterImporting |= !IsSavedToFile();
+ }
+ }
+
+ AssetSaveInfo saveInfo = GetAsset().Save(std::move(importerInfo));
+ return saveInfo;
+}
+
+bool CE::AssetEditorSystemBase::IsSavedToFile() const
+{
+ const std::shared_ptr mostRecentState = mMementoStack.GetMostRecentState();
+
+ if (mostRecentState == nullptr)
+ {
+ return true;
+ }
+ return mostRecentState->mSimilarityToFile.mDoesStateMatchFile;
+}
+
+CE::AssetEditorMementoStack CE::AssetEditorSystemBase::ExtractStack()
+{
+ if (mActionToAdd.valid())
+ {
+ mActionToAdd.get();
+ CheckForDifferences();
+ }
+
+ return std::move(mMementoStack);
+}
+
+void CE::AssetEditorSystemBase::InsertMementoStack(AssetEditorMementoStack stack)
+{
+ mMementoStack = std::move(stack);
+
+ for (const std::shared_ptr& action : mMementoStack.mActions)
+ {
+ // Our asset might have depended on other assets
+ // that have now been renamed, deleted, or otherwise
+ // altered.
+ action->mRequiresReserialization = true;
+ }
+}
+
+bool CE::AssetEditorSystemBase::Begin(ImGuiWindowFlags flags)
+{
+ const bool isWindowOpen = EditorSystem::Begin(flags | (IsSavedToFile() ? 0 : ImGuiWindowFlags_UnsavedDocument));
+ mAssetMutex.lock();
+
+ if (isWindowOpen)
+ {
+ if (!Input::Get().HasFocus())
+ {
+ return isWindowOpen;
+ }
+
+ const auto requestApplyStateChange = [this]
+ {
+ // The refreshing will return the asset editor to the
+ // top state in the stack
+ Editor::Get().Refresh({ 0, {}, GetName() });
+ };
+
+ if (Input::Get().IsKeyboardKeyHeld(Input::KeyboardKey::LeftControl)
+ || Input::Get().IsKeyboardKeyHeld(Input::KeyboardKey::RightControl))
+ {
+ if (Input::Get().WasKeyboardKeyPressed(Input::KeyboardKey::Z)
+ && mMementoStack.TryUndo())
+ {
+ requestApplyStateChange();
+ return isWindowOpen;
+ }
+
+ if (Input::Get().WasKeyboardKeyPressed(Input::KeyboardKey::Y)
+ && mMementoStack.TryRedo())
+ {
+ requestApplyStateChange();
+ return isWindowOpen;
+ }
+
+ if (Input::Get().WasKeyboardKeyPressed(Input::KeyboardKey::S))
+ {
+ SaveToFile();
+ }
+ }
+
+ CheckForDifferences();
+ }
+
+ return isWindowOpen;
+}
+
+void CE::AssetEditorSystemBase::End()
+{
+ mAssetMutex.unlock();
+ EditorSystem::End();
+}
+
+void CE::AssetEditorSystemBase::ShowSaveButton()
+{
+ if (ImGui::Button(ICON_FA_FLOPPY_O))
+ {
+ SaveToFile();
+ }
+}
+
+CE::MetaType CE::AssetEditorSystemBase::Reflect()
+{
+ return { MetaType::T{}, "AssetEditorSystemBase", MetaType::Base{} };
+}
+
+void CE::AssetEditorSystemBase::CheckForDifferences()
+{
+ if (mActionToAdd.valid())
+ {
+ if (!IsFutureReady(mActionToAdd))
+ {
+ return;
+ }
+
+ std::optional change = mActionToAdd.get();
+
+ if (change.has_value())
+ {
+ mMementoStack.Do(std::make_shared(std::move(*change)));
+ }
+
+ mDifferenceCheckTimer.Reset();
+ }
+
+ if (mDifferenceCheckTimer.GetSecondsElapsed() < sDifferenceCheckCooldown)
+ {
+ return;
+ }
+
+ const auto updateIsSameAsFile = [this](AssetEditorMementoStack::Action& action)
+ {
+ const std::optional destinationPath = GetDestinationFile();
+
+ if (!destinationPath.has_value()
+ || !std::filesystem::exists(*destinationPath))
+ {
+ action.mSimilarityToFile = {};
+ return;
+ }
+
+ const std::filesystem::file_time_type destinationWriteTime = std::filesystem::last_write_time(*destinationPath);
+
+ if (mCachedFile.mLastWriteTime != destinationWriteTime)
+ {
+ mCachedFile.mLastWriteTime = destinationWriteTime;
+
+ std::optional loadInfo = AssetLoadInfo::LoadFromFile(*destinationPath);
+
+ if (!loadInfo.has_value())
+ {
+ LOG(LogEditor, Error, "Could not load asset from file {}", destinationPath->string());
+ action.mSimilarityToFile = {};
+ return;
+ }
+
+ const auto assetPtr = ConstructAsset(*loadInfo);
+ mCachedFile.mFileContents = Reserialize(assetPtr->Save().ToString());
+ }
+
+ if (action.mSimilarityToFile.mFileWriteTimeLastTimeWeChecked == mCachedFile.mLastWriteTime)
+ {
+ return;
+ }
+
+ if (action.mRequiresReserialization)
+ {
+ action.mState = Reserialize(action.mState);
+ action.mRequiresReserialization = false;
+ }
+
+ action.mSimilarityToFile.mFileWriteTimeLastTimeWeChecked = mCachedFile.mLastWriteTime;
+ action.mSimilarityToFile.mDoesStateMatchFile = action.mState == mCachedFile.mFileContents;
+ };
+
+ mActionToAdd = ThreadPool::Get().Enqueue(
+ [this,
+ topAction = mMementoStack.GetMostRecentState(),
+ currentState = SaveToMemory().ToString(),
+ updateIsSameAsFile]() -> std::optional
+ {
+ if (topAction != nullptr)
+ {
+ updateIsSameAsFile(*topAction);
+ }
+
+ AssetEditorMementoStack::Action action{};
+ action.mState = [this]
+ {
+ std::unique_lock lock{ mAssetMutex };
+ return SaveToMemory().ToString();
+ }();
+
+ // Exactlyy the same as before, no changes were made
+ if (topAction != nullptr
+ && action.mState == topAction->mState)
+ {
+ return std::nullopt;
+ }
+
+ action.mState = Reserialize(action.mState);
+
+ if (topAction == nullptr)
+ {
+ updateIsSameAsFile(action);
+ return action;
+ }
+
+ if (topAction->mRequiresReserialization)
+ {
+ topAction->mState = Reserialize(topAction->mState);
+ topAction->mRequiresReserialization = false;
+ }
+
+ if (topAction->mState != action.mState)
+ {
+ LOG(LogEditor, Verbose, "Change detected for {}", GetName());
+ updateIsSameAsFile(action);
+ return action;
+ }
+ return std::nullopt;
+ });
+}
+
+std::string CE::AssetEditorSystemBase::Reserialize(std::string_view serialized)
+{
+ std::optional loadInfo = AssetLoadInfo::LoadFromStream(std::make_unique(serialized));
+
+ if (!loadInfo.has_value())
+ {
+ LOG(LogEditor, Error, "Failed to load metadata, metadata was invalid somehow?");
+ return {};
+ }
+
+ const auto assetPtr = ConstructAsset(*loadInfo);
+ const AssetSaveInfo reserialized = assetPtr->Save();
+ return reserialized.ToString();
+}
+
+std::string CE::Internal::GetSystemNameBasedOnAssetName(const std::string_view assetName)
+{
+ return std::string{ assetName };
+}
+
+std::string CE::Internal::GetAssetNameBasedOnSystemName(const std::string_view systemName)
+{
+ return std::string{ systemName };
+}
\ No newline at end of file
diff --git a/Source/EditorSystems/AssetEditorSystems/LevelEditorSystem.cpp b/Source/EditorSystems/AssetEditorSystems/LevelEditorSystem.cpp
index 3a3cb89a..67f83d97 100644
--- a/Source/EditorSystems/AssetEditorSystems/LevelEditorSystem.cpp
+++ b/Source/EditorSystems/AssetEditorSystems/LevelEditorSystem.cpp
@@ -20,8 +20,6 @@ void CE::LevelEditorSystem::Tick(const float deltaTime)
return;
}
- AssetEditorSystem::Tick(deltaTime);
-
if (ImGui::BeginMenuBar())
{
ShowSaveButton();
diff --git a/Source/EditorSystems/AssetEditorSystems/MaterialEditorSystem.cpp b/Source/EditorSystems/AssetEditorSystems/MaterialEditorSystem.cpp
index 5da34672..55735ca6 100644
--- a/Source/EditorSystems/AssetEditorSystems/MaterialEditorSystem.cpp
+++ b/Source/EditorSystems/AssetEditorSystems/MaterialEditorSystem.cpp
@@ -11,7 +11,7 @@ CE::MaterialEditorSystem::MaterialEditorSystem(Material&& asset) :
CE::MaterialEditorSystem::~MaterialEditorSystem() = default;
-void CE::MaterialEditorSystem::Tick(const float deltaTime)
+void CE::MaterialEditorSystem::Tick([[maybe_unused]] const float deltaTime)
{
if (!Begin(ImGuiWindowFlags_MenuBar))
{
@@ -19,8 +19,6 @@ void CE::MaterialEditorSystem::Tick(const float deltaTime)
return;
}
- AssetEditorSystem::Tick(deltaTime);
-
if (ImGui::BeginMenuBar())
{
ShowSaveButton();
diff --git a/Source/EditorSystems/AssetEditorSystems/PrefabEditorSystem.cpp b/Source/EditorSystems/AssetEditorSystems/PrefabEditorSystem.cpp
index fcd641bd..ef10b951 100644
--- a/Source/EditorSystems/AssetEditorSystems/PrefabEditorSystem.cpp
+++ b/Source/EditorSystems/AssetEditorSystems/PrefabEditorSystem.cpp
@@ -57,8 +57,6 @@ void CE::PrefabEditorSystem::Tick(const float deltaTime)
return;
}
- AssetEditorSystem::Tick(deltaTime);
-
if (ImGui::BeginMenuBar())
{
ShowSaveButton();
diff --git a/Source/EditorSystems/AssetEditorSystems/ScriptEditorSystem/ScriptClassPanel.cpp b/Source/EditorSystems/AssetEditorSystems/ScriptEditorSystem/ScriptClassPanel.cpp
index 954b6852..2ad0969f 100644
--- a/Source/EditorSystems/AssetEditorSystems/ScriptEditorSystem/ScriptClassPanel.cpp
+++ b/Source/EditorSystems/AssetEditorSystems/ScriptEditorSystem/ScriptClassPanel.cpp
@@ -5,6 +5,7 @@
// it was all in one file.
#include "EditorSystems/AssetEditorSystems/ScriptEditorSystem.h"
#include "Utilities/Events.h"
+#include "Utilities/StringFunctions.h"
#include "Utilities/Imgui/ImguiHelpers.h"
diff --git a/Source/EditorSystems/AssetEditorSystems/ScriptEditorSystem/ScriptEditorSystem.cpp b/Source/EditorSystems/AssetEditorSystems/ScriptEditorSystem/ScriptEditorSystem.cpp
index 8770314a..f6f0c972 100644
--- a/Source/EditorSystems/AssetEditorSystems/ScriptEditorSystem/ScriptEditorSystem.cpp
+++ b/Source/EditorSystems/AssetEditorSystems/ScriptEditorSystem/ScriptEditorSystem.cpp
@@ -29,7 +29,7 @@ CE::ScriptEditorSystem::~ScriptEditorSystem()
}
}
-void CE::ScriptEditorSystem::Tick(const float deltaTime)
+void CE::ScriptEditorSystem::Tick([[maybe_unused]] const float deltaTime)
{
if (!Begin(ImGuiWindowFlags_MenuBar))
{
@@ -37,7 +37,6 @@ void CE::ScriptEditorSystem::Tick(const float deltaTime)
return;
}
- AssetEditorSystem::Tick(deltaTime);
ax::NodeEditor::SetCurrentEditor(mContext);
if (ImGui::BeginMenuBar())
diff --git a/Source/EditorSystems/ImporterSystem.cpp b/Source/EditorSystems/ImporterSystem.cpp
index 757bc1e9..5aaf8243 100644
--- a/Source/EditorSystems/ImporterSystem.cpp
+++ b/Source/EditorSystems/ImporterSystem.cpp
@@ -168,11 +168,12 @@ void CE::ImporterSystem::Tick(const float dt)
Preview();
uint32 numOfConflicts = ShowDuplicateAssetsErrors();
- numOfConflicts += ShowErrorsToWarnAboutDiscardChanges();
+ numOfConflicts += ShowErrorsToWarnAboutOverwritingChanges();
numOfConflicts += ShowReadOnlyErrors();
ImGui::Checkbox("Exclude duplicates", &sExcludeDuplicates);
ImGui::Checkbox("Ignore read only", &sIgnoreReadOnly);
+ ImGui::Checkbox("Overwrite changes", &sOverWriteChanges);
if (numOfConflicts != 0)
{
@@ -679,8 +680,13 @@ uint32 CE::ImporterSystem::ShowDuplicateAssetsErrors()
return numOfErrors;
}
-uint32 CE::ImporterSystem::ShowErrorsToWarnAboutDiscardChanges()
+uint32 CE::ImporterSystem::ShowErrorsToWarnAboutOverwritingChanges()
{
+ if (sOverWriteChanges)
+ {
+ return 0;
+ }
+
bool isOpenAlready{}, shouldDisplay{};
uint32 numOfErrors{};
diff --git a/Source/World/Archiver.cpp b/Source/World/Archiver.cpp
index 982980a6..e3c7467d 100644
--- a/Source/World/Archiver.cpp
+++ b/Source/World/Archiver.cpp
@@ -7,6 +7,7 @@
#include "Components/TransformComponent.h"
#include "Assets/Prefabs/PrefabEntityFactory.h"
#include "Components/PrefabOriginComponent.h"
+#include "Core/ThreadPool.h"
#include "Meta/MetaType.h"
#include "Meta/MetaManager.h"
#include "Meta/MetaFuncId.h"
@@ -306,58 +307,75 @@ CE::BinaryGSONObject CE::Archiver::SerializeInternal(const World& world, std::ve
BinaryGSONObject save{ "SerializedWorld" };
std::sort(entitiesToSerialize.begin(), entitiesToSerialize.end());
-
save.AddGSONMember("entities") << entitiesToSerialize;
- size_t numOfStorages{};
- for ([[maybe_unused]] auto _ : reg.Storage())
- {
- ++numOfStorages;
- }
+ const auto storageRange = reg.Storage();
+ const size_t numOfStorages = std::distance(storageRange.begin(), storageRange.end());
+
save.ReserveChildren(numOfStorages);
+ std::vector> storageFutures{};
+ storageFutures.reserve(numOfStorages);
+
for (auto&& [typeId, storage] : reg.Storage())
{
- if (storage.empty())
- {
- continue;
- }
+ BinaryGSONObject& serializedComponentClass = save.AddGSONObject({});
- const std::optional serializeArg = GetComponentClassSerializeArg(storage);
+ storageFutures.emplace_back(ThreadPool::Get().Enqueue(
+ [&]
+ {
+ if (storage.empty())
+ {
+ return;
+ }
- if (!serializeArg.has_value())
- {
- continue;
- }
+ const std::optional serializeArg = GetComponentClassSerializeArg(storage);
+
+ if (!serializeArg.has_value())
+ {
+ return;
+ }
- BinaryGSONObject& serializedComponentClass = save.AddGSONObject(serializeArg->mComponentClass.GetName());
- serializedComponentClass.ReserveChildren(storage.size());
+ serializedComponentClass.SetName(serializeArg->mComponentClass.GetName());
+ serializedComponentClass.ReserveChildren(storage.size());
- for (const entt::entity entity : storage)
- {
- if (!allEntitiesInWorldAreBeingSerialized
- && !std::binary_search(entitiesToSerialize.begin(), entitiesToSerialize.end(), entity))
- {
- continue;
- }
+ for (const entt::entity entity : storage)
+ {
+ if (!allEntitiesInWorldAreBeingSerialized
+ && !std::binary_search(entitiesToSerialize.begin(), entitiesToSerialize.end(), entity))
+ {
+ continue;
+ }
- SerializeSingleComponent(reg,
- serializedComponentClass,
- entity,
- *serializeArg);
- }
+ SerializeSingleComponent(reg,
+ serializedComponentClass,
+ entity,
+ *serializeArg);
+ }
- // We want to guarantee that after deserializing this and then reserializing it, we get the same result.
- // But entt::registry will jumble up the order of the entities for us.
- // This ensures that we get the same order every time we save.
- std::sort(serializedComponentClass.GetChildren().begin(), serializedComponentClass.GetChildren().end(),
- [](const BinaryGSONObject& lhs, const BinaryGSONObject& rhs)
- {
- // Faster than string comparisons
- return *reinterpret_cast(lhs.GetName().c_str()) < *reinterpret_cast(rhs.GetName().c_str());
- });
+ // We want to guarantee that after deserializing this and then reserializing it, we get the same result.
+ // But entt::registry will jumble up the order of the entities for us.
+ // This ensures that we get the same order every time we save.
+ std::sort(serializedComponentClass.GetChildren().begin(), serializedComponentClass.GetChildren().end(),
+ [](const BinaryGSONObject& lhs, const BinaryGSONObject& rhs)
+ {
+ // Faster than string comparisons
+ return *reinterpret_cast(lhs.GetName().c_str()) < *reinterpret_cast(rhs.GetName().c_str());
+ });
+ }));
+ }
+
+ for (const std::future& future : storageFutures)
+ {
+ future.wait();
}
+ std::erase_if(save.GetChildren(),
+ [](const BinaryGSONObject& obj)
+ {
+ return obj.GetName().empty();
+ });
+
return save;
}