Fixed Script data for entity copying

This commit is contained in:
Simon Lübeß
2024-01-09 13:41:54 +01:00
parent b93e081e0a
commit 99a12a118c
3 changed files with 71 additions and 29 deletions
+37 -4
View File
@@ -859,11 +859,17 @@ static class ScriptEngine
for (let (id, script) in _entityScriptInstances) for (let (id, script) in _entityScriptInstances)
{ {
SerializedObject object = new SerializedObject(allObjects, script.ScriptClass.FullName, script.EntityId); SerializeScriptInstance(script, allObjects);
object.Serialize(script);
} }
} }
/// Serializes the given script instance
public static void SerializeScriptInstance(ScriptInstance script, Dictionary<UUID, SerializedObject> allObjects)
{
SerializedObject object = new SerializedObject(allObjects, script.ScriptClass.FullName, script.EntityId);
object.Serialize(script);
}
/// Deserializes the given data into the script instances /// Deserializes the given data into the script instances
public static void DeserializeScriptInstances(Dictionary<UUID, SerializedObject> allObjects) public static void DeserializeScriptInstances(Dictionary<UUID, SerializedObject> allObjects)
{ {
@@ -871,9 +877,36 @@ static class ScriptEngine
for (let (id, script) in _entityScriptInstances) for (let (id, script) in _entityScriptInstances)
{ {
if (allObjects.TryGetValue(id, let object)) DeserializeScriptInstance(id, script, allObjects);
}
}
/// Deserializes the given script instance.
/// @returns true if the script had serialized data; false otherwise.
public static bool DeserializeScriptInstance(UUID id, ScriptInstance script, Dictionary<UUID, SerializedObject> allObjects)
{
if (allObjects.TryGetValue(id, let object))
{
object.Deserialize(script);
return true;
}
return false;
}
/// Replaces Entity references in the given serialized data using the specified translation table.
public static void FixupSerializedIds(Dictionary<UUID, UUID> originalToCopyIds, Dictionary<UUID, SerializedObject> allObjects)
{
for (let (id, object) in allObjects)
{
for (var fieldData in ref object.Fields.Values)
{ {
object.Deserialize(script); if (fieldData.PrimitiveType != .EntityReference && fieldData.PrimitiveType != .ComponentReference)
continue;
if (originalToCopyIds.TryGetValue(fieldData.Data.EngineObject.ID, let copyId))
fieldData.Data.EngineObject.ID = copyId;
} }
} }
} }
+25 -19
View File
@@ -8,6 +8,7 @@ using GlitchyEngine.Content;
using GlitchyEngine.Scripting; using GlitchyEngine.Scripting;
using GlitchyEngine.Math; using GlitchyEngine.Math;
using GlitchyEngine.Scripting.Classes; using GlitchyEngine.Scripting.Classes;
using GlitchyEngine.Serialization;
namespace GlitchyEngine.World namespace GlitchyEngine.World
{ {
@@ -773,10 +774,14 @@ namespace GlitchyEngine.World
*/ */
public Entity CreateInstance(Entity entity) public Entity CreateInstance(Entity entity)
{ {
Dictionary<UUID, SerializedObject> serializedData = scope .();
defer { ClearDictionaryAndDeleteValues!(serializedData); }
List<Entity> newEntities = scope .(); List<Entity> newEntities = scope .();
Dictionary<EcsEntity, EcsEntity> sourceToTargetEntity = scope .(); Dictionary<EcsEntity, EcsEntity> sourceToTargetEntity = scope .();
Dictionary<UUID, UUID> sourceIdToTargetId = scope .(); Dictionary<UUID, UUID> sourceIdToTargetId = scope .();
Dictionary<UUID, Entity> targetIdToSourceEntity = scope .(); Dictionary<UUID, Entity> targetIdToSourceEntity = scope .();
List<(UUID oldId, ScriptInstance scriptInstance)> newScripts = scope .();
Entity CopyEntityAndChildren(Entity original, Entity? copyParent) Entity CopyEntityAndChildren(Entity original, Entity? copyParent)
{ {
@@ -809,15 +814,16 @@ namespace GlitchyEngine.World
targetScript.ScriptClassName = sourceScript.ScriptClassName; targetScript.ScriptClassName = sourceScript.ScriptClassName;
// Initializes the created instance if (sourceScript.Instance != null)
// TODO: this returns false, if no script with ScriptClassName exists, we have to handle this case correctly I think. {
ScriptEngine.InitializeInstance(copy, targetScript); ScriptEngine.SerializeScriptInstance(sourceScript.Instance, serializedData);
// TODO: Copy Data from one instance to another // Initializes the created instance
//ScriptEngine.CopyFieldsToInstance(targetScript, sourceScript); // TODO: this returns false, if no script with ScriptClassName exists, we have to handle this case correctly I think.
ScriptEngine.InitializeInstance(copy, targetScript);
// TODO: Only if we are in Runtime newScripts.Add((original.UUID, targetScript.Instance));
targetScript.Instance.InvokeOnCreate(); }
} }
// This is kinda slow because it's in O(n*m) where n is the tree depth and m is the total number of entities in the scene... // This is kinda slow because it's in O(n*m) where n is the tree depth and m is the total number of entities in the scene...
@@ -831,21 +837,21 @@ namespace GlitchyEngine.World
Entity newEntity = CopyEntityAndChildren(entity, null); Entity newEntity = CopyEntityAndChildren(entity, null);
for (let copy in newEntities) // Replace old IDs with new ones
ScriptEngine.FixupSerializedIds(sourceIdToTargetId, serializedData);
// Use separate loops for deserialization and OnCreate to ensure complete entities and references in OnCreate
for (let (originalId, newScriptInstance) in newScripts)
{ {
if (copy.TryGetComponent<ScriptComponent>(let targetScript)) ScriptEngine.DeserializeScriptInstance(originalId, newScriptInstance, serializedData);
{
Entity originalEntity = targetIdToSourceEntity[copy.UUID];
if (!originalEntity.TryGetComponent<ScriptComponent>(let sourceScript))
continue;
ScriptEngine.CopyFieldsToInstance(targetScript, sourceScript, sourceIdToTargetId);
//targetScript.Instance.InvokeOnCreate();
}
} }
for (let (_, newScriptInstance) in newScripts)
{
if (ScriptEngine.ApplicationInfo.IsInPlayMode || newScriptInstance.ScriptClass.RunInEditMode)
newScriptInstance.InvokeOnCreate();
}
return newEntity; return newEntity;
} }
@@ -504,7 +504,7 @@ public class DeserializationObject
public unsafe object? DeserializeClass(string fieldName, Type fieldType) public unsafe object? DeserializeClass(string fieldName, Type fieldType)
{ {
if (fieldType.GetGenericTypeDefinition() == typeof(List<>)) if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(List<>))
{ {
return DeserializeList(fieldName, fieldType, fieldType.GetGenericArguments()[0]); return DeserializeList(fieldName, fieldType, fieldType.GetGenericArguments()[0]);
} }
@@ -514,7 +514,10 @@ public class DeserializationObject
if (isEntity || isComponent) if (isEntity || isComponent)
{ {
var data = (DataHelper.EngineObjectReferenceHelper)GetFieldValue(fieldName, isEntity ? SerializationType.EntityReference : SerializationType.ComponentReference); object? fieldData = GetFieldValue(fieldName, isEntity ? SerializationType.EntityReference : SerializationType.ComponentReference);
if (fieldData is not DataHelper.EngineObjectReferenceHelper data)
return NoValueDeserialized;
UUID id = data.Id; UUID id = data.Id;