Add support for compiling and running engine without C# scripting

(configurable via `EngineConfiguration.UseCSharp` in Flax.Build)
This commit is contained in:
Wojtek Figat
2021-10-23 16:41:57 +02:00
parent 0b3d6b03ac
commit 8938f13a0b
86 changed files with 1244 additions and 688 deletions

View File

@@ -30,8 +30,10 @@
#include "Engine/Engine/Globals.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Serialization/JsonTools.h"
#if USE_MONO
#include <ThirdParty/mono-2.0/mono/metadata/mono-debug.h>
#include <ThirdParty/mono-2.0/mono/metadata/object.h>
#endif
extern void registerFlaxEngineInternalCalls();
@@ -55,8 +57,8 @@ public:
namespace
{
MDomain* _monoRootDomain = nullptr;
MDomain* _monoScriptsDomain = nullptr;
MDomain* _rootDomain = nullptr;
MDomain* _scriptsDomain = nullptr;
CriticalSection _objectsLocker;
#define USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING 0
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
@@ -134,24 +136,24 @@ bool ScriptingService::Init()
engineAssembly->Unloading.Bind(onEngineUnloading);
// Initialize managed runtime
if (MCore::Instance()->LoadEngine())
if (MCore::LoadEngine())
{
LOG(Fatal, "Mono initialization failed.");
return true;
}
// Cache root domain
_monoRootDomain = MCore::Instance()->GetRootDomain();
_rootDomain = MCore::GetRootDomain();
#if USE_SINGLE_DOMAIN
#if USE_SCRIPTING_SINGLE_DOMAIN
// Use single root domain
auto domain = _monoRootDomain;
auto domain = _rootDomain;
#else
// Create Mono domain for scripts
auto domain = MCore::Instance()->CreateDomain(TEXT("Scripts Domain"));
// Create Mono domain for scripts
auto domain = MCore::CreateDomain("Scripts Domain");
#endif
domain->SetCurrentDomain(true);
_monoScriptsDomain = domain;
_scriptsDomain = domain;
// Add internal calls
registerFlaxEngineInternalCalls();
@@ -169,6 +171,9 @@ bool ScriptingService::Init()
return false;
}
#if COMPILE_WITHOUT_CSHARP
#define INVOKE_EVENT(name)
#else
#define INVOKE_EVENT(name) \
if (!_isEngineAssemblyLoaded) return; \
if (_method_##name == nullptr) \
@@ -186,9 +191,10 @@ bool ScriptingService::Init()
return; \
} \
} \
MonoObject* exception = nullptr; \
MObject* exception = nullptr; \
_method_##name->Invoke(nullptr, nullptr, &exception); \
DebugLog::LogException(exception)
#endif
void ScriptingService::Update()
{
@@ -227,12 +233,12 @@ void ScriptingService::BeforeExit()
MDomain* Scripting::GetRootDomain()
{
return _monoRootDomain;
return _rootDomain;
}
MDomain* Scripting::GetScriptsDomain()
{
return _monoScriptsDomain;
return _scriptsDomain;
}
void Scripting::ProcessBuildInfoPath(String& path, const String& projectFolderPath)
@@ -276,10 +282,9 @@ bool Scripting::LoadBinaryModules(const String& path, const String& projectFolde
// Load all references
auto referencesMember = document.FindMember("References");
if (referencesMember != document.MemberEnd())
if (referencesMember != document.MemberEnd() && referencesMember->value.IsArray())
{
auto& referencesArray = referencesMember->value;
ASSERT(referencesArray.IsArray());
for (rapidjson::SizeType i = 0; i < referencesArray.Size(); i++)
{
auto& reference = referencesArray[i];
@@ -308,10 +313,9 @@ bool Scripting::LoadBinaryModules(const String& path, const String& projectFolde
// Load all binary modules
auto binaryModulesMember = document.FindMember("BinaryModules");
if (binaryModulesMember != document.MemberEnd())
if (binaryModulesMember != document.MemberEnd() && binaryModulesMember->value.IsArray())
{
auto& binaryModulesArray = binaryModulesMember->value;
ASSERT(binaryModulesArray.IsArray());
for (rapidjson::SizeType i = 0; i < binaryModulesArray.Size(); i++)
{
auto& binaryModule = binaryModulesArray[i];
@@ -396,6 +400,7 @@ bool Scripting::LoadBinaryModules(const String& path, const String& projectFolde
}
}
#if !COMPILE_WITHOUT_CSHARP
// C#
if (managedPath.HasChars() && !((ManagedBinaryModule*)module)->Assembly->IsLoaded())
{
@@ -405,6 +410,7 @@ bool Scripting::LoadBinaryModules(const String& path, const String& projectFolde
return true;
}
}
#endif
BinaryModuleLoaded(module);
}
@@ -419,12 +425,14 @@ bool Scripting::Load()
// Note: this action can be called from main thread (due to Mono problems with assemblies actions from other threads)
ASSERT(IsInMainThread());
#if !COMPILE_WITHOUT_CSHARP
// Load C# core assembly
if (GetBinaryModuleCorlib()->Assembly->Load(mono_get_corlib()))
{
LOG(Error, "Failed to load corlib C# assembly.");
return true;
}
#endif
// Load FlaxEngine
const String flaxEnginePath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll");
@@ -539,7 +547,7 @@ void Scripting::Release()
ObjectsRemovalService::Flush();
// Switch domain
auto rootDomain = MCore::Instance()->GetRootDomain();
auto rootDomain = MCore::GetRootDomain();
if (rootDomain)
{
if (!rootDomain->SetCurrentDomain(false))
@@ -548,8 +556,8 @@ void Scripting::Release()
}
}
#if !USE_SINGLE_DOMAIN
MCore::Instance()->UnloadDomain("Scripts Domain");
#if !USE_SCRIPTING_SINGLE_DOMAIN
MCore::UnloadDomain("Scripts Domain");
#endif
}
@@ -647,25 +655,6 @@ void Scripting::Reload(bool canTriggerSceneReload)
#endif
MClass* Scripting::FindClass(MonoClass* monoClass)
{
if (monoClass == nullptr)
return nullptr;
PROFILE_CPU();
auto& modules = BinaryModule::GetModules();
for (auto module : modules)
{
auto managedModule = dynamic_cast<ManagedBinaryModule*>(module);
if (managedModule && managedModule->Assembly->IsLoaded())
{
MClass* result = managedModule->Assembly->GetClass(monoClass);
if (result != nullptr)
return result;
}
}
return nullptr;
}
MClass* Scripting::FindClass(const StringAnsiView& fullname)
{
if (fullname.IsEmpty())
@@ -685,6 +674,27 @@ MClass* Scripting::FindClass(const StringAnsiView& fullname)
return nullptr;
}
#if USE_MONO
MClass* Scripting::FindClass(MonoClass* monoClass)
{
if (monoClass == nullptr)
return nullptr;
PROFILE_CPU();
auto& modules = BinaryModule::GetModules();
for (auto module : modules)
{
auto managedModule = dynamic_cast<ManagedBinaryModule*>(module);
if (managedModule && managedModule->Assembly->IsLoaded())
{
MClass* result = managedModule->Assembly->GetClass(monoClass);
if (result != nullptr)
return result;
}
}
return nullptr;
}
MonoClass* Scripting::FindClassNative(const StringAnsiView& fullname)
{
if (fullname.IsEmpty())
@@ -704,6 +714,8 @@ MonoClass* Scripting::FindClassNative(const StringAnsiView& fullname)
return nullptr;
}
#endif
ScriptingTypeHandle Scripting::FindScriptingType(const StringAnsiView& fullname)
{
if (fullname.IsEmpty())
@@ -816,7 +828,7 @@ ScriptingObject* Scripting::TryFindObject(Guid id, MClass* type)
return result;
}
ScriptingObject* Scripting::FindObject(const MonoObject* managedInstance)
ScriptingObject* Scripting::FindObject(const MObject* managedInstance)
{
if (managedInstance == nullptr)
return nullptr;
@@ -938,13 +950,14 @@ bool initFlaxEngine()
if (StdTypesContainer::Instance()->Gather())
return true;
#if !COMPILE_WITHOUT_CSHARP
// Init C# class library
{
auto scriptingClass = Scripting::GetStaticClass();
ASSERT(scriptingClass);
const auto initMethod = scriptingClass->GetMethod("Init");
ASSERT(initMethod);
MonoObject* exception = nullptr;
MObject* exception = nullptr;
initMethod->Invoke(nullptr, nullptr, &exception);
if (exception)
{
@@ -952,10 +965,11 @@ bool initFlaxEngine()
ex.Log(LogType::Fatal, TEXT("FlaxEngine.Scripting.Init"));
return true;
}
// TODO: move it somewhere to game instance class or similar
MainRenderTask::Instance = New<MainRenderTask>();
}
#endif
// TODO: move it somewhere to game instance class or similar
MainRenderTask::Instance = New<MainRenderTask>();
return false;
}
@@ -989,5 +1003,5 @@ void ScriptingService::Dispose()
{
Scripting::Release();
MCore::Instance()->UnloadEngine();
MCore::UnloadEngine();
}