Files
glitchy-engine-beef/GlitchyEngine/src/Scripting/ScriptClass.bf
T
Simon Lübeß 754c149493 Entity Creation and Destruction from scripts
- ScriptEngine: Allow destroying scripts during runtime
- Scripting:
    - Create new entities using Constructors
    - Added Destroy functions for Entities
    - Added SetScript/RemoveScript functions for Entities
    - Allow adding multiple components at once
    - Added Is-Method
- Mono Wrapper: Added Array-Functions
- WorldEnumerator: Fixed crash due to dangling list pointer after entity creation
- Scene:
    - Basic create Rigidbodies during runtime
    - Start of entity copying (For runtime use)
    - Added DestroyEntityDeferred
- SceneSerializer:
    - Added option to assign new IDs to entities
2023-09-15 23:22:27 +02:00

465 lines
13 KiB
Beef
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<T>()
{
#unwarn
return *(T*)&_data;
}
public void SetData<T>(T value) mut
{
*(T*)&_data = value;
}
}
public typealias ScriptFieldMap = Dictionary<String, ScriptFieldInstance>;
abstract class SharpType : RefCounter
{
protected String _namespace ~ delete _;
protected String _className ~ delete _;
protected String _fullName ~ delete _;
protected ScriptFieldType _scriptType;
protected Dictionary<StringView, ScriptField> _monoFields ~ delete _;
public StringView Namespace => _namespace;
public StringView ClassName => _className;
public StringView FullName => _fullName;
public ScriptFieldType ScriptType => _scriptType;
public Dictionary<StringView, ScriptField> 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<MonoClassField*>();´
_monoFields = new Dictionary<StringView, ScriptField>();
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<uint64, EnumValue> _values = .();
public Dictionary<uint64, EnumValue> 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<MonoClassField*> _monoFields ~ delete _;
private Dictionary<StringView, ScriptField> _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<StringView, ScriptField> 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<T>(MonoMethod* method, MonoObject* instance, params void*[] args)
{
MonoObject* object = Invoke(method, instance, args.Ptr);
return *(T*)Mono.mono_object_unbox(object);
}
public T GetFieldValue<T>(MonoObject* instance, MonoClassField* field)
{
T value = default;
Mono.mono_field_get_value(instance, field, &value);
return value;
}
public void SetFieldValue<T>(MonoObject* instance, MonoClassField* field, in T value)
{
Mono.mono_field_set_value(instance, field, &value);
}
public void SetFieldValue<T>(MonoObject* instance, MonoClassField* field, in T value) where T : struct*
{
Mono.mono_field_set_value(instance, field, value);
}
}