diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index a1be4a179..48b148e3f 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -230,7 +230,8 @@ void Asset::OnDeleteObject() Content::GetRegistry()->DeleteAsset(id, nullptr); // Delete file - Content::deleteFileSafety(path, id); + if (!IsVirtual()) + Content::deleteFileSafety(path, id); } #endif } @@ -288,8 +289,13 @@ void Asset::OnScriptingDispose() void Asset::ChangeID(const Guid& newId) { - // Don't allow to change asset ids - CRASH; + // Only virtual asset can change ID + if (!IsVirtual()) + CRASH; + + const Guid oldId = _id; + ManagedScriptingObject::ChangeID(newId); + Content::onAssetChangeId(this, oldId, newId); } bool Asset::LastLoadFailed() const diff --git a/Source/Engine/Content/Content.cpp b/Source/Engine/Content/Content.cpp index c44043f79..6d2e52b85 100644 --- a/Source/Engine/Content/Content.cpp +++ b/Source/Engine/Content/Content.cpp @@ -460,12 +460,16 @@ Asset* Content::LoadAsync(const StringView& path, const ScriptingTypeHandle& typ Array Content::GetAssets() { Array assets; + AssetsLocker.Lock(); Assets.GetValues(assets); + AssetsLocker.Unlock(); return assets; } const Dictionary& Content::GetAssetsRaw() { + AssetsLocker.Lock(); + AssetsLocker.Unlock(); return Assets; } @@ -862,6 +866,13 @@ void Content::onAssetUnload(Asset* asset) LoadedAssetsToInvoke.Remove(asset); } +void Content::onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId) +{ + ScopeLock locker(AssetsLocker); + Assets.Remove(oldId); + Assets.Add(newId, asset); +} + bool Content::IsAssetTypeIdInvalid(const ScriptingTypeHandle& type, const ScriptingTypeHandle& assetType) { // Skip if no restrictions for the type diff --git a/Source/Engine/Content/Content.h b/Source/Engine/Content/Content.h index ba40f6e21..5cdee2ccd 100644 --- a/Source/Engine/Content/Content.h +++ b/Source/Engine/Content/Content.h @@ -358,6 +358,7 @@ private: static void tryCallOnLoaded(Asset* asset); static void onAssetLoaded(Asset* asset); static void onAssetUnload(Asset* asset); + static void onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId); static Asset* load(const Guid& id, const ScriptingTypeHandle& type, AssetInfo& assetInfo); private: diff --git a/Source/Engine/Content/JsonAsset.cpp b/Source/Engine/Content/JsonAsset.cpp index 0fc5df599..4a6d92c66 100644 --- a/Source/Engine/Content/JsonAsset.cpp +++ b/Source/Engine/Content/JsonAsset.cpp @@ -45,6 +45,29 @@ String JsonAssetBase::GetData() const return String((const char*)buffer.GetString(), (int32)buffer.GetSize()); } +bool JsonAssetBase::Init(const StringView& dataTypeName, const StringAnsiView& dataJson) +{ + CHECK_RETURN(IsVirtual(), true); + unload(true); + DataTypeName = dataTypeName; + DataEngineBuild = FLAXENGINE_VERSION_BUILD; + + // Parse json document + { + PROFILE_CPU_NAMED("Json.Parse"); + Document.Parse(dataJson.Get(), dataJson.Length()); + } + if (Document.HasParseError()) + { + Log::JsonParseException(Document.GetParseError(), Document.GetErrorOffset()); + return true; + } + Data = &Document; + + // Load asset-specific data + return loadAsset() != LoadResult::Ok; +} + const String& JsonAssetBase::GetPath() const { #if USE_EDITOR @@ -114,6 +137,9 @@ void JsonAssetBase::GetReferences(Array& output) const Asset::LoadResult JsonAssetBase::loadAsset() { + if (IsVirtual()) + return LoadResult::Ok; + // Load data (raw json file in editor, cooked asset in build game) #if USE_EDITOR BytesContainer data; diff --git a/Source/Engine/Content/JsonAsset.h b/Source/Engine/Content/JsonAsset.h index 25d463b16..7aa794ed8 100644 --- a/Source/Engine/Content/JsonAsset.h +++ b/Source/Engine/Content/JsonAsset.h @@ -50,6 +50,15 @@ public: /// API_PROPERTY() String GetData() const; + /// + /// Initializes the virtual Json asset with custom data. + /// + /// Can be used only for virtual assets created at runtime. + /// The data type name from the header. Allows to recognize the data type. + /// The Json with serialized data. + /// True if failed, otherwise false. + API_FUNCTION() bool Init(const StringView& dataTypeName, const StringAnsiView& dataJson); + #if USE_EDITOR /// /// Parses Json string to find any object references inside it. It can produce list of references to assets and/or scene objects. Supported only in Editor. diff --git a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp index 5de65e2d9..7e9742ba2 100644 --- a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp +++ b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp @@ -661,10 +661,16 @@ bool Prefab::ApplyAll(Actor* targetActor) // Assign references to the prefabs allPrefabs.EnsureCapacity(Math::RoundUpToPowerOf2(Math::Max(30, nestedPrefabIds.Count()))); + const Dictionary& assetsRaw = Content::GetAssetsRaw(); + for (auto& e : assetsRaw) + { + if (e.Value->GetTypeHandle() == Prefab::TypeInitializer) + nestedPrefabIds.AddUnique(e.Key); + } for (int32 i = 0; i < nestedPrefabIds.Count(); i++) { const auto nestedPrefab = Content::LoadAsync(nestedPrefabIds[i]); - if (nestedPrefab && nestedPrefab != this) + if (nestedPrefab && nestedPrefab != this && (nestedPrefab->Flags & ObjectFlags::WasMarkedToDelete) == 0) { allPrefabs.Add(nestedPrefab); } @@ -1079,6 +1085,10 @@ bool Prefab::UpdateInternal(const Array& defaultInstanceObjects, r LOG(Info, "Updating prefab data"); // Reload prefab data + if (IsVirtual()) + { + return Init(TypeName, StringAnsiView(tmpBuffer.GetString(), (int32)tmpBuffer.GetSize())); + } #if 1 // Set to 0 to use memory-only reload that does not modifies the source file - useful for testing and debugging prefabs apply #if COMPILE_WITH_ASSETS_IMPORTER Locker.Unlock(); diff --git a/Source/Engine/Level/Prefabs/Prefab.cpp b/Source/Engine/Level/Prefabs/Prefab.cpp index a54101ab0..24e9ee1fc 100644 --- a/Source/Engine/Level/Prefabs/Prefab.cpp +++ b/Source/Engine/Level/Prefabs/Prefab.cpp @@ -11,7 +11,7 @@ #include "Engine/Scripting/Scripting.h" #endif -REGISTER_JSON_ASSET(Prefab, "FlaxEngine.Prefab", false); +REGISTER_JSON_ASSET(Prefab, "FlaxEngine.Prefab", true); Prefab::Prefab(const SpawnParams& params, const AssetInfo* info) : JsonAssetBase(params, info)