From d81471419d28b45e03da612f202d7e39a271308c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Wed, 10 Apr 2024 14:42:07 +0200 Subject: [PATCH] Refactored script instance serialization and now the refactoring is finally finished... --- GlitchyEditor/src/EditorLayer.bf | 27 ++---- GlitchyEngine/src/Scripting/ScriptEngine.bf | 69 +------------ .../src/Scripting/ScriptInstanceSerializer.bf | 97 +++++++++++++++++++ GlitchyEngine/src/World/Scene.bf | 11 ++- GlitchyEngine/src/World/SceneSerializer.bf | 23 ++--- 5 files changed, 127 insertions(+), 100 deletions(-) create mode 100644 GlitchyEngine/src/Scripting/ScriptInstanceSerializer.bf diff --git a/GlitchyEditor/src/EditorLayer.bf b/GlitchyEditor/src/EditorLayer.bf index 0a6b4cf..72ad47e 100644 --- a/GlitchyEditor/src/EditorLayer.bf +++ b/GlitchyEditor/src/EditorLayer.bf @@ -811,14 +811,14 @@ namespace GlitchyEditor ImGui.FocusWindow(window); } - append Dictionary _prePlaySerializedData = .() ~ ClearDictionaryAndDeleteValues!(_prePlaySerializedData); + append ScriptInstanceSerializer _scriptSerializer = .(); /// Starts the play mode for the current scene private void OnScenePlay() { - Log.EngineLogger.AssertDebug(_prePlaySerializedData.Count == 0, "Somehow some entities are serialized."); + Log.EngineLogger.AssertDebug(_scriptSerializer.SerializedObjectCount == 0, "Somehow some entities are serialized."); - ScriptEngine.SerializeScriptInstances(_prePlaySerializedData); + _scriptSerializer.SerializeScriptInstances(); _editor.SceneViewportWindow.EditorMode = false; _sceneState = .Play; @@ -829,7 +829,7 @@ namespace GlitchyEditor SetActiveScene(runtimeScene, startRuntime: true, startSimulation: true, newPlayMode: .Play); - ScriptEngine.DeserializeScriptInstances(_prePlaySerializedData); + _scriptSerializer.DeserializeScriptInstances(); } _editor.CurrentScene = _activeScene; @@ -947,22 +947,15 @@ namespace GlitchyEditor SwitchToEditorWindow(); // Reconstruct state before play - ScriptEngine.DeserializeScriptInstances(_prePlaySerializedData); - - ClearPrePlaySerializedData(); - } - - /// Clears the serialized data cache. - private void ClearPrePlaySerializedData() - { - ClearDictionaryAndDeleteValues!(_prePlaySerializedData); + _scriptSerializer.DeserializeScriptInstances(); + _scriptSerializer.Clear(); } /// Stops the scene and cleans up the subsystems to allow loading another scene. private void CloseCurrentScene() { // Clear serialized data, so that we don't waste time deserializing it. - ClearPrePlaySerializedData(); + _scriptSerializer.Clear(); OnSceneStop(); } @@ -983,7 +976,7 @@ namespace GlitchyEditor } /// Sets the current editor scene - private void SetEditorScene(Scene scene, Dictionary serializedObjects = null) + private void SetEditorScene(Scene scene, ScriptInstanceSerializer serializedObjects = null) { _editorScene?.Stop(); @@ -998,7 +991,7 @@ namespace GlitchyEditor if (serializedObjects != null) { // Reconstruct state - ScriptEngine.DeserializeScriptInstances(serializedObjects); + serializedObjects.DeserializeScriptInstances(); } } @@ -1118,7 +1111,7 @@ namespace GlitchyEditor // Make sure we actually loaded something! if (result case .Ok) { - SetEditorScene(newScene, serializer.SerializedObjects); + SetEditorScene(newScene, serializer.ScriptSerializer); String relativePath = scope .(); Path.GetRelativePath(filename, _currentProject.WorkspacePath, relativePath); diff --git a/GlitchyEngine/src/Scripting/ScriptEngine.bf b/GlitchyEngine/src/Scripting/ScriptEngine.bf index f14f28a..89bc5e9 100644 --- a/GlitchyEngine/src/Scripting/ScriptEngine.bf +++ b/GlitchyEngine/src/Scripting/ScriptEngine.bf @@ -96,7 +96,7 @@ static class ScriptEngine private static Dictionary _entityScripts = new .() ~ DeleteDictionaryAndReleaseValues!(_); - private static Dictionary _entityScriptInstances = new .() ~ { + internal static Dictionary _entityScriptInstances = new .() ~ { for (var entry in _) { entry.value?.ReleaseRef(); @@ -485,9 +485,9 @@ static class ScriptEngine Log.EngineLogger.Info("Reloading script assemblies."); - Dictionary serializedData = scope .(); + ScriptInstanceSerializer contextSerializer = scope .(); - SerializeScriptInstances(serializedData); + contextSerializer.SerializeScriptInstances(); Mono.mono_domain_set(s_RootDomain, true); @@ -513,9 +513,7 @@ static class ScriptEngine } } - DeserializeScriptInstances(serializedData); - - ClearDictionaryAndDeleteValues!(serializedData); + contextSerializer.DeserializeScriptInstances(); Log.EngineLogger.Info("Script assemblies reloaded!"); } @@ -582,63 +580,4 @@ static class ScriptEngine Classes.EntityEditor.ShowEntityEditor(scriptComponent.Instance, entity.UUID); } - - /// Serializes all script instances - public static void SerializeScriptInstances(Dictionary allObjects) - { - Debug.Profiler.ProfileFunction!(); - - for (let (id, script) in _entityScriptInstances) - { - SerializeScriptInstance(script, allObjects); - } - } - - /// Serializes the given script instance - public static void SerializeScriptInstance(ScriptInstance script, Dictionary allObjects) - { - SerializedObject object = new SerializedObject(allObjects, script.ScriptClass.FullName, script.EntityId); - object.Serialize(script); - } - - /// Deserializes the given data into the script instances - public static void DeserializeScriptInstances(Dictionary allObjects) - { - Debug.Profiler.ProfileFunction!(); - - for (let (id, script) in _entityScriptInstances) - { - 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 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 originalToCopyIds, Dictionary allObjects) - { - for (let (id, object) in allObjects) - { - for (var fieldData in ref object.Fields.Values) - { - if (fieldData.PrimitiveType != .EntityReference && fieldData.PrimitiveType != .ComponentReference) - continue; - - if (originalToCopyIds.TryGetValue(fieldData.Data.EngineObject.ID, let copyId)) - fieldData.Data.EngineObject.ID = copyId; - } - } - } } diff --git a/GlitchyEngine/src/Scripting/ScriptInstanceSerializer.bf b/GlitchyEngine/src/Scripting/ScriptInstanceSerializer.bf new file mode 100644 index 0000000..b6a8dc3 --- /dev/null +++ b/GlitchyEngine/src/Scripting/ScriptInstanceSerializer.bf @@ -0,0 +1,97 @@ +using System.Collections; +using GlitchyEngine.Core; +using GlitchyEngine.Serialization; + +namespace GlitchyEngine.Scripting; + +using internal GlitchyEngine.Scripting; + +/// Provides capability to serialize and deserialize C# objects managed by the script engine +public class ScriptInstanceSerializer +{ + append Dictionary _serializedData = .(); + + public int SerializedObjectCount => _serializedData.Count; + + public Dictionary SerializedObjects => _serializedData; + + public ~this() + { + Clear(); + delete:append _serializedData; + } + + public void Clear() + { + ClearDictionaryAndDeleteValues!(_serializedData); + } + + /// Serializes all script instances that are currently managed by the ScriptEngine. + public void SerializeScriptInstances() + { + Debug.Profiler.ProfileFunction!(); + + for (let (id, script) in ScriptEngine._entityScriptInstances) + { + SerializeScriptInstance(script); + } + } + + /// Serializes the given script instance. + public void SerializeScriptInstance(ScriptInstance script) + { + SerializedObject object = new SerializedObject(_serializedData, script.ScriptClass.FullName, script.EntityId); + object.Serialize(script); + } + + /// Deserializes this context into the instances currently managed by the script engine. + public void DeserializeScriptInstances() + { + Debug.Profiler.ProfileFunction!(); + + for (let (id, script) in ScriptEngine._entityScriptInstances) + { + DeserializeScriptInstance(id, script); + } + } + + /// Deserializes the data into the given script instance, if there is data available. + /// @returns true if the script had serialized data; false otherwise. + public bool DeserializeScriptInstance(UUID id, ScriptInstance script) + { + if (_serializedData.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 void FixupSerializedIds(Dictionary originalToCopyIds) + { + for (let (id, object) in _serializedData) + { + for (var fieldData in ref object.Fields.Values) + { + if (fieldData.PrimitiveType != .EntityReference && fieldData.PrimitiveType != .ComponentReference) + continue; + + if (originalToCopyIds.TryGetValue(fieldData.Data.EngineObject.ID, let copyId)) + fieldData.Data.EngineObject.ID = copyId; + } + } + } + + public SerializedObject GetSerializedObject(UUID id) + { + return _serializedData[id]; + } + + public bool TryGetSerializedObject(UUID id, out SerializedObject object) + { + return _serializedData.TryGetValue(id, out object); + } +} diff --git a/GlitchyEngine/src/World/Scene.bf b/GlitchyEngine/src/World/Scene.bf index 20770df..3fe7d17 100644 --- a/GlitchyEngine/src/World/Scene.bf +++ b/GlitchyEngine/src/World/Scene.bf @@ -958,8 +958,9 @@ namespace GlitchyEngine.World */ public Entity CreateInstance(Entity entity) { - Dictionary serializedData = scope .(); - defer { ClearDictionaryAndDeleteValues!(serializedData); } + //Dictionary serializedData = scope .(); + ScriptInstanceSerializer scriptSerializer = scope .(); + //defer { ClearDictionaryAndDeleteValues!(serializedData); } List newEntities = scope .(); Dictionary sourceToTargetEntity = scope .(); @@ -1002,7 +1003,7 @@ namespace GlitchyEngine.World if (sourceScript.Instance != null) { - ScriptEngine.SerializeScriptInstance(sourceScript.Instance, serializedData); + scriptSerializer.SerializeScriptInstance(sourceScript.Instance); // Initializes the created instance // TODO: this returns false, if no script with ScriptClassName exists, we have to handle this case correctly I think. @@ -1024,13 +1025,13 @@ namespace GlitchyEngine.World Entity newEntity = CopyEntityAndChildren(entity, null); // Replace old IDs with new ones - ScriptEngine.FixupSerializedIds(sourceIdToTargetId, serializedData); + scriptSerializer.FixupSerializedIds(sourceIdToTargetId); // Use separate loops for deserialization and OnCreate to ensure complete entities and references in OnCreate for (let (originalId, newScriptInstance) in newScripts) { - ScriptEngine.DeserializeScriptInstance(originalId, newScriptInstance, serializedData); + scriptSerializer.DeserializeScriptInstance(originalId, newScriptInstance); } for (let (_, newScriptInstance) in newScripts) diff --git a/GlitchyEngine/src/World/SceneSerializer.bf b/GlitchyEngine/src/World/SceneSerializer.bf index 5722815..4e1b8c3 100644 --- a/GlitchyEngine/src/World/SceneSerializer.bf +++ b/GlitchyEngine/src/World/SceneSerializer.bf @@ -28,11 +28,11 @@ class SceneSerializer // Maps from ID in the prefab file to the actual ID in the scene. private Dictionary _fileToSceneId; - private Dictionary _serializedObjects ~ DeleteDictionaryAndValues!(_); + private append ScriptInstanceSerializer _scriptSerializer = .(); //Dictionary _serializedObjects ~ DeleteDictionaryAndValues!(_); private HashSet _objectsNotWritten; - public Dictionary SerializedObjects => _serializedObjects; + public ScriptInstanceSerializer ScriptSerializer => _scriptSerializer; public this(Scene scene) { @@ -43,13 +43,11 @@ class SceneSerializer { Debug.Profiler.ProfileResourceFunction!(); - _serializedObjects = new .(); + _scriptSerializer.SerializeScriptInstances(); - ScriptEngine.SerializeScriptInstances(_serializedObjects); + _objectsNotWritten = new .(_scriptSerializer.SerializedObjectCount); - _objectsNotWritten = new .(_serializedObjects.Count); - - for (UUID key in _serializedObjects.Keys) + for (UUID key in _scriptSerializer.SerializedObjects.Keys) { _objectsNotWritten.Add(key); } @@ -84,11 +82,12 @@ class SceneSerializer writer.Identifier("ReferencedObjects"); + // Write all objects that don't belong to an entity. (referenced Arrays, Classes, etc...) using (writer.ArrayBlock()) { for (UUID id in _objectsNotWritten) { - Serialize.Value(writer, _serializedObjects[id]); + Serialize.Value(writer, _scriptSerializer.GetSerializedObject(id)); } } @@ -237,7 +236,7 @@ class SceneSerializer { Serialize.Value(writer, "ScriptClass", component.ScriptClassName); - if (_serializedObjects.TryGetValue(entity.UUID, let value)) + if (_scriptSerializer.TryGetSerializedObject(entity.UUID, let value)) { Serialize.Value(writer, "Fields", value); @@ -285,8 +284,6 @@ class SceneSerializer { Debug.Profiler.ProfileResourceFunction!(); - _serializedObjects = new .(); - _parentIdToChild = scope Dictionary(); _entitiesMissingParent = scope List<(Entity Entity, UUID ParentId)>(); _fileToSceneId = scope Dictionary(); @@ -344,7 +341,7 @@ class SceneSerializer Try!(reader.EntryEnd()); } - Try!(SerializedObject.BonDeserialize(reader, _serializedObjects, gBonEnv)); + Try!(SerializedObject.BonDeserialize(reader, _scriptSerializer.SerializedObjects, gBonEnv)); first = false; } @@ -667,7 +664,7 @@ class SceneSerializer Try!(reader.EntryEnd()); if (Try!(reader.Identifier()) == "Fields") - SerializedObject.BonDeserialize(reader, _serializedObjects, gBonEnv); + SerializedObject.BonDeserialize(reader, _scriptSerializer.SerializedObjects, gBonEnv); } return .Ok;