using Mono; using System; using GlitchLog; using System.Reflection; using GlitchyEngine.Events; using GlitchyEngine.Math; using GlitchyEngine.World; using GlitchyEngine.Core; using System.Collections; using Box2D; using GlitchyEngine.Scripting; using GlitchyEngine.Serialization; namespace GlitchyEngine.Scripting; using internal GlitchyEngine.Scripting; static class ScriptGlue { private static Dictionary s_AddComponentMethods = new .() ~ delete _; private static Dictionary s_HasComponentMethods = new .() ~ delete _; private static Dictionary s_RemoveComponentMethods = new .() ~ delete _; struct RegisterCallAttribute : Attribute { public String MethodName; public this(String methodName) { MethodName = methodName; } } /* Adding this attribute to a method will log method entry and returned Result errors */ [AttributeUsage(.Method)] struct RegisterMethodAttribute : Attribute, IOnMethodInit { [Comptime] public void OnMethodInit(MethodInfo method, Self* prev) { for (var methodInfo in typeof(ScriptGlue).GetMethods(.Static)) { if (methodInfo.GetCustomAttribute() case .Ok(let attribute)) { String functionType = scope $"{methodInfo.ReturnType}({methodInfo.GetParamsDecl(.. scope .())})"; String line = scope $"RegisterCall(\"{attribute.MethodName}\", => {methodInfo.Name});\n"; Compiler.EmitMethodEntry(method, line); } } } } public static void Init() { RegisterCalls(); RegisterMathFunctions(); } public static void RegisterManagedComponents() { Debug.Profiler.ProfileFunction!(); s_AddComponentMethods.Clear(); s_HasComponentMethods.Clear(); s_RemoveComponentMethods.Clear(); RegisterComponent("GlitchyEngine.Core.Transform"); RegisterComponent("GlitchyEngine.Physics.Rigidbody2D"); RegisterComponent("GlitchyEngine.Core.Camera"); } [RegisterMethod] private static void RegisterCalls() { // Generated at CompTime } private static void RegisterComponent(StringView cSharpClassName = "") where T : struct, new { String className; if (cSharpClassName.IsWhiteSpace) { className = scope:: $"GlitchyEngine."; typeof(T).GetName(className); } else { className = scope:: String(cSharpClassName); } className.EnsureNullTerminator(); MonoType* managedType = Mono.mono_reflection_type_from_name(className.CStr(), ScriptEngine.[Friend]s_CoreAssemblyImage); if (managedType != null) { s_AddComponentMethods[managedType] = (entity) => entity.AddComponent(); s_HasComponentMethods[managedType] = (entity) => entity.HasComponent(); s_RemoveComponentMethods[managedType] = (entity) => entity.RemoveComponent(); } else { Log.EngineLogger.AssertDebug(managedType != null, scope $"No C# component with name \"{className}\" found for Beef type \"{typeof(T)}\""); } } /// Gets the component of the specified type that is attached to the given entity. Or null, if the entity doesn't exist or doesn't have the specified component. static T* GetComponentSafe(UUID entityId) where T: struct, new { Result foundEntity = ScriptEngine.Context.GetEntityByID(entityId); if (foundEntity case .Ok(let entity)) { if (entity.TryGetComponent(let component)) { return component; } else { ThrowArgumentException(null, scope $"The entity has no component of type {typeof(T)} or it was deleted."); } } else { ThrowArgumentException(null, "The entity doesn't exist or was deleted."); } } /// Gets the component of the specified type that is attached to the given entity. Or null, if the entity doesn't exist or doesn't have the specified component. static bool TryGetComponentSafe(UUID entityId, out T* component) where T: struct, new { Result foundEntity = ScriptEngine.Context.GetEntityByID(entityId); if (foundEntity case .Ok(let entity)) { if (entity.TryGetComponent(out component)) { return true; } else { Log.ClientLogger.Error($"Entity {entityId} has no {nameof(T)}."); } } else { Log.ClientLogger.Error($"No entity with ID {entityId} found."); } component = null; return false; } #region Exception Helpers /// Throws an argument exception. [NoReturn] static void ThrowArgumentException(char8* argument, char8* message) { MonoException* exception = Mono.mono_get_exception_argument(argument, message); Mono.mono_raise_exception(exception); } #endregion #region Log [RegisterCall("ScriptGlue::Log_LogMessage")] static void Log_LogMessage(int32 logLevel, MonoString* message) { char8* utfMessage = Mono.mono_string_to_utf8(message); Log.ClientLogger.Log((LogLevel)logLevel, StringView(utfMessage)); Mono.mono_free(utfMessage); } [RegisterCall("ScriptGlue::Log_LogException")] static void Log_LogException(MonoException* exception, UUID entityId) { ScriptEngine.HandleMonoException(exception, entityId); } #endregion #region Input [RegisterCall("Input::IsKeyPressed")] static bool Input_IsKeyPressed(Key key) => Input.IsKeyPressed(key); [RegisterCall("Input::IsKeyReleased")] static bool Input_IsKeyReleased(Key key) => Input.IsKeyReleased(key); [RegisterCall("Input::IsKeyToggled")] static bool Input_IsKeyToggled(Key key) => Input.IsKeyToggled(key); [RegisterCall("Input::IsKeyPressing")] static bool Input_IsKeyPressing(Key key) => Input.IsKeyPressing(key); [RegisterCall("Input::IsKeyReleasing")] static bool Input_IsKeyReleasing(Key key) => Input.IsKeyReleasing(key); [RegisterCall("Input::IsMouseButtonPressed")] static bool Input_IsMouseButtonPressed(MouseButton mouseButton) => Input.IsMouseButtonPressed(mouseButton); [RegisterCall("Input::IsMouseButtonReleased")] static bool Input_IsMouseButtonReleased(MouseButton mouseButton) => Input.IsMouseButtonReleased(mouseButton); [RegisterCall("Input::IsMouseButtonPressing")] static bool Input_IsMouseButtonPressing(MouseButton mouseButton) => Input.IsMouseButtonPressing(mouseButton); [RegisterCall("Input::IsMouseButtonReleasing")] static bool Input_IsMouseButtonReleasing(MouseButton mouseButton) => Input.IsMouseButtonReleasing(mouseButton); #endregion Input #region Scene/Entity stuff [RegisterCall("ScriptGlue::Entity_Create")] static void Entity_Create(MonoObject* scriptInstance, MonoString* monoEntityName, MonoArray* componentTypes, out UUID entityId) { char8* entityName = Mono.mono_string_to_utf8(monoEntityName); Entity entity = ScriptEngine.Context.CreateEntity(StringView(entityName)); Mono.mono_free(entityName); entityId = entity.UUID; // TODO: Script instance if (componentTypes != null) { Entity_AddComponents(entityId, componentTypes); } } [RegisterCall("ScriptGlue::Entity_Destroy")] static void Entity_Destroy(UUID entityId) { Entity entity = ScriptEngine.Context.GetEntityByID(entityId); ScriptEngine.Context.DestroyEntityDeferred(entity); } [RegisterCall("ScriptGlue::Entity_CreateInstance")] static void Entity_CreateInstance(UUID entityId, out UUID newEntityId) { Entity entity = ScriptEngine.Context.GetEntityByID(entityId); Entity newEntity = ScriptEngine.Context.CreateInstance(entity); newEntityId = newEntity.UUID; } [RegisterCall("ScriptGlue::Entity_AddComponents")] static void Entity_AddComponents(UUID entityId, MonoArray* componentTypes) { if (componentTypes == null) return; uint length = Mono.mono_array_length(componentTypes); for (uint i < length) { MonoReflectionType* reflectionType = Mono.mono_array_get(componentTypes, i); if (reflectionType != null) Entity_AddComponent(entityId, reflectionType); } } [RegisterCall("ScriptGlue::Entity_AddComponent")] static void Entity_AddComponent(UUID entityId, MonoReflectionType* componentType) { MonoType* type = Mono.mono_reflection_type_get_type(componentType); Entity entity = ScriptEngine.Context.GetEntityByID(entityId); if (s_AddComponentMethods.TryGetValue(type, let addMethod)) addMethod(entity); else Log.EngineLogger.AssertDebug(false, "No managed component with the given type registered."); } [RegisterCall("ScriptGlue::Entity_HasComponent")] static bool Entity_HasComponent(UUID entityId, MonoReflectionType* componentType) { MonoType* type = Mono.mono_reflection_type_get_type(componentType); Result foundEntity = ScriptEngine.Context.GetEntityByID(entityId); if (foundEntity case .Ok(let entity)) { if (s_HasComponentMethods.TryGetValue(type, let hasMethod)) return hasMethod(entity); } else { Log.ClientLogger.Warning($"No entity found with the given id \"{entityId}\"."); return false; } Log.EngineLogger.AssertDebug(false, "No managed component with the given type registered."); return false; } [RegisterCall("ScriptGlue::Entity_RemoveComponent")] static void Entity_RemoveComponent(UUID entityId, MonoReflectionType* componentType) { MonoType* type = Mono.mono_reflection_type_get_type(componentType); Entity entity = ScriptEngine.Context.GetEntityByID(entityId); if (s_RemoveComponentMethods.TryGetValue(type, let removeMethod)) removeMethod(entity); else Log.EngineLogger.AssertDebug(false, "No managed component with the given type registered."); } [RegisterCall("ScriptGlue::Entity_FindEntityWithName")] static void Entity_FindEntityWithName(MonoString* monoName, UUID* outUuid) { *outUuid = UUID(0); char8* entityName = Mono.mono_string_to_utf8(monoName); StringView nameString = StringView(entityName); Result entityResult = ScriptEngine.Context.GetEntityByName(nameString); Mono.mono_free(entityName); if (entityResult case .Ok(let entity)) *outUuid = entity.UUID; } [RegisterCall("ScriptGlue::Entity_GetScriptInstance")] static void Entity_GetScriptInstance(UUID entityId, out MonoObject* instance) { instance = ScriptEngine.GetManagedInstance(entityId); } [RegisterCall("ScriptGlue::Entity_SetScript")] static MonoObject* Entity_SetScript(UUID entityId, MonoReflectionType* scriptType) { Entity entity = ScriptEngine.Context.GetEntityByID(entityId); if (!entity.HasComponent()) { entity.AddComponent(); } if (entity.TryGetComponent(let scriptComponent)) { scriptComponent.Instance = null; MonoType* type = Mono.mono_reflection_type_get_type(scriptType); scriptComponent.ScriptClassName = StringView(Mono.mono_type_full_name(type)); // Initializes the created instance // TODO: this returns false, if no script with ScriptClassName exists, we have to handle this case correctly I think. ScriptEngine.InitializeInstance(entity, scriptComponent); return scriptComponent.Instance.MonoInstance; } Log.EngineLogger.AssertDebug(false, "Failed to set script."); return null; } [RegisterCall("ScriptGlue::Entity_RemoveScript")] static void Entity_RemoveScript(UUID entityId) { Entity entity = ScriptEngine.Context.GetEntityByID(entityId); if (entity.TryGetComponent(let scriptComponent)) { ScriptEngine.DestroyInstance(entity, scriptComponent); entity.RemoveComponent(); } } [RegisterCall("ScriptGlue::Entity_GetName")] static MonoString* Entity_GetName(UUID entityId) { Result foundEntity = ScriptEngine.Context.GetEntityByID(entityId); if (foundEntity case .Ok(let entity)) { MonoString* name = Mono.mono_string_new_len(ScriptEngine.[Friend]s_AppDomain, entity.Name.Ptr, (.)entity.Name.Length); return name; } return null; } [RegisterCall("ScriptGlue::Entity_SetName")] static void Entity_SetName(UUID entityId, MonoString* name) { Entity entity = ScriptEngine.Context.GetEntityByID(entityId); char8* rawName = Mono.mono_string_to_utf8(name); entity.Name = StringView(rawName); Mono.mono_free(rawName); } #endregion #region TransformComponent [RegisterCall("ScriptGlue::Transform_GetTranslation")] static void Transform_GetTranslation(UUID entityId, out float3 translation) { Scene scene = ScriptEngine.Context; Entity entity = scene.GetEntityByID(entityId); translation = entity.Transform.Position; } [RegisterCall("ScriptGlue::Transform_SetTranslation")] static void Transform_SetTranslation(UUID entityId, in float3 translation) { Scene scene = ScriptEngine.Context; Entity entity = scene.GetEntityByID(entityId); entity.Transform.Position = translation; // if necessary reposition Rigidbody2D if (entity.TryGetComponent(let rigidbody2D)) { rigidbody2D.SetPosition(translation.XY); } } #endregion TransformComponent #region Rigidbody2D [RegisterCall("ScriptGlue::Rigidbody2D_ApplyForce")] static void Rigidbody2D_ApplyForce(UUID entityId, in float2 force, in float2 point, bool wakeUp) { Scene scene = ScriptEngine.Context; Entity entity = scene.GetEntityByID(entityId); var rigidBody = entity.GetComponent(); Box2D.Body.ApplyForce(rigidBody.[Friend]RuntimeBody, force, point, wakeUp); } [RegisterCall("ScriptGlue::Rigidbody2D_ApplyForceToCenter")] static void Rigidbody2D_ApplyForceToCenter(UUID entityId, in float2 force, bool wakeUp) { Scene scene = ScriptEngine.Context; Entity entity = scene.GetEntityByID(entityId); var rigidBody = entity.GetComponent(); Box2D.Body.ApplyForceToCenter(rigidBody.[Friend]RuntimeBody, force, wakeUp); } [RegisterCall("ScriptGlue::Rigidbody2D_SetPosition")] static void Rigidbody2D_SetPosition(UUID entityId, in float2 position) { Scene scene = ScriptEngine.Context; Entity entity = scene.GetEntityByID(entityId); var rigidBody = entity.GetComponent(); rigidBody.SetPosition(position); } [RegisterCall("ScriptGlue::Rigidbody2D_GetPosition")] static void Rigidbody2D_GetPosition(UUID entityId, out float2 position) { Scene scene = ScriptEngine.Context; Entity entity = scene.GetEntityByID(entityId); var rigidBody = entity.GetComponent(); position = rigidBody.GetPosition(); } [RegisterCall("ScriptGlue::Rigidbody2D_SetRotation")] static void Rigidbody2D_SetRotation(UUID entityId, in float rotation) { Scene scene = ScriptEngine.Context; Entity entity = scene.GetEntityByID(entityId); var rigidBody = entity.GetComponent(); rigidBody.SetAngle(rotation); } [RegisterCall("ScriptGlue::Rigidbody2D_GetRotation")] static void Rigidbody2D_GetRotation(UUID entityId, out float rotation) { Scene scene = ScriptEngine.Context; Entity entity = scene.GetEntityByID(entityId); var rigidBody = entity.GetComponent(); rotation = rigidBody.GetAngle(); } [RegisterCall("ScriptGlue::Rigidbody2D_GetLinearVelocity")] static void Rigidbody2D_GetLinearVelocity(UUID entityId, out float2 velocity) { Scene scene = ScriptEngine.Context; Entity entity = scene.GetEntityByID(entityId); var rigidBody = entity.GetComponent(); velocity = rigidBody.GetLinearVelocity(); } [RegisterCall("ScriptGlue::Rigidbody2D_SetLinearVelocity")] static void Rigidbody2D_SetLinearVelocity(UUID entityId, in float2 velocity) { Scene scene = ScriptEngine.Context; Entity entity = scene.GetEntityByID(entityId); var rigidBody = entity.GetComponent(); rigidBody.SetLinearVelocity(velocity); } [RegisterCall("ScriptGlue::Rigidbody2D_GetAngularVelocity")] static void Rigidbody2D_GetAngularVelocity(UUID entityId, out float velocity) { Scene scene = ScriptEngine.Context; Entity entity = scene.GetEntityByID(entityId); var rigidBody = entity.GetComponent(); velocity = rigidBody.GetAngularVelocity(); } [RegisterCall("ScriptGlue::Rigidbody2D_SetAngularVelocity")] static void Rigidbody2D_SetAngularVelocity(UUID entityId, in float velocity) { Scene scene = ScriptEngine.Context; Entity entity = scene.GetEntityByID(entityId); var rigidBody = entity.GetComponent(); rigidBody.SetAngularVelocity(velocity); } #endregion Rigidbody2D #region Camera [RegisterCall("ScriptGlue::Camera_GetProjectionType")] static void Camera_GetProjectionType(UUID entityId, out SceneCamera.ProjectionType projectionType) { CameraComponent* camera = GetComponentSafe(entityId); projectionType = camera.Camera.ProjectionType; } [RegisterCall("ScriptGlue::Camera_SetProjectionType")] static void Camera_SetProjectionType(UUID entityId, in SceneCamera.ProjectionType projectionType) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.ProjectionType = projectionType; } // Native Implementierungen in Beef für die Kamera-Properties [RegisterCall("ScriptGlue::Camera_GetPerspectiveFovY")] static void Camera_GetPerspectiveFovY(UUID entityId, out float fovY) { CameraComponent* camera = GetComponentSafe(entityId); fovY = camera.Camera.PerspectiveFovY; } [RegisterCall("ScriptGlue::Camera_SetPerspectiveFovY")] static void Camera_SetPerspectiveFovY(UUID entityId, float fovY) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.PerspectiveFovY = fovY; } [RegisterCall("ScriptGlue::Camera_GetPerspectiveNearPlane")] static void Camera_GetPerspectiveNearPlane(UUID entityId, out float nearPlane) { CameraComponent* camera = GetComponentSafe(entityId); nearPlane = camera.Camera.PerspectiveNearPlane; } [RegisterCall("ScriptGlue::Camera_SetPerspectiveNearPlane")] static void Camera_SetPerspectiveNearPlane(UUID entityId, float nearPlane) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.PerspectiveNearPlane = nearPlane; } [RegisterCall("ScriptGlue::Camera_GetPerspectiveFarPlane")] static void Camera_GetPerspectiveFarPlane(UUID entityId, out float farPlane) { CameraComponent* camera = GetComponentSafe(entityId); farPlane = camera.Camera.PerspectiveFarPlane; } [RegisterCall("ScriptGlue::Camera_SetPerspectiveFarPlane")] static void Camera_SetPerspectiveFarPlane(UUID entityId, float farPlane) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.PerspectiveFarPlane = farPlane; } [RegisterCall("ScriptGlue::Camera_GetOrthographicHeight")] static void Camera_GetOrthographicHeight(UUID entityId, out float height) { CameraComponent* camera = GetComponentSafe(entityId); height = camera.Camera.OrthographicHeight; } [RegisterCall("ScriptGlue::Camera_SetOrthographicHeight")] static void Camera_SetOrthographicHeight(UUID entityId, float height) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.OrthographicHeight = height; } [RegisterCall("ScriptGlue::Camera_SetOrthographicNearPlane")] static void Camera_SetOrthographicNearPlane(UUID entityId, float nearPlane) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.OrthographicNearPlane = nearPlane; } [RegisterCall("ScriptGlue::Camera_GetOrthographicNearPlane")] static void Camera_GetOrthographicNearPlane(UUID entityId, out float nearPlane) { CameraComponent* camera = GetComponentSafe(entityId); nearPlane = camera.Camera.OrthographicNearPlane; } [RegisterCall("ScriptGlue::Camera_SetOrthographicFarPlane")] static void Camera_SetOrthographicFarPlane(UUID entityId, float farPlane) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.OrthographicFarPlane = farPlane; } [RegisterCall("ScriptGlue::Camera_GetOrthographicFarPlane")] static void Camera_GetOrthographicFarPlane(UUID entityId, out float farPlane) { CameraComponent* camera = GetComponentSafe(entityId); farPlane = camera.Camera.OrthographicFarPlane; } [RegisterCall("ScriptGlue::Camera_SetAspectRatio")] static void Camera_SetAspectRatio(UUID entityId, float aspectRatio) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.AspectRatio = aspectRatio; } [RegisterCall("ScriptGlue::Camera_GetAspectRatio")] static void Camera_GetAspectRatio(UUID entityId, out float aspectRatio) { CameraComponent* camera = GetComponentSafe(entityId); aspectRatio = camera.Camera.AspectRatio; } [RegisterCall("ScriptGlue::Camera_SetFixedAspectRatio")] static void Camera_SetFixedAspectRatio(UUID entityId, bool fixedAspectRatio) { CameraComponent* camera = GetComponentSafe(entityId); camera.Camera.FixedAspectRatio = fixedAspectRatio; } [RegisterCall("ScriptGlue::Camera_GetFixedAspectRatio")] static void Camera_GetFixedAspectRatio(UUID entityId, out bool fixedAspectRatio) { CameraComponent* camera = GetComponentSafe(entityId); fixedAspectRatio = camera.Camera.FixedAspectRatio; } #endregion #region Physics2D // TODO: We need a wrapper class! [RegisterCall("ScriptGlue::Physics2D_GetGravity")] static void Physics2D_GetGravity(out float2 gravity) { Scene scene = ScriptEngine.Context; var box2DGravity = Box2D.World.GetGravity(scene.[Friend]_physicsWorld2D); gravity = *(float2*)&box2DGravity; } [RegisterCall("ScriptGlue::Physics2D_SetGravity")] static void Physics2D_SetGravity(in float2 gravity) { Scene scene = ScriptEngine.Context; #unwarn Box2D.World.SetGravity(scene.[Friend]_physicsWorld2D, gravity); } #endregion Physics2D #region Math private static void RegisterMathFunctions() { RegisterCall("ScriptGlue::modf_float", => GlitchyEngine.Math.modf); RegisterCall("ScriptGlue::modf_float2", => GlitchyEngine.Math.modf); RegisterCall("ScriptGlue::modf_float3", => GlitchyEngine.Math.modf); RegisterCall("ScriptGlue::modf_float4", => GlitchyEngine.Math.modf); RegisterHalfFunctions(); } private static void RegisterHalfFunctions() { RegisterCall("Math.Half::FromFloat32", (value, halfValue) => halfValue = GlitchyEngine.Math.half.FromFloat32(value)); RegisterCall("Math.Half::ToFloat32", (value, floatValue) => floatValue = GlitchyEngine.Math.half.ToFloat32(value)); RegisterCall("Math.Half::LessThan_Impl", (left, right, result) => result = left < right); RegisterCall("Math.Half::LessThanOrEqual_Impl", (left, right, result) => result = left <= right); RegisterCall("Math.Half::GreaterThan_Impl", (left, right, result) => result = left > right); RegisterCall("Math.Half::GreaterThanOrEqual_Impl", (left, right, result) => result = left >= right); RegisterCall("Math.Half::Add_Impl", (left, right, result) => result = left + right); RegisterCall("Math.Half::Subtract_Impl", (left, right, result) => result = left - right); RegisterCall("Math.Half::Multiply_Impl", (left, right, result) => result = left * right); RegisterCall("Math.Half::Divide_Impl", (left, right, result) => result = left / right); RegisterCall("Math.Half::Modulo_Impl", (left, right, result) => result = left % right); RegisterCall("Math.Half::Negate_Impl", (value, result) => result = -value); RegisterCall("Math.Half::Increment_Impl", (value, result) => result = ++value); RegisterCall("Math.Half::Decrement_Impl", (value, result) => result = --value); RegisterCall("Math.Half::IsNegative_Impl", (value) => value.IsNegative); RegisterCall("Math.Half::IsFinite_Impl", (value) => value.IsFinite); RegisterCall("Math.Half::IsInfinity_Impl", (value) => value.IsInfinity); RegisterCall("Math.Half::IsNan_Impl", (value) => value.IsNaN); RegisterCall("Math.Half::IsSubnormal_Impl", (value) => value.IsSubnormal); } #endregion [RegisterCall("ScriptGlue::UUID_CreateNew")] static void UUID_Create(out UUID id) { id = UUID.Create(); } #region Application [RegisterCall("ScriptGlue::Application_IsEditor")] static bool Application_IsEditor() { return ScriptEngine.ApplicationInfo.IsEditor; } [RegisterCall("ScriptGlue::Application_IsPlayer")] static bool Application_IsPlayer() { return ScriptEngine.ApplicationInfo.IsPlayer; } [RegisterCall("ScriptGlue::Application_IsInEditMode")] static bool Application_IsInEditMode() { return ScriptEngine.ApplicationInfo.IsInEditMode; } [RegisterCall("ScriptGlue::Application_IsInPlayMode")] static bool Application_IsInPlayMode() { return ScriptEngine.ApplicationInfo.IsInPlayMode; } #endregion #region Serialization [RegisterCall("ScriptGlue::Serialization_SerializeField")] static void Serialization_SerializeField(void* serializationContext, SerializationType type, MonoString* nameObject, MonoObject* valueObject, MonoString* fullTypeName) { SerializedObject context = Internal.UnsafeCastToObject(serializationContext) as SerializedObject; Log.EngineLogger.AssertDebug(context != null); char8* name = Mono.mono_string_to_utf8(nameObject); context.AddField(StringView(name), type, valueObject, fullTypeName); Mono.mono_free(name); } [RegisterCall("ScriptGlue::Serialization_CreateObject")] static void Serialization_CreateObject(void* currentContext, MonoString* typeName, out void* newContext, out UUID newId) { SerializedObject context = Internal.UnsafeCastToObject(currentContext) as SerializedObject; Log.EngineLogger.AssertDebug(context != null); char8* rawTypeName = Mono.mono_string_to_utf8(typeName); SerializedObject newObject = new SerializedObject(context.AllObjects, StringView(rawTypeName)); Mono.mono_free(rawTypeName); newContext = Internal.UnsafeCastToPtr(newObject); newId = newObject.Id; } [RegisterCall("ScriptGlue::Serialization_DeserializeField")] public static void Serialization_DeserializeField(void* internalContext, SerializationType expectedType, MonoString* fieldName, uint8* target, out SerializationType actualType) { SerializedObject context = Internal.UnsafeCastToObject(internalContext) as SerializedObject; Log.EngineLogger.AssertDebug(context != null); char8* name = Mono.mono_string_to_utf8(fieldName); context.GetField(StringView(name), expectedType, target, out actualType); Mono.mono_free(name); } [RegisterCall("ScriptGlue::Serialization_GetObject")] public static void Serialization_GetObject(void* internalContext, UUID id, out void* objectContext) { SerializedObject context = Internal.UnsafeCastToObject(internalContext) as SerializedObject; Log.EngineLogger.AssertDebug(context != null); objectContext = null; Log.EngineLogger.AssertDebug(context.AllObjects.ContainsKey(context.Id)); if (!context.AllObjects.TryGetValue(id, let foundObject)) return; objectContext = Internal.UnsafeCastToPtr(foundObject); } [RegisterCall("ScriptGlue::Serialization_GetObjectTypeName")] public static void Serialization_GetObjectTypeName(void* internalContext, out MonoString* fullTypeName) { SerializedObject context = Internal.UnsafeCastToObject(internalContext) as SerializedObject; Log.EngineLogger.AssertDebug(context != null); fullTypeName = Mono.mono_string_new(ScriptEngine.[Friend]s_AppDomain, context.TypeName); } #endregion private static void RegisterCall(String name, T method) where T : var { Mono.mono_add_internal_call(scope $"GlitchyEngine.{name}", (void*)method); } }