From 7d0804af91e7fe489efb906fe1ef313601b5f55e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 20 Jan 2025 23:53:13 +0100 Subject: [PATCH] Add content deprecation upgrades support to prefabs and scenes when loading levels --- Source/Engine/Level/Level.cpp | 42 ++++++++++++++++++- Source/Engine/Level/Level.h | 2 +- Source/Engine/Level/Prefabs/Prefab.Apply.cpp | 44 ++++++++++++++++++++ Source/Engine/Level/Prefabs/Prefab.h | 13 ++++-- Source/Engine/Level/Scene/Scene.cpp | 2 + Source/Engine/Level/SceneObjectsFactory.cpp | 14 +++++++ Source/Engine/Level/SceneObjectsFactory.h | 3 ++ 7 files changed, 113 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index f24a853e3..e1636ea35 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -7,6 +7,7 @@ #include "SceneObjectsFactory.h" #include "Scene/Scene.h" #include "Engine/Content/Content.h" +#include "Engine/Content/Deprecated.h" #include "Engine/Core/Cache.h" #include "Engine/Core/Collections/CollectionPoolCache.h" #include "Engine/Core/ObjectsRemovalService.h" @@ -827,7 +828,7 @@ bool Level::loadScene(JsonAsset* sceneAsset) return true; } - return loadScene(*sceneAsset->Data, sceneAsset->DataEngineBuild); + return loadScene(*sceneAsset->Data, sceneAsset->DataEngineBuild, nullptr, &sceneAsset->GetPath()); } bool Level::loadScene(const BytesContainer& sceneData, Scene** outScene) @@ -866,11 +867,14 @@ bool Level::loadScene(rapidjson_flax::Document& document, Scene** outScene) return loadScene(data->value, saveEngineBuild, outScene); } -bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** outScene) +bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** outScene, const String* assetPath) { PROFILE_CPU_NAMED("Level.LoadScene"); if (outScene) *outScene = nullptr; +#if USE_EDITOR + ContentDeprecated::Clear(); +#endif LOG(Info, "Loading scene..."); Stopwatch stopwatch; _lastSceneLoadTime = DateTime::Now(); @@ -1002,6 +1006,9 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou if (context.Async) { ScenesLock.Unlock(); // Unlock scenes from Main Thread so Job Threads can use it to safely setup actors hierarchy (see Actor::Deserialize) +#if USE_EDITOR + volatile int64 deprecated = 0; +#endif JobSystem::Execute([&](int32 i) { i++; // Start from 1. at index [0] was scene @@ -1011,9 +1018,17 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou auto& idMapping = Scripting::ObjectsLookupIdMapping.Get(); idMapping = &context.GetModifier()->IdsMapping; SceneObjectsFactory::Deserialize(context, obj, data[i]); +#if USE_EDITOR + if (ContentDeprecated::Clear()) + Platform::InterlockedIncrement(&deprecated); +#endif idMapping = nullptr; } }, dataCount - 1); +#if USE_EDITOR + if (deprecated != 0) + ContentDeprecated::Mark(); +#endif ScenesLock.Lock(); } else @@ -1103,6 +1118,28 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou LOG(Info, "Scene loaded in {0}ms", stopwatch.GetMilliseconds()); if (outScene) *outScene = scene; + +#if USE_EDITOR + // Resave assets that use deprecated data format + for (auto& e : context.DeprecatedPrefabs) + { + AssetReference prefab = e.Item; + LOG(Info, "Resaving asset '{}' that uses deprecated data format", prefab->GetPath()); + if (prefab->Resave()) + { + LOG(Error, "Failed to resave asset '{}'", prefab->GetPath()); + } + } + if (ContentDeprecated::Clear() && assetPath) + { + LOG(Info, "Resaving asset '{}' that uses deprecated data format", *assetPath); + if (saveScene(scene, *assetPath)) + { + LOG(Error, "Failed to resave asset '{}'", *assetPath); + } + } +#endif + return false; } @@ -1125,6 +1162,7 @@ bool LevelImpl::saveScene(Scene* scene) bool LevelImpl::saveScene(Scene* scene, const String& path) { + PROFILE_CPU_NAMED("Level.SaveScene"); ASSERT(scene && EnumHasNoneFlags(scene->Flags, ObjectFlags::WasMarkedToDelete)); auto sceneId = scene->GetID(); diff --git a/Source/Engine/Level/Level.h b/Source/Engine/Level/Level.h index b9c6b698a..2b3022d5b 100644 --- a/Source/Engine/Level/Level.h +++ b/Source/Engine/Level/Level.h @@ -551,5 +551,5 @@ private: static bool loadScene(JsonAsset* sceneAsset); static bool loadScene(const BytesContainer& sceneData, Scene** outScene = nullptr); static bool loadScene(rapidjson_flax::Document& document, Scene** outScene = nullptr); - static bool loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** outScene = nullptr); + static bool loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** outScene = nullptr, const String* assetPath = nullptr); }; diff --git a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp index f8dcde410..895096f73 100644 --- a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp +++ b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp @@ -732,6 +732,50 @@ bool Prefab::ApplyAll(Actor* targetActor) return false; } +bool Prefab::Resave() +{ + if (OnCheckSave()) + return true; + PROFILE_CPU_NAMED("Prefab.Resave"); + ScopeLock lock(Locker); + + Dictionary objectIds; + objectIds.EnsureCapacity(ObjectsIds.Count()); + for (int32 i = 0; i < ObjectsIds.Count(); i++) + { + Guid id = ObjectsIds[i]; + objectIds.Add(id, id); + } + PrefabManager::SpawnOptions options; + options.WithLink = false; + options.IDs = &objectIds; + auto instance = PrefabManager::SpawnPrefab(this, options); + if (instance == nullptr) + return true; + + // Serialize to json data + CollectionPoolCache::ScopeCache sceneObjects = ActorsCache::SceneObjectsListCache.Get(); + SceneQuery::GetAllSerializableSceneObjects(instance, *sceneObjects.Value); + rapidjson_flax::StringBuffer dataBuffer; + { + PrettyJsonWriter writerObj(dataBuffer); + PrefabInstanceData::SerializeObjects(*sceneObjects.Value, writerObj); + } + + // Remove temporary objects + instance->DeleteObject(); + instance = nullptr; + + // Save to file + if (CreateJson::Create(GetPath(), dataBuffer, TypeName)) + { + LOG(Warning, "Failed to serialize prefab data to the asset."); + return true; + } + + return false; +} + bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPrefab, PrefabInstancesData& prefabInstancesData) { PROFILE_CPU_NAMED("Prefab.Apply"); diff --git a/Source/Engine/Level/Prefabs/Prefab.h b/Source/Engine/Level/Prefabs/Prefab.h index 5b7de8637..7ac3ea406 100644 --- a/Source/Engine/Level/Prefabs/Prefab.h +++ b/Source/Engine/Level/Prefabs/Prefab.h @@ -10,7 +10,7 @@ class Actor; class SceneObject; /// -/// Json asset that stores the collection of scene objects including actors and scripts. In general it can serve as any grouping of scene objects (for example a level) or be used as a form of a template instantiated and reused throughout the scene. +/// Json asset that stores the collection of scene objects including actors and scripts. In general, it can serve as any grouping of scene objects (for example a level) or be used as a form of a template instantiated and reused throughout the scene. /// /// API_CLASS(NoSpawn) class FLAXENGINE_API Prefab : public JsonAssetBase @@ -74,11 +74,16 @@ public: /// /// Applies the difference from the prefab object instance, saves the changes and synchronizes them with the active instances of the prefab asset. /// - /// - /// Applies all the changes from not only the given actor instance but all actors created within that prefab instance. - /// + /// Applies all the changes from not only the given actor instance but all actors created within that prefab instance. /// The root actor of spawned prefab instance to use as modified changes sources. + /// True if failed, otherwise false. bool ApplyAll(Actor* targetActor); + + /// + /// Resaves the prefab asset to the file by serializing default instance in the latest format and defaults. + /// + /// True if failed, otherwise false. + API_FUNCTION() bool Resave(); #endif private: diff --git a/Source/Engine/Level/Scene/Scene.cpp b/Source/Engine/Level/Scene/Scene.cpp index f562dfbe4..7ef8eb40b 100644 --- a/Source/Engine/Level/Scene/Scene.cpp +++ b/Source/Engine/Level/Scene/Scene.cpp @@ -5,6 +5,7 @@ #include "Engine/Level/Level.h" #include "Engine/Content/AssetInfo.h" #include "Engine/Content/Content.h" +#include "Engine/Content/Deprecated.h" #include "Engine/Content/Factories/JsonAssetFactory.h" #include "Engine/Physics/Colliders/MeshCollider.h" #include "Engine/Level/Actors/StaticModel.h" @@ -306,6 +307,7 @@ void Scene::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) if (e != stream.MemberEnd()) { // Upgrade from old single hidden navmesh data into NavMesh actors on a scene + MARK_CONTENT_DEPRECATED(); AssetReference dataAsset; Serialization::Deserialize(e->value, dataAsset, modifier); const auto settings = NavigationSettings::Get(); diff --git a/Source/Engine/Level/SceneObjectsFactory.cpp b/Source/Engine/Level/SceneObjectsFactory.cpp index 520154b28..fabf73aff 100644 --- a/Source/Engine/Level/SceneObjectsFactory.cpp +++ b/Source/Engine/Level/SceneObjectsFactory.cpp @@ -19,6 +19,7 @@ #include "Engine/Threading/Threading.h" #include "Engine/Level/Scripts/MissingScript.h" #endif +#include "Engine/Content/Deprecated.h" #include "Engine/Level/Scripts/ModelPrefab.h" #if USE_EDITOR @@ -199,6 +200,7 @@ SceneObject* SceneObjectsFactory::Spawn(Context& context, const ISerializable::D else { // [Deprecated: 18.07.2019 expires 18.07.2020] + MARK_CONTENT_DEPRECATED(); const auto typeIdMember = stream.FindMember("TypeID"); if (typeIdMember == stream.MemberEnd()) { @@ -286,7 +288,19 @@ void SceneObjectsFactory::Deserialize(Context& context, SceneObject* obj, ISeria // Deserialize prefab data (recursive prefab loading to support nested prefabs) const auto prevVersion = modifier->EngineBuild; modifier->EngineBuild = prefab->DataEngineBuild; +#if USE_EDITOR + bool prevDeprecated = ContentDeprecated::Clear(); +#endif Deserialize(context, obj, *(ISerializable::DeserializeStream*)prefabData); +#if USE_EDITOR + if (ContentDeprecated::Clear(prevDeprecated)) + { + // Prefab contains deprecated data format + context.Locker.Lock(); + context.DeprecatedPrefabs.Add(prefab); + context.Locker.Unlock(); + } +#endif modifier->EngineBuild = prevVersion; } diff --git a/Source/Engine/Level/SceneObjectsFactory.h b/Source/Engine/Level/SceneObjectsFactory.h index e749375b2..15369c707 100644 --- a/Source/Engine/Level/SceneObjectsFactory.h +++ b/Source/Engine/Level/SceneObjectsFactory.h @@ -31,6 +31,9 @@ public: Dictionary ObjectToInstance; CriticalSection Locker; ThreadLocal Modifiers; +#if USE_EDITOR + HashSet DeprecatedPrefabs; +#endif Context(ISerializeModifier* modifier); ~Context();