Skip to content

Commit 4727667

Browse files
authored
Merge pull request #3276 from stride3d/feature/launcher-avalonia-cherrypick
Cross-platform Stride.Launcher
2 parents 7fadc52 + 857033a commit 4727667

159 files changed

Lines changed: 8757 additions & 4853 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build-launcher.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ jobs:
5959
- name: Build
6060
run: |
6161
dotnet build build\Stride.Launcher.slnx `
62+
-nr:false `
6263
-v:m -p:WarningLevel=0 `
6364
-p:Configuration=${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} `
6465
-p:StridePlatforms=Windows `

build/Stride.Launcher.slnx

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,29 @@
11
<Solution>
2-
<Project Path="../sources/assets/Stride.Core.Packages/Stride.Core.Packages.csproj" />
3-
<Project Path="../sources/core/Stride.Core.Design/Stride.Core.Design.csproj" />
4-
<Project Path="../sources/core/Stride.Core.IO/Stride.Core.IO.csproj" />
5-
<Project Path="../sources/core/Stride.Core.Mathematics/Stride.Core.Mathematics.csproj" />
6-
<Project Path="../sources/core/Stride.Core.MicroThreading/Stride.Core.MicroThreading.csproj" />
7-
<Project Path="../sources/core/Stride.Core.Reflection/Stride.Core.Reflection.csproj" />
8-
<Project Path="../sources/core/Stride.Core.Serialization/Stride.Core.Serialization.csproj" />
9-
<Project Path="../sources/core/Stride.Core.Translation/Stride.Core.Translation.csproj" />
10-
<Project Path="../sources/core/Stride.Core.Yaml/Stride.Core.Yaml.csproj" />
11-
<Project Path="../sources/core/Stride.Core/Stride.Core.csproj" />
12-
<Project Path="../sources/editor/Stride.Editor.CrashReport/Stride.Editor.CrashReport.csproj" />
2+
<Folder Name="/10-CoreRuntime/">
3+
<Project Path="../sources/core/Stride.Core.CompilerServices/Stride.Core.CompilerServices.csproj" />
4+
<Project Path="../sources/core/Stride.Core.IO/Stride.Core.IO.csproj" />
5+
<Project Path="../sources/core/Stride.Core.Mathematics/Stride.Core.Mathematics.csproj" />
6+
<Project Path="../sources/core/Stride.Core.MicroThreading/Stride.Core.MicroThreading.csproj" />
7+
<Project Path="../sources/core/Stride.Core.Serialization/Stride.Core.Serialization.csproj" />
8+
<Project Path="../sources/core/Stride.Core/Stride.Core.csproj" />
9+
</Folder>
10+
<Folder Name="/30-CoreDesign/">
11+
<Project Path="../sources/core/Stride.Core.Design/Stride.Core.Design.csproj" />
12+
<Project Path="../sources/core/Stride.Core.Reflection/Stride.Core.Reflection.csproj" />
13+
<Project Path="../sources/core/Stride.Core.Translation/Stride.Core.Translation.csproj" />
14+
<Project Path="../sources/core/Stride.Core.Yaml/Stride.Core.Yaml.csproj" />
15+
</Folder>
16+
<Folder Name="/40-Assets/">
17+
<Project Path="../sources/assets/Stride.Core.Packages/Stride.Core.Packages.csproj" />
18+
</Folder>
19+
<Folder Name="/50-Presentation/">
20+
<Project Path="../sources/presentation/Stride.Core.Presentation.Avalonia/Stride.Core.Presentation.Avalonia.csproj" />
21+
<Project Path="../sources/presentation/Stride.Core.Presentation/Stride.Core.Presentation.csproj" />
22+
</Folder>
23+
<Folder Name="/90-Tools/">
24+
<Project Path="../sources/launcher/Stride.VisualStudio.Commands.Interfaces.Client/Stride.VisualStudio.Commands.Interfaces.Client.csproj" />
25+
</Folder>
1326
<Project Path="../sources/launcher/Stride.Cli/Stride.Cli.csproj" />
27+
<Project Path="../sources/launcher/Stride.Launcher.Tests/Stride.Launcher.Tests.csproj" />
1428
<Project Path="../sources/launcher/Stride.Launcher/Stride.Launcher.csproj" />
15-
<Project Path="../sources/presentation/Stride.Core.Presentation.Dialogs/Stride.Core.Presentation.Dialogs.csproj" />
16-
<Project Path="../sources/presentation/Stride.Core.Presentation.Wpf/Stride.Core.Presentation.Wpf.csproj" />
17-
<Project Path="../sources/presentation/Stride.Core.Presentation/Stride.Core.Presentation.csproj" />
18-
<Project Path="../sources/presentation/Stride.Core.Translation.Presentation/Stride.Core.Translation.Presentation.csproj" />
1929
</Solution>

docs/launcher/README.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Stride Launcher — Overview
2+
3+
The Stride Launcher is the entry point end users run after installing Stride. It is an Avalonia MVVM application that manages the locally installed Stride versions (download, update, uninstall), exposes recent projects and VSIX extensions for Visual Studio, surfaces release notes, news and documentation, and finally starts the selected version of Game Studio.
4+
5+
The launcher's sources live in [sources/launcher/](../../sources/launcher/). The application itself is [Stride.Launcher](../../sources/launcher/Stride.Launcher/), built against `net10.0` with RIDs `linux-x64` and `win-x64`. It is distributed as a NuGet package (`Stride.Launcher`) and wrapped by an Advanced Installer setup on Windows.
6+
7+
## Big picture
8+
9+
```mermaid
10+
flowchart TD
11+
User["User"]
12+
Setup["StrideSetup.exe<br/>Advanced Installer bundle<br/>sources/launcher/Setup/"]
13+
Prereq["launcher-prerequisites.exe<br/>sources/launcher/Prerequisites/"]
14+
Exe["Stride.Launcher.exe<br/>Avalonia MVVM app<br/>sources/launcher/Stride.Launcher/"]
15+
NuGet["NuGet feed<br/>(packages.stride3d.net, nuget.org)"]
16+
Store["NugetStore<br/>sources/assets/Stride.Core.Packages/"]
17+
GS["Stride.GameStudio<br/>(selected version)"]
18+
19+
User -- "runs" --> Setup
20+
Setup -- "installs" --> Exe
21+
Setup -- "installs" --> Prereq
22+
Exe -- "uses" --> Store
23+
Store -- "fetches packages" --> NuGet
24+
Exe -- "starts" --> GS
25+
GS -. "optional: /LauncherWindowHandle" .-> Exe
26+
```
27+
28+
The launcher has three responsibilities:
29+
30+
1. **Self-update.** On start, check NuGet for a newer `Stride.Launcher` package and optionally replace the current executable before the UI is shown. See [self-update.md](self-update.md).
31+
2. **Version management.** List available Stride versions, download/install/uninstall them through `NugetStore`, and track a single "active" version. See [versions.md](versions.md).
32+
3. **Launch Game Studio.** Locate the main executable of the active version, start it with the right arguments, and optionally auto-close when Game Studio signals back via `/LauncherWindowHandle`. See [lifecycle.md](lifecycle.md).
33+
34+
## Projects
35+
36+
The launcher codebase is small and self-contained under [sources/launcher/](../../sources/launcher/):
37+
38+
| Directory | Role |
39+
|---|---|
40+
| [Stride.Launcher/](../../sources/launcher/Stride.Launcher/) | Avalonia MVVM application (`Stride.Launcher.exe`) |
41+
| [Prerequisites/](../../sources/launcher/Prerequisites/) | Advanced Installer project producing `launcher-prerequisites.exe` (Windows only) |
42+
| [Setup/](../../sources/launcher/Setup/) | Advanced Installer project producing the user-facing `StrideSetup.exe` bundle (Windows only) |
43+
44+
The launcher depends on two Stride libraries:
45+
46+
- [Stride.Core.Packages](../../sources/assets/Stride.Core.Packages/) — the `NugetStore` abstraction used to talk to NuGet feeds.
47+
- [Stride.Core.Presentation.Avalonia](../../sources/presentation/Stride.Core.Presentation.Avalonia/) — the shared Avalonia MVVM framework (dispatcher, dialogs, markdown viewer integration, etc.). The WPF equivalent in the editor is `Stride.Core.Presentation.Wpf`.
48+
49+
A handful of files from the editor are linked in directly (not as project references) to keep the launcher dependency graph minimal:
50+
51+
- `EditorPath.cs` — resolves user data paths (`LauncherSettings.conf`, `launcher.lock`, MRU, etc.).
52+
- `PackageSessionHelper.Solution.cs` — parses `.sln` files to discover the Stride version used by a recent project.
53+
- The `Stride.Core.MostRecentlyUsedFiles` shared project — shared MRU list infrastructure.
54+
55+
See [projects.md](projects.md) for the full layout and each file's role.
56+
57+
## When you need these systems
58+
59+
> **Decision tree:**
60+
>
61+
> - Adding a new UI page/tab or a new version-list entry kind?
62+
> **A new ViewModel + View under Stride.Launcher.** See [viewmodels.md](viewmodels.md) and [views.md](views.md).
63+
>
64+
> - Changing how a Stride version is downloaded, updated, or uninstalled?
65+
> **`StrideVersionViewModel` and `PackageVersionViewModel`** — they drive `NugetStore`. See [versions.md](versions.md).
66+
>
67+
> - Changing how the launcher updates itself?
68+
> **`SelfUpdater`** and the self-update window. See [self-update.md](self-update.md).
69+
>
70+
> - Adding a new command-line argument or action?
71+
> **`LauncherArguments` + `Launcher.ProcessArguments`.** See [lifecycle.md](lifecycle.md#command-line-arguments).
72+
>
73+
> - Persisting a new user preference?
74+
> **`LauncherSettings`** (launcher-owned) or **`GameStudioSettings`** (shared with Game Studio). See [settings.md](settings.md).
75+
>
76+
> - Adding a new localized string or URL?
77+
> **`Assets/Localization/Strings.resx` / `Urls.resx`** (+ `.ja-JP` variants). See [localization.md](localization.md).
78+
>
79+
> - Working on the Windows installer or prerequisites bundle?
80+
> **Advanced Installer projects under `Prerequisites/` and `Setup/`.** See [packaging.md](packaging.md).
81+
>
82+
> - Running/debugging on Linux and something behaves differently?
83+
> **Platform-specific code paths.** See [cross-platform.md](cross-platform.md).
84+
85+
## Spoke files
86+
87+
| File | Covers |
88+
|---|---|
89+
| [projects.md](projects.md) | Directory and file layout, external dependencies, linked files |
90+
| [lifecycle.md](lifecycle.md) | Entry point, single-instance mutex, command-line arguments, error codes, crash reporting |
91+
| [viewmodels.md](viewmodels.md) | `MainViewModel`, version view models, recent projects, news/docs/announcement view models |
92+
| [views.md](views.md) | XAML views, windows, converters, markdown viewer integration |
93+
| [versions.md](versions.md) | Version discovery, install/uninstall flow through `NugetStore`, framework selection, beta filter, dev redirects |
94+
| [self-update.md](self-update.md) | Launcher self-update: NuGet update probe, force-reinstall, file swap, restart |
95+
| [settings.md](settings.md) | `LauncherSettings`, `GameStudioSettings`, config file locations |
96+
| [localization.md](localization.md) | `Strings.resx` / `Urls.resx`, designer classes, adding a new language |
97+
| [packaging.md](packaging.md) | `Stride.Launcher.nuspec`, Advanced Installer projects, `StrideSetup.exe`, versioning |
98+
| [cross-platform.md](cross-platform.md) | Windows-only code paths, Registry usage, Linux/macOS porting status (xplat-launcher) |
99+
| [port-status.md](port-status.md) | Full delta vs the WPF launcher on `master` — including silent regressions — and a phased roadmap to close the gap |

docs/launcher/cross-platform.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Cross-platform notes
2+
3+
The launcher is in the middle of a Windows → Avalonia cross-platform port (`xplat-launcher` stream). It targets `net10.0` with RIDs `linux-x64` and `win-x64`; WPF has been fully replaced by Avalonia 12. This file lists the code paths that still behave differently per OS and what the remaining gaps are.
4+
5+
## Executable shape
6+
7+
| Platform | Game Studio file | How it is started |
8+
|---|---|---|
9+
| Windows | `Stride.GameStudio.Avalonia.Desktop.exe` (with fallback to `Stride.GameStudio.exe`) | `Process.Start(exe, args)` |
10+
| Linux | `Stride.GameStudio.Avalonia.Desktop.dll` | `Process.Start("dotnet", $"{dll} {args}")` |
11+
12+
The choice is in `StrideVersionViewModel.GetExecutableNames` and `MainViewModel.StartStudio`. The switch on `Path.GetExtension(mainExecutable)` decides whether to invoke `dotnet` or the binary directly.
13+
14+
## Windows-only code paths
15+
16+
Searching for `OperatingSystem.IsWindows()` in the launcher shows the remaining divergences.
17+
18+
### Prerequisites installer
19+
20+
`StrideStoreVersionViewModel.RunPrerequisitesInstaller` runs `{InstallPath}/Bin/Prerequisites/install-prerequisites.exe`. That binary is a Windows installer — it is simply skipped on non-Windows since the DirectX / .NET prerequisites it ships don't apply.
21+
22+
### Visual Studio integration
23+
24+
`VsixVersionViewModel` relies on `Stride.Core.CodeEditorSupport.VisualStudio.VisualStudioVersions` to find installed VS instances. This uses `vswhere` internally and returns nothing on Linux/macOS, so the VSIX entries are effectively hidden. The code does not branch explicitly — it just finds no targets and the command stays disabled.
25+
26+
### Installers
27+
28+
Advanced Installer projects ([Prerequisites/](../../sources/launcher/Prerequisites/), [Setup/](../../sources/launcher/Setup/)) are Windows-only by construction. The MSBuild `PackageInstaller` target silently skips when `AdvancedInstaller.com` is not on `PATH`. On Linux/macOS, distribute the launcher via `dotnet publish -r {rid} --self-contained`. See [packaging.md](packaging.md).
29+
30+
## Telemetry and privacy policy
31+
32+
Both `Stride.Metrics` / `MetricsClient` (telemetry) and `PrivacyPolicyHelper` (first-run consent prompt, uninstall-time revoke) have been **intentionally and permanently removed** from the launcher. They will not be ported. No cleanup of legacy privacy-policy state is performed on uninstall — telemetry was removed, so any residual registry keys or settings files from the old WPF launcher are harmless orphans and do not need to be scrubbed.
33+
34+
## Launcher ↔ GameStudio IPC
35+
36+
When `AutoCloseLauncher` is on, the launcher passes IPC information to Game Studio via a CLI argument so Game Studio can signal back once it has started.
37+
38+
| Platform | Argument | Mechanism |
39+
|---|---|---|
40+
| Windows | `/LauncherWindowHandle <hwnd>` | Game Studio sends a Win32 `WM_CLOSE` message to the launcher's HWND. `MainWindow.OnOpened` captures the HWND via `TryGetPlatformHandle()`. |
41+
| Linux (and other non-Windows) | `/LauncherPipe <pipeName>` | The launcher starts a `NamedPipeServerStream` and waits for a connection (up to 2 minutes). Game Studio connects via `NamedPipeClientStream` once its main window is loaded and writes one byte. The launcher raises `CloseRequested` and the window closes gracefully. |
42+
43+
Both paths are implemented:
44+
- **Launcher side:** `MainViewModel.StartStudio` — branches on `OperatingSystem.IsWindows()`. Non-Windows path starts `WaitForGameStudioPipeSignalAsync` and injects `/LauncherPipe <name>`. `MainWindow.axaml.cs` wires `CloseRequested → OnClosingAsync`.
45+
- **Game Studio side:** `Program.ParseLauncherArgs` (in `Stride.GameStudio.Avalonia.Desktop`) parses both argument styles and sets `App.LauncherNotifier`. `MainWindow.OnLoaded` fires it once.
46+
47+
## Settings paths
48+
49+
All config paths go through `EditorPath` (linked file from `Stride.Core.Assets.Editor`). `EditorPath.UserDataPath` resolves to:
50+
51+
- `%LocalAppData%\Stride\` on Windows
52+
- `$XDG_DATA_HOME/Stride/` (or `~/.local/share/Stride/`) on Linux
53+
- `~/Library/Application Support/Stride/` on macOS
54+
55+
The launcher writes `LauncherSettings.conf` and the `launcher.lock` single-instance marker under these paths. No OS-specific branching is needed.
56+
57+
## Icons
58+
59+
Window icons (`Launcher.ico`) are served through Avalonia's resource system. The `.ico` format is used on every platform; Avalonia picks the best-matching size at runtime.
60+
61+
## Recent-project "Show in Explorer"
62+
63+
`RecentProjectViewModel.Explore` reveals the selected recent project in the platform's native file manager:
64+
65+
- **Windows:** `explorer.exe /select,{path}` (unchanged from master).
66+
- **macOS:** `open -R {path}` — reveals the file in Finder.
67+
- **Linux:** `dbus-send` invocation of `org.freedesktop.FileManager1.ShowItems` on the session bus (implemented by GNOME Nautilus, KDE Dolphin, Cinnamon Nemo, XFCE Thunar, LXDE PCManFM, and others). Falls back to `xdg-open {parent-dir}` when the DBus call fails — e.g. on minimal WMs without an `org.freedesktop.FileManager1` implementer, or headless environments without a session bus.
68+
69+
All failures are swallowed silently (no dialog, no crash) since they are not actionable from inside the launcher.
70+
71+
## Testing surface
72+
73+
When exercising changes, run the launcher on both Windows and Linux. Known gaps that will not reproduce on Linux:
74+
75+
- Self-update using the `force-reinstall` path (downloads a `StrideSetup.exe` — Windows only).
76+
- First-install VSIX prompt (no VS instances).
77+
- Prerequisites installer on first run.
78+
79+
Conversely, on Windows the `dotnet` launch path (the `.dll` branch in `StartStudio`) is unreachable unless a Linux-built package is opened.

0 commit comments

Comments
 (0)