feat: expand editor scripting asset and viewport flow

This commit is contained in:
2026-04-03 13:22:30 +08:00
parent ed8c27fde2
commit a05d0b80a2
124 changed files with 10397 additions and 1737 deletions

View File

@@ -6,12 +6,18 @@
#include "Core/EditorContext.h"
#include "Core/EditorEvents.h"
#include "Core/EventBus.h"
#include "Scripting/EditorScriptAssemblyBuilder.h"
#include "UI/BuiltInIcons.h"
#include "Platform/Win32Utf8.h"
#include "Platform/WindowsProcessDiagnostics.h"
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/Debug/Logger.h>
#include <XCEngine/Scripting/ScriptEngine.h>
#ifdef XCENGINE_ENABLE_MONO_SCRIPTING
#include <XCEngine/Scripting/Mono/MonoScriptRuntime.h>
#endif
#include <chrono>
#include <filesystem>
#include <windows.h>
namespace XCEngine {
@@ -22,6 +28,123 @@ Application& Application::Get() {
return instance;
}
void Application::InitializeScriptingRuntime(const std::string& projectPath) {
ShutdownScriptingRuntime();
const std::filesystem::path assemblyDirectoryPath =
std::filesystem::path(Platform::Utf8ToWide(projectPath)) / L"Library" / L"ScriptAssemblies";
m_scriptRuntimeStatus.assemblyDirectory = Platform::WideToUtf8(assemblyDirectoryPath.wstring());
#ifdef XCENGINE_ENABLE_MONO_SCRIPTING
namespace fs = std::filesystem;
auto& logger = Debug::Logger::Get();
const fs::path assemblyDirectory = assemblyDirectoryPath;
m_scriptRuntimeStatus.backendEnabled = true;
::XCEngine::Scripting::MonoScriptRuntime::Settings settings;
settings.assemblyDirectory = assemblyDirectory;
settings.corlibDirectory = assemblyDirectory;
settings.coreAssemblyPath = assemblyDirectory / L"XCEngine.ScriptCore.dll";
settings.appAssemblyPath = assemblyDirectory / L"GameScripts.dll";
std::error_code ec;
const bool hasCoreAssembly = fs::exists(settings.coreAssemblyPath, ec);
ec.clear();
const bool hasAppAssembly = fs::exists(settings.appAssemblyPath, ec);
ec.clear();
const bool hasCorlibAssembly = fs::exists(assemblyDirectory / L"mscorlib.dll", ec);
m_scriptRuntimeStatus.assembliesFound = hasCoreAssembly && hasAppAssembly && hasCorlibAssembly;
if (!hasCoreAssembly || !hasAppAssembly || !hasCorlibAssembly) {
m_scriptRuntimeStatus.statusMessage =
"Script assemblies were not found in " + Platform::WideToUtf8(assemblyDirectory.wstring()) +
". Script class discovery is disabled until the managed assemblies are built.";
logger.Warning(Debug::LogCategory::Scripting, m_scriptRuntimeStatus.statusMessage.c_str());
::XCEngine::Scripting::ScriptEngine::Get().SetRuntime(nullptr);
return;
}
auto runtime = std::make_unique<::XCEngine::Scripting::MonoScriptRuntime>(settings);
if (!runtime->Initialize()) {
m_scriptRuntimeStatus.statusMessage =
"Failed to initialize editor script runtime: " + runtime->GetLastError();
logger.Warning(Debug::LogCategory::Scripting, m_scriptRuntimeStatus.statusMessage.c_str());
::XCEngine::Scripting::ScriptEngine::Get().SetRuntime(nullptr);
return;
}
::XCEngine::Scripting::ScriptEngine::Get().SetRuntime(runtime.get());
m_scriptRuntimeStatus.runtimeLoaded = true;
m_scriptRuntime = std::move(runtime);
logger.Info(Debug::LogCategory::Scripting, "Editor script runtime initialized.");
#else
(void)projectPath;
m_scriptRuntimeStatus.backendEnabled = false;
m_scriptRuntimeStatus.statusMessage = "This editor build does not include Mono scripting support.";
::XCEngine::Scripting::ScriptEngine::Get().SetRuntime(nullptr);
#endif
}
void Application::ShutdownScriptingRuntime() {
::XCEngine::Scripting::ScriptEngine::Get().OnRuntimeStop();
::XCEngine::Scripting::ScriptEngine::Get().SetRuntime(nullptr);
#ifdef XCENGINE_ENABLE_MONO_SCRIPTING
m_scriptRuntime.reset();
#endif
m_scriptRuntimeStatus = {};
}
bool Application::ReloadScriptingRuntime() {
if (!m_editorContext) {
return false;
}
const std::string& projectPath = m_editorContext->GetProjectPath();
if (projectPath.empty()) {
return false;
}
InitializeScriptingRuntime(projectPath);
return m_scriptRuntimeStatus.runtimeLoaded;
}
bool Application::RebuildScriptingAssemblies() {
if (!m_editorContext) {
return false;
}
const std::string& projectPath = m_editorContext->GetProjectPath();
if (projectPath.empty()) {
return false;
}
#ifdef XCENGINE_ENABLE_MONO_SCRIPTING
auto& logger = Debug::Logger::Get();
logger.Info(Debug::LogCategory::Scripting, "Rebuilding project script assemblies...");
// Release the currently loaded project assembly before invoking the compiler.
// Otherwise GameScripts.dll can remain locked by the active Mono app domain.
ShutdownScriptingRuntime();
const ::XCEngine::Editor::Scripting::EditorScriptAssemblyBuildResult buildResult =
::XCEngine::Editor::Scripting::EditorScriptAssemblyBuilder::RebuildProjectAssemblies(projectPath);
if (!buildResult.succeeded) {
m_scriptRuntimeStatus.statusMessage = buildResult.message;
logger.Error(Debug::LogCategory::Scripting, buildResult.message.c_str());
return false;
}
logger.Info(Debug::LogCategory::Scripting, buildResult.message.c_str());
return ReloadScriptingRuntime();
#else
m_scriptRuntimeStatus.statusMessage = "This editor build does not include Mono scripting support.";
return false;
#endif
}
bool Application::InitializeWindowRenderer(HWND hwnd) {
RECT clientRect = {};
if (!GetClientRect(hwnd, &clientRect)) {
@@ -150,6 +273,7 @@ bool Application::Initialize(HWND hwnd) {
logger.Info(Debug::LogCategory::General, "Initializing editor context...");
InitializeEditorContext(projectRoot);
InitializeScriptingRuntime(projectRoot);
logger.Info(Debug::LogCategory::General, "Initializing ImGui backend...");
InitializeImGui(hwnd);
logger.Info(Debug::LogCategory::General, "Attaching editor layer...");
@@ -171,6 +295,7 @@ void Application::Shutdown() {
UI::ShutdownBuiltInIcons();
m_imguiBackend.Shutdown();
m_imguiSession.Shutdown();
ShutdownScriptingRuntime();
ShutdownEditorContext();
if (m_resourceManagerInitialized) {
::XCEngine::Resources::ResourceManager::Get().Shutdown();
@@ -230,6 +355,7 @@ bool Application::SwitchProject(const std::string& projectPath) {
logger.Info(Debug::LogCategory::General, infoMessage.c_str());
::XCEngine::Resources::ResourceManager::Get().SetResourceRoot(projectPath.c_str());
InitializeScriptingRuntime(projectPath);
m_lastWindowTitle.clear();
UpdateWindowTitle();