feat(scripting): support managed script component api
This commit is contained in:
@@ -53,6 +53,8 @@ public:
|
||||
|
||||
bool HasManagedInstance(const ScriptComponent* component) const;
|
||||
size_t GetManagedInstanceCount() const { return m_instances.size(); }
|
||||
MonoObject* GetManagedInstanceObject(const ScriptComponent* component) const;
|
||||
MonoObject* CreateManagedComponentWrapper(MonoClass* componentClass, uint64_t gameObjectUUID);
|
||||
|
||||
bool TryGetFieldValue(
|
||||
const ScriptComponent* component,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Debug/Logger.h"
|
||||
#include "Scene/Scene.h"
|
||||
#include "Scripting/ScriptComponent.h"
|
||||
#include "Scripting/ScriptEngine.h"
|
||||
|
||||
#include <mono/jit/jit.h>
|
||||
#include <mono/metadata/appdomain.h>
|
||||
@@ -38,6 +39,7 @@ struct MonoRootState {
|
||||
|
||||
enum class ManagedComponentKind {
|
||||
Unknown,
|
||||
Script,
|
||||
Transform,
|
||||
Camera,
|
||||
Light,
|
||||
@@ -45,6 +47,14 @@ enum class ManagedComponentKind {
|
||||
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;
|
||||
@@ -94,40 +104,62 @@ std::string MonoStringToUtf8(MonoString* stringObject) {
|
||||
return result;
|
||||
}
|
||||
|
||||
ManagedComponentKind ResolveManagedComponentKind(MonoReflectionType* reflectionType) {
|
||||
MonoScriptRuntime* GetActiveMonoScriptRuntime() {
|
||||
return dynamic_cast<MonoScriptRuntime*>(ScriptEngine::Get().GetRuntime());
|
||||
}
|
||||
|
||||
ManagedComponentTypeInfo ResolveManagedComponentTypeInfo(MonoReflectionType* reflectionType) {
|
||||
ManagedComponentTypeInfo typeInfo;
|
||||
if (!reflectionType) {
|
||||
return ManagedComponentKind::Unknown;
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
MonoType* monoType = mono_reflection_type_get_type(reflectionType);
|
||||
if (!monoType) {
|
||||
return ManagedComponentKind::Unknown;
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
MonoClass* monoClass = mono_class_from_mono_type(monoType);
|
||||
if (!monoClass) {
|
||||
return ManagedComponentKind::Unknown;
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
const std::string namespaceName = SafeString(mono_class_get_namespace(monoClass));
|
||||
const std::string className = SafeString(mono_class_get_name(monoClass));
|
||||
if (namespaceName == "XCEngine" && className == "Transform") {
|
||||
return ManagedComponentKind::Transform;
|
||||
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 (namespaceName == "XCEngine" && className == "Camera") {
|
||||
return ManagedComponentKind::Camera;
|
||||
if (typeInfo.namespaceName == "XCEngine" && typeInfo.className == "Camera") {
|
||||
typeInfo.kind = ManagedComponentKind::Camera;
|
||||
return typeInfo;
|
||||
}
|
||||
if (namespaceName == "XCEngine" && className == "Light") {
|
||||
return ManagedComponentKind::Light;
|
||||
if (typeInfo.namespaceName == "XCEngine" && typeInfo.className == "Light") {
|
||||
typeInfo.kind = ManagedComponentKind::Light;
|
||||
return typeInfo;
|
||||
}
|
||||
if (namespaceName == "XCEngine" && className == "MeshFilter") {
|
||||
return ManagedComponentKind::MeshFilter;
|
||||
if (typeInfo.namespaceName == "XCEngine" && typeInfo.className == "MeshFilter") {
|
||||
typeInfo.kind = ManagedComponentKind::MeshFilter;
|
||||
return typeInfo;
|
||||
}
|
||||
if (namespaceName == "XCEngine" && className == "MeshRenderer") {
|
||||
return ManagedComponentKind::MeshRenderer;
|
||||
if (typeInfo.namespaceName == "XCEngine" && typeInfo.className == "MeshRenderer") {
|
||||
typeInfo.kind = ManagedComponentKind::MeshRenderer;
|
||||
return typeInfo;
|
||||
}
|
||||
|
||||
return ManagedComponentKind::Unknown;
|
||||
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;
|
||||
}
|
||||
|
||||
Components::GameObject* FindGameObjectByUUIDRecursive(Components::GameObject* gameObject, uint64_t uuid) {
|
||||
@@ -214,6 +246,7 @@ bool HasNativeComponent(Components::GameObject* gameObject, ManagedComponentKind
|
||||
return gameObject->GetComponent<Components::MeshFilterComponent>() != nullptr;
|
||||
case ManagedComponentKind::MeshRenderer:
|
||||
return gameObject->GetComponent<Components::MeshRendererComponent>() != nullptr;
|
||||
case ManagedComponentKind::Script:
|
||||
case ManagedComponentKind::Unknown:
|
||||
return false;
|
||||
}
|
||||
@@ -245,6 +278,7 @@ Components::Component* AddOrGetNativeComponent(Components::GameObject* gameObjec
|
||||
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;
|
||||
}
|
||||
@@ -252,6 +286,28 @@ Components::Component* AddOrGetNativeComponent(Components::GameObject* gameObjec
|
||||
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 (!component || !component->HasScriptClass()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (component->GetAssemblyName() == typeInfo.assemblyName
|
||||
&& component->GetNamespaceName() == typeInfo.namespaceName
|
||||
&& component->GetClassName() == typeInfo.className) {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Components::CameraComponent* FindCameraComponent(uint64_t gameObjectUUID) {
|
||||
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
|
||||
return gameObject ? gameObject->GetComponent<Components::CameraComponent>() : nullptr;
|
||||
@@ -337,21 +393,69 @@ void InternalCall_GameObject_SetActive(uint64_t gameObjectUUID, mono_bool active
|
||||
|
||||
mono_bool InternalCall_GameObject_HasComponent(uint64_t gameObjectUUID, MonoReflectionType* componentType) {
|
||||
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
|
||||
return HasNativeComponent(gameObject, ResolveManagedComponentKind(componentType)) ? 1 : 0;
|
||||
}
|
||||
|
||||
uint64_t InternalCall_GameObject_GetComponent(uint64_t gameObjectUUID, MonoReflectionType* componentType) {
|
||||
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
|
||||
if (!HasNativeComponent(gameObject, ResolveManagedComponentKind(componentType))) {
|
||||
return 0;
|
||||
const ManagedComponentTypeInfo typeInfo = ResolveManagedComponentTypeInfo(componentType);
|
||||
if (typeInfo.kind == ManagedComponentKind::Script) {
|
||||
return FindMatchingScriptComponent(gameObject, typeInfo) ? 1 : 0;
|
||||
}
|
||||
|
||||
return gameObjectUUID;
|
||||
return HasNativeComponent(gameObject, typeInfo.kind) ? 1 : 0;
|
||||
}
|
||||
|
||||
uint64_t InternalCall_GameObject_AddComponent(uint64_t gameObjectUUID, MonoReflectionType* componentType) {
|
||||
MonoObject* InternalCall_GameObject_GetComponent(uint64_t gameObjectUUID, MonoReflectionType* componentType) {
|
||||
Components::GameObject* gameObject = FindGameObjectByUUID(gameObjectUUID);
|
||||
return AddOrGetNativeComponent(gameObject, ResolveManagedComponentKind(componentType)) ? gameObjectUUID : 0;
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -1199,6 +1303,11 @@ bool MonoScriptRuntime::HasManagedInstance(const ScriptComponent* component) con
|
||||
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,
|
||||
@@ -1766,6 +1875,37 @@ bool MonoScriptRuntime::ApplyStoredFields(
|
||||
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;
|
||||
|
||||
@@ -37,7 +37,8 @@ void ScriptEngine::OnRuntimeStart(Components::Scene* scene) {
|
||||
CollectScriptComponents(root);
|
||||
}
|
||||
|
||||
for (const ScriptInstanceKey& key : m_scriptOrder) {
|
||||
const std::vector<ScriptInstanceKey> startupKeys = m_scriptOrder;
|
||||
for (const ScriptInstanceKey& key : startupKeys) {
|
||||
auto it = m_scriptStates.find(key);
|
||||
if (it == m_scriptStates.end()) {
|
||||
continue;
|
||||
@@ -87,7 +88,8 @@ void ScriptEngine::OnFixedUpdate(float fixedDeltaTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const ScriptInstanceKey& key : m_scriptOrder) {
|
||||
const std::vector<ScriptInstanceKey> updateKeys = m_scriptOrder;
|
||||
for (const ScriptInstanceKey& key : updateKeys) {
|
||||
auto it = m_scriptStates.find(key);
|
||||
if (it == m_scriptStates.end()) {
|
||||
continue;
|
||||
@@ -107,7 +109,8 @@ void ScriptEngine::OnUpdate(float deltaTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const ScriptInstanceKey& key : m_scriptOrder) {
|
||||
const std::vector<ScriptInstanceKey> updateKeys = m_scriptOrder;
|
||||
for (const ScriptInstanceKey& key : updateKeys) {
|
||||
auto it = m_scriptStates.find(key);
|
||||
if (it == m_scriptStates.end()) {
|
||||
continue;
|
||||
@@ -133,7 +136,8 @@ void ScriptEngine::OnLateUpdate(float deltaTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const ScriptInstanceKey& key : m_scriptOrder) {
|
||||
const std::vector<ScriptInstanceKey> updateKeys = m_scriptOrder;
|
||||
for (const ScriptInstanceKey& key : updateKeys) {
|
||||
auto it = m_scriptStates.find(key);
|
||||
if (it == m_scriptStates.end()) {
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user