#include "Scripting/Mono/MonoScriptRuntime.h" #include "Components/CameraComponent.h" #include "Components/GameObject.h" #include "Components/LightComponent.h" #include "Components/MeshFilterComponent.h" #include "Components/MeshRendererComponent.h" #include "Components/TransformComponent.h" #include "Debug/Logger.h" #include "Input/InputManager.h" #include "Scene/Scene.h" #include "Scripting/ScriptComponent.h" #include "Scripting/ScriptEngine.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace XCEngine { namespace Scripting { namespace { struct MonoRootState { MonoDomain* rootDomain = nullptr; bool initialized = false; }; enum class ManagedComponentKind { Unknown, Script, Transform, Camera, Light, MeshFilter, MeshRenderer, }; struct ManagedComponentTypeInfo { ManagedComponentKind kind = ManagedComponentKind::Unknown; MonoClass* monoClass = nullptr; std::string assemblyName; std::string namespaceName; std::string className; }; MonoRootState& GetMonoRootState() { static MonoRootState state; return state; } Components::Scene*& GetInternalCallScene() { static Components::Scene* scene = nullptr; return scene; } float& GetInternalCallDeltaTime() { static float deltaTime = 0.0f; return deltaTime; } bool& GetInternalCallRegistrationState() { static bool registered = false; return registered; } std::string BuildFullClassName(const std::string& namespaceName, const std::string& className) { return namespaceName.empty() ? className : namespaceName + "." + className; } std::string BuildClassKey( const std::string& assemblyName, const std::string& namespaceName, const std::string& className) { return assemblyName + "|" + BuildFullClassName(namespaceName, className); } std::string SafeString(const char* value) { return value ? std::string(value) : std::string(); } bool IsMonoClassOrSubclass(MonoClass* monoClass, MonoClass* potentialBaseClass) { if (!monoClass || !potentialBaseClass) { return false; } return monoClass == potentialBaseClass || mono_class_is_subclass_of(monoClass, potentialBaseClass, 0) != 0; } std::string MonoStringToUtf8(MonoString* stringObject) { if (!stringObject) { return std::string(); } char* utf8 = mono_string_to_utf8(stringObject); std::string result = utf8 ? std::string(utf8) : std::string(); if (utf8) { mono_free(utf8); } return result; } MonoScriptRuntime* GetActiveMonoScriptRuntime() { return dynamic_cast(ScriptEngine::Get().GetRuntime()); } ManagedComponentTypeInfo ResolveManagedComponentTypeInfo(MonoClass* monoClass) { ManagedComponentTypeInfo typeInfo; if (!monoClass) { return typeInfo; } typeInfo.monoClass = monoClass; typeInfo.namespaceName = SafeString(mono_class_get_namespace(monoClass)); typeInfo.className = SafeString(mono_class_get_name(monoClass)); if (typeInfo.namespaceName == "XCEngine" && typeInfo.className == "Transform") { typeInfo.kind = ManagedComponentKind::Transform; return typeInfo; } if (typeInfo.namespaceName == "XCEngine" && typeInfo.className == "Camera") { typeInfo.kind = ManagedComponentKind::Camera; return typeInfo; } if (typeInfo.namespaceName == "XCEngine" && typeInfo.className == "Light") { typeInfo.kind = ManagedComponentKind::Light; return typeInfo; } if (typeInfo.namespaceName == "XCEngine" && typeInfo.className == "MeshFilter") { typeInfo.kind = ManagedComponentKind::MeshFilter; return typeInfo; } if (typeInfo.namespaceName == "XCEngine" && typeInfo.className == "MeshRenderer") { typeInfo.kind = ManagedComponentKind::MeshRenderer; return typeInfo; } MonoScriptRuntime* runtime = GetActiveMonoScriptRuntime(); if (runtime && runtime->IsClassAvailable( runtime->GetSettings().appAssemblyName, typeInfo.namespaceName, typeInfo.className)) { typeInfo.kind = ManagedComponentKind::Script; typeInfo.assemblyName = runtime->GetSettings().appAssemblyName; } return typeInfo; } ManagedComponentTypeInfo ResolveManagedComponentTypeInfo(MonoReflectionType* reflectionType) { if (!reflectionType) { return {}; } MonoType* monoType = mono_reflection_type_get_type(reflectionType); if (!monoType) { return {}; } return ResolveManagedComponentTypeInfo(mono_class_from_mono_type(monoType)); } Components::GameObject* FindGameObjectByUUIDRecursive(Components::GameObject* gameObject, uint64_t uuid) { if (!gameObject) { return nullptr; } if (gameObject->GetUUID() == uuid) { return gameObject; } for (Components::GameObject* child : gameObject->GetChildren()) { if (Components::GameObject* found = FindGameObjectByUUIDRecursive(child, uuid)) { return found; } } return nullptr; } ScriptComponent* FindScriptComponentByUUIDRecursive(Components::GameObject* gameObject, uint64_t scriptComponentUUID) { if (!gameObject || scriptComponentUUID == 0) { return nullptr; } for (ScriptComponent* component : gameObject->GetComponents()) { if (component && component->GetScriptComponentUUID() == scriptComponentUUID) { return component; } } for (Components::GameObject* child : gameObject->GetChildren()) { if (ScriptComponent* found = FindScriptComponentByUUIDRecursive(child, scriptComponentUUID)) { return found; } } return nullptr; } Components::GameObject* FindGameObjectByUUID(uint64_t uuid) { Components::Scene* scene = GetInternalCallScene(); if (!scene || uuid == 0) { return nullptr; } for (Components::GameObject* root : scene->GetRootGameObjects()) { if (Components::GameObject* found = FindGameObjectByUUIDRecursive(root, uuid)) { return found; } } return nullptr; } ScriptComponent* FindScriptComponentByUUID(uint64_t scriptComponentUUID) { Components::Scene* scene = GetInternalCallScene(); if (!scene || scriptComponentUUID == 0) { return nullptr; } for (Components::GameObject* root : scene->GetRootGameObjects()) { if (ScriptComponent* found = FindScriptComponentByUUIDRecursive(root, scriptComponentUUID)) { return found; } } return nullptr; } Components::Component* FindNativeComponent(Components::GameObject* gameObject, ManagedComponentKind componentKind) { if (!gameObject) { return nullptr; } switch (componentKind) { case ManagedComponentKind::Transform: return gameObject->GetTransform(); case ManagedComponentKind::Camera: return gameObject->GetComponent(); case ManagedComponentKind::Light: return gameObject->GetComponent(); case ManagedComponentKind::MeshFilter: return gameObject->GetComponent(); case ManagedComponentKind::MeshRenderer: return gameObject->GetComponent(); case ManagedComponentKind::Script: case ManagedComponentKind::Unknown: return nullptr; } return nullptr; } bool HasNativeComponent(Components::GameObject* gameObject, ManagedComponentKind componentKind) { return FindNativeComponent(gameObject, componentKind) != nullptr; } bool IsMatchingScriptComponent(const ScriptComponent* component, const ManagedComponentTypeInfo& typeInfo) { return component && component->HasScriptClass() && typeInfo.kind == ManagedComponentKind::Script && component->GetAssemblyName() == typeInfo.assemblyName && component->GetNamespaceName() == typeInfo.namespaceName && component->GetClassName() == typeInfo.className; } Components::Component* AddOrGetNativeComponent(Components::GameObject* gameObject, ManagedComponentKind componentKind) { if (!gameObject) { return nullptr; } switch (componentKind) { case ManagedComponentKind::Transform: return gameObject->GetTransform(); case ManagedComponentKind::Camera: return gameObject->GetComponent() ? static_cast(gameObject->GetComponent()) : static_cast(gameObject->AddComponent()); case ManagedComponentKind::Light: return gameObject->GetComponent() ? static_cast(gameObject->GetComponent()) : static_cast(gameObject->AddComponent()); case ManagedComponentKind::MeshFilter: return gameObject->GetComponent() ? static_cast(gameObject->GetComponent()) : static_cast(gameObject->AddComponent()); case ManagedComponentKind::MeshRenderer: return gameObject->GetComponent() ? static_cast(gameObject->GetComponent()) : static_cast(gameObject->AddComponent()); case ManagedComponentKind::Script: case ManagedComponentKind::Unknown: return nullptr; } return nullptr; } ScriptComponent* FindMatchingScriptComponent( Components::GameObject* gameObject, const ManagedComponentTypeInfo& typeInfo) { if (!gameObject || typeInfo.kind != ManagedComponentKind::Script) { return nullptr; } for (ScriptComponent* component : gameObject->GetComponents()) { if (IsMatchingScriptComponent(component, typeInfo)) { return component; } } return nullptr; } ScriptComponent* FindMatchingScriptComponentInChildren( Components::GameObject* gameObject, const ManagedComponentTypeInfo& typeInfo) { if (!gameObject) { return nullptr; } if (ScriptComponent* component = FindMatchingScriptComponent(gameObject, typeInfo)) { return component; } for (Components::GameObject* child : gameObject->GetChildren()) { if (ScriptComponent* component = FindMatchingScriptComponentInChildren(child, typeInfo)) { return component; } } return nullptr; } ScriptComponent* FindMatchingScriptComponentInParent( Components::GameObject* gameObject, const ManagedComponentTypeInfo& typeInfo) { while (gameObject) { if (ScriptComponent* component = FindMatchingScriptComponent(gameObject, typeInfo)) { return component; } gameObject = gameObject->GetParent(); } return nullptr; } Components::Component* FindNativeComponentInChildren( Components::GameObject* gameObject, ManagedComponentKind componentKind) { if (!gameObject) { return nullptr; } if (Components::Component* component = FindNativeComponent(gameObject, componentKind)) { return component; } for (Components::GameObject* child : gameObject->GetChildren()) { if (Components::Component* component = FindNativeComponentInChildren(child, componentKind)) { return component; } } return nullptr; } Components::Component* FindNativeComponentInParent( Components::GameObject* gameObject, ManagedComponentKind componentKind) { while (gameObject) { if (Components::Component* component = FindNativeComponent(gameObject, componentKind)) { return component; } gameObject = gameObject->GetParent(); } return nullptr; } bool DestroyNativeComponentInstance(Components::GameObject* gameObject, Components::Component* component) { if (!gameObject || !component || component == gameObject->GetTransform()) { return false; } if (component->IsEnabled() && gameObject->IsActiveInHierarchy()) { component->OnDisable(); } component->OnDestroy(); return gameObject->RemoveComponent(component); } Components::CameraComponent* FindCameraComponent(uint64_t gameObjectUUID) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); return gameObject ? gameObject->GetComponent() : nullptr; } Components::LightComponent* FindLightComponent(uint64_t gameObjectUUID) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); return gameObject ? gameObject->GetComponent() : nullptr; } Components::MeshFilterComponent* FindMeshFilterComponent(uint64_t gameObjectUUID) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); return gameObject ? gameObject->GetComponent() : nullptr; } Components::MeshRendererComponent* FindMeshRendererComponent(uint64_t gameObjectUUID) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); return gameObject ? gameObject->GetComponent() : nullptr; } Components::Space ResolveManagedSpace(int32_t value) { return value == static_cast(Components::Space::World) ? Components::Space::World : Components::Space::Self; } MonoArray* CreateManagedComponentArray(MonoClass* componentClass, const std::vector& components) { if (!componentClass) { return nullptr; } MonoDomain* domain = mono_domain_get(); if (!domain) { return nullptr; } MonoArray* array = mono_array_new(domain, componentClass, static_cast(components.size())); if (!array) { return nullptr; } for (uintptr_t index = 0; index < components.size(); ++index) { mono_array_setref(array, index, components[index]); } return array; } void LogManagedMessage( XCEngine::Debug::LogLevel level, MonoString* message, MonoString* file, int32_t line, MonoString* member) { const std::string messageText = MonoStringToUtf8(message); const std::string fileText = MonoStringToUtf8(file); const std::string memberText = MonoStringToUtf8(member); XCEngine::Debug::Logger::Get().Log( level, XCEngine::Debug::LogCategory::Scripting, XCEngine::Containers::String(messageText.c_str()), fileText.c_str(), line, memberText.c_str()); } void InternalCall_Debug_Log(MonoString* message, MonoString* file, int32_t line, MonoString* member) { LogManagedMessage(XCEngine::Debug::LogLevel::Info, message, file, line, member); } void InternalCall_Debug_LogWarning(MonoString* message, MonoString* file, int32_t line, MonoString* member) { LogManagedMessage(XCEngine::Debug::LogLevel::Warning, message, file, line, member); } void InternalCall_Debug_LogError(MonoString* message, MonoString* file, int32_t line, MonoString* member) { LogManagedMessage(XCEngine::Debug::LogLevel::Error, message, file, line, member); } float InternalCall_Time_GetDeltaTime() { return GetInternalCallDeltaTime(); } float InternalCall_Time_GetFixedDeltaTime() { return ScriptEngine::Get().GetRuntimeFixedDeltaTime(); } mono_bool InternalCall_Input_GetKey(int32_t keyCode) { return XCEngine::Input::InputManager::Get().IsKeyDown( static_cast(keyCode)) ? 1 : 0; } mono_bool InternalCall_Input_GetKeyDown(int32_t keyCode) { return XCEngine::Input::InputManager::Get().IsKeyPressed( static_cast(keyCode)) ? 1 : 0; } mono_bool InternalCall_Input_GetKeyUp(int32_t keyCode) { return XCEngine::Input::InputManager::Get().IsKeyReleased( static_cast(keyCode)) ? 1 : 0; } mono_bool InternalCall_Input_GetMouseButton(int32_t button) { return XCEngine::Input::InputManager::Get().IsMouseButtonDown( static_cast(button)) ? 1 : 0; } mono_bool InternalCall_Input_GetMouseButtonDown(int32_t button) { return XCEngine::Input::InputManager::Get().IsMouseButtonClicked( static_cast(button)) ? 1 : 0; } mono_bool InternalCall_Input_GetMouseButtonUp(int32_t button) { return XCEngine::Input::InputManager::Get().IsMouseButtonReleased( static_cast(button)) ? 1 : 0; } mono_bool InternalCall_Input_GetButton(MonoString* buttonName) { return XCEngine::Input::InputManager::Get().GetButton( XCEngine::Containers::String(MonoStringToUtf8(buttonName).c_str())) ? 1 : 0; } mono_bool InternalCall_Input_GetButtonDown(MonoString* buttonName) { return XCEngine::Input::InputManager::Get().GetButtonDown( XCEngine::Containers::String(MonoStringToUtf8(buttonName).c_str())) ? 1 : 0; } mono_bool InternalCall_Input_GetButtonUp(MonoString* buttonName) { return XCEngine::Input::InputManager::Get().GetButtonUp( XCEngine::Containers::String(MonoStringToUtf8(buttonName).c_str())) ? 1 : 0; } float InternalCall_Input_GetAxis(MonoString* axisName) { return XCEngine::Input::InputManager::Get().GetAxis( XCEngine::Containers::String(MonoStringToUtf8(axisName).c_str())); } float InternalCall_Input_GetAxisRaw(MonoString* axisName) { return XCEngine::Input::InputManager::Get().GetAxisRaw( XCEngine::Containers::String(MonoStringToUtf8(axisName).c_str())); } mono_bool InternalCall_Input_GetAnyKey() { return XCEngine::Input::InputManager::Get().IsAnyKeyDown() ? 1 : 0; } mono_bool InternalCall_Input_GetAnyKeyDown() { return XCEngine::Input::InputManager::Get().IsAnyKeyPressed() ? 1 : 0; } void InternalCall_Input_GetMousePosition(XCEngine::Math::Vector3* outPosition) { if (!outPosition) { return; } const XCEngine::Math::Vector2 position = XCEngine::Input::InputManager::Get().GetMousePosition(); *outPosition = XCEngine::Math::Vector3(position.x, position.y, 0.0f); } void InternalCall_Input_GetMouseScrollDelta(XCEngine::Math::Vector2* outDelta) { if (!outDelta) { return; } *outDelta = XCEngine::Math::Vector2( 0.0f, XCEngine::Input::InputManager::Get().GetMouseScrollDelta()); } MonoString* InternalCall_GameObject_GetName(uint64_t gameObjectUUID) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); return mono_string_new( mono_domain_get(), gameObject ? gameObject->GetName().c_str() : ""); } void InternalCall_GameObject_SetName(uint64_t gameObjectUUID, MonoString* name) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject) { return; } gameObject->SetName(MonoStringToUtf8(name)); } MonoString* InternalCall_GameObject_GetTag(uint64_t gameObjectUUID) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); return mono_string_new( mono_domain_get(), gameObject ? gameObject->GetTag().c_str() : ""); } void InternalCall_GameObject_SetTag(uint64_t gameObjectUUID, MonoString* tag) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject) { return; } gameObject->SetTag(MonoStringToUtf8(tag)); } mono_bool InternalCall_GameObject_CompareTag(uint64_t gameObjectUUID, MonoString* tag) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); return (gameObject && gameObject->CompareTag(MonoStringToUtf8(tag))) ? 1 : 0; } int32_t InternalCall_GameObject_GetLayer(uint64_t gameObjectUUID) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); return gameObject ? static_cast(gameObject->GetLayer()) : 0; } void InternalCall_GameObject_SetLayer(uint64_t gameObjectUUID, int32_t layer) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject) { return; } gameObject->SetLayer(static_cast(std::clamp(layer, 0, 31))); } mono_bool InternalCall_GameObject_GetActiveSelf(uint64_t gameObjectUUID) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); return (gameObject && gameObject->IsActive()) ? 1 : 0; } mono_bool InternalCall_GameObject_GetActiveInHierarchy(uint64_t gameObjectUUID) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); return (gameObject && gameObject->IsActiveInHierarchy()) ? 1 : 0; } void InternalCall_GameObject_SetActive(uint64_t gameObjectUUID, mono_bool active) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject) { return; } gameObject->SetActive(active != 0); } mono_bool InternalCall_GameObject_HasComponent(uint64_t gameObjectUUID, MonoReflectionType* componentType) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); const ManagedComponentTypeInfo typeInfo = ResolveManagedComponentTypeInfo(componentType); if (typeInfo.kind == ManagedComponentKind::Script) { return FindMatchingScriptComponent(gameObject, typeInfo) ? 1 : 0; } return HasNativeComponent(gameObject, typeInfo.kind) ? 1 : 0; } MonoObject* InternalCall_GameObject_GetComponent(uint64_t gameObjectUUID, MonoReflectionType* componentType) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject) { return nullptr; } MonoScriptRuntime* runtime = GetActiveMonoScriptRuntime(); if (!runtime) { return nullptr; } const ManagedComponentTypeInfo typeInfo = ResolveManagedComponentTypeInfo(componentType); if (typeInfo.kind == ManagedComponentKind::Script) { ScriptComponent* component = FindMatchingScriptComponent(gameObject, typeInfo); if (!component) { return nullptr; } if (!runtime->HasManagedInstance(component)) { ScriptEngine::Get().OnScriptComponentEnabled(component); } return runtime->GetManagedInstanceObject(component); } if (!HasNativeComponent(gameObject, typeInfo.kind) || !typeInfo.monoClass) { return nullptr; } return runtime->CreateManagedComponentWrapper(typeInfo.monoClass, gameObjectUUID); } MonoArray* InternalCall_GameObject_GetComponents(uint64_t gameObjectUUID, MonoReflectionType* componentType) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject) { return nullptr; } MonoScriptRuntime* runtime = GetActiveMonoScriptRuntime(); if (!runtime) { return nullptr; } const ManagedComponentTypeInfo typeInfo = ResolveManagedComponentTypeInfo(componentType); if (!typeInfo.monoClass) { return nullptr; } std::vector managedComponents; auto appendNativeComponents = [&](const auto& nativeComponents) { for (const auto* component : nativeComponents) { if (!component || !component->GetGameObject()) { continue; } if (MonoObject* managedObject = runtime->CreateManagedComponentWrapper(typeInfo.monoClass, component->GetGameObject()->GetUUID())) { managedComponents.push_back(managedObject); } } }; switch (typeInfo.kind) { case ManagedComponentKind::Script: for (ScriptComponent* component : gameObject->GetComponents()) { if (!IsMatchingScriptComponent(component, typeInfo)) { continue; } if (!runtime->HasManagedInstance(component)) { ScriptEngine::Get().OnScriptComponentEnabled(component); } if (MonoObject* managedObject = runtime->GetManagedInstanceObject(component)) { managedComponents.push_back(managedObject); } } break; case ManagedComponentKind::Transform: appendNativeComponents(gameObject->GetComponents()); break; case ManagedComponentKind::Camera: appendNativeComponents(gameObject->GetComponents()); break; case ManagedComponentKind::Light: appendNativeComponents(gameObject->GetComponents()); break; case ManagedComponentKind::MeshFilter: appendNativeComponents(gameObject->GetComponents()); break; case ManagedComponentKind::MeshRenderer: appendNativeComponents(gameObject->GetComponents()); break; case ManagedComponentKind::Unknown: return nullptr; } return CreateManagedComponentArray(typeInfo.monoClass, managedComponents); } MonoObject* InternalCall_GameObject_GetComponentInChildren(uint64_t gameObjectUUID, MonoReflectionType* componentType) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject) { return nullptr; } MonoScriptRuntime* runtime = GetActiveMonoScriptRuntime(); if (!runtime) { return nullptr; } const ManagedComponentTypeInfo typeInfo = ResolveManagedComponentTypeInfo(componentType); if (typeInfo.kind == ManagedComponentKind::Script) { ScriptComponent* component = FindMatchingScriptComponentInChildren(gameObject, typeInfo); if (!component) { return nullptr; } if (!runtime->HasManagedInstance(component)) { ScriptEngine::Get().OnScriptComponentEnabled(component); } return runtime->GetManagedInstanceObject(component); } Components::Component* component = FindNativeComponentInChildren(gameObject, typeInfo.kind); if (!component || !typeInfo.monoClass) { return nullptr; } return runtime->CreateManagedComponentWrapper(typeInfo.monoClass, component->GetGameObject()->GetUUID()); } MonoObject* InternalCall_GameObject_GetComponentInParent(uint64_t gameObjectUUID, MonoReflectionType* componentType) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject) { return nullptr; } MonoScriptRuntime* runtime = GetActiveMonoScriptRuntime(); if (!runtime) { return nullptr; } const ManagedComponentTypeInfo typeInfo = ResolveManagedComponentTypeInfo(componentType); if (typeInfo.kind == ManagedComponentKind::Script) { ScriptComponent* component = FindMatchingScriptComponentInParent(gameObject, typeInfo); if (!component) { return nullptr; } if (!runtime->HasManagedInstance(component)) { ScriptEngine::Get().OnScriptComponentEnabled(component); } return runtime->GetManagedInstanceObject(component); } Components::Component* component = FindNativeComponentInParent(gameObject, typeInfo.kind); if (!component || !typeInfo.monoClass) { return nullptr; } return runtime->CreateManagedComponentWrapper(typeInfo.monoClass, component->GetGameObject()->GetUUID()); } MonoObject* InternalCall_GameObject_AddComponent(uint64_t gameObjectUUID, MonoReflectionType* componentType) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject) { return nullptr; } MonoScriptRuntime* runtime = GetActiveMonoScriptRuntime(); if (!runtime) { return nullptr; } const ManagedComponentTypeInfo typeInfo = ResolveManagedComponentTypeInfo(componentType); if (typeInfo.kind == ManagedComponentKind::Script) { ScriptComponent* component = gameObject->AddComponent(); component->SetScriptClass(typeInfo.assemblyName, typeInfo.namespaceName, typeInfo.className); return runtime->GetManagedInstanceObject(component); } if (!AddOrGetNativeComponent(gameObject, typeInfo.kind) || !typeInfo.monoClass) { return nullptr; } return runtime->CreateManagedComponentWrapper(typeInfo.monoClass, gameObjectUUID); } uint64_t InternalCall_GameObject_Find(MonoString* name) { Components::Scene* scene = GetInternalCallScene(); if (!scene) { return 0; } Components::GameObject* gameObject = scene->Find(MonoStringToUtf8(name)); return gameObject ? gameObject->GetUUID() : 0; } uint64_t InternalCall_GameObject_Create(MonoString* name, uint64_t parentGameObjectUUID) { Components::Scene* scene = GetInternalCallScene(); if (!scene) { return 0; } Components::GameObject* parent = nullptr; if (parentGameObjectUUID != 0) { parent = FindGameObjectByUUID(parentGameObjectUUID); if (!parent || parent->GetScene() != scene) { return 0; } } std::string objectName = MonoStringToUtf8(name); if (objectName.empty()) { objectName = "GameObject"; } Components::GameObject* created = scene->CreateGameObject(objectName, parent); return created ? created->GetUUID() : 0; } void InternalCall_GameObject_Destroy(uint64_t gameObjectUUID) { Components::Scene* scene = GetInternalCallScene(); Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!scene || !gameObject || gameObject->GetScene() != scene) { return; } scene->DestroyGameObject(gameObject); } void InternalCall_Object_Destroy(MonoObject* object) { MonoScriptRuntime* runtime = GetActiveMonoScriptRuntime(); if (!runtime || !object) { return; } runtime->DestroyManagedObject(object); } mono_bool InternalCall_Behaviour_GetEnabled(uint64_t scriptComponentUUID) { ScriptComponent* component = FindScriptComponentByUUID(scriptComponentUUID); return (component && component->IsEnabled()) ? 1 : 0; } void InternalCall_Behaviour_SetEnabled(uint64_t scriptComponentUUID, mono_bool enabled) { ScriptComponent* component = FindScriptComponentByUUID(scriptComponentUUID); if (!component) { return; } component->SetEnabled(enabled != 0); } void InternalCall_Transform_GetLocalPosition(uint64_t gameObjectUUID, XCEngine::Math::Vector3* outPosition) { if (!outPosition) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { *outPosition = XCEngine::Math::Vector3::Zero(); return; } *outPosition = gameObject->GetTransform()->GetLocalPosition(); } void InternalCall_Transform_SetLocalPosition(uint64_t gameObjectUUID, XCEngine::Math::Vector3* position) { if (!position) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { return; } gameObject->GetTransform()->SetLocalPosition(*position); } void InternalCall_Transform_GetLocalRotation(uint64_t gameObjectUUID, XCEngine::Math::Quaternion* outRotation) { if (!outRotation) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { *outRotation = XCEngine::Math::Quaternion::Identity(); return; } *outRotation = gameObject->GetTransform()->GetLocalRotation(); } void InternalCall_Transform_SetLocalRotation(uint64_t gameObjectUUID, XCEngine::Math::Quaternion* rotation) { if (!rotation) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { return; } gameObject->GetTransform()->SetLocalRotation(*rotation); } void InternalCall_Transform_GetLocalScale(uint64_t gameObjectUUID, XCEngine::Math::Vector3* outScale) { if (!outScale) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { *outScale = XCEngine::Math::Vector3::One(); return; } *outScale = gameObject->GetTransform()->GetLocalScale(); } void InternalCall_Transform_SetLocalScale(uint64_t gameObjectUUID, XCEngine::Math::Vector3* scale) { if (!scale) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { return; } gameObject->GetTransform()->SetLocalScale(*scale); } void InternalCall_Transform_GetLocalEulerAngles(uint64_t gameObjectUUID, XCEngine::Math::Vector3* outEulerAngles) { if (!outEulerAngles) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { *outEulerAngles = XCEngine::Math::Vector3::Zero(); return; } *outEulerAngles = gameObject->GetTransform()->GetLocalEulerAngles(); } void InternalCall_Transform_SetLocalEulerAngles(uint64_t gameObjectUUID, XCEngine::Math::Vector3* eulerAngles) { if (!eulerAngles) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { return; } gameObject->GetTransform()->SetLocalEulerAngles(*eulerAngles); } void InternalCall_Transform_GetPosition(uint64_t gameObjectUUID, XCEngine::Math::Vector3* outPosition) { if (!outPosition) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { *outPosition = XCEngine::Math::Vector3::Zero(); return; } *outPosition = gameObject->GetTransform()->GetPosition(); } void InternalCall_Transform_SetPosition(uint64_t gameObjectUUID, XCEngine::Math::Vector3* position) { if (!position) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { return; } gameObject->GetTransform()->SetPosition(*position); } void InternalCall_Transform_GetRotation(uint64_t gameObjectUUID, XCEngine::Math::Quaternion* outRotation) { if (!outRotation) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { *outRotation = XCEngine::Math::Quaternion::Identity(); return; } *outRotation = gameObject->GetTransform()->GetRotation(); } void InternalCall_Transform_SetRotation(uint64_t gameObjectUUID, XCEngine::Math::Quaternion* rotation) { if (!rotation) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { return; } gameObject->GetTransform()->SetRotation(*rotation); } void InternalCall_Transform_GetScale(uint64_t gameObjectUUID, XCEngine::Math::Vector3* outScale) { if (!outScale) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { *outScale = XCEngine::Math::Vector3::One(); return; } *outScale = gameObject->GetTransform()->GetScale(); } void InternalCall_Transform_SetScale(uint64_t gameObjectUUID, XCEngine::Math::Vector3* scale) { if (!scale) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { return; } gameObject->GetTransform()->SetScale(*scale); } void InternalCall_Transform_GetForward(uint64_t gameObjectUUID, XCEngine::Math::Vector3* outForward) { if (!outForward) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { *outForward = XCEngine::Math::Vector3::Forward(); return; } *outForward = gameObject->GetTransform()->GetForward(); } void InternalCall_Transform_GetRight(uint64_t gameObjectUUID, XCEngine::Math::Vector3* outRight) { if (!outRight) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { *outRight = XCEngine::Math::Vector3::Right(); return; } *outRight = gameObject->GetTransform()->GetRight(); } void InternalCall_Transform_GetUp(uint64_t gameObjectUUID, XCEngine::Math::Vector3* outUp) { if (!outUp) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { *outUp = XCEngine::Math::Vector3::Up(); return; } *outUp = gameObject->GetTransform()->GetUp(); } void InternalCall_Transform_Translate(uint64_t gameObjectUUID, XCEngine::Math::Vector3* translation, int32_t relativeTo) { if (!translation) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { return; } gameObject->GetTransform()->Translate(*translation, ResolveManagedSpace(relativeTo)); } void InternalCall_Transform_Rotate(uint64_t gameObjectUUID, XCEngine::Math::Vector3* eulers, int32_t relativeTo) { if (!eulers) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { return; } gameObject->GetTransform()->Rotate(*eulers, ResolveManagedSpace(relativeTo)); } void InternalCall_Transform_RotateAxisAngle( uint64_t gameObjectUUID, XCEngine::Math::Vector3* axis, float angle, int32_t relativeTo) { if (!axis) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { return; } gameObject->GetTransform()->Rotate(*axis, angle, ResolveManagedSpace(relativeTo)); } void InternalCall_Transform_LookAt(uint64_t gameObjectUUID, XCEngine::Math::Vector3* target) { if (!target) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { return; } gameObject->GetTransform()->LookAt(*target); } void InternalCall_Transform_LookAtWithUp( uint64_t gameObjectUUID, XCEngine::Math::Vector3* target, XCEngine::Math::Vector3* up) { if (!target || !up) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { return; } gameObject->GetTransform()->LookAt(*target, *up); } void InternalCall_Transform_TransformPoint(uint64_t gameObjectUUID, XCEngine::Math::Vector3* point, XCEngine::Math::Vector3* outPoint) { if (!point || !outPoint) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { *outPoint = *point; return; } *outPoint = gameObject->GetTransform()->TransformPoint(*point); } void InternalCall_Transform_InverseTransformPoint(uint64_t gameObjectUUID, XCEngine::Math::Vector3* point, XCEngine::Math::Vector3* outPoint) { if (!point || !outPoint) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { *outPoint = *point; return; } *outPoint = gameObject->GetTransform()->InverseTransformPoint(*point); } void InternalCall_Transform_TransformDirection(uint64_t gameObjectUUID, XCEngine::Math::Vector3* direction, XCEngine::Math::Vector3* outDirection) { if (!direction || !outDirection) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { *outDirection = *direction; return; } *outDirection = gameObject->GetTransform()->TransformDirection(*direction); } void InternalCall_Transform_InverseTransformDirection(uint64_t gameObjectUUID, XCEngine::Math::Vector3* direction, XCEngine::Math::Vector3* outDirection) { if (!direction || !outDirection) { return; } Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetTransform()) { *outDirection = *direction; return; } *outDirection = gameObject->GetTransform()->InverseTransformDirection(*direction); } uint64_t InternalCall_Transform_GetParent(uint64_t gameObjectUUID) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || !gameObject->GetParent()) { return 0; } return gameObject->GetParent()->GetUUID(); } void InternalCall_Transform_SetParent(uint64_t gameObjectUUID, uint64_t parentGameObjectUUID, mono_bool worldPositionStays) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject) { return; } Components::GameObject* parent = parentGameObjectUUID != 0 ? FindGameObjectByUUID(parentGameObjectUUID) : nullptr; gameObject->SetParent(parent, worldPositionStays != 0); } int32_t InternalCall_Transform_GetChildCount(uint64_t gameObjectUUID) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); return gameObject ? static_cast(gameObject->GetChildCount()) : 0; } uint64_t InternalCall_Transform_GetChild(uint64_t gameObjectUUID, int32_t index) { Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject || index < 0) { return 0; } Components::GameObject* child = gameObject->GetChild(static_cast(index)); return child ? child->GetUUID() : 0; } float InternalCall_Camera_GetFieldOfView(uint64_t gameObjectUUID) { Components::CameraComponent* component = FindCameraComponent(gameObjectUUID); return component ? component->GetFieldOfView() : 0.0f; } void InternalCall_Camera_SetFieldOfView(uint64_t gameObjectUUID, float value) { Components::CameraComponent* component = FindCameraComponent(gameObjectUUID); if (!component) { return; } component->SetFieldOfView(value); } float InternalCall_Camera_GetNearClipPlane(uint64_t gameObjectUUID) { Components::CameraComponent* component = FindCameraComponent(gameObjectUUID); return component ? component->GetNearClipPlane() : 0.0f; } void InternalCall_Camera_SetNearClipPlane(uint64_t gameObjectUUID, float value) { Components::CameraComponent* component = FindCameraComponent(gameObjectUUID); if (!component) { return; } component->SetNearClipPlane(value); } float InternalCall_Camera_GetFarClipPlane(uint64_t gameObjectUUID) { Components::CameraComponent* component = FindCameraComponent(gameObjectUUID); return component ? component->GetFarClipPlane() : 0.0f; } void InternalCall_Camera_SetFarClipPlane(uint64_t gameObjectUUID, float value) { Components::CameraComponent* component = FindCameraComponent(gameObjectUUID); if (!component) { return; } component->SetFarClipPlane(value); } float InternalCall_Camera_GetDepth(uint64_t gameObjectUUID) { Components::CameraComponent* component = FindCameraComponent(gameObjectUUID); return component ? component->GetDepth() : 0.0f; } void InternalCall_Camera_SetDepth(uint64_t gameObjectUUID, float value) { Components::CameraComponent* component = FindCameraComponent(gameObjectUUID); if (!component) { return; } component->SetDepth(value); } mono_bool InternalCall_Camera_GetPrimary(uint64_t gameObjectUUID) { Components::CameraComponent* component = FindCameraComponent(gameObjectUUID); return (component && component->IsPrimary()) ? 1 : 0; } void InternalCall_Camera_SetPrimary(uint64_t gameObjectUUID, mono_bool value) { Components::CameraComponent* component = FindCameraComponent(gameObjectUUID); if (!component) { return; } component->SetPrimary(value != 0); } float InternalCall_Light_GetIntensity(uint64_t gameObjectUUID) { Components::LightComponent* component = FindLightComponent(gameObjectUUID); return component ? component->GetIntensity() : 0.0f; } void InternalCall_Light_SetIntensity(uint64_t gameObjectUUID, float value) { Components::LightComponent* component = FindLightComponent(gameObjectUUID); if (!component) { return; } component->SetIntensity(value); } float InternalCall_Light_GetRange(uint64_t gameObjectUUID) { Components::LightComponent* component = FindLightComponent(gameObjectUUID); return component ? component->GetRange() : 0.0f; } void InternalCall_Light_SetRange(uint64_t gameObjectUUID, float value) { Components::LightComponent* component = FindLightComponent(gameObjectUUID); if (!component) { return; } component->SetRange(value); } float InternalCall_Light_GetSpotAngle(uint64_t gameObjectUUID) { Components::LightComponent* component = FindLightComponent(gameObjectUUID); return component ? component->GetSpotAngle() : 0.0f; } void InternalCall_Light_SetSpotAngle(uint64_t gameObjectUUID, float value) { Components::LightComponent* component = FindLightComponent(gameObjectUUID); if (!component) { return; } component->SetSpotAngle(value); } mono_bool InternalCall_Light_GetCastsShadows(uint64_t gameObjectUUID) { Components::LightComponent* component = FindLightComponent(gameObjectUUID); return (component && component->GetCastsShadows()) ? 1 : 0; } void InternalCall_Light_SetCastsShadows(uint64_t gameObjectUUID, mono_bool value) { Components::LightComponent* component = FindLightComponent(gameObjectUUID); if (!component) { return; } component->SetCastsShadows(value != 0); } MonoString* InternalCall_MeshFilter_GetMeshPath(uint64_t gameObjectUUID) { Components::MeshFilterComponent* component = FindMeshFilterComponent(gameObjectUUID); return mono_string_new( mono_domain_get(), component ? component->GetMeshPath().c_str() : ""); } void InternalCall_MeshFilter_SetMeshPath(uint64_t gameObjectUUID, MonoString* path) { Components::MeshFilterComponent* component = FindMeshFilterComponent(gameObjectUUID); if (!component) { return; } component->SetMeshPath(MonoStringToUtf8(path)); } int32_t InternalCall_MeshRenderer_GetMaterialCount(uint64_t gameObjectUUID) { Components::MeshRendererComponent* component = FindMeshRendererComponent(gameObjectUUID); return component ? static_cast(component->GetMaterialCount()) : 0; } MonoString* InternalCall_MeshRenderer_GetMaterialPath(uint64_t gameObjectUUID, int32_t index) { Components::MeshRendererComponent* component = FindMeshRendererComponent(gameObjectUUID); const std::string path = (component && index >= 0) ? component->GetMaterialPath(static_cast(index)) : std::string(); return mono_string_new(mono_domain_get(), path.c_str()); } void InternalCall_MeshRenderer_SetMaterialPath(uint64_t gameObjectUUID, int32_t index, MonoString* path) { Components::MeshRendererComponent* component = FindMeshRendererComponent(gameObjectUUID); if (!component || index < 0) { return; } component->SetMaterialPath(static_cast(index), MonoStringToUtf8(path)); } void InternalCall_MeshRenderer_ClearMaterials(uint64_t gameObjectUUID) { Components::MeshRendererComponent* component = FindMeshRendererComponent(gameObjectUUID); if (!component) { return; } component->ClearMaterials(); } mono_bool InternalCall_MeshRenderer_GetCastShadows(uint64_t gameObjectUUID) { Components::MeshRendererComponent* component = FindMeshRendererComponent(gameObjectUUID); return (component && component->GetCastShadows()) ? 1 : 0; } void InternalCall_MeshRenderer_SetCastShadows(uint64_t gameObjectUUID, mono_bool value) { Components::MeshRendererComponent* component = FindMeshRendererComponent(gameObjectUUID); if (!component) { return; } component->SetCastShadows(value != 0); } mono_bool InternalCall_MeshRenderer_GetReceiveShadows(uint64_t gameObjectUUID) { Components::MeshRendererComponent* component = FindMeshRendererComponent(gameObjectUUID); return (component && component->GetReceiveShadows()) ? 1 : 0; } void InternalCall_MeshRenderer_SetReceiveShadows(uint64_t gameObjectUUID, mono_bool value) { Components::MeshRendererComponent* component = FindMeshRendererComponent(gameObjectUUID); if (!component) { return; } component->SetReceiveShadows(value != 0); } int32_t InternalCall_MeshRenderer_GetRenderLayer(uint64_t gameObjectUUID) { Components::MeshRendererComponent* component = FindMeshRendererComponent(gameObjectUUID); return component ? static_cast(component->GetRenderLayer()) : 0; } void InternalCall_MeshRenderer_SetRenderLayer(uint64_t gameObjectUUID, int32_t value) { Components::MeshRendererComponent* component = FindMeshRendererComponent(gameObjectUUID); if (!component) { return; } component->SetRenderLayer(static_cast(std::max(value, 0))); } void RegisterInternalCalls() { if (GetInternalCallRegistrationState()) { return; } mono_add_internal_call("XCEngine.InternalCalls::Debug_Log", reinterpret_cast(&InternalCall_Debug_Log)); mono_add_internal_call("XCEngine.InternalCalls::Debug_LogWarning", reinterpret_cast(&InternalCall_Debug_LogWarning)); mono_add_internal_call("XCEngine.InternalCalls::Debug_LogError", reinterpret_cast(&InternalCall_Debug_LogError)); mono_add_internal_call("XCEngine.InternalCalls::Time_GetDeltaTime", reinterpret_cast(&InternalCall_Time_GetDeltaTime)); mono_add_internal_call("XCEngine.InternalCalls::Time_GetFixedDeltaTime", reinterpret_cast(&InternalCall_Time_GetFixedDeltaTime)); mono_add_internal_call("XCEngine.InternalCalls::Input_GetKey", reinterpret_cast(&InternalCall_Input_GetKey)); mono_add_internal_call("XCEngine.InternalCalls::Input_GetKeyDown", reinterpret_cast(&InternalCall_Input_GetKeyDown)); mono_add_internal_call("XCEngine.InternalCalls::Input_GetKeyUp", reinterpret_cast(&InternalCall_Input_GetKeyUp)); mono_add_internal_call("XCEngine.InternalCalls::Input_GetMouseButton", reinterpret_cast(&InternalCall_Input_GetMouseButton)); mono_add_internal_call("XCEngine.InternalCalls::Input_GetMouseButtonDown", reinterpret_cast(&InternalCall_Input_GetMouseButtonDown)); mono_add_internal_call("XCEngine.InternalCalls::Input_GetMouseButtonUp", reinterpret_cast(&InternalCall_Input_GetMouseButtonUp)); mono_add_internal_call("XCEngine.InternalCalls::Input_GetButton", reinterpret_cast(&InternalCall_Input_GetButton)); mono_add_internal_call("XCEngine.InternalCalls::Input_GetButtonDown", reinterpret_cast(&InternalCall_Input_GetButtonDown)); mono_add_internal_call("XCEngine.InternalCalls::Input_GetButtonUp", reinterpret_cast(&InternalCall_Input_GetButtonUp)); mono_add_internal_call("XCEngine.InternalCalls::Input_GetAxis", reinterpret_cast(&InternalCall_Input_GetAxis)); mono_add_internal_call("XCEngine.InternalCalls::Input_GetAxisRaw", reinterpret_cast(&InternalCall_Input_GetAxisRaw)); mono_add_internal_call("XCEngine.InternalCalls::Input_GetAnyKey", reinterpret_cast(&InternalCall_Input_GetAnyKey)); mono_add_internal_call("XCEngine.InternalCalls::Input_GetAnyKeyDown", reinterpret_cast(&InternalCall_Input_GetAnyKeyDown)); mono_add_internal_call("XCEngine.InternalCalls::Input_GetMousePosition", reinterpret_cast(&InternalCall_Input_GetMousePosition)); mono_add_internal_call("XCEngine.InternalCalls::Input_GetMouseScrollDelta", reinterpret_cast(&InternalCall_Input_GetMouseScrollDelta)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetName", reinterpret_cast(&InternalCall_GameObject_GetName)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_SetName", reinterpret_cast(&InternalCall_GameObject_SetName)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetTag", reinterpret_cast(&InternalCall_GameObject_GetTag)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_SetTag", reinterpret_cast(&InternalCall_GameObject_SetTag)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_CompareTag", reinterpret_cast(&InternalCall_GameObject_CompareTag)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetLayer", reinterpret_cast(&InternalCall_GameObject_GetLayer)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_SetLayer", reinterpret_cast(&InternalCall_GameObject_SetLayer)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetActiveSelf", reinterpret_cast(&InternalCall_GameObject_GetActiveSelf)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetActiveInHierarchy", reinterpret_cast(&InternalCall_GameObject_GetActiveInHierarchy)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_SetActive", reinterpret_cast(&InternalCall_GameObject_SetActive)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_HasComponent", reinterpret_cast(&InternalCall_GameObject_HasComponent)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetComponent", reinterpret_cast(&InternalCall_GameObject_GetComponent)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetComponents", reinterpret_cast(&InternalCall_GameObject_GetComponents)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetComponentInChildren", reinterpret_cast(&InternalCall_GameObject_GetComponentInChildren)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetComponentInParent", reinterpret_cast(&InternalCall_GameObject_GetComponentInParent)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_AddComponent", reinterpret_cast(&InternalCall_GameObject_AddComponent)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_Find", reinterpret_cast(&InternalCall_GameObject_Find)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_Create", reinterpret_cast(&InternalCall_GameObject_Create)); mono_add_internal_call("XCEngine.InternalCalls::GameObject_Destroy", reinterpret_cast(&InternalCall_GameObject_Destroy)); mono_add_internal_call("XCEngine.InternalCalls::Object_Destroy", reinterpret_cast(&InternalCall_Object_Destroy)); mono_add_internal_call("XCEngine.InternalCalls::Behaviour_GetEnabled", reinterpret_cast(&InternalCall_Behaviour_GetEnabled)); mono_add_internal_call("XCEngine.InternalCalls::Behaviour_SetEnabled", reinterpret_cast(&InternalCall_Behaviour_SetEnabled)); mono_add_internal_call("XCEngine.InternalCalls::Transform_GetLocalPosition", reinterpret_cast(&InternalCall_Transform_GetLocalPosition)); mono_add_internal_call("XCEngine.InternalCalls::Transform_SetLocalPosition", reinterpret_cast(&InternalCall_Transform_SetLocalPosition)); mono_add_internal_call("XCEngine.InternalCalls::Transform_GetLocalRotation", reinterpret_cast(&InternalCall_Transform_GetLocalRotation)); mono_add_internal_call("XCEngine.InternalCalls::Transform_SetLocalRotation", reinterpret_cast(&InternalCall_Transform_SetLocalRotation)); mono_add_internal_call("XCEngine.InternalCalls::Transform_GetLocalScale", reinterpret_cast(&InternalCall_Transform_GetLocalScale)); mono_add_internal_call("XCEngine.InternalCalls::Transform_SetLocalScale", reinterpret_cast(&InternalCall_Transform_SetLocalScale)); mono_add_internal_call("XCEngine.InternalCalls::Transform_GetLocalEulerAngles", reinterpret_cast(&InternalCall_Transform_GetLocalEulerAngles)); mono_add_internal_call("XCEngine.InternalCalls::Transform_SetLocalEulerAngles", reinterpret_cast(&InternalCall_Transform_SetLocalEulerAngles)); mono_add_internal_call("XCEngine.InternalCalls::Transform_GetPosition", reinterpret_cast(&InternalCall_Transform_GetPosition)); mono_add_internal_call("XCEngine.InternalCalls::Transform_SetPosition", reinterpret_cast(&InternalCall_Transform_SetPosition)); mono_add_internal_call("XCEngine.InternalCalls::Transform_GetRotation", reinterpret_cast(&InternalCall_Transform_GetRotation)); mono_add_internal_call("XCEngine.InternalCalls::Transform_SetRotation", reinterpret_cast(&InternalCall_Transform_SetRotation)); mono_add_internal_call("XCEngine.InternalCalls::Transform_GetScale", reinterpret_cast(&InternalCall_Transform_GetScale)); mono_add_internal_call("XCEngine.InternalCalls::Transform_SetScale", reinterpret_cast(&InternalCall_Transform_SetScale)); mono_add_internal_call("XCEngine.InternalCalls::Transform_GetForward", reinterpret_cast(&InternalCall_Transform_GetForward)); mono_add_internal_call("XCEngine.InternalCalls::Transform_GetRight", reinterpret_cast(&InternalCall_Transform_GetRight)); mono_add_internal_call("XCEngine.InternalCalls::Transform_GetUp", reinterpret_cast(&InternalCall_Transform_GetUp)); mono_add_internal_call("XCEngine.InternalCalls::Transform_Translate", reinterpret_cast(&InternalCall_Transform_Translate)); mono_add_internal_call("XCEngine.InternalCalls::Transform_Rotate", reinterpret_cast(&InternalCall_Transform_Rotate)); mono_add_internal_call("XCEngine.InternalCalls::Transform_RotateAxisAngle", reinterpret_cast(&InternalCall_Transform_RotateAxisAngle)); mono_add_internal_call("XCEngine.InternalCalls::Transform_LookAt", reinterpret_cast(&InternalCall_Transform_LookAt)); mono_add_internal_call("XCEngine.InternalCalls::Transform_LookAtWithUp", reinterpret_cast(&InternalCall_Transform_LookAtWithUp)); mono_add_internal_call("XCEngine.InternalCalls::Transform_TransformPoint", reinterpret_cast(&InternalCall_Transform_TransformPoint)); mono_add_internal_call("XCEngine.InternalCalls::Transform_InverseTransformPoint", reinterpret_cast(&InternalCall_Transform_InverseTransformPoint)); mono_add_internal_call("XCEngine.InternalCalls::Transform_TransformDirection", reinterpret_cast(&InternalCall_Transform_TransformDirection)); mono_add_internal_call("XCEngine.InternalCalls::Transform_InverseTransformDirection", reinterpret_cast(&InternalCall_Transform_InverseTransformDirection)); mono_add_internal_call("XCEngine.InternalCalls::Transform_GetParent", reinterpret_cast(&InternalCall_Transform_GetParent)); mono_add_internal_call("XCEngine.InternalCalls::Transform_SetParent", reinterpret_cast(&InternalCall_Transform_SetParent)); mono_add_internal_call("XCEngine.InternalCalls::Transform_GetChildCount", reinterpret_cast(&InternalCall_Transform_GetChildCount)); mono_add_internal_call("XCEngine.InternalCalls::Transform_GetChild", reinterpret_cast(&InternalCall_Transform_GetChild)); mono_add_internal_call("XCEngine.InternalCalls::Camera_GetFieldOfView", reinterpret_cast(&InternalCall_Camera_GetFieldOfView)); mono_add_internal_call("XCEngine.InternalCalls::Camera_SetFieldOfView", reinterpret_cast(&InternalCall_Camera_SetFieldOfView)); mono_add_internal_call("XCEngine.InternalCalls::Camera_GetNearClipPlane", reinterpret_cast(&InternalCall_Camera_GetNearClipPlane)); mono_add_internal_call("XCEngine.InternalCalls::Camera_SetNearClipPlane", reinterpret_cast(&InternalCall_Camera_SetNearClipPlane)); mono_add_internal_call("XCEngine.InternalCalls::Camera_GetFarClipPlane", reinterpret_cast(&InternalCall_Camera_GetFarClipPlane)); mono_add_internal_call("XCEngine.InternalCalls::Camera_SetFarClipPlane", reinterpret_cast(&InternalCall_Camera_SetFarClipPlane)); mono_add_internal_call("XCEngine.InternalCalls::Camera_GetDepth", reinterpret_cast(&InternalCall_Camera_GetDepth)); mono_add_internal_call("XCEngine.InternalCalls::Camera_SetDepth", reinterpret_cast(&InternalCall_Camera_SetDepth)); mono_add_internal_call("XCEngine.InternalCalls::Camera_GetPrimary", reinterpret_cast(&InternalCall_Camera_GetPrimary)); mono_add_internal_call("XCEngine.InternalCalls::Camera_SetPrimary", reinterpret_cast(&InternalCall_Camera_SetPrimary)); mono_add_internal_call("XCEngine.InternalCalls::Light_GetIntensity", reinterpret_cast(&InternalCall_Light_GetIntensity)); mono_add_internal_call("XCEngine.InternalCalls::Light_SetIntensity", reinterpret_cast(&InternalCall_Light_SetIntensity)); mono_add_internal_call("XCEngine.InternalCalls::Light_GetRange", reinterpret_cast(&InternalCall_Light_GetRange)); mono_add_internal_call("XCEngine.InternalCalls::Light_SetRange", reinterpret_cast(&InternalCall_Light_SetRange)); mono_add_internal_call("XCEngine.InternalCalls::Light_GetSpotAngle", reinterpret_cast(&InternalCall_Light_GetSpotAngle)); mono_add_internal_call("XCEngine.InternalCalls::Light_SetSpotAngle", reinterpret_cast(&InternalCall_Light_SetSpotAngle)); mono_add_internal_call("XCEngine.InternalCalls::Light_GetCastsShadows", reinterpret_cast(&InternalCall_Light_GetCastsShadows)); mono_add_internal_call("XCEngine.InternalCalls::Light_SetCastsShadows", reinterpret_cast(&InternalCall_Light_SetCastsShadows)); mono_add_internal_call("XCEngine.InternalCalls::MeshFilter_GetMeshPath", reinterpret_cast(&InternalCall_MeshFilter_GetMeshPath)); mono_add_internal_call("XCEngine.InternalCalls::MeshFilter_SetMeshPath", reinterpret_cast(&InternalCall_MeshFilter_SetMeshPath)); mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_GetMaterialCount", reinterpret_cast(&InternalCall_MeshRenderer_GetMaterialCount)); mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_GetMaterialPath", reinterpret_cast(&InternalCall_MeshRenderer_GetMaterialPath)); mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_SetMaterialPath", reinterpret_cast(&InternalCall_MeshRenderer_SetMaterialPath)); mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_ClearMaterials", reinterpret_cast(&InternalCall_MeshRenderer_ClearMaterials)); mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_GetCastShadows", reinterpret_cast(&InternalCall_MeshRenderer_GetCastShadows)); mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_SetCastShadows", reinterpret_cast(&InternalCall_MeshRenderer_SetCastShadows)); mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_GetReceiveShadows", reinterpret_cast(&InternalCall_MeshRenderer_GetReceiveShadows)); mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_SetReceiveShadows", reinterpret_cast(&InternalCall_MeshRenderer_SetReceiveShadows)); mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_GetRenderLayer", reinterpret_cast(&InternalCall_MeshRenderer_GetRenderLayer)); mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_SetRenderLayer", reinterpret_cast(&InternalCall_MeshRenderer_SetRenderLayer)); GetInternalCallRegistrationState() = true; } } // namespace MonoScriptRuntime::MonoScriptRuntime(Settings settings) : m_settings(std::move(settings)) { ResolveSettings(); } MonoScriptRuntime::~MonoScriptRuntime() { Shutdown(); } bool MonoScriptRuntime::Initialize() { ResolveSettings(); m_lastError.clear(); if (m_initialized) { return true; } if (!InitializeRootDomain()) { return false; } if (!CreateAppDomain()) { return false; } if (!LoadAssemblies()) { DestroyAppDomain(); return false; } if (!DiscoverScriptClasses()) { DestroyAppDomain(); return false; } m_initialized = true; return true; } void MonoScriptRuntime::Shutdown() { ClearManagedInstances(); ClearClassCache(); m_coreAssembly = nullptr; m_appAssembly = nullptr; m_coreImage = nullptr; m_appImage = nullptr; m_componentClass = nullptr; m_behaviourClass = nullptr; m_gameObjectClass = nullptr; m_monoBehaviourClass = nullptr; m_serializeFieldAttributeClass = nullptr; m_gameObjectConstructor = nullptr; m_managedGameObjectUUIDField = nullptr; m_gameObjectUUIDField = nullptr; m_scriptComponentUUIDField = nullptr; DestroyAppDomain(); m_activeScene = nullptr; GetInternalCallScene() = nullptr; GetInternalCallDeltaTime() = 0.0f; m_initialized = false; } bool MonoScriptRuntime::IsClassAvailable( const std::string& assemblyName, const std::string& namespaceName, const std::string& className) const { return FindClassMetadata(assemblyName, namespaceName, className) != nullptr; } bool MonoScriptRuntime::TryGetAvailableScriptClasses( std::vector& outClasses) const { outClasses.clear(); if (!m_initialized) { return false; } outClasses.reserve(m_classes.size()); for (const auto& [key, metadata] : m_classes) { (void)key; outClasses.push_back( ScriptClassDescriptor{ metadata.assemblyName, metadata.namespaceName, metadata.className }); } std::sort( outClasses.begin(), outClasses.end(), [](const ScriptClassDescriptor& lhs, const ScriptClassDescriptor& rhs) { if (lhs.assemblyName != rhs.assemblyName) { return lhs.assemblyName < rhs.assemblyName; } if (lhs.namespaceName != rhs.namespaceName) { return lhs.namespaceName < rhs.namespaceName; } return lhs.className < rhs.className; }); return true; } std::vector MonoScriptRuntime::GetScriptClassNames(const std::string& assemblyName) const { std::vector classes; if (!TryGetAvailableScriptClasses(classes)) { return {}; } std::vector classNames; classNames.reserve(classes.size()); for (const ScriptClassDescriptor& descriptor : classes) { if (!assemblyName.empty() && descriptor.assemblyName != assemblyName) { continue; } classNames.push_back(descriptor.GetFullName()); } return classNames; } bool MonoScriptRuntime::TryGetClassFieldMetadata( const std::string& assemblyName, const std::string& namespaceName, const std::string& className, std::vector& outFields) const { outFields.clear(); const ClassMetadata* metadata = FindClassMetadata(assemblyName, namespaceName, className); if (!metadata) { return false; } outFields.reserve(metadata->fields.size()); for (const auto& [fieldName, fieldMetadata] : metadata->fields) { outFields.push_back(ScriptFieldMetadata{fieldName, fieldMetadata.type}); } std::sort( outFields.begin(), outFields.end(), [](const ScriptFieldMetadata& lhs, const ScriptFieldMetadata& rhs) { return lhs.name < rhs.name; }); return true; } bool MonoScriptRuntime::TryGetClassFieldDefaultValues( const std::string& assemblyName, const std::string& namespaceName, const std::string& className, std::vector& outFields) const { outFields.clear(); const ClassMetadata* metadata = FindClassMetadata(assemblyName, namespaceName, className); if (!metadata) { return false; } SetCurrentDomain(); MonoObject* instance = mono_object_new(m_appDomain, metadata->monoClass); if (!instance) { return false; } mono_runtime_object_init(instance); outFields.reserve(metadata->fields.size()); for (const auto& [fieldName, fieldMetadata] : metadata->fields) { ScriptFieldValue value; if (!TryReadFieldValue(instance, fieldMetadata, value)) { outFields.clear(); return false; } outFields.push_back(ScriptFieldDefaultValue{fieldName, fieldMetadata.type, value}); } std::sort( outFields.begin(), outFields.end(), [](const ScriptFieldDefaultValue& lhs, const ScriptFieldDefaultValue& rhs) { return lhs.fieldName < rhs.fieldName; }); return true; } bool MonoScriptRuntime::HasManagedInstance(const ScriptComponent* component) const { const InstanceData* instanceData = FindInstance(component); return instanceData != nullptr && GetManagedObject(*instanceData) != nullptr; } MonoObject* MonoScriptRuntime::GetManagedInstanceObject(const ScriptComponent* component) const { const InstanceData* instanceData = FindInstance(component); return instanceData ? GetManagedObject(*instanceData) : nullptr; } bool MonoScriptRuntime::TryGetFieldValue( const ScriptComponent* component, const std::string& fieldName, ScriptFieldValue& outValue) const { const InstanceData* instanceData = FindInstance(component); if (!instanceData || !instanceData->classMetadata) { return false; } const auto fieldIt = instanceData->classMetadata->fields.find(fieldName); if (fieldIt == instanceData->classMetadata->fields.end()) { return false; } MonoObject* instance = GetManagedObject(*instanceData); if (!instance) { return false; } return TryReadFieldValue(instance, fieldIt->second, outValue); } void MonoScriptRuntime::OnRuntimeStart(Components::Scene* scene) { m_activeScene = nullptr; GetInternalCallDeltaTime() = 0.0f; if (Initialize()) { m_activeScene = scene; GetInternalCallScene() = scene; } } void MonoScriptRuntime::OnRuntimeStop(Components::Scene* scene) { (void)scene; ClearManagedInstances(); m_activeScene = nullptr; GetInternalCallScene() = nullptr; GetInternalCallDeltaTime() = 0.0f; } bool MonoScriptRuntime::TrySetManagedFieldValue( const ScriptRuntimeContext& context, const std::string& fieldName, const ScriptFieldValue& value) { const InstanceData* instanceData = FindInstance(context); if (!instanceData || !instanceData->classMetadata) { return false; } const auto metadataIt = instanceData->classMetadata->fields.find(fieldName); if (metadataIt == instanceData->classMetadata->fields.end()) { return false; } if (!IsScriptFieldValueCompatible(metadataIt->second.type, value)) { return false; } MonoObject* instance = GetManagedObject(*instanceData); if (!instance) { return false; } return TrySetFieldValue(instance, metadataIt->second, value); } bool MonoScriptRuntime::TryGetManagedFieldValue( const ScriptRuntimeContext& context, const std::string& fieldName, ScriptFieldValue& outValue) const { const InstanceData* instanceData = FindInstance(context); if (!instanceData || !instanceData->classMetadata) { return false; } const auto metadataIt = instanceData->classMetadata->fields.find(fieldName); if (metadataIt == instanceData->classMetadata->fields.end()) { return false; } MonoObject* instance = GetManagedObject(*instanceData); if (!instance) { return false; } return TryReadFieldValue(instance, metadataIt->second, outValue); } void MonoScriptRuntime::SyncManagedFieldsToStorage(const ScriptRuntimeContext& context) { if (!context.component) { return; } const InstanceData* instanceData = FindInstance(context); if (!instanceData || !instanceData->classMetadata) { return; } MonoObject* instance = GetManagedObject(*instanceData); if (!instance) { return; } ScriptFieldStorage& fieldStorage = context.component->GetFieldStorage(); const std::vector fieldNames = fieldStorage.GetFieldNames(); for (const std::string& fieldName : fieldNames) { StoredScriptField* storedField = fieldStorage.FindField(fieldName); if (!storedField) { continue; } const auto metadataIt = instanceData->classMetadata->fields.find(fieldName); if (metadataIt == instanceData->classMetadata->fields.end() || storedField->type != metadataIt->second.type) { continue; } ScriptFieldValue value; if (!TryReadFieldValue(instance, metadataIt->second, value)) { continue; } fieldStorage.SetFieldValue(fieldName, storedField->type, value); } } bool MonoScriptRuntime::CreateScriptInstance(const ScriptRuntimeContext& context) { if (!context.component) { SetError("Cannot create a managed script instance without a ScriptComponent."); return false; } if (FindInstance(context)) { return true; } if (!Initialize()) { return false; } const std::string assemblyName = context.component->GetAssemblyName().empty() ? m_settings.appAssemblyName : context.component->GetAssemblyName(); const ClassMetadata* classMetadata = FindClassMetadata( assemblyName, context.component->GetNamespaceName(), context.component->GetClassName()); if (!classMetadata) { SetError("Managed script class was not found: " + assemblyName + "|" + context.component->GetFullClassName()); return false; } SetCurrentDomain(); MonoObject* instance = mono_object_new(m_appDomain, classMetadata->monoClass); if (!instance) { SetError("Mono failed to allocate a managed object for " + classMetadata->fullName + "."); return false; } mono_runtime_object_init(instance); if (!ApplyContextFields(context, instance) || !ApplyStoredFields(context, *classMetadata, instance)) { return false; } const uint32_t gcHandle = mono_gchandle_new(instance, false); const InstanceKey key{context.gameObjectUUID, context.scriptComponentUUID}; m_instances[key] = InstanceData{classMetadata, gcHandle}; return true; } void MonoScriptRuntime::DestroyScriptInstance(const ScriptRuntimeContext& context) { InstanceData* instanceData = FindInstance(context); if (!instanceData) { return; } if (instanceData->gcHandle != 0) { mono_gchandle_free(instanceData->gcHandle); } m_instances.erase(InstanceKey{context.gameObjectUUID, context.scriptComponentUUID}); } void MonoScriptRuntime::InvokeMethod( const ScriptRuntimeContext& context, ScriptLifecycleMethod method, float deltaTime) { const InstanceData* instanceData = FindInstance(context); if (!instanceData || !instanceData->classMetadata) { return; } MonoMethod* managedMethod = instanceData->classMetadata->lifecycleMethods[static_cast(method)]; if (!managedMethod) { return; } MonoObject* instance = GetManagedObject(*instanceData); if (!instance) { return; } const float previousDeltaTime = GetInternalCallDeltaTime(); GetInternalCallDeltaTime() = deltaTime; InvokeManagedMethod(instance, managedMethod); GetInternalCallDeltaTime() = previousDeltaTime; } size_t MonoScriptRuntime::InstanceKeyHasher::operator()(const InstanceKey& key) const { const size_t h1 = std::hash{}(key.gameObjectUUID); const size_t h2 = std::hash{}(key.scriptComponentUUID); return h1 ^ (h2 + 0x9e3779b97f4a7c15ULL + (h1 << 6) + (h1 >> 2)); } void MonoScriptRuntime::ResolveSettings() { if (!m_settings.coreAssemblyPath.empty() && m_settings.assemblyDirectory.empty()) { m_settings.assemblyDirectory = m_settings.coreAssemblyPath.parent_path(); } if (!m_settings.appAssemblyPath.empty() && m_settings.assemblyDirectory.empty()) { m_settings.assemblyDirectory = m_settings.appAssemblyPath.parent_path(); } if (m_settings.coreAssemblyPath.empty() && !m_settings.assemblyDirectory.empty()) { m_settings.coreAssemblyPath = m_settings.assemblyDirectory / (m_settings.coreAssemblyName + ".dll"); } if (m_settings.appAssemblyPath.empty() && !m_settings.assemblyDirectory.empty()) { m_settings.appAssemblyPath = m_settings.assemblyDirectory / (m_settings.appAssemblyName + ".dll"); } if (m_settings.corlibDirectory.empty()) { if (!m_settings.assemblyDirectory.empty()) { m_settings.corlibDirectory = m_settings.assemblyDirectory; } else if (!m_settings.coreAssemblyPath.empty()) { m_settings.corlibDirectory = m_settings.coreAssemblyPath.parent_path(); } } } bool MonoScriptRuntime::InitializeRootDomain() { MonoRootState& rootState = GetMonoRootState(); if (rootState.initialized) { return true; } if (!m_settings.corlibDirectory.empty()) { const std::string corlibDirectory = m_settings.corlibDirectory.string(); mono_set_assemblies_path(corlibDirectory.c_str()); } mono_config_parse(nullptr); rootState.rootDomain = mono_jit_init_version("XCEngineRootDomain", "v4.0.30319"); if (!rootState.rootDomain) { SetError("Failed to initialize the Mono root domain."); return false; } mono_domain_set(rootState.rootDomain, true); RegisterInternalCalls(); rootState.initialized = true; return true; } bool MonoScriptRuntime::CreateAppDomain() { if (m_appDomain) { return true; } MonoRootState& rootState = GetMonoRootState(); if (!rootState.rootDomain) { SetError("Mono root domain is not initialized."); return false; } mono_domain_set(rootState.rootDomain, true); m_appDomain = mono_domain_create_appdomain(const_cast("XCEngineScriptDomain"), nullptr); if (!m_appDomain) { SetError("Failed to create the Mono app domain."); return false; } mono_domain_set(m_appDomain, true); return true; } void MonoScriptRuntime::DestroyAppDomain() { if (!m_appDomain) { return; } MonoRootState& rootState = GetMonoRootState(); if (rootState.rootDomain) { mono_domain_set(rootState.rootDomain, true); } mono_domain_unload(m_appDomain); m_appDomain = nullptr; if (rootState.rootDomain) { mono_domain_set(rootState.rootDomain, true); } } void MonoScriptRuntime::SetCurrentDomain() const { if (m_appDomain) { mono_domain_set(m_appDomain, true); } } bool MonoScriptRuntime::LoadAssemblies() { if (m_settings.coreAssemblyPath.empty() || m_settings.appAssemblyPath.empty()) { SetError("Managed assembly paths are not configured."); return false; } if (!std::filesystem::exists(m_settings.coreAssemblyPath)) { SetError("Script core assembly does not exist: " + m_settings.coreAssemblyPath.string()); return false; } if (!std::filesystem::exists(m_settings.appAssemblyPath)) { SetError("Game scripts assembly does not exist: " + m_settings.appAssemblyPath.string()); return false; } SetCurrentDomain(); m_coreAssembly = mono_domain_assembly_open(m_appDomain, m_settings.coreAssemblyPath.string().c_str()); if (!m_coreAssembly) { SetError("Failed to load script core assembly: " + m_settings.coreAssemblyPath.string()); return false; } m_coreImage = mono_assembly_get_image(m_coreAssembly); if (!m_coreImage) { SetError("Failed to access the script core image."); return false; } m_appAssembly = mono_domain_assembly_open(m_appDomain, m_settings.appAssemblyPath.string().c_str()); if (!m_appAssembly) { SetError("Failed to load game scripts assembly: " + m_settings.appAssemblyPath.string()); return false; } m_appImage = mono_assembly_get_image(m_appAssembly); if (!m_appImage) { SetError("Failed to access the game scripts image."); return false; } return true; } bool MonoScriptRuntime::DiscoverScriptClasses() { ClearClassCache(); m_componentClass = mono_class_from_name( m_coreImage, m_settings.baseNamespace.c_str(), "Component"); if (!m_componentClass) { SetError("Failed to locate the managed Component base type."); return false; } m_behaviourClass = mono_class_from_name( m_coreImage, m_settings.baseNamespace.c_str(), "Behaviour"); if (!m_behaviourClass) { SetError("Failed to locate the managed Behaviour base type."); return false; } m_gameObjectClass = mono_class_from_name( m_coreImage, m_settings.baseNamespace.c_str(), "GameObject"); if (!m_gameObjectClass) { SetError("Failed to locate the managed GameObject wrapper type."); return false; } m_gameObjectConstructor = mono_class_get_method_from_name(m_gameObjectClass, ".ctor", 1); m_managedGameObjectUUIDField = mono_class_get_field_from_name(m_gameObjectClass, "m_uuid"); if (!m_gameObjectConstructor || !m_managedGameObjectUUIDField) { SetError("Failed to locate the managed GameObject constructor or UUID field."); return false; } m_monoBehaviourClass = mono_class_from_name( m_coreImage, m_settings.baseNamespace.c_str(), m_settings.baseClassName.c_str()); if (!m_monoBehaviourClass) { SetError("Failed to locate the managed base type " + BuildFullClassName(m_settings.baseNamespace, m_settings.baseClassName) + "."); return false; } m_serializeFieldAttributeClass = mono_class_from_name( m_coreImage, m_settings.baseNamespace.c_str(), "SerializeField"); if (!m_serializeFieldAttributeClass) { SetError("Failed to locate the managed SerializeField attribute type."); return false; } m_gameObjectUUIDField = mono_class_get_field_from_name(m_componentClass, "m_gameObjectUUID"); m_scriptComponentUUIDField = mono_class_get_field_from_name(m_behaviourClass, "m_scriptComponentUUID"); if (!m_gameObjectUUIDField || !m_scriptComponentUUIDField) { SetError("Failed to locate the managed context fields for Component/Behaviour."); return false; } DiscoverScriptClassesInImage(m_settings.appAssemblyName, m_appImage); return true; } void MonoScriptRuntime::DiscoverScriptClassesInImage(const std::string& assemblyName, MonoImage* image) { if (!image) { return; } const int typeCount = mono_image_get_table_rows(image, MONO_TABLE_TYPEDEF); for (int index = 1; index <= typeCount; ++index) { const uint32_t typeToken = mono_metadata_make_token(MONO_TABLE_TYPEDEF, index); MonoClass* monoClass = mono_class_get(image, typeToken); if (!monoClass || !IsMonoBehaviourSubclass(monoClass)) { continue; } if ((mono_class_get_flags(monoClass) & MONO_TYPE_ATTR_ABSTRACT) != 0) { continue; } ClassMetadata metadata; metadata.assemblyName = assemblyName; metadata.namespaceName = SafeString(mono_class_get_namespace(monoClass)); metadata.className = SafeString(mono_class_get_name(monoClass)); metadata.fullName = BuildFullClassName(metadata.namespaceName, metadata.className); metadata.monoClass = monoClass; for (size_t methodIndex = 0; methodIndex < LifecycleMethodCount; ++methodIndex) { metadata.lifecycleMethods[methodIndex] = mono_class_get_method_from_name( monoClass, ToLifecycleMethodName(static_cast(methodIndex)), 0); } void* fieldIterator = nullptr; while (MonoClassField* field = mono_class_get_fields(monoClass, &fieldIterator)) { const uint32_t fieldFlags = mono_field_get_flags(field); if ((fieldFlags & MONO_FIELD_ATTR_STATIC) != 0 || (fieldFlags & MONO_FIELD_ATTR_LITERAL) != 0 || (fieldFlags & MONO_FIELD_ATTR_INIT_ONLY) != 0) { continue; } const bool isPublicField = (fieldFlags & MONO_FIELD_ATTR_PUBLIC) != 0; if (!isPublicField && !HasSerializeFieldAttribute(field)) { continue; } FieldMetadata fieldMetadata = BuildFieldMetadata(field); if (fieldMetadata.type == ScriptFieldType::None) { continue; } metadata.fields.emplace(mono_field_get_name(field), std::move(fieldMetadata)); } m_classes.emplace( BuildClassKey(metadata.assemblyName, metadata.namespaceName, metadata.className), std::move(metadata)); } } bool MonoScriptRuntime::IsMonoBehaviourSubclass(MonoClass* monoClass) const { if (!monoClass || !m_monoBehaviourClass || monoClass == m_monoBehaviourClass) { return false; } MonoClass* current = monoClass; while (current) { if (current == m_monoBehaviourClass) { return true; } current = mono_class_get_parent(current); } return false; } bool MonoScriptRuntime::IsSupportedComponentFieldClass(MonoClass* monoClass) const { if (!IsMonoClassOrSubclass(monoClass, m_componentClass)) { return false; } if (monoClass == m_componentClass || monoClass == m_behaviourClass || monoClass == m_monoBehaviourClass) { return false; } if (IsScriptComponentFieldClass(monoClass)) { return true; } const std::string namespaceName = SafeString(mono_class_get_namespace(monoClass)); const std::string className = SafeString(mono_class_get_name(monoClass)); if (namespaceName != m_settings.baseNamespace) { return false; } return className == "Transform" || className == "Camera" || className == "Light" || className == "MeshFilter" || className == "MeshRenderer"; } bool MonoScriptRuntime::IsScriptComponentFieldClass(MonoClass* monoClass) const { if (!monoClass || monoClass == m_monoBehaviourClass) { return false; } return IsMonoClassOrSubclass(monoClass, m_monoBehaviourClass); } bool MonoScriptRuntime::HasSerializeFieldAttribute(MonoClassField* field) const { if (!field || !m_serializeFieldAttributeClass) { return false; } MonoClass* ownerClass = mono_field_get_parent(field); if (!ownerClass) { return false; } MonoCustomAttrInfo* attributes = mono_custom_attrs_from_field(ownerClass, field); if (!attributes) { return false; } const mono_bool hasAttribute = mono_custom_attrs_has_attr(attributes, m_serializeFieldAttributeClass); mono_custom_attrs_free(attributes); return hasAttribute != 0; } MonoScriptRuntime::FieldMetadata MonoScriptRuntime::BuildFieldMetadata(MonoClassField* field) const { FieldMetadata metadata; metadata.field = field; if (!field) { return metadata; } MonoType* monoType = mono_field_get_type(field); if (!monoType) { return metadata; } switch (mono_type_get_type(monoType)) { case MONO_TYPE_R4: metadata.type = ScriptFieldType::Float; return metadata; case MONO_TYPE_R8: metadata.type = ScriptFieldType::Double; return metadata; case MONO_TYPE_BOOLEAN: metadata.type = ScriptFieldType::Bool; return metadata; case MONO_TYPE_I4: metadata.type = ScriptFieldType::Int32; return metadata; case MONO_TYPE_U8: metadata.type = ScriptFieldType::UInt64; return metadata; case MONO_TYPE_STRING: metadata.type = ScriptFieldType::String; return metadata; case MONO_TYPE_CLASS: { MonoClass* referenceClass = mono_class_from_mono_type(monoType); if (!referenceClass) { return metadata; } const std::string namespaceName = SafeString(mono_class_get_namespace(referenceClass)); const std::string className = SafeString(mono_class_get_name(referenceClass)); if (namespaceName == m_settings.baseNamespace && className == "GameObject") { metadata.type = ScriptFieldType::GameObject; return metadata; } if (IsSupportedComponentFieldClass(referenceClass)) { metadata.type = ScriptFieldType::Component; metadata.componentClass = referenceClass; } return metadata; } case MONO_TYPE_VALUETYPE: { MonoClass* valueTypeClass = mono_class_from_mono_type(monoType); if (!valueTypeClass) { return metadata; } if (mono_class_is_enum(valueTypeClass) != 0) { MonoType* underlyingType = mono_class_enum_basetype(valueTypeClass); if (!underlyingType) { return metadata; } switch (mono_type_get_type(underlyingType)) { case MONO_TYPE_I1: case MONO_TYPE_U1: case MONO_TYPE_I2: case MONO_TYPE_U2: case MONO_TYPE_I4: case MONO_TYPE_U4: metadata.type = ScriptFieldType::Int32; metadata.isEnum = true; metadata.enumUnderlyingType = static_cast(mono_type_get_type(underlyingType)); return metadata; default: return metadata; } } const std::string namespaceName = SafeString(mono_class_get_namespace(valueTypeClass)); const std::string className = SafeString(mono_class_get_name(valueTypeClass)); if (namespaceName != m_settings.baseNamespace) { return metadata; } if (className == "Vector2") { metadata.type = ScriptFieldType::Vector2; return metadata; } if (className == "Vector3") { metadata.type = ScriptFieldType::Vector3; return metadata; } if (className == "Vector4") { metadata.type = ScriptFieldType::Vector4; return metadata; } return metadata; } default: return metadata; } } const char* MonoScriptRuntime::ToLifecycleMethodName(ScriptLifecycleMethod method) { switch (method) { case ScriptLifecycleMethod::Awake: return "Awake"; case ScriptLifecycleMethod::OnEnable: return "OnEnable"; case ScriptLifecycleMethod::Start: return "Start"; case ScriptLifecycleMethod::FixedUpdate: return "FixedUpdate"; case ScriptLifecycleMethod::Update: return "Update"; case ScriptLifecycleMethod::LateUpdate: return "LateUpdate"; case ScriptLifecycleMethod::OnDisable: return "OnDisable"; case ScriptLifecycleMethod::OnDestroy: return "OnDestroy"; } return ""; } const MonoScriptRuntime::ClassMetadata* MonoScriptRuntime::FindClassMetadata( const std::string& assemblyName, const std::string& namespaceName, const std::string& className) const { const auto it = m_classes.find(BuildClassKey(assemblyName, namespaceName, className)); return it != m_classes.end() ? &it->second : nullptr; } MonoScriptRuntime::InstanceData* MonoScriptRuntime::FindInstance(const ScriptRuntimeContext& context) { const auto it = m_instances.find(InstanceKey{context.gameObjectUUID, context.scriptComponentUUID}); return it != m_instances.end() ? &it->second : nullptr; } const MonoScriptRuntime::InstanceData* MonoScriptRuntime::FindInstance(const ScriptRuntimeContext& context) const { const auto it = m_instances.find(InstanceKey{context.gameObjectUUID, context.scriptComponentUUID}); return it != m_instances.end() ? &it->second : nullptr; } const MonoScriptRuntime::InstanceData* MonoScriptRuntime::FindInstance(const ScriptComponent* component) const { if (!component || !component->GetGameObject()) { return nullptr; } const auto it = m_instances.find(InstanceKey{ component->GetGameObject()->GetUUID(), component->GetScriptComponentUUID() }); return it != m_instances.end() ? &it->second : nullptr; } MonoObject* MonoScriptRuntime::GetManagedObject(const InstanceData& instanceData) const { if (instanceData.gcHandle == 0) { return nullptr; } SetCurrentDomain(); return mono_gchandle_get_target(instanceData.gcHandle); } bool MonoScriptRuntime::ApplyContextFields(const ScriptRuntimeContext& context, MonoObject* instance) { if (!instance) { return false; } SetCurrentDomain(); if (m_gameObjectUUIDField) { uint64_t gameObjectUUID = context.gameObjectUUID; mono_field_set_value(instance, m_gameObjectUUIDField, &gameObjectUUID); } if (m_scriptComponentUUIDField) { uint64_t scriptComponentUUID = context.scriptComponentUUID; mono_field_set_value(instance, m_scriptComponentUUIDField, &scriptComponentUUID); } return true; } bool MonoScriptRuntime::ApplyStoredFields( const ScriptRuntimeContext& context, const ClassMetadata& metadata, MonoObject* instance) { if (!context.component || !instance) { return false; } const ScriptFieldStorage& fieldStorage = context.component->GetFieldStorage(); for (const std::string& fieldName : fieldStorage.GetFieldNames()) { const StoredScriptField* storedField = fieldStorage.FindField(fieldName); if (!storedField) { continue; } const auto metadataIt = metadata.fields.find(fieldName); if (metadataIt == metadata.fields.end() || storedField->type != metadataIt->second.type) { continue; } if (!TrySetFieldValue(instance, metadataIt->second, storedField->value)) { return false; } } return true; } MonoObject* MonoScriptRuntime::CreateManagedComponentWrapper(MonoClass* componentClass, uint64_t gameObjectUUID) { if (!m_initialized || !componentClass || gameObjectUUID == 0) { return nullptr; } SetCurrentDomain(); MonoMethod* constructor = mono_class_get_method_from_name(componentClass, ".ctor", 1); if (!constructor) { return nullptr; } MonoObject* managedObject = mono_object_new(m_appDomain, componentClass); if (!managedObject) { return nullptr; } void* args[1]; uint64_t uuidArgument = gameObjectUUID; args[0] = &uuidArgument; MonoObject* exception = nullptr; mono_runtime_invoke(constructor, managedObject, args, &exception); if (exception) { RecordException(exception); return nullptr; } return managedObject; } MonoObject* MonoScriptRuntime::CreateManagedGameObject(uint64_t gameObjectUUID) { if (gameObjectUUID == 0 || !m_gameObjectClass || !m_gameObjectConstructor) { return nullptr; } SetCurrentDomain(); MonoObject* managedObject = mono_object_new(m_appDomain, m_gameObjectClass); if (!managedObject) { return nullptr; } void* args[1]; uint64_t uuidArgument = gameObjectUUID; args[0] = &uuidArgument; MonoObject* exception = nullptr; mono_runtime_invoke(m_gameObjectConstructor, managedObject, args, &exception); if (exception) { RecordException(exception); return nullptr; } return managedObject; } bool MonoScriptRuntime::TryExtractComponentReference( MonoObject* managedObject, ComponentReference& outReference) const { outReference = ComponentReference{}; if (!managedObject) { return true; } if (!m_componentClass || !m_gameObjectUUIDField) { return false; } SetCurrentDomain(); MonoClass* monoClass = mono_object_get_class(managedObject); if (!IsMonoClassOrSubclass(monoClass, m_componentClass)) { return false; } uint64_t gameObjectUUID = 0; mono_field_get_value(managedObject, m_gameObjectUUIDField, &gameObjectUUID); if (gameObjectUUID == 0) { return false; } uint64_t scriptComponentUUID = 0; if (m_scriptComponentUUIDField && IsMonoClassOrSubclass(monoClass, m_behaviourClass)) { mono_field_get_value(managedObject, m_scriptComponentUUIDField, &scriptComponentUUID); } outReference = ComponentReference{gameObjectUUID, scriptComponentUUID}; return true; } bool MonoScriptRuntime::DestroyManagedObject(MonoObject* managedObject) { if (!m_initialized || !managedObject) { return false; } SetCurrentDomain(); MonoClass* monoClass = mono_object_get_class(managedObject); if (!monoClass) { return false; } if (monoClass == m_gameObjectClass) { uint64_t gameObjectUUID = 0; mono_field_get_value(managedObject, m_managedGameObjectUUIDField, &gameObjectUUID); Components::Scene* scene = GetInternalCallScene(); Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!scene || !gameObject || gameObject->GetScene() != scene) { return false; } scene->DestroyGameObject(gameObject); return true; } if (monoClass != m_componentClass && !mono_class_is_subclass_of(monoClass, m_componentClass, false)) { return false; } uint64_t gameObjectUUID = 0; mono_field_get_value(managedObject, m_gameObjectUUIDField, &gameObjectUUID); Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID); if (!gameObject) { return false; } if (monoClass == m_behaviourClass || mono_class_is_subclass_of(monoClass, m_behaviourClass, false)) { uint64_t scriptComponentUUID = 0; mono_field_get_value(managedObject, m_scriptComponentUUIDField, &scriptComponentUUID); if (scriptComponentUUID != 0) { ScriptComponent* component = FindScriptComponentByUUID(scriptComponentUUID); return DestroyNativeComponentInstance(gameObject, component); } } const ManagedComponentTypeInfo typeInfo = ResolveManagedComponentTypeInfo(monoClass); switch (typeInfo.kind) { case ManagedComponentKind::Camera: return DestroyNativeComponentInstance(gameObject, gameObject->GetComponent()); case ManagedComponentKind::Light: return DestroyNativeComponentInstance(gameObject, gameObject->GetComponent()); case ManagedComponentKind::MeshFilter: return DestroyNativeComponentInstance(gameObject, gameObject->GetComponent()); case ManagedComponentKind::MeshRenderer: return DestroyNativeComponentInstance(gameObject, gameObject->GetComponent()); case ManagedComponentKind::Transform: case ManagedComponentKind::Script: case ManagedComponentKind::Unknown: return false; } return false; } bool MonoScriptRuntime::TryExtractGameObjectReference( MonoObject* managedObject, GameObjectReference& outReference) const { outReference = GameObjectReference{}; if (!managedObject) { return true; } if (!m_gameObjectClass || !m_managedGameObjectUUIDField) { return false; } if (mono_object_get_class(managedObject) != m_gameObjectClass) { return false; } uint64_t gameObjectUUID = 0; mono_field_get_value(managedObject, m_managedGameObjectUUIDField, &gameObjectUUID); outReference = GameObjectReference{gameObjectUUID}; return true; } bool MonoScriptRuntime::TrySetFieldValue( MonoObject* instance, const FieldMetadata& fieldMetadata, const ScriptFieldValue& value) { if (!instance || !fieldMetadata.field) { return false; } SetCurrentDomain(); if (fieldMetadata.isEnum) { if (!std::holds_alternative(value)) { return false; } const int32_t storedValue = std::get(value); switch (fieldMetadata.enumUnderlyingType) { case MONO_TYPE_I1: { if (storedValue < std::numeric_limits::min() || storedValue > std::numeric_limits::max()) { return false; } int8_t nativeValue = static_cast(storedValue); mono_field_set_value(instance, fieldMetadata.field, &nativeValue); return true; } case MONO_TYPE_U1: { if (storedValue < 0 || storedValue > std::numeric_limits::max()) { return false; } uint8_t nativeValue = static_cast(storedValue); mono_field_set_value(instance, fieldMetadata.field, &nativeValue); return true; } case MONO_TYPE_I2: { if (storedValue < std::numeric_limits::min() || storedValue > std::numeric_limits::max()) { return false; } int16_t nativeValue = static_cast(storedValue); mono_field_set_value(instance, fieldMetadata.field, &nativeValue); return true; } case MONO_TYPE_U2: { if (storedValue < 0 || storedValue > std::numeric_limits::max()) { return false; } uint16_t nativeValue = static_cast(storedValue); mono_field_set_value(instance, fieldMetadata.field, &nativeValue); return true; } case MONO_TYPE_I4: { int32_t nativeValue = storedValue; mono_field_set_value(instance, fieldMetadata.field, &nativeValue); return true; } case MONO_TYPE_U4: { if (storedValue < 0) { return false; } uint32_t nativeValue = static_cast(storedValue); mono_field_set_value(instance, fieldMetadata.field, &nativeValue); return true; } default: return false; } } switch (fieldMetadata.type) { case ScriptFieldType::Float: { float nativeValue = std::get(value); mono_field_set_value(instance, fieldMetadata.field, &nativeValue); return true; } case ScriptFieldType::Double: { double nativeValue = std::get(value); mono_field_set_value(instance, fieldMetadata.field, &nativeValue); return true; } case ScriptFieldType::Bool: { mono_bool nativeValue = std::get(value) ? 1 : 0; mono_field_set_value(instance, fieldMetadata.field, &nativeValue); return true; } case ScriptFieldType::Int32: { int32_t nativeValue = std::get(value); mono_field_set_value(instance, fieldMetadata.field, &nativeValue); return true; } case ScriptFieldType::UInt64: { uint64_t nativeValue = std::get(value); mono_field_set_value(instance, fieldMetadata.field, &nativeValue); return true; } case ScriptFieldType::String: { MonoString* managedString = mono_string_new(m_appDomain, std::get(value).c_str()); mono_field_set_value(instance, fieldMetadata.field, managedString); return true; } case ScriptFieldType::Vector2: { Math::Vector2 nativeValue = std::get(value); mono_field_set_value(instance, fieldMetadata.field, &nativeValue); return true; } case ScriptFieldType::Vector3: { Math::Vector3 nativeValue = std::get(value); mono_field_set_value(instance, fieldMetadata.field, &nativeValue); return true; } case ScriptFieldType::Vector4: { Math::Vector4 nativeValue = std::get(value); mono_field_set_value(instance, fieldMetadata.field, &nativeValue); return true; } case ScriptFieldType::GameObject: { const GameObjectReference reference = std::get(value); MonoObject* managedGameObject = CreateManagedGameObject(reference.gameObjectUUID); if (reference.gameObjectUUID != 0 && !managedGameObject) { return false; } mono_field_set_value(instance, fieldMetadata.field, managedGameObject); return true; } case ScriptFieldType::Component: { const ComponentReference reference = std::get(value); if (reference.gameObjectUUID == 0 && reference.scriptComponentUUID == 0) { mono_field_set_value(instance, fieldMetadata.field, nullptr); return true; } if (!fieldMetadata.componentClass) { return false; } MonoObject* managedComponent = nullptr; if (IsScriptComponentFieldClass(fieldMetadata.componentClass)) { if (reference.gameObjectUUID == 0 || reference.scriptComponentUUID == 0) { return false; } ScriptComponent* component = FindScriptComponentByUUID(reference.scriptComponentUUID); if (!component || !component->GetGameObject() || component->GetGameObject()->GetUUID() != reference.gameObjectUUID || component->GetAssemblyName() != m_settings.appAssemblyName || component->GetNamespaceName() != SafeString(mono_class_get_namespace(fieldMetadata.componentClass)) || component->GetClassName() != SafeString(mono_class_get_name(fieldMetadata.componentClass))) { return false; } if (!HasManagedInstance(component)) { ScriptEngine::Get().OnScriptComponentEnabled(component); } managedComponent = GetManagedInstanceObject(component); if (!managedComponent) { return false; } } else { if (reference.gameObjectUUID == 0 || reference.scriptComponentUUID != 0) { return false; } Components::GameObject* targetObject = FindGameObjectByUUID(reference.gameObjectUUID); if (!targetObject) { return false; } const std::string namespaceName = SafeString(mono_class_get_namespace(fieldMetadata.componentClass)); const std::string className = SafeString(mono_class_get_name(fieldMetadata.componentClass)); if (namespaceName != m_settings.baseNamespace) { return false; } bool hasComponent = false; if (className == "Transform") { hasComponent = targetObject->GetTransform() != nullptr; } else if (className == "Camera") { hasComponent = targetObject->GetComponent() != nullptr; } else if (className == "Light") { hasComponent = targetObject->GetComponent() != nullptr; } else if (className == "MeshFilter") { hasComponent = targetObject->GetComponent() != nullptr; } else if (className == "MeshRenderer") { hasComponent = targetObject->GetComponent() != nullptr; } else { return false; } if (!hasComponent) { return false; } managedComponent = CreateManagedComponentWrapper(fieldMetadata.componentClass, reference.gameObjectUUID); if (!managedComponent) { return false; } } mono_field_set_value(instance, fieldMetadata.field, managedComponent); return true; } case ScriptFieldType::None: return false; } return false; } bool MonoScriptRuntime::TryReadFieldValue( MonoObject* instance, const FieldMetadata& fieldMetadata, ScriptFieldValue& outValue) const { if (!instance || !fieldMetadata.field) { return false; } if (fieldMetadata.isEnum) { switch (fieldMetadata.enumUnderlyingType) { case MONO_TYPE_I1: { int8_t nativeValue = 0; mono_field_get_value(instance, fieldMetadata.field, &nativeValue); outValue = static_cast(nativeValue); return true; } case MONO_TYPE_U1: { uint8_t nativeValue = 0; mono_field_get_value(instance, fieldMetadata.field, &nativeValue); outValue = static_cast(nativeValue); return true; } case MONO_TYPE_I2: { int16_t nativeValue = 0; mono_field_get_value(instance, fieldMetadata.field, &nativeValue); outValue = static_cast(nativeValue); return true; } case MONO_TYPE_U2: { uint16_t nativeValue = 0; mono_field_get_value(instance, fieldMetadata.field, &nativeValue); outValue = static_cast(nativeValue); return true; } case MONO_TYPE_I4: { int32_t nativeValue = 0; mono_field_get_value(instance, fieldMetadata.field, &nativeValue); outValue = nativeValue; return true; } case MONO_TYPE_U4: { uint32_t nativeValue = 0; mono_field_get_value(instance, fieldMetadata.field, &nativeValue); if (nativeValue > static_cast(std::numeric_limits::max())) { return false; } outValue = static_cast(nativeValue); return true; } default: return false; } } switch (fieldMetadata.type) { case ScriptFieldType::Float: { float nativeValue = 0.0f; mono_field_get_value(instance, fieldMetadata.field, &nativeValue); outValue = nativeValue; return true; } case ScriptFieldType::Double: { double nativeValue = 0.0; mono_field_get_value(instance, fieldMetadata.field, &nativeValue); outValue = nativeValue; return true; } case ScriptFieldType::Bool: { mono_bool nativeValue = 0; mono_field_get_value(instance, fieldMetadata.field, &nativeValue); outValue = (nativeValue != 0); return true; } case ScriptFieldType::Int32: { int32_t nativeValue = 0; mono_field_get_value(instance, fieldMetadata.field, &nativeValue); outValue = nativeValue; return true; } case ScriptFieldType::UInt64: { uint64_t nativeValue = 0; mono_field_get_value(instance, fieldMetadata.field, &nativeValue); outValue = nativeValue; return true; } case ScriptFieldType::String: { MonoObject* managedObject = mono_field_get_value_object(m_appDomain, fieldMetadata.field, instance); if (!managedObject) { outValue = std::string(); return true; } MonoString* managedString = reinterpret_cast(managedObject); char* utf8 = mono_string_to_utf8(managedString); outValue = utf8 ? std::string(utf8) : std::string(); if (utf8) { mono_free(utf8); } return true; } case ScriptFieldType::Vector2: { Math::Vector2 nativeValue; mono_field_get_value(instance, fieldMetadata.field, &nativeValue); outValue = nativeValue; return true; } case ScriptFieldType::Vector3: { Math::Vector3 nativeValue; mono_field_get_value(instance, fieldMetadata.field, &nativeValue); outValue = nativeValue; return true; } case ScriptFieldType::Vector4: { Math::Vector4 nativeValue; mono_field_get_value(instance, fieldMetadata.field, &nativeValue); outValue = nativeValue; return true; } case ScriptFieldType::GameObject: { MonoObject* managedObject = mono_field_get_value_object(m_appDomain, fieldMetadata.field, instance); GameObjectReference reference; if (!TryExtractGameObjectReference(managedObject, reference)) { return false; } outValue = reference; return true; } case ScriptFieldType::Component: { MonoObject* managedObject = mono_field_get_value_object(m_appDomain, fieldMetadata.field, instance); ComponentReference reference; if (!TryExtractComponentReference(managedObject, reference)) { return false; } outValue = reference; return true; } case ScriptFieldType::None: return false; } return false; } void MonoScriptRuntime::ClearManagedInstances() { for (auto& [key, instanceData] : m_instances) { (void)key; if (instanceData.gcHandle != 0) { mono_gchandle_free(instanceData.gcHandle); } } m_instances.clear(); } void MonoScriptRuntime::ClearClassCache() { m_classes.clear(); } bool MonoScriptRuntime::InvokeManagedMethod(MonoObject* instance, MonoMethod* method) { if (!instance || !method) { return false; } SetCurrentDomain(); MonoObject* exception = nullptr; mono_runtime_invoke(method, instance, nullptr, &exception); if (exception) { RecordException(exception); return false; } return true; } void MonoScriptRuntime::RecordException(MonoObject* exception) { m_lastError = "Managed exception"; if (!exception) { return; } MonoObject* secondaryException = nullptr; MonoString* exceptionString = mono_object_to_string(exception, &secondaryException); if (!exceptionString || secondaryException) { return; } char* utf8 = mono_string_to_utf8(exceptionString); if (!utf8) { return; } m_lastError = utf8; mono_free(utf8); } void MonoScriptRuntime::SetError(const std::string& error) { m_lastError = error; } } // namespace Scripting } // namespace XCEngine