Add content deprecation upgrades support to prefabs and scenes when loading levels

This commit is contained in:
Wojtek Figat
2025-01-20 23:53:13 +01:00
parent 67f12596e2
commit 7d0804af91
7 changed files with 113 additions and 7 deletions

View File

@@ -7,6 +7,7 @@
#include "SceneObjectsFactory.h" #include "SceneObjectsFactory.h"
#include "Scene/Scene.h" #include "Scene/Scene.h"
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Core/Cache.h" #include "Engine/Core/Cache.h"
#include "Engine/Core/Collections/CollectionPoolCache.h" #include "Engine/Core/Collections/CollectionPoolCache.h"
#include "Engine/Core/ObjectsRemovalService.h" #include "Engine/Core/ObjectsRemovalService.h"
@@ -827,7 +828,7 @@ bool Level::loadScene(JsonAsset* sceneAsset)
return true; 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) 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); 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"); PROFILE_CPU_NAMED("Level.LoadScene");
if (outScene) if (outScene)
*outScene = nullptr; *outScene = nullptr;
#if USE_EDITOR
ContentDeprecated::Clear();
#endif
LOG(Info, "Loading scene..."); LOG(Info, "Loading scene...");
Stopwatch stopwatch; Stopwatch stopwatch;
_lastSceneLoadTime = DateTime::Now(); _lastSceneLoadTime = DateTime::Now();
@@ -1002,6 +1006,9 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
if (context.Async) if (context.Async)
{ {
ScenesLock.Unlock(); // Unlock scenes from Main Thread so Job Threads can use it to safely setup actors hierarchy (see Actor::Deserialize) 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) JobSystem::Execute([&](int32 i)
{ {
i++; // Start from 1. at index [0] was scene 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(); auto& idMapping = Scripting::ObjectsLookupIdMapping.Get();
idMapping = &context.GetModifier()->IdsMapping; idMapping = &context.GetModifier()->IdsMapping;
SceneObjectsFactory::Deserialize(context, obj, data[i]); SceneObjectsFactory::Deserialize(context, obj, data[i]);
#if USE_EDITOR
if (ContentDeprecated::Clear())
Platform::InterlockedIncrement(&deprecated);
#endif
idMapping = nullptr; idMapping = nullptr;
} }
}, dataCount - 1); }, dataCount - 1);
#if USE_EDITOR
if (deprecated != 0)
ContentDeprecated::Mark();
#endif
ScenesLock.Lock(); ScenesLock.Lock();
} }
else 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()); LOG(Info, "Scene loaded in {0}ms", stopwatch.GetMilliseconds());
if (outScene) if (outScene)
*outScene = scene; *outScene = scene;
#if USE_EDITOR
// Resave assets that use deprecated data format
for (auto& e : context.DeprecatedPrefabs)
{
AssetReference<Prefab> 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; return false;
} }
@@ -1125,6 +1162,7 @@ bool LevelImpl::saveScene(Scene* scene)
bool LevelImpl::saveScene(Scene* scene, const String& path) bool LevelImpl::saveScene(Scene* scene, const String& path)
{ {
PROFILE_CPU_NAMED("Level.SaveScene");
ASSERT(scene && EnumHasNoneFlags(scene->Flags, ObjectFlags::WasMarkedToDelete)); ASSERT(scene && EnumHasNoneFlags(scene->Flags, ObjectFlags::WasMarkedToDelete));
auto sceneId = scene->GetID(); auto sceneId = scene->GetID();

View File

@@ -551,5 +551,5 @@ private:
static bool loadScene(JsonAsset* sceneAsset); static bool loadScene(JsonAsset* sceneAsset);
static bool loadScene(const BytesContainer& sceneData, Scene** outScene = nullptr); static bool loadScene(const BytesContainer& sceneData, Scene** outScene = nullptr);
static bool loadScene(rapidjson_flax::Document& document, 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);
}; };

View File

@@ -732,6 +732,50 @@ bool Prefab::ApplyAll(Actor* targetActor)
return false; return false;
} }
bool Prefab::Resave()
{
if (OnCheckSave())
return true;
PROFILE_CPU_NAMED("Prefab.Resave");
ScopeLock lock(Locker);
Dictionary<Guid, Guid> 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<ActorsCache::SceneObjectsListType>::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) bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPrefab, PrefabInstancesData& prefabInstancesData)
{ {
PROFILE_CPU_NAMED("Prefab.Apply"); PROFILE_CPU_NAMED("Prefab.Apply");

View File

@@ -10,7 +10,7 @@ class Actor;
class SceneObject; class SceneObject;
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
/// <seealso cref="JsonAssetBase" /> /// <seealso cref="JsonAssetBase" />
API_CLASS(NoSpawn) class FLAXENGINE_API Prefab : public JsonAssetBase API_CLASS(NoSpawn) class FLAXENGINE_API Prefab : public JsonAssetBase
@@ -74,11 +74,16 @@ public:
/// <summary> /// <summary>
/// Applies the difference from the prefab object instance, saves the changes and synchronizes them with the active instances of the prefab asset. /// Applies the difference from the prefab object instance, saves the changes and synchronizes them with the active instances of the prefab asset.
/// </summary> /// </summary>
/// <remarks> /// <remarks>Applies all the changes from not only the given actor instance but all actors created within that prefab instance.</remarks>
/// Applies all the changes from not only the given actor instance but all actors created within that prefab instance.
/// </remarks>
/// <param name="targetActor">The root actor of spawned prefab instance to use as modified changes sources.</param> /// <param name="targetActor">The root actor of spawned prefab instance to use as modified changes sources.</param>
/// <returns>True if failed, otherwise false.</returns>
bool ApplyAll(Actor* targetActor); bool ApplyAll(Actor* targetActor);
/// <summary>
/// Resaves the prefab asset to the file by serializing default instance in the latest format and defaults.
/// </summary>
/// <returns>True if failed, otherwise false.</returns>
API_FUNCTION() bool Resave();
#endif #endif
private: private:

View File

@@ -5,6 +5,7 @@
#include "Engine/Level/Level.h" #include "Engine/Level/Level.h"
#include "Engine/Content/AssetInfo.h" #include "Engine/Content/AssetInfo.h"
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
#include "Engine/Content/Deprecated.h"
#include "Engine/Content/Factories/JsonAssetFactory.h" #include "Engine/Content/Factories/JsonAssetFactory.h"
#include "Engine/Physics/Colliders/MeshCollider.h" #include "Engine/Physics/Colliders/MeshCollider.h"
#include "Engine/Level/Actors/StaticModel.h" #include "Engine/Level/Actors/StaticModel.h"
@@ -306,6 +307,7 @@ void Scene::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
if (e != stream.MemberEnd()) if (e != stream.MemberEnd())
{ {
// Upgrade from old single hidden navmesh data into NavMesh actors on a scene // Upgrade from old single hidden navmesh data into NavMesh actors on a scene
MARK_CONTENT_DEPRECATED();
AssetReference<RawDataAsset> dataAsset; AssetReference<RawDataAsset> dataAsset;
Serialization::Deserialize(e->value, dataAsset, modifier); Serialization::Deserialize(e->value, dataAsset, modifier);
const auto settings = NavigationSettings::Get(); const auto settings = NavigationSettings::Get();

View File

@@ -19,6 +19,7 @@
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include "Engine/Level/Scripts/MissingScript.h" #include "Engine/Level/Scripts/MissingScript.h"
#endif #endif
#include "Engine/Content/Deprecated.h"
#include "Engine/Level/Scripts/ModelPrefab.h" #include "Engine/Level/Scripts/ModelPrefab.h"
#if USE_EDITOR #if USE_EDITOR
@@ -199,6 +200,7 @@ SceneObject* SceneObjectsFactory::Spawn(Context& context, const ISerializable::D
else else
{ {
// [Deprecated: 18.07.2019 expires 18.07.2020] // [Deprecated: 18.07.2019 expires 18.07.2020]
MARK_CONTENT_DEPRECATED();
const auto typeIdMember = stream.FindMember("TypeID"); const auto typeIdMember = stream.FindMember("TypeID");
if (typeIdMember == stream.MemberEnd()) 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) // Deserialize prefab data (recursive prefab loading to support nested prefabs)
const auto prevVersion = modifier->EngineBuild; const auto prevVersion = modifier->EngineBuild;
modifier->EngineBuild = prefab->DataEngineBuild; modifier->EngineBuild = prefab->DataEngineBuild;
#if USE_EDITOR
bool prevDeprecated = ContentDeprecated::Clear();
#endif
Deserialize(context, obj, *(ISerializable::DeserializeStream*)prefabData); 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; modifier->EngineBuild = prevVersion;
} }

View File

@@ -31,6 +31,9 @@ public:
Dictionary<Guid, int32> ObjectToInstance; Dictionary<Guid, int32> ObjectToInstance;
CriticalSection Locker; CriticalSection Locker;
ThreadLocal<ISerializeModifier*> Modifiers; ThreadLocal<ISerializeModifier*> Modifiers;
#if USE_EDITOR
HashSet<Prefab*> DeprecatedPrefabs;
#endif
Context(ISerializeModifier* modifier); Context(ISerializeModifier* modifier);
~Context(); ~Context();