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 @@ + +