Add concrete component script field support

This commit is contained in:
2026-04-03 16:51:42 +08:00
parent 73415915e6
commit e0e5c1fcaa
11 changed files with 507 additions and 4 deletions

View File

@@ -92,6 +92,15 @@ 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();
@@ -2316,6 +2325,40 @@ bool MonoScriptRuntime::IsMonoBehaviourSubclass(MonoClass* monoClass) const {
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;
@@ -2377,6 +2420,12 @@ MonoScriptRuntime::FieldMetadata MonoScriptRuntime::BuildFieldMetadata(MonoClass
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;
@@ -2593,6 +2642,40 @@ MonoObject* MonoScriptRuntime::CreateManagedGameObject(uint64_t gameObjectUUID)
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;
@@ -2811,6 +2894,85 @@ bool MonoScriptRuntime::TrySetFieldValue(
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;
}
@@ -2947,6 +3109,16 @@ bool MonoScriptRuntime::TryReadFieldValue(
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;
}