using Mono; using System; using GlitchyEngine.Core; using System.Collections; using GlitchyEngine.Scripting.Classes; namespace GlitchyEngine.Scripting; using internal GlitchyEngine.Scripting; public struct ScriptField { public StringView Name; internal MonoClassField* _monoField; public bool IsStatic; public ScriptFieldType FieldType; public SharpType SharpType; public int SizeInBytes; internal this(StringView name, MonoClassField* monoField, bool isStatic, ScriptFieldType fieldType, int sizeInBytes, SharpType sharpType) { Name = name; _monoField = monoField; IsStatic = isStatic; FieldType = fieldType; SharpType = sharpType; SizeInBytes = sizeInBytes; } public bool IsType(SharpClass otherClass, bool checkIfSubtype) { return IsType(otherClass.GetMonoType(), checkIfSubtype); } public bool IsType(MonoType* otherType, bool checkIfSubtype) { MonoType* myType = GetMonoType(); if (checkIfSubtype) { MonoClass* myClass = Mono.mono_type_get_class(myType); MonoClass* otherClass = Mono.mono_type_get_class(otherType); return Mono.mono_class_is_subclass_of(myClass, otherClass, false); } else { return myType == otherType; } } public MonoType* GetMonoType() { return Mono.mono_field_get_type(_monoField); } } public struct ScriptFieldInstance { public ScriptFieldType Type; // TODO: I hate this, but we need to be able to store an entire matrix internal uint8[sizeof(GlitchyEngine.Math.Matrix)] _data; public this(ScriptFieldType type) { Type = type; _data = default; } public T GetData() { #unwarn return *(T*)&_data; } public void SetData(T value) mut { *(T*)&_data = value; } } public typealias ScriptFieldMap = Dictionary; abstract class SharpType : RefCounter { protected String _namespace ~ delete _; protected String _className ~ delete _; protected String _fullName ~ delete _; protected ScriptFieldType _scriptType; protected Dictionary _monoFields ~ delete _; public StringView Namespace => _namespace; public StringView ClassName => _className; public StringView FullName => _fullName; public ScriptFieldType ScriptType => _scriptType; public Dictionary Fields => _monoFields; public this(StringView classNamespace, StringView className, ScriptFieldType scriptType) { _namespace = new String(classNamespace); _className = new String(className); _fullName = new $"{_namespace}.{_className}"; _scriptType = scriptType; ScriptEngine.RegisterSharpType(this); } } class SharpClass : SharpType { protected internal MonoClass* _monoClass; public this(StringView classNamespace, StringView className, MonoImage* image, ScriptFieldType fieldType = .Class) : base(classNamespace, className, fieldType) { _monoClass = Mono.mono_class_from_name(image, _namespace, _className); Log.EngineLogger.AssertDebug(_monoClass != null); ExtractFields(); } internal MonoType* GetMonoType() { return Mono.mono_class_get_type(_monoClass); } internal bool IsType(MonoType* type) { return GetMonoType() == type; } internal bool IsSubclass(MonoClass* @class) { return Mono.mono_class_is_subclass_of(_monoClass, @class, false); } private void ExtractFields() { //_monoFields = new List();ยด _monoFields = new Dictionary(); void* iterator = null; MonoClassField* currentField = null; while ((currentField = Mono.mono_class_get_fields(_monoClass, &iterator)) != null) { StringView name = StringView(Mono.mono_field_get_name(currentField)); MonoType* type = Mono.mono_field_get_type(currentField); ScriptFieldType fieldType = ScriptEngineHelper.GetScriptFieldType(type); SharpType sharpType = null; // If field type is none the field might be a struct, class or enum if (fieldType == .None) { sharpType = ScriptEngine.GetSharpType(type); fieldType = sharpType?.ScriptType ?? .None; if (sharpType == null) continue; sharpType.ReleaseRef(); } //Mono.MonoTypeEnum fieldType = Mono.Mono.mono_type_get_type(type); FieldAttribute flags = (.)Mono.mono_field_get_flags(currentField); MonoCustomAttrInfo* attributes = Mono.mono_custom_attrs_from_field(_monoClass, currentField); if (flags.HasFlag(.Public) || (attributes != null && Mono.mono_custom_attrs_has_attr(attributes, ScriptEngine.Attributes.s_ShowInEditorAttribute))) { MonoClass* fieldClass = Mono.mono_type_get_class(type); int sizeInBytes = 8; if (fieldClass != null) { sizeInBytes = Mono.mono_class_instance_size(fieldClass); } else { } _monoFields[name] = .(name, currentField, flags.HasFlag(.Static), fieldType, sizeInBytes, sharpType); } } } } struct EnumValue { public StringView Name; public uint64 Value; public this(StringView name, uint64 value) { Name = name; Value = value; } } class SharpEnum : SharpClass { private append Dictionary _values = .(); public Dictionary Values => _values; private int _underlyingSize = 0; public this(StringView classNamespace, StringView className, MonoImage* image) : base(classNamespace, className, image, .Enum) { ExtractEnumValues(); } private void ExtractEnumValues() { _underlyingSize = Mono.mono_class_instance_size(_monoClass); Log.EngineLogger.Assert(_underlyingSize != 0); var vtable = Mono.mono_class_vtable(ScriptEngine.[Friend]s_AppDomain, _monoClass); void* iterator = null; MonoClassField* currentField = null; while ((currentField = Mono.mono_class_get_fields(_monoClass, &iterator)) != null) { MonoType* fieldType = Mono.mono_field_get_type(currentField); MonoClass* fieldClass = Mono.mono_type_get_class(fieldType); FieldAttribute fieldFlags = (.)Mono.mono_field_get_flags(currentField); if (fieldFlags.HasFlag(.Public) && fieldFlags.HasFlag(.Static) && fieldClass != null && Mono.mono_class_is_subclass_of(fieldClass, _monoClass, false)) { StringView fieldName = StringView(Mono.mono_field_get_name(currentField)); uint64 value = 0; Mono.mono_field_static_get_value(vtable, currentField, &value); EnumValue enumValue = .(fieldName, value); _values.Add(value, enumValue); } } } } class SharpStruct : SharpClass { protected int _size; public int Size => _size; public this(StringView classNamespace, StringView className, MonoImage* image) : base(classNamespace, className, image) { } } class ScriptClass : SharpClass { /*private String _namespace ~ delete _; private String _className ~ delete _; private String _fullName ~ delete _; private MonoClass* _monoClass; //private List _monoFields ~ delete _; private Dictionary _monoFields ~ delete _;*/ //typealias ConstructorMethod = function void(MonoObject* instance, UUID uuid, MonoException** exception); typealias OnCreateMethod = function MonoObject*(MonoObject* instance, MonoException** exception); typealias OnUpdateMethod = function MonoObject*(MonoObject* instance, float deltaTime, MonoException** exception); typealias OnDestroyMethod = function MonoObject*(MonoObject* instance, MonoException** exception); private MonoMethod* _constructor; //private ConstructorMethod _constructor; private OnCreateMethod _onCreate; private OnUpdateMethod _onUpdate; private OnDestroyMethod _onDestroy; private function [CallingConvention(.Cdecl)] MonoObject*(MonoObject* instance, Collision2D collision, MonoException** exception) _onCollisionEnter2D; private function [CallingConvention(.Cdecl)] MonoObject*(MonoObject* instance, Collision2D collision, MonoException** exception) _onCollisionLeave2D; //private OnCollisionEnter2DMethod _onCollisionEnter2D; //private OnDestroyMethod _onCollisionLeave2D; private MonoMethod* _onCollisionEnter2DMethod; private MonoMethod* _onCollisionLeave2DMethod; public bool HasCollisionEnter2D => _onCollisionEnter2DMethod != null; public bool HasCollisionLeave2D => _onCollisionLeave2DMethod != null; /*public StringView Namespace => _namespace; public StringView ClassName => _className; public StringView FullName => _fullName; public Dictionary Fields => _monoFields;*/ [AllowAppend] public this(StringView classNamespace, StringView className, MonoImage* image, ScriptFieldType scriptFieldType = .Class) : base(classNamespace, className, image, scriptFieldType) { //_constructor = (ConstructorMethod)GetMethodThunk(".ctor", 1); // GetMethod(".ctor", 1);// _constructor = GetMethod(".ctor", 1); _onCreate = (OnCreateMethod)GetMethodThunk("OnCreate"); _onUpdate = (OnUpdateMethod)GetMethodThunk("OnUpdate", 1); _onDestroy = (OnDestroyMethod)GetMethodThunk("OnDestroy"); _onCollisionEnter2D = (.)GetMethodThunk("OnCollisionEnter2D", 1); _onCollisionLeave2D = (.)GetMethodThunk("OnCollisionLeave2D", 1); _onCollisionEnter2DMethod = GetMethod("OnCollisionEnter2D", 1); _onCollisionLeave2DMethod = GetMethod("OnCollisionLeave2D", 1); } public void OnCreate(MonoObject* instance, out MonoException* exception) { exception = null; if (_onCreate != null) _onCreate(instance, &exception); } public void OnUpdate(MonoObject* instance, float deltaTime, out MonoException* exception) { exception = null; if (_onUpdate != null) _onUpdate(instance, deltaTime, &exception); } public void OnDestroy(MonoObject* instance, out MonoException* exception) { exception = null; if (_onDestroy != null) _onDestroy(instance, &exception); } public void OnCollisionEnter2D(MonoObject* instance, Collision2D collision, out MonoException* exception) { exception = null; // TODO: use method thunk if (_onCollisionEnter2DMethod != null) { #unwarn void*[1] args = .(&collision); Invoke(_onCollisionEnter2DMethod, instance, (.)&args); } // if (_onCollisionEnter2D != null) // _onCollisionEnter2D(instance, collision, &exception); } public void OnCollisionLeave2D(MonoObject* instance, Collision2D collision, out MonoException* exception) { exception = null; // TODO: use method thunk if (_onCollisionLeave2DMethod != null) { #unwarn void*[1] args = .(&collision); Invoke(_onCollisionLeave2DMethod, instance, (.)&args); } // if (_onCollisionEnter2D != null) // _onCollisionEnter2D(instance, collision, &exception); } public MonoObject* CreateInstance(UUID uuid, out MonoException* exception) { MonoObject* instance = Mono.mono_object_new(ScriptEngine.[Friend]s_AppDomain, _monoClass); // Invoke empty constructor to initialize fields with default values specified in the script itself Mono.mono_runtime_object_init(instance); // Invoke constructor with UUID #unwarn ScriptEngine.[Friend]s_EngineObject.Invoke(ScriptEngine.[Friend]s_EngineObject._constructor, instance, out exception, &uuid); return instance; } public MonoObject* CreateInstance() { MonoObject* instance = Mono.mono_object_new(ScriptEngine.[Friend]s_AppDomain, _monoClass); // Invoke empty constructor to fill fields Mono.mono_runtime_object_init(instance); return instance; } public MonoMethod* GetMethod(StringView name, int argCount = 0) { return Mono.mono_class_get_method_from_name(_monoClass, name.ToScopeCStr!(), argCount); } public void* GetMethodThunk(StringView name, int argCount = 0) { MonoMethod* method = GetMethod(name, argCount); if (method == null) return null; return Mono.mono_method_get_unmanaged_thunk(method); } public MonoObject* Invoke(MonoMethod* method, MonoObject* instance, void** args = null) { MonoObject* exception = null; MonoObject* result = Mono.mono_runtime_invoke(method, instance, args, &exception); if (exception != null) ScriptEngine.HandleMonoException((MonoException*)exception); return result; } public MonoObject* Invoke(MonoMethod* method, MonoObject* instance, out MonoException* exception, params void*[] args) { exception = null; MonoObject* result = Mono.mono_runtime_invoke(method, instance, args.Ptr, (.)&exception); return result; } public T Invoke(MonoMethod* method, MonoObject* instance, params void*[] args) { MonoObject* object = Invoke(method, instance, args.Ptr); return *(T*)Mono.mono_object_unbox(object); } public T GetFieldValue(MonoObject* instance, MonoClassField* field) { T value = default; Mono.mono_field_get_value(instance, field, &value); return value; } public void SetFieldValue(MonoObject* instance, MonoClassField* field, in T value) { Mono.mono_field_set_value(instance, field, &value); } public void SetFieldValue(MonoObject* instance, MonoClassField* field, in T value) where T : struct* { Mono.mono_field_set_value(instance, field, value); } }