feat(scripting): add mono csharp runtime foundation

This commit is contained in:
2026-03-27 13:07:39 +08:00
parent 134a80b334
commit b06932724c
33 changed files with 4227 additions and 18 deletions

View File

@@ -0,0 +1,26 @@
#pragma once
#include <XCEngine/Scene/Scene.h>
namespace XCEngine {
namespace Components {
class SceneRuntime {
public:
void Start(Scene* scene);
void Stop();
void FixedUpdate(float fixedDeltaTime);
void Update(float deltaTime);
void LateUpdate(float deltaTime);
bool IsRunning() const { return m_running; }
Scene* GetScene() const { return m_scene; }
private:
Scene* m_scene = nullptr;
bool m_running = false;
};
} // namespace Components
} // namespace XCEngine

View File

@@ -0,0 +1,185 @@
#pragma once
#include <XCEngine/Scripting/IScriptRuntime.h>
#include <XCEngine/Scripting/ScriptField.h>
#include <array>
#include <filesystem>
#include <string>
#include <unordered_map>
#include <vector>
typedef struct _MonoDomain MonoDomain;
typedef struct _MonoAssembly MonoAssembly;
typedef struct _MonoImage MonoImage;
typedef struct _MonoClass MonoClass;
typedef struct _MonoObject MonoObject;
typedef struct _MonoMethod MonoMethod;
typedef struct _MonoClassField MonoClassField;
namespace XCEngine {
namespace Scripting {
class ScriptComponent;
class MonoScriptRuntime : public IScriptRuntime {
public:
struct Settings {
std::filesystem::path assemblyDirectory;
std::filesystem::path corlibDirectory;
std::filesystem::path coreAssemblyPath;
std::filesystem::path appAssemblyPath;
std::string coreAssemblyName = "XCEngine.ScriptCore";
std::string appAssemblyName = "GameScripts";
std::string baseNamespace = "XCEngine";
std::string baseClassName = "MonoBehaviour";
};
explicit MonoScriptRuntime(Settings settings = {});
~MonoScriptRuntime() override;
bool Initialize();
void Shutdown();
bool IsInitialized() const { return m_initialized; }
const Settings& GetSettings() const { return m_settings; }
const std::string& GetLastError() const { return m_lastError; }
bool IsClassAvailable(
const std::string& assemblyName,
const std::string& namespaceName,
const std::string& className) const;
std::vector<std::string> GetScriptClassNames(const std::string& assemblyName = std::string()) const;
bool HasManagedInstance(const ScriptComponent* component) const;
size_t GetManagedInstanceCount() const { return m_instances.size(); }
bool TryGetFieldValue(
const ScriptComponent* component,
const std::string& fieldName,
ScriptFieldValue& outValue) const;
template<typename T>
bool TryGetFieldValue(const ScriptComponent* component, const std::string& fieldName, T& outValue) const {
ScriptFieldValue value;
if (!TryGetFieldValue(component, fieldName, value)) {
return false;
}
const auto* typedValue = std::get_if<T>(&value);
if (!typedValue) {
return false;
}
outValue = *typedValue;
return true;
}
void OnRuntimeStart(Components::Scene* scene) override;
void OnRuntimeStop(Components::Scene* scene) override;
bool CreateScriptInstance(const ScriptRuntimeContext& context) override;
void DestroyScriptInstance(const ScriptRuntimeContext& context) override;
void InvokeMethod(const ScriptRuntimeContext& context, ScriptLifecycleMethod method, float deltaTime) override;
private:
static constexpr size_t LifecycleMethodCount = 8;
struct FieldMetadata {
ScriptFieldType type = ScriptFieldType::None;
MonoClassField* field = nullptr;
};
struct ClassMetadata {
std::string assemblyName;
std::string namespaceName;
std::string className;
std::string fullName;
MonoClass* monoClass = nullptr;
std::array<MonoMethod*, LifecycleMethodCount> lifecycleMethods{};
std::unordered_map<std::string, FieldMetadata> fields;
};
struct InstanceKey {
uint64_t gameObjectUUID = 0;
uint64_t scriptComponentUUID = 0;
bool operator==(const InstanceKey& other) const {
return gameObjectUUID == other.gameObjectUUID
&& scriptComponentUUID == other.scriptComponentUUID;
}
};
struct InstanceKeyHasher {
size_t operator()(const InstanceKey& key) const;
};
struct InstanceData {
const ClassMetadata* classMetadata = nullptr;
uint32_t gcHandle = 0;
};
void ResolveSettings();
bool InitializeRootDomain();
bool CreateAppDomain();
void DestroyAppDomain();
void SetCurrentDomain() const;
bool LoadAssemblies();
bool DiscoverScriptClasses();
void DiscoverScriptClassesInImage(const std::string& assemblyName, MonoImage* image);
bool IsMonoBehaviourSubclass(MonoClass* monoClass) const;
ScriptFieldType MapMonoFieldType(MonoClassField* field) const;
static const char* ToLifecycleMethodName(ScriptLifecycleMethod method);
const ClassMetadata* FindClassMetadata(
const std::string& assemblyName,
const std::string& namespaceName,
const std::string& className) const;
InstanceData* FindInstance(const ScriptRuntimeContext& context);
const InstanceData* FindInstance(const ScriptRuntimeContext& context) const;
const InstanceData* FindInstance(const ScriptComponent* component) const;
MonoObject* GetManagedObject(const InstanceData& instanceData) const;
bool ApplyContextFields(const ScriptRuntimeContext& context, MonoObject* instance);
bool ApplyStoredFields(const ScriptRuntimeContext& context, const ClassMetadata& metadata, MonoObject* instance);
MonoObject* CreateManagedGameObject(uint64_t gameObjectUUID);
bool TryExtractGameObjectReference(MonoObject* managedObject, GameObjectReference& outReference) const;
bool TrySetFieldValue(MonoObject* instance, const FieldMetadata& fieldMetadata, const ScriptFieldValue& value);
bool TryReadFieldValue(MonoObject* instance, const FieldMetadata& fieldMetadata, ScriptFieldValue& outValue) const;
void ClearManagedInstances();
void ClearClassCache();
bool InvokeManagedMethod(MonoObject* instance, MonoMethod* method);
void RecordException(MonoObject* exception);
void SetError(const std::string& error);
Settings m_settings;
Components::Scene* m_activeScene = nullptr;
bool m_initialized = false;
std::string m_lastError;
MonoDomain* m_appDomain = nullptr;
MonoAssembly* m_coreAssembly = nullptr;
MonoAssembly* m_appAssembly = nullptr;
MonoImage* m_coreImage = nullptr;
MonoImage* m_appImage = nullptr;
MonoClass* m_componentClass = nullptr;
MonoClass* m_behaviourClass = nullptr;
MonoClass* m_gameObjectClass = nullptr;
MonoClass* m_monoBehaviourClass = nullptr;
MonoMethod* m_gameObjectConstructor = nullptr;
MonoClassField* m_managedGameObjectUUIDField = nullptr;
MonoClassField* m_gameObjectUUIDField = nullptr;
MonoClassField* m_scriptComponentUUIDField = nullptr;
std::unordered_map<std::string, ClassMetadata> m_classes;
std::unordered_map<InstanceKey, InstanceData, InstanceKeyHasher> m_instances;
};
} // namespace Scripting
} // namespace XCEngine