diff --git a/sources/Directory.Packages.props b/sources/Directory.Packages.props
index 764041d30f..1379d39654 100644
--- a/sources/Directory.Packages.props
+++ b/sources/Directory.Packages.props
@@ -147,4 +147,4 @@
-
+
\ No newline at end of file
diff --git a/sources/editor/Stride.Core.Assets.Editor.Avalonia/Views/ImageResources.axaml b/sources/editor/Stride.Core.Assets.Editor.Avalonia/Views/ImageResources.axaml
index c8249c12ab..aac884df41 100644
--- a/sources/editor/Stride.Core.Assets.Editor.Avalonia/Views/ImageResources.axaml
+++ b/sources/editor/Stride.Core.Assets.Editor.Avalonia/Views/ImageResources.axaml
@@ -151,4 +151,14 @@
+
+
+
+
+
+ F1 M 2,2 L 14,8 L 2,14 Z
+
+
+
+
diff --git a/sources/editor/Stride.GameStudio.Avalonia/App.axaml.cs b/sources/editor/Stride.GameStudio.Avalonia/App.axaml.cs
index 4445532993..e65588ba17 100644
--- a/sources/editor/Stride.GameStudio.Avalonia/App.axaml.cs
+++ b/sources/editor/Stride.GameStudio.Avalonia/App.axaml.cs
@@ -4,6 +4,7 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins;
+using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Styling;
diff --git a/sources/editor/Stride.GameStudio.Avalonia/ViewModels/MainViewModel.cs b/sources/editor/Stride.GameStudio.Avalonia/ViewModels/MainViewModel.cs
index c7ad06f1d2..c05e340f62 100644
--- a/sources/editor/Stride.GameStudio.Avalonia/ViewModels/MainViewModel.cs
+++ b/sources/editor/Stride.GameStudio.Avalonia/ViewModels/MainViewModel.cs
@@ -45,6 +45,7 @@ public MainViewModel(IViewModelServiceProvider serviceProvider)
OpenDebugWindowCommand = new AnonymousTaskCommand(serviceProvider, OnOpenDebugWindow, () => DialogService.HasMainWindow);
OpenSettingsWindowCommand = new AnonymousTaskCommand(serviceProvider, OnOpenSettingsWindow, () => DialogService.HasMainWindow);
OpenWebPageCommand = new AnonymousTaskCommand(serviceProvider, OnOpenWebPage);
+ RunCurrentProjectCommand = new AnonymousTaskCommand(serviceProvider, RunCurrentProject);
Status = new StatusViewModel(ServiceProvider);
Status.PushStatus("Ready");
@@ -80,6 +81,8 @@ public string Title
public ICommandBase OpenWebPageCommand { get; }
+ public ICommandBase RunCurrentProjectCommand { get; }
+
private EditorDialogService DialogService => ServiceProvider.Get();
public async Task OpenSession(UFile? filePath, CancellationToken token = default)
@@ -141,6 +144,128 @@ private void OnExit()
DialogService.Exit();
}
+ private static async Task BuildProject(string projectPath, string framework, string workingDirectory)
+ {
+ using var process = new Process();
+ process.StartInfo = new ProcessStartInfo
+ {
+ FileName = "dotnet",
+ Arguments = $"build \"{projectPath}\" --framework {framework}",
+ WorkingDirectory = workingDirectory,
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ CreateNoWindow = true,
+ };
+
+ process.OutputDataReceived += (_, e) => { if (e.Data != null) Console.Out.WriteLine("[build] " + e.Data); };
+ process.ErrorDataReceived += (_, e) => { if (e.Data != null) Console.Error.WriteLine("[build-err] " + e.Data); };
+
+ try
+ {
+ process.Start();
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+ await process.WaitForExitAsync();
+ return process.ExitCode == 0;
+ }
+ catch (Exception ex)
+ {
+ await Console.Error.WriteLineAsync("Build process failed: " + ex);
+ return false;
+ }
+ }
+
+ private async Task RunCurrentProject()
+ {
+ var mainProjectPath = Session?.CurrentProject?.RootDirectory;
+ if (mainProjectPath == null) return;
+
+ var projectDir = Path.GetDirectoryName(mainProjectPath.FullPath);
+ var projectBaseName = Path.GetFileNameWithoutExtension(mainProjectPath.FullPath);
+
+ string platformSuffix, framework, platformRuntime;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ platformSuffix = "Windows";
+ platformRuntime = "win-x64";
+ framework = "net8.0-windows";
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ platformSuffix = "Linux";
+ platformRuntime = "linux-x64";
+ framework = "net8.0-linux";
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ platformSuffix = "macOS";
+ platformRuntime = "osx-x64";
+ framework = "net8.0-macos";
+ }
+ else
+ {
+ await ShowError("Unsupported OS platform");
+ return;
+ }
+
+ var platformProjectName = $"{projectBaseName}.{platformSuffix}.csproj";
+ var platformProjectPath = Path.Combine(projectDir, $"{projectBaseName}.{platformSuffix}", platformProjectName);
+ var execPath = Path.Combine(projectDir, "Bin", platformSuffix, "Debug", platformRuntime);
+ var dllPath = Path.Combine(execPath, $"{projectBaseName}.{platformSuffix}.dll");
+
+
+ if (!File.Exists(platformProjectPath))
+ {
+ await ShowError($"Platform-specific project not found: {platformProjectPath}");
+ return;
+ }
+
+ Status.PushStatus("Building project...");
+ await Console.Out.WriteLineAsync("Building project...");
+ bool buildSuccess = await Task.Run(() => BuildProject(platformProjectPath, framework, projectDir));
+ if (!buildSuccess)
+ {
+ Status.PushStatus("Build failed.");
+ await Console.Out.WriteLineAsync("Build failed.");
+ await ShowError("Build failed. See output for details.");
+ return;
+ }
+
+ Status.PushStatus("Running project...");
+ await Console.Out.WriteLineAsync("Running project...");
+ var process = new Process
+ {
+ StartInfo = new ProcessStartInfo
+ {
+ FileName = "dotnet",
+ Arguments = $"\"{dllPath}\"",
+ WorkingDirectory = projectDir,
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ CreateNoWindow = false,
+ }
+ };
+
+ process.OutputDataReceived += (_, e) => { if (e.Data != null) Console.Out.WriteLine("[run] " + e.Data); };
+ process.ErrorDataReceived += (_, e) => { if (e.Data != null) Console.Error.WriteLine("[run-err] " + e.Data); };
+
+ try
+ {
+ process.Start();
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+ }
+ catch (Exception ex)
+ {
+ await Console.Error.WriteLineAsync("Run process failed: " + ex);
+ await ShowError("Failed to start the game process. See output for details.");
+ }
+
+ // FIXME: should we wait for process end?
+ }
+
private async Task OnOpen(UFile? initialPath)
{
await OpenSession(initialPath);
@@ -169,4 +294,9 @@ private async Task OnOpenWebPage(string url)
await ServiceProvider.Get().MessageBoxAsync(message, MessageBoxButton.OK, MessageBoxImage.Error);
}
}
+
+ private async Task ShowError(string message)
+ {
+ await ServiceProvider.Get().MessageBoxAsync(message, MessageBoxButton.OK, MessageBoxImage.Error);
+ }
}
diff --git a/sources/editor/Stride.GameStudio.Avalonia/Views/MainView.axaml b/sources/editor/Stride.GameStudio.Avalonia/Views/MainView.axaml
index a5a29d7137..031e1b6402 100644
--- a/sources/editor/Stride.GameStudio.Avalonia/Views/MainView.axaml
+++ b/sources/editor/Stride.GameStudio.Avalonia/Views/MainView.axaml
@@ -85,7 +85,14 @@
+
+