344 lines
13 KiB
C++
344 lines
13 KiB
C++
#pragma once
|
|
|
|
#include <XCEngine/Scripting/IScriptRuntime.h>
|
|
#include <XCEngine/Scripting/ScriptField.h>
|
|
|
|
#include <array>
|
|
#include <filesystem>
|
|
#include <memory>
|
|
#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 Rendering {
|
|
namespace Pipelines {
|
|
struct ManagedRenderPipelineAssetDescriptor;
|
|
} // namespace Pipelines
|
|
} // namespace Rendering
|
|
|
|
namespace Scripting {
|
|
|
|
class ScriptComponent;
|
|
class MonoManagedRenderPipelineAssetRuntime;
|
|
class MonoManagedRenderPipelineBridge;
|
|
class MonoManagedRenderPipelineStageRecorder;
|
|
|
|
class MonoScriptRuntime : public IScriptRuntime {
|
|
public:
|
|
struct ManagedAssemblyDescriptor {
|
|
std::string name;
|
|
std::filesystem::path path;
|
|
};
|
|
|
|
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::vector<ManagedAssemblyDescriptor> engineAssemblies;
|
|
std::string baseNamespace = "XCEngine";
|
|
std::string baseClassName = "MonoBehaviour";
|
|
};
|
|
|
|
explicit MonoScriptRuntime(Settings settings = {});
|
|
~MonoScriptRuntime() override;
|
|
|
|
static constexpr const char* EngineAssemblyManifestFileName =
|
|
"XCEngine.EngineAssemblies.txt";
|
|
static std::filesystem::path GetEngineAssemblyManifestPath(
|
|
const std::filesystem::path& assemblyDirectory);
|
|
static bool DiscoverEngineAssemblies(
|
|
Settings& ioSettings,
|
|
std::string* outError = nullptr);
|
|
|
|
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 TryGetAvailableScriptClasses(
|
|
std::vector<ScriptClassDescriptor>& outClasses) const override;
|
|
bool TryGetAvailableRenderPipelineAssetClasses(
|
|
std::vector<ScriptClassDescriptor>& outClasses) const override;
|
|
bool TryGetClassFieldMetadata(
|
|
const std::string& assemblyName,
|
|
const std::string& namespaceName,
|
|
const std::string& className,
|
|
std::vector<ScriptFieldMetadata>& outFields) const override;
|
|
bool TryGetClassFieldDefaultValues(
|
|
const std::string& assemblyName,
|
|
const std::string& namespaceName,
|
|
const std::string& className,
|
|
std::vector<ScriptFieldDefaultValue>& outFields) const override;
|
|
|
|
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 DestroyManagedObject(MonoObject* managedObject);
|
|
MonoObject* GetExternalManagedObject(uint32_t gcHandle) const;
|
|
uint32_t RetainExternalManagedObjectReference(MonoObject* managedObject);
|
|
void ReleaseExternalManagedObject(uint32_t gcHandle);
|
|
bool IsScriptableRenderPipelineAssetObject(MonoObject* managedObject) const;
|
|
bool TryEnsureManagedRenderPipelineAssetHandle(
|
|
Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor& ioDescriptor);
|
|
|
|
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 TrySetManagedFieldValue(
|
|
const ScriptRuntimeContext& context,
|
|
const std::string& fieldName,
|
|
const ScriptFieldValue& value) override;
|
|
bool TryGetManagedFieldValue(
|
|
const ScriptRuntimeContext& context,
|
|
const std::string& fieldName,
|
|
ScriptFieldValue& outValue) const override;
|
|
void SyncManagedFieldsToStorage(const ScriptRuntimeContext& context) override;
|
|
bool CreateScriptInstance(const ScriptRuntimeContext& context) override;
|
|
void DestroyScriptInstance(const ScriptRuntimeContext& context) override;
|
|
void InvokeMethod(const ScriptRuntimeContext& context, ScriptLifecycleMethod method, float deltaTime) override;
|
|
void InvokePhysicsMessage(
|
|
const ScriptRuntimeContext& context,
|
|
ScriptPhysicsMessage message,
|
|
Components::GameObject* other) override;
|
|
|
|
private:
|
|
static constexpr size_t LifecycleMethodCount = 8;
|
|
static constexpr size_t PhysicsMessageCount = 6;
|
|
|
|
struct PhysicsMessageMethods {
|
|
MonoMethod* withGameObject = nullptr;
|
|
MonoMethod* withoutArgs = nullptr;
|
|
};
|
|
|
|
struct FieldMetadata {
|
|
ScriptFieldType type = ScriptFieldType::None;
|
|
MonoClassField* field = nullptr;
|
|
MonoClass* componentClass = nullptr;
|
|
bool isEnum = false;
|
|
int32_t enumUnderlyingType = 0;
|
|
};
|
|
|
|
struct ClassMetadata {
|
|
std::string assemblyName;
|
|
std::string namespaceName;
|
|
std::string className;
|
|
std::string fullName;
|
|
MonoClass* monoClass = nullptr;
|
|
std::array<MonoMethod*, LifecycleMethodCount> lifecycleMethods{};
|
|
std::array<PhysicsMessageMethods, PhysicsMessageCount> physicsMessageMethods{};
|
|
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;
|
|
};
|
|
|
|
struct ExternalManagedObjectData {
|
|
MonoClass* monoClass = nullptr;
|
|
uint32_t gcHandle = 0;
|
|
};
|
|
|
|
struct LoadedManagedAssemblyData {
|
|
MonoAssembly* assembly = nullptr;
|
|
MonoImage* image = nullptr;
|
|
};
|
|
|
|
bool ResolveSettings();
|
|
bool InitializeRootDomain();
|
|
bool CreateAppDomain();
|
|
void DestroyAppDomain();
|
|
void SetCurrentDomain() const;
|
|
|
|
bool LoadAssemblies();
|
|
bool LoadAssemblyImage(
|
|
const ManagedAssemblyDescriptor& descriptor,
|
|
const char* roleLabel,
|
|
MonoAssembly*& outAssembly,
|
|
MonoImage*& outImage);
|
|
bool DiscoverScriptClasses();
|
|
void DiscoverScriptClassesInImage(const std::string& assemblyName, MonoImage* image);
|
|
void DiscoverRenderPipelineAssetClassesInImage(
|
|
const std::string& assemblyName,
|
|
MonoImage* image);
|
|
bool IsMonoBehaviourSubclass(MonoClass* monoClass) const;
|
|
bool IsSupportedComponentFieldClass(MonoClass* monoClass) const;
|
|
bool IsScriptComponentFieldClass(MonoClass* monoClass) const;
|
|
|
|
FieldMetadata BuildFieldMetadata(MonoClassField* field) const;
|
|
|
|
static const char* ToLifecycleMethodName(ScriptLifecycleMethod method);
|
|
static const char* ToPhysicsMessageMethodName(ScriptPhysicsMessage message);
|
|
|
|
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 HasSerializeFieldAttribute(MonoClassField* field) const;
|
|
bool TryExtractComponentReference(MonoObject* managedObject, ComponentReference& outReference) const;
|
|
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 ClearExternalManagedObjects();
|
|
void ClearClassCache();
|
|
MonoImage* FindLoadedAssemblyImage(const std::string& assemblyName) const;
|
|
bool ResolveManagedClass(
|
|
const std::string& assemblyName,
|
|
const std::string& namespaceName,
|
|
const std::string& className,
|
|
MonoClass*& outClass) const;
|
|
bool CreateExternalManagedObject(
|
|
const std::string& assemblyName,
|
|
const std::string& namespaceName,
|
|
const std::string& className,
|
|
uint32_t& outHandle);
|
|
bool CreateExternalManagedObject(
|
|
MonoClass* monoClass,
|
|
uint32_t& outHandle);
|
|
uint32_t RetainExternalManagedObject(MonoObject* instance);
|
|
void DestroyExternalManagedObject(uint32_t gcHandle);
|
|
MonoObject* CreateManagedScriptableRenderContext(uint64_t nativeHandle);
|
|
MonoObject* CreateManagedCameraRenderRequestContext(
|
|
uint64_t nativeHandle);
|
|
MonoObject* CreateManagedRenderSceneSetupContext(
|
|
uint64_t nativeHandle);
|
|
MonoObject* CreateManagedDirectionalShadowExecutionContext(
|
|
uint64_t nativeHandle);
|
|
MonoObject* CreateManagedScriptableRenderPipelinePlanningContext(
|
|
uint64_t nativeHandle);
|
|
MonoObject* GetManagedObject(uint32_t gcHandle) const;
|
|
MonoMethod* ResolveManagedMethod(
|
|
MonoClass* monoClass,
|
|
const char* methodName,
|
|
int parameterCount) const;
|
|
MonoMethod* ResolveManagedMethod(
|
|
MonoObject* instance,
|
|
const char* methodName,
|
|
int parameterCount) const;
|
|
bool InvokeManagedMethod(
|
|
MonoObject* instance,
|
|
MonoMethod* method,
|
|
void** args = nullptr,
|
|
MonoObject** outReturnValue = nullptr);
|
|
void RecordException(MonoObject* exception);
|
|
void SetError(const std::string& error);
|
|
|
|
friend class MonoManagedRenderPipelineBridge;
|
|
friend class MonoManagedRenderPipelineAssetRuntime;
|
|
friend class MonoManagedRenderPipelineStageRecorder;
|
|
|
|
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;
|
|
MonoClass* m_scriptableRenderPipelineAssetClass = nullptr;
|
|
MonoClass* m_scriptableRenderPipelineClass = nullptr;
|
|
MonoClass* m_scriptableRenderContextClass = nullptr;
|
|
MonoClass* m_cameraRenderRequestContextClass = nullptr;
|
|
MonoClass* m_renderSceneSetupContextClass = nullptr;
|
|
MonoClass* m_directionalShadowExecutionContextClass = nullptr;
|
|
MonoClass* m_scriptableRenderPipelinePlanningContextClass = nullptr;
|
|
MonoClass* m_serializeFieldAttributeClass = nullptr;
|
|
MonoMethod* m_gameObjectConstructor = nullptr;
|
|
MonoMethod* m_scriptableRenderContextConstructor = nullptr;
|
|
MonoMethod* m_cameraRenderRequestContextConstructor =
|
|
nullptr;
|
|
MonoMethod* m_renderSceneSetupContextConstructor =
|
|
nullptr;
|
|
MonoMethod* m_directionalShadowExecutionContextConstructor =
|
|
nullptr;
|
|
MonoMethod* m_scriptableRenderPipelinePlanningContextConstructor =
|
|
nullptr;
|
|
MonoClassField* m_managedGameObjectUUIDField = nullptr;
|
|
MonoClassField* m_gameObjectUUIDField = nullptr;
|
|
MonoClassField* m_scriptComponentUUIDField = nullptr;
|
|
|
|
std::unordered_map<std::string, ClassMetadata> m_classes;
|
|
std::vector<ScriptClassDescriptor> m_renderPipelineAssetClasses;
|
|
std::unordered_map<std::string, LoadedManagedAssemblyData>
|
|
m_loadedManagedAssemblies;
|
|
std::unordered_map<InstanceKey, InstanceData, InstanceKeyHasher> m_instances;
|
|
std::unordered_map<uint32_t, ExternalManagedObjectData> m_externalManagedObjects;
|
|
std::shared_ptr<void> m_runtimeLifetimeToken;
|
|
};
|
|
|
|
} // namespace Scripting
|
|
} // namespace XCEngine
|