Files
XCEngine/engine/src/Scripting/Mono/MonoScriptRuntime.cpp

3200 lines
116 KiB
C++

#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 <mono/jit/jit.h>
#include <mono/metadata/appdomain.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/attrdefs.h>
#include <mono/metadata/blob.h>
#include <mono/metadata/class.h>
#include <mono/metadata/image.h>
#include <mono/metadata/loader.h>
#include <mono/metadata/metadata.h>
#include <mono/metadata/mono-config.h>
#include <mono/metadata/object.h>
#include <mono/metadata/reflection.h>
#include <algorithm>
#include <limits>
#include <utility>
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<MonoScriptRuntime*>(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<ScriptComponent>()) {
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<Components::CameraComponent>();
case ManagedComponentKind::Light:
return gameObject->GetComponent<Components::LightComponent>();
case ManagedComponentKind::MeshFilter:
return gameObject->GetComponent<Components::MeshFilterComponent>();
case ManagedComponentKind::MeshRenderer:
return gameObject->GetComponent<Components::MeshRendererComponent>();
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<Components::CameraComponent>()
? static_cast<Components::Component*>(gameObject->GetComponent<Components::CameraComponent>())
: static_cast<Components::Component*>(gameObject->AddComponent<Components::CameraComponent>());
case ManagedComponentKind::Light:
return gameObject->GetComponent<Components::LightComponent>()
? static_cast<Components::Component*>(gameObject->GetComponent<Components::LightComponent>())
: static_cast<Components::Component*>(gameObject->AddComponent<Components::LightComponent>());
case ManagedComponentKind::MeshFilter:
return gameObject->GetComponent<Components::MeshFilterComponent>()
? static_cast<Components::Component*>(gameObject->GetComponent<Components::MeshFilterComponent>())
: static_cast<Components::Component*>(gameObject->AddComponent<Components::MeshFilterComponent>());
case ManagedComponentKind::MeshRenderer:
return gameObject->GetComponent<Components::MeshRendererComponent>()
? static_cast<Components::Component*>(gameObject->GetComponent<Components::MeshRendererComponent>())
: static_cast<Components::Component*>(gameObject->AddComponent<Components::MeshRendererComponent>());
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<ScriptComponent>()) {
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<Components::CameraComponent>() : nullptr;
}
Components::LightComponent* FindLightComponent(uint64_t gameObjectUUID) {
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
return gameObject ? gameObject->GetComponent<Components::LightComponent>() : nullptr;
}
Components::MeshFilterComponent* FindMeshFilterComponent(uint64_t gameObjectUUID) {
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
return gameObject ? gameObject->GetComponent<Components::MeshFilterComponent>() : nullptr;
}
Components::MeshRendererComponent* FindMeshRendererComponent(uint64_t gameObjectUUID) {
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
return gameObject ? gameObject->GetComponent<Components::MeshRendererComponent>() : nullptr;
}
Components::Space ResolveManagedSpace(int32_t value) {
return value == static_cast<int32_t>(Components::Space::World)
? Components::Space::World
: Components::Space::Self;
}
MonoArray* CreateManagedComponentArray(MonoClass* componentClass, const std::vector<MonoObject*>& components) {
if (!componentClass) {
return nullptr;
}
MonoDomain* domain = mono_domain_get();
if (!domain) {
return nullptr;
}
MonoArray* array = mono_array_new(domain, componentClass, static_cast<uintptr_t>(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<XCEngine::Input::KeyCode>(keyCode)) ? 1 : 0;
}
mono_bool InternalCall_Input_GetKeyDown(int32_t keyCode) {
return XCEngine::Input::InputManager::Get().IsKeyPressed(
static_cast<XCEngine::Input::KeyCode>(keyCode)) ? 1 : 0;
}
mono_bool InternalCall_Input_GetKeyUp(int32_t keyCode) {
return XCEngine::Input::InputManager::Get().IsKeyReleased(
static_cast<XCEngine::Input::KeyCode>(keyCode)) ? 1 : 0;
}
mono_bool InternalCall_Input_GetMouseButton(int32_t button) {
return XCEngine::Input::InputManager::Get().IsMouseButtonDown(
static_cast<XCEngine::Input::MouseButton>(button)) ? 1 : 0;
}
mono_bool InternalCall_Input_GetMouseButtonDown(int32_t button) {
return XCEngine::Input::InputManager::Get().IsMouseButtonClicked(
static_cast<XCEngine::Input::MouseButton>(button)) ? 1 : 0;
}
mono_bool InternalCall_Input_GetMouseButtonUp(int32_t button) {
return XCEngine::Input::InputManager::Get().IsMouseButtonReleased(
static_cast<XCEngine::Input::MouseButton>(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<int32_t>(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<uint8_t>(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<MonoObject*> 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<ScriptComponent>()) {
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<Components::TransformComponent>());
break;
case ManagedComponentKind::Camera:
appendNativeComponents(gameObject->GetComponents<Components::CameraComponent>());
break;
case ManagedComponentKind::Light:
appendNativeComponents(gameObject->GetComponents<Components::LightComponent>());
break;
case ManagedComponentKind::MeshFilter:
appendNativeComponents(gameObject->GetComponents<Components::MeshFilterComponent>());
break;
case ManagedComponentKind::MeshRenderer:
appendNativeComponents(gameObject->GetComponents<Components::MeshRendererComponent>());
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<ScriptComponent>();
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<int32_t>(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<size_t>(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<int32_t>(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<size_t>(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<size_t>(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<int32_t>(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<uint32_t>(std::max(value, 0)));
}
void RegisterInternalCalls() {
if (GetInternalCallRegistrationState()) {
return;
}
mono_add_internal_call("XCEngine.InternalCalls::Debug_Log", reinterpret_cast<const void*>(&InternalCall_Debug_Log));
mono_add_internal_call("XCEngine.InternalCalls::Debug_LogWarning", reinterpret_cast<const void*>(&InternalCall_Debug_LogWarning));
mono_add_internal_call("XCEngine.InternalCalls::Debug_LogError", reinterpret_cast<const void*>(&InternalCall_Debug_LogError));
mono_add_internal_call("XCEngine.InternalCalls::Time_GetDeltaTime", reinterpret_cast<const void*>(&InternalCall_Time_GetDeltaTime));
mono_add_internal_call("XCEngine.InternalCalls::Time_GetFixedDeltaTime", reinterpret_cast<const void*>(&InternalCall_Time_GetFixedDeltaTime));
mono_add_internal_call("XCEngine.InternalCalls::Input_GetKey", reinterpret_cast<const void*>(&InternalCall_Input_GetKey));
mono_add_internal_call("XCEngine.InternalCalls::Input_GetKeyDown", reinterpret_cast<const void*>(&InternalCall_Input_GetKeyDown));
mono_add_internal_call("XCEngine.InternalCalls::Input_GetKeyUp", reinterpret_cast<const void*>(&InternalCall_Input_GetKeyUp));
mono_add_internal_call("XCEngine.InternalCalls::Input_GetMouseButton", reinterpret_cast<const void*>(&InternalCall_Input_GetMouseButton));
mono_add_internal_call("XCEngine.InternalCalls::Input_GetMouseButtonDown", reinterpret_cast<const void*>(&InternalCall_Input_GetMouseButtonDown));
mono_add_internal_call("XCEngine.InternalCalls::Input_GetMouseButtonUp", reinterpret_cast<const void*>(&InternalCall_Input_GetMouseButtonUp));
mono_add_internal_call("XCEngine.InternalCalls::Input_GetButton", reinterpret_cast<const void*>(&InternalCall_Input_GetButton));
mono_add_internal_call("XCEngine.InternalCalls::Input_GetButtonDown", reinterpret_cast<const void*>(&InternalCall_Input_GetButtonDown));
mono_add_internal_call("XCEngine.InternalCalls::Input_GetButtonUp", reinterpret_cast<const void*>(&InternalCall_Input_GetButtonUp));
mono_add_internal_call("XCEngine.InternalCalls::Input_GetAxis", reinterpret_cast<const void*>(&InternalCall_Input_GetAxis));
mono_add_internal_call("XCEngine.InternalCalls::Input_GetAxisRaw", reinterpret_cast<const void*>(&InternalCall_Input_GetAxisRaw));
mono_add_internal_call("XCEngine.InternalCalls::Input_GetAnyKey", reinterpret_cast<const void*>(&InternalCall_Input_GetAnyKey));
mono_add_internal_call("XCEngine.InternalCalls::Input_GetAnyKeyDown", reinterpret_cast<const void*>(&InternalCall_Input_GetAnyKeyDown));
mono_add_internal_call("XCEngine.InternalCalls::Input_GetMousePosition", reinterpret_cast<const void*>(&InternalCall_Input_GetMousePosition));
mono_add_internal_call("XCEngine.InternalCalls::Input_GetMouseScrollDelta", reinterpret_cast<const void*>(&InternalCall_Input_GetMouseScrollDelta));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetName", reinterpret_cast<const void*>(&InternalCall_GameObject_GetName));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_SetName", reinterpret_cast<const void*>(&InternalCall_GameObject_SetName));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetTag", reinterpret_cast<const void*>(&InternalCall_GameObject_GetTag));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_SetTag", reinterpret_cast<const void*>(&InternalCall_GameObject_SetTag));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_CompareTag", reinterpret_cast<const void*>(&InternalCall_GameObject_CompareTag));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetLayer", reinterpret_cast<const void*>(&InternalCall_GameObject_GetLayer));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_SetLayer", reinterpret_cast<const void*>(&InternalCall_GameObject_SetLayer));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetActiveSelf", reinterpret_cast<const void*>(&InternalCall_GameObject_GetActiveSelf));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetActiveInHierarchy", reinterpret_cast<const void*>(&InternalCall_GameObject_GetActiveInHierarchy));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_SetActive", reinterpret_cast<const void*>(&InternalCall_GameObject_SetActive));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_HasComponent", reinterpret_cast<const void*>(&InternalCall_GameObject_HasComponent));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetComponent", reinterpret_cast<const void*>(&InternalCall_GameObject_GetComponent));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetComponents", reinterpret_cast<const void*>(&InternalCall_GameObject_GetComponents));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetComponentInChildren", reinterpret_cast<const void*>(&InternalCall_GameObject_GetComponentInChildren));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_GetComponentInParent", reinterpret_cast<const void*>(&InternalCall_GameObject_GetComponentInParent));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_AddComponent", reinterpret_cast<const void*>(&InternalCall_GameObject_AddComponent));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_Find", reinterpret_cast<const void*>(&InternalCall_GameObject_Find));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_Create", reinterpret_cast<const void*>(&InternalCall_GameObject_Create));
mono_add_internal_call("XCEngine.InternalCalls::GameObject_Destroy", reinterpret_cast<const void*>(&InternalCall_GameObject_Destroy));
mono_add_internal_call("XCEngine.InternalCalls::Object_Destroy", reinterpret_cast<const void*>(&InternalCall_Object_Destroy));
mono_add_internal_call("XCEngine.InternalCalls::Behaviour_GetEnabled", reinterpret_cast<const void*>(&InternalCall_Behaviour_GetEnabled));
mono_add_internal_call("XCEngine.InternalCalls::Behaviour_SetEnabled", reinterpret_cast<const void*>(&InternalCall_Behaviour_SetEnabled));
mono_add_internal_call("XCEngine.InternalCalls::Transform_GetLocalPosition", reinterpret_cast<const void*>(&InternalCall_Transform_GetLocalPosition));
mono_add_internal_call("XCEngine.InternalCalls::Transform_SetLocalPosition", reinterpret_cast<const void*>(&InternalCall_Transform_SetLocalPosition));
mono_add_internal_call("XCEngine.InternalCalls::Transform_GetLocalRotation", reinterpret_cast<const void*>(&InternalCall_Transform_GetLocalRotation));
mono_add_internal_call("XCEngine.InternalCalls::Transform_SetLocalRotation", reinterpret_cast<const void*>(&InternalCall_Transform_SetLocalRotation));
mono_add_internal_call("XCEngine.InternalCalls::Transform_GetLocalScale", reinterpret_cast<const void*>(&InternalCall_Transform_GetLocalScale));
mono_add_internal_call("XCEngine.InternalCalls::Transform_SetLocalScale", reinterpret_cast<const void*>(&InternalCall_Transform_SetLocalScale));
mono_add_internal_call("XCEngine.InternalCalls::Transform_GetLocalEulerAngles", reinterpret_cast<const void*>(&InternalCall_Transform_GetLocalEulerAngles));
mono_add_internal_call("XCEngine.InternalCalls::Transform_SetLocalEulerAngles", reinterpret_cast<const void*>(&InternalCall_Transform_SetLocalEulerAngles));
mono_add_internal_call("XCEngine.InternalCalls::Transform_GetPosition", reinterpret_cast<const void*>(&InternalCall_Transform_GetPosition));
mono_add_internal_call("XCEngine.InternalCalls::Transform_SetPosition", reinterpret_cast<const void*>(&InternalCall_Transform_SetPosition));
mono_add_internal_call("XCEngine.InternalCalls::Transform_GetRotation", reinterpret_cast<const void*>(&InternalCall_Transform_GetRotation));
mono_add_internal_call("XCEngine.InternalCalls::Transform_SetRotation", reinterpret_cast<const void*>(&InternalCall_Transform_SetRotation));
mono_add_internal_call("XCEngine.InternalCalls::Transform_GetScale", reinterpret_cast<const void*>(&InternalCall_Transform_GetScale));
mono_add_internal_call("XCEngine.InternalCalls::Transform_SetScale", reinterpret_cast<const void*>(&InternalCall_Transform_SetScale));
mono_add_internal_call("XCEngine.InternalCalls::Transform_GetForward", reinterpret_cast<const void*>(&InternalCall_Transform_GetForward));
mono_add_internal_call("XCEngine.InternalCalls::Transform_GetRight", reinterpret_cast<const void*>(&InternalCall_Transform_GetRight));
mono_add_internal_call("XCEngine.InternalCalls::Transform_GetUp", reinterpret_cast<const void*>(&InternalCall_Transform_GetUp));
mono_add_internal_call("XCEngine.InternalCalls::Transform_Translate", reinterpret_cast<const void*>(&InternalCall_Transform_Translate));
mono_add_internal_call("XCEngine.InternalCalls::Transform_Rotate", reinterpret_cast<const void*>(&InternalCall_Transform_Rotate));
mono_add_internal_call("XCEngine.InternalCalls::Transform_RotateAxisAngle", reinterpret_cast<const void*>(&InternalCall_Transform_RotateAxisAngle));
mono_add_internal_call("XCEngine.InternalCalls::Transform_LookAt", reinterpret_cast<const void*>(&InternalCall_Transform_LookAt));
mono_add_internal_call("XCEngine.InternalCalls::Transform_LookAtWithUp", reinterpret_cast<const void*>(&InternalCall_Transform_LookAtWithUp));
mono_add_internal_call("XCEngine.InternalCalls::Transform_TransformPoint", reinterpret_cast<const void*>(&InternalCall_Transform_TransformPoint));
mono_add_internal_call("XCEngine.InternalCalls::Transform_InverseTransformPoint", reinterpret_cast<const void*>(&InternalCall_Transform_InverseTransformPoint));
mono_add_internal_call("XCEngine.InternalCalls::Transform_TransformDirection", reinterpret_cast<const void*>(&InternalCall_Transform_TransformDirection));
mono_add_internal_call("XCEngine.InternalCalls::Transform_InverseTransformDirection", reinterpret_cast<const void*>(&InternalCall_Transform_InverseTransformDirection));
mono_add_internal_call("XCEngine.InternalCalls::Transform_GetParent", reinterpret_cast<const void*>(&InternalCall_Transform_GetParent));
mono_add_internal_call("XCEngine.InternalCalls::Transform_SetParent", reinterpret_cast<const void*>(&InternalCall_Transform_SetParent));
mono_add_internal_call("XCEngine.InternalCalls::Transform_GetChildCount", reinterpret_cast<const void*>(&InternalCall_Transform_GetChildCount));
mono_add_internal_call("XCEngine.InternalCalls::Transform_GetChild", reinterpret_cast<const void*>(&InternalCall_Transform_GetChild));
mono_add_internal_call("XCEngine.InternalCalls::Camera_GetFieldOfView", reinterpret_cast<const void*>(&InternalCall_Camera_GetFieldOfView));
mono_add_internal_call("XCEngine.InternalCalls::Camera_SetFieldOfView", reinterpret_cast<const void*>(&InternalCall_Camera_SetFieldOfView));
mono_add_internal_call("XCEngine.InternalCalls::Camera_GetNearClipPlane", reinterpret_cast<const void*>(&InternalCall_Camera_GetNearClipPlane));
mono_add_internal_call("XCEngine.InternalCalls::Camera_SetNearClipPlane", reinterpret_cast<const void*>(&InternalCall_Camera_SetNearClipPlane));
mono_add_internal_call("XCEngine.InternalCalls::Camera_GetFarClipPlane", reinterpret_cast<const void*>(&InternalCall_Camera_GetFarClipPlane));
mono_add_internal_call("XCEngine.InternalCalls::Camera_SetFarClipPlane", reinterpret_cast<const void*>(&InternalCall_Camera_SetFarClipPlane));
mono_add_internal_call("XCEngine.InternalCalls::Camera_GetDepth", reinterpret_cast<const void*>(&InternalCall_Camera_GetDepth));
mono_add_internal_call("XCEngine.InternalCalls::Camera_SetDepth", reinterpret_cast<const void*>(&InternalCall_Camera_SetDepth));
mono_add_internal_call("XCEngine.InternalCalls::Camera_GetPrimary", reinterpret_cast<const void*>(&InternalCall_Camera_GetPrimary));
mono_add_internal_call("XCEngine.InternalCalls::Camera_SetPrimary", reinterpret_cast<const void*>(&InternalCall_Camera_SetPrimary));
mono_add_internal_call("XCEngine.InternalCalls::Light_GetIntensity", reinterpret_cast<const void*>(&InternalCall_Light_GetIntensity));
mono_add_internal_call("XCEngine.InternalCalls::Light_SetIntensity", reinterpret_cast<const void*>(&InternalCall_Light_SetIntensity));
mono_add_internal_call("XCEngine.InternalCalls::Light_GetRange", reinterpret_cast<const void*>(&InternalCall_Light_GetRange));
mono_add_internal_call("XCEngine.InternalCalls::Light_SetRange", reinterpret_cast<const void*>(&InternalCall_Light_SetRange));
mono_add_internal_call("XCEngine.InternalCalls::Light_GetSpotAngle", reinterpret_cast<const void*>(&InternalCall_Light_GetSpotAngle));
mono_add_internal_call("XCEngine.InternalCalls::Light_SetSpotAngle", reinterpret_cast<const void*>(&InternalCall_Light_SetSpotAngle));
mono_add_internal_call("XCEngine.InternalCalls::Light_GetCastsShadows", reinterpret_cast<const void*>(&InternalCall_Light_GetCastsShadows));
mono_add_internal_call("XCEngine.InternalCalls::Light_SetCastsShadows", reinterpret_cast<const void*>(&InternalCall_Light_SetCastsShadows));
mono_add_internal_call("XCEngine.InternalCalls::MeshFilter_GetMeshPath", reinterpret_cast<const void*>(&InternalCall_MeshFilter_GetMeshPath));
mono_add_internal_call("XCEngine.InternalCalls::MeshFilter_SetMeshPath", reinterpret_cast<const void*>(&InternalCall_MeshFilter_SetMeshPath));
mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_GetMaterialCount", reinterpret_cast<const void*>(&InternalCall_MeshRenderer_GetMaterialCount));
mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_GetMaterialPath", reinterpret_cast<const void*>(&InternalCall_MeshRenderer_GetMaterialPath));
mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_SetMaterialPath", reinterpret_cast<const void*>(&InternalCall_MeshRenderer_SetMaterialPath));
mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_ClearMaterials", reinterpret_cast<const void*>(&InternalCall_MeshRenderer_ClearMaterials));
mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_GetCastShadows", reinterpret_cast<const void*>(&InternalCall_MeshRenderer_GetCastShadows));
mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_SetCastShadows", reinterpret_cast<const void*>(&InternalCall_MeshRenderer_SetCastShadows));
mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_GetReceiveShadows", reinterpret_cast<const void*>(&InternalCall_MeshRenderer_GetReceiveShadows));
mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_SetReceiveShadows", reinterpret_cast<const void*>(&InternalCall_MeshRenderer_SetReceiveShadows));
mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_GetRenderLayer", reinterpret_cast<const void*>(&InternalCall_MeshRenderer_GetRenderLayer));
mono_add_internal_call("XCEngine.InternalCalls::MeshRenderer_SetRenderLayer", reinterpret_cast<const void*>(&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<ScriptClassDescriptor>& 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<std::string> MonoScriptRuntime::GetScriptClassNames(const std::string& assemblyName) const {
std::vector<ScriptClassDescriptor> classes;
if (!TryGetAvailableScriptClasses(classes)) {
return {};
}
std::vector<std::string> 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<ScriptFieldMetadata>& 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<ScriptFieldDefaultValue>& 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<std::string> 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<size_t>(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<uint64_t>{}(key.gameObjectUUID);
const size_t h2 = std::hash<uint64_t>{}(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<char*>("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<ScriptLifecycleMethod>(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<int32_t>(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<Components::CameraComponent>());
case ManagedComponentKind::Light:
return DestroyNativeComponentInstance(gameObject, gameObject->GetComponent<Components::LightComponent>());
case ManagedComponentKind::MeshFilter:
return DestroyNativeComponentInstance(gameObject, gameObject->GetComponent<Components::MeshFilterComponent>());
case ManagedComponentKind::MeshRenderer:
return DestroyNativeComponentInstance(gameObject, gameObject->GetComponent<Components::MeshRendererComponent>());
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<int32_t>(value)) {
return false;
}
const int32_t storedValue = std::get<int32_t>(value);
switch (fieldMetadata.enumUnderlyingType) {
case MONO_TYPE_I1: {
if (storedValue < std::numeric_limits<int8_t>::min()
|| storedValue > std::numeric_limits<int8_t>::max()) {
return false;
}
int8_t nativeValue = static_cast<int8_t>(storedValue);
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
return true;
}
case MONO_TYPE_U1: {
if (storedValue < 0 || storedValue > std::numeric_limits<uint8_t>::max()) {
return false;
}
uint8_t nativeValue = static_cast<uint8_t>(storedValue);
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
return true;
}
case MONO_TYPE_I2: {
if (storedValue < std::numeric_limits<int16_t>::min()
|| storedValue > std::numeric_limits<int16_t>::max()) {
return false;
}
int16_t nativeValue = static_cast<int16_t>(storedValue);
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
return true;
}
case MONO_TYPE_U2: {
if (storedValue < 0 || storedValue > std::numeric_limits<uint16_t>::max()) {
return false;
}
uint16_t nativeValue = static_cast<uint16_t>(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<uint32_t>(storedValue);
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
return true;
}
default:
return false;
}
}
switch (fieldMetadata.type) {
case ScriptFieldType::Float: {
float nativeValue = std::get<float>(value);
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
return true;
}
case ScriptFieldType::Double: {
double nativeValue = std::get<double>(value);
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
return true;
}
case ScriptFieldType::Bool: {
mono_bool nativeValue = std::get<bool>(value) ? 1 : 0;
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
return true;
}
case ScriptFieldType::Int32: {
int32_t nativeValue = std::get<int32_t>(value);
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
return true;
}
case ScriptFieldType::UInt64: {
uint64_t nativeValue = std::get<uint64_t>(value);
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
return true;
}
case ScriptFieldType::String: {
MonoString* managedString = mono_string_new(m_appDomain, std::get<std::string>(value).c_str());
mono_field_set_value(instance, fieldMetadata.field, managedString);
return true;
}
case ScriptFieldType::Vector2: {
Math::Vector2 nativeValue = std::get<Math::Vector2>(value);
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
return true;
}
case ScriptFieldType::Vector3: {
Math::Vector3 nativeValue = std::get<Math::Vector3>(value);
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
return true;
}
case ScriptFieldType::Vector4: {
Math::Vector4 nativeValue = std::get<Math::Vector4>(value);
mono_field_set_value(instance, fieldMetadata.field, &nativeValue);
return true;
}
case ScriptFieldType::GameObject: {
const GameObjectReference reference = std::get<GameObjectReference>(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<ComponentReference>(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<Components::CameraComponent>() != nullptr;
} else if (className == "Light") {
hasComponent = targetObject->GetComponent<Components::LightComponent>() != nullptr;
} else if (className == "MeshFilter") {
hasComponent = targetObject->GetComponent<Components::MeshFilterComponent>() != nullptr;
} else if (className == "MeshRenderer") {
hasComponent = targetObject->GetComponent<Components::MeshRendererComponent>() != 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<int32_t>(nativeValue);
return true;
}
case MONO_TYPE_U1: {
uint8_t nativeValue = 0;
mono_field_get_value(instance, fieldMetadata.field, &nativeValue);
outValue = static_cast<int32_t>(nativeValue);
return true;
}
case MONO_TYPE_I2: {
int16_t nativeValue = 0;
mono_field_get_value(instance, fieldMetadata.field, &nativeValue);
outValue = static_cast<int32_t>(nativeValue);
return true;
}
case MONO_TYPE_U2: {
uint16_t nativeValue = 0;
mono_field_get_value(instance, fieldMetadata.field, &nativeValue);
outValue = static_cast<int32_t>(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<uint32_t>(std::numeric_limits<int32_t>::max())) {
return false;
}
outValue = static_cast<int32_t>(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<MonoString*>(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