Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions Docs/LinuxDeploymentAndUpdates.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,11 @@ self-update loop when a source-built launcher reports stale version metadata.

If `/settings/updates` has already detected a Bootstrap update and Quasar is
running under Bootstrap, the **Force activate** button writes a
`Updates/bootstrap-update-request.json` request. Bootstrap watches for that file,
consumes it, and runs the same verified self-update path immediately instead of
waiting for the next 15-minute monitor tick. Managed Magnetar servers stay
running; the web UI reconnects after the launcher restarts.
`Updates/bootstrap-update-request.json` request containing the detected
version and platform asset. Bootstrap watches for that file, consumes it, and
runs the same verified self-update path for that requested release immediately
instead of waiting for the next 15-minute monitor tick. Managed Magnetar servers
stay running; the web UI reconnects after the launcher restarts.

## Install

Expand Down
9 changes: 5 additions & 4 deletions Docs/QuasarArchitecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -485,8 +485,8 @@ Linux-first cutover ownership:
- Bootstrap self-update drains only when the primary release asset is actually
newer than the running launcher's normalized release identity
- `/settings/updates` can also write `Updates/bootstrap-update-request.json`
to ask Bootstrap to run the self-update path immediately when a launcher
update is already detected
with the detected version and asset to ask Bootstrap to run the self-update
path for that requested launcher release immediately

This implies a two-layer deployment:

Expand Down Expand Up @@ -536,8 +536,9 @@ Practical guarantee:
Bootstrap updates normally activate from Bootstrap's own update monitor. When
the Updates page has detected a newer launcher asset and the worker is running
under Bootstrap, an admin can force activation from the UI. The worker writes a
request file under `Updates/`; Bootstrap consumes it with a watcher and runs the
same checksum-verified self-update path immediately.
request file under `Updates/` containing the detected version and asset;
Bootstrap consumes it with a watcher and runs the same checksum-verified
self-update path for that requested release immediately.

### Future proxy update flow

Expand Down
16 changes: 8 additions & 8 deletions Docs/Reference/data/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -424,8 +424,8 @@
"path": "Quasar.Bootstrap/Program.cs",
"name": "Program.cs",
"ext": ".cs",
"size": 83171,
"sha256": "9ee4a08e03e5d1e05be1b80d34122d198a83ea4de9d4f8834bdcdad2e4ba8afc",
"size": 86406,
"sha256": "e489e9690e86fd4dc2fb06b2bfb6acf77281f8b499d54d50e6137b2dbea27d87",
"module": "Quasar.Bootstrap",
"tier": 1,
"status": "pending"
Expand Down Expand Up @@ -944,8 +944,8 @@
"path": "Quasar/Components/Pages/WorldTemplateQuickImportDialog.razor",
"name": "WorldTemplateQuickImportDialog.razor",
"ext": ".razor",
"size": 22441,
"sha256": "b64cbfc5295ae8afe92a398c97ff7633295ea4e42419ee19f1c11b2fb7903aa8",
"size": 22497,
"sha256": "0a136a75e52ad78fbbd607c04ba6c8838f9fac17a1d489bbfadeb066492e49d1",
"module": "Quasar.Components",
"tier": 2,
"status": "pending"
Expand All @@ -954,8 +954,8 @@
"path": "Quasar/Components/Pages/WorldTemplates.razor",
"name": "WorldTemplates.razor",
"ext": ".razor",
"size": 16948,
"sha256": "3ff78df70ba81029e4ac0c0cdaa06567921c26a3632d6113b7ebbf4e47c4096d",
"size": 17004,
"sha256": "4308323d98f7cbffd85c1d3c1b586f9d0a29ad09a6cc8e6b7dc716a598eb14c6",
"module": "Quasar.Components",
"tier": 2,
"status": "pending"
Expand Down Expand Up @@ -2014,8 +2014,8 @@
"path": "Quasar/Services/WorldTemplateImportLocationService.cs",
"name": "WorldTemplateImportLocationService.cs",
"ext": ".cs",
"size": 8228,
"sha256": "d64db611f8d7b2bc464dd223163bff39405aa84fb61a02a16d119e16447d5e96",
"size": 10034,
"sha256": "1a89ae1b139e4f18e772f3559dec8191469447c91a3d8d5a4a04d63857acd013",
"module": "Quasar.Services.Core",
"tier": 1,
"status": "pending"
Expand Down
10 changes: 5 additions & 5 deletions Docs/Reference/files/Quasar.Bootstrap/Program.cs.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ Entry point and core logic for the Quasar launcher. It implements three CLI comm
| `StartAsync` | Creates dirs, downloads an initial UI worker into `ManagedRuntime/WebService/<version>` when no packaged/active worker exists, ensures an active-release pointer exists, activates it, starts `FileSystemWatcher`s on the active pointer and Bootstrap update request file. |
| `StopAsync` | Sets `_isStopping`, stops the pointer watcher, Bootstrap update request watcher, and launcher update monitor, drains/retires the current worker, stopping managed servers only when `!PreserveServersOnShutdown`. |
| `StartBootstrapUpdateMonitor` / `RunBootstrapUpdateMonitorAsync` | Starts the self-upgrade loop on Linux and Windows when updates are enabled; checks after 30 s and then every configured update interval. |
| `StartWatchingBootstrapUpdateRequests` / `QueueBootstrapUpdateRequest` | Watches `Updates/bootstrap-update-request.json`, debounces file events, deletes the request, logs the admin-triggered activation, and calls the same Bootstrap self-upgrade path immediately. |
| `TryUpgradeBootstrapAsync` / `ResolveBootstrapPayloadDirectory` / `ApplyBootstrapUpdate` | Serializes self-upgrade attempts, finds an actually newer non-draft release containing the platform `BootstrapAssetName`, verifies `SHA256SUMS`, extracts it, accepts either a flat launcher archive or one single top-level installer directory, skips drain/restart if the downloaded launcher is byte-identical to the installed launcher, preserves existing `appsettings.json`, replaces launcher files, drains the UI worker without stopping managed servers, then restarts: on Linux exits with code 75 so systemd restarts the updated launcher; on Windows spawns a detached `Quasar.exe serve --quiet` and exits 0 (Scheduled Task restart-on-failure is the safety net). |
| `StartWatchingBootstrapUpdateRequests` / `QueueBootstrapUpdateRequest` | Watches `Updates/bootstrap-update-request.json`, debounces file events, reads the requested Bootstrap version/asset, deletes the request, logs the admin-triggered activation, and calls the Bootstrap self-upgrade path for that requested release immediately. |
| `TryUpgradeBootstrapAsync` / `ResolveBootstrapPayloadDirectory` / `ApplyBootstrapUpdate` | Serializes self-upgrade attempts, finds either the latest allowed non-draft release containing the platform `BootstrapAssetName` (periodic monitor) or the exact version/asset from a worker request (force activation), verifies `SHA256SUMS`, extracts it, accepts either a flat launcher archive or one single top-level installer directory, skips drain/restart if the downloaded launcher is byte-identical to the installed launcher, preserves existing `appsettings.json`, replaces launcher files, drains the UI worker without stopping managed servers, then restarts: on Linux exits with code 75 so systemd restarts the updated launcher; on Windows spawns a detached `Quasar.exe serve --quiet` and exits 0 (Scheduled Task restart-on-failure is the safety net). |
| `IsReleasePointerUsable` / `IsKnownReleasePath` | Validates active-release pointers. In service mode, Bootstrap rejects stale pointers to arbitrary external build directories and only trusts packaged `WebService/`, managed web releases, staged legacy updates, or explicit environment-configured worker paths. |
| `ActivateCurrentReleaseAsync` | Under `_activationLock`: drains the current worker without stopping managed servers, starts the new worker, waits for `/api/health` (60 s), swaps it in, then prunes inactive managed web-release directories. |
| `StartWorkerAsync` | Copies install-directory `appsettings.json` into the worker directory, launches the worker with env vars (`QUASAR_MODE=service`, `QUASAR_LAUNCHER_TOKEN`, `QUASAR_BOOTSTRAP_VERSION`, `QUASAR_INSTALL_DIR`, `QUASAR_PRESERVE_SERVERS_ON_SHUTDOWN`, foreground console-logging), and pumps stdout/stderr in foreground. |
| `SyncInstallAppSettingsToWorker` | Keeps the managed worker's base `appsettings.json` aligned with the stable launcher/install directory before process start. |
| `DrainAndRetireWorkerAsync` | POSTs `/api/internal/drain?delaySeconds=&stopServers=` with `X-Quasar-Launcher-Token`, waits for exit, force-kills on timeout. |
| `HandleWorkerExited` | On unexpected worker exit (not stopping), restarts via `ActivateCurrentReleaseAsync(force: true)`. |
| `TryConsumeLauncherShutdownRequest` | Detects and deletes the worker-written `launcher-shutdown-request` file, allowing UI-driven Quasar shutdown to exit Bootstrap without restarting the worker. |
| `TryConsumeBootstrapUpdateRequest` | Detects and deletes the worker-written Bootstrap update request file before running immediate self-upgrade. |
| `TryConsumeBootstrapUpdateRequest` | Detects, deserializes, and deletes the worker-written Bootstrap update request file before running immediate self-upgrade for the requested release. |
| `HandleReleasePointerChanged` | Debounces pointer file changes 250 ms then re-activates. |
| `TryMigrateStagedActiveRelease` | Migrates legacy active pointers that still target `Updates/Staged/<version>` into `ManagedRuntime/WebService/<version>` before launch. |
| `TryBuildInitialReleasePointer` | Resolves worker by priority: `QUASAR_WEB_EXE`/`MAGNETAR_WEB_EXE` → `QUASAR_WEB_DLL`/`MAGNETAR_WEB_DLL` → packaged `WebService/Quasar(.exe)` → ancestor-walk for `Quasar.dll`/`Quasar.exe`. |
Expand All @@ -56,5 +56,5 @@ Entry point and core logic for the Quasar launcher. It implements three CLI comm
## Notes
- The `Quasar.Bootstrap` named mutex serializes spawn attempts across processes on a machine.
- `IsCurrentBootstrapAssembly` / `IsCurrentBootstrapExecutable` prevent pointing the worker at the bootstrap itself; RID-targeted DLL paths are rejected when no sibling `runtimeconfig.json` exists (avoids libhostpolicy failures from the `obj/` tree).
- `PreserveServersOnShutdown` and `QUASAR_INSTALL_DIR` are propagated to the worker so the launcher and worker agree on shutdown policy and the update service can sync resolved appsettings back to the stable install directory. A worker-written `launcher-shutdown-request` file lets Bootstrap exit cleanly for full Quasar shutdown while preserving servers; a worker-written `Updates/bootstrap-update-request.json` file lets the Updates page ask Bootstrap to run launcher self-update immediately.
- Initial UI worker download scans GitHub releases for the newest non-draft release containing the configured UI asset (`WebAssetName`, OS-selected), extracts it into `ManagedRuntime/WebService/<version>`, and validates the extracted web layout before activation; launcher self-upgrade scans the primary Quasar release stream for the platform `BootstrapAssetName` and compares against the normalized release identity, not raw `AssemblyVersion`. Launcher self-upgrade strips the single `quasar-installer-*` archive directory when present, while still accepting older flat launcher archives. If version metadata is stale but the installed launcher already matches the downloaded update byte-for-byte, Bootstrap logs and skips the worker drain/restart instead of repeating the same self-update every check. Both periodic and request-file-triggered Bootstrap updates share a semaphore and the same verified install path. Both scans honor `Quasar:Updates:IncludePrerelease`, including the data-directory override written by the Updates page after Bootstrap restarts. Service-mode active-release pointer validation prevents a previously written local `bin/Debug` worker path from overriding the installed packaged worker.
- `PreserveServersOnShutdown` and `QUASAR_INSTALL_DIR` are propagated to the worker so the launcher and worker agree on shutdown policy and the update service can sync resolved appsettings back to the stable install directory. A worker-written `launcher-shutdown-request` file lets Bootstrap exit cleanly for full Quasar shutdown while preserving servers; a worker-written `Updates/bootstrap-update-request.json` file identifies the detected target version/asset and lets the Updates page ask Bootstrap to run launcher self-update immediately.
- Initial UI worker download scans GitHub releases for the newest non-draft release containing the configured UI asset (`WebAssetName`, OS-selected), extracts it into `ManagedRuntime/WebService/<version>`, and validates the extracted web layout before activation; periodic launcher self-upgrade scans the primary Quasar release stream for the platform `BootstrapAssetName` and compares against the normalized release identity, not raw `AssemblyVersion`. Request-file-triggered Bootstrap updates resolve the exact version/asset requested by the worker, so force activation can apply a candidate the running launcher has not selected from its own periodic stream yet. Launcher self-upgrade strips the single `quasar-installer-*` archive directory when present, while still accepting older flat launcher archives. If version metadata is stale but the installed launcher already matches the downloaded update byte-for-byte, Bootstrap logs and skips the worker drain/restart instead of repeating the same self-update every check. Both periodic and request-file-triggered Bootstrap updates share a semaphore and the same verified install path. Periodic scans honor `Quasar:Updates:IncludePrerelease`, including the data-directory override written by the Updates page after Bootstrap restarts. Service-mode active-release pointer validation prevents a previously written local `bin/Debug` worker path from overriding the installed packaged worker.
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ MudBlazor dialog for adding a Space Engineers world template without leaving the
- **`[Parameter]` `InitialConfigProfileId`** — preselects the currently selected server-editor config profile for the "existing profile" mod path.
- **Key UI**
- Step 1 `MudTabs` card with separate Predefined Worlds and Custom Import panels; hidden panels are not kept alive.
- Predefined Worlds tab lists discovered installed DS templates with search, Refresh, source/category display, and per-row Add buttons. The table uses `installed-world-template-*` classes so the left Add column remains visible while long source paths truncate within the dialog width.
- Predefined Worlds tab lists discovered installed DS templates with search, Refresh, short relative source/category display, and per-row Add buttons. The table uses `installed-world-template-*` classes so the untitled left Add column remains visible while long source paths truncate within the dialog width.
- Custom Import tab contains the original `MudForm` and `@bind-IsValid` details form: required Name, optional multi-line Description, and source world path text field + "Browse" button that opens `FolderPickerDialog`.
- Step 2 mod handling view when source mods are found, with radio options for creating a profile, importing into an existing profile, or doing nothing. The info alert explains that Quasar writes the selected profile's session settings and mods into the active world's `Sandbox_config.sbc` on server start.
- Mod preview table listing display name and Workshop ID.
- Back / Cancel / primary action buttons; the primary Continue button is only shown for Custom Import or the mod-handling step, while Predefined Worlds uses row-level Add buttons. Primary text shows "Importing..." while `_importing` is true.
- **`OpenFolderPickerAsync`** — opens `FolderPickerDialog` with the current path, remembered last path, or first existing DS content shortcut; applies and remembers the selected path on non-cancelled result.
- **`ImportPredefinedTemplateAsync`** — copies display name, description, and source path from an `InstalledWorldTemplateSource`, then follows the same mod-detection/import flow as Custom Import.
- **`ImportPredefinedTemplateAsync`** — copies display name, description, and absolute source path from an `InstalledWorldTemplateSource`, then follows the same mod-detection/import flow as Custom Import.
- **`ContinueAsync` / `ContinueFromDetailsAsync`** — validates custom details, reads source mods via `WorldSandboxConfigEditor.ReadMods`, advances to mod handling when mods exist, or imports immediately when no mods are present.
- **`ImportAsync`** — validates selected mod action, imports the world template, applies the profile action, and closes with `Ok(WorldTemplateQuickImportResult)`.
- **`ApplyModActionAsync`** — merges mods into an existing profile or creates a new profile preloaded with mods; the create path opens `ConfigsPageDialog` full-screen on the new profile so it can be edited before returning to the server editor.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Routable page at `/world-templates` for managing reusable Space Engineers world
- **`[Inject]`:** `QuasarWorldTemplateCatalog WorldTemplateCatalog`, `ISnackbar Snackbar`, `IDialogService DialogService`, `WorldTemplateImportLocationService ImportLocations`
- **Key UI**
- Left panel (xl:5) — `MudTabs` import card with separate Predefined Worlds and Custom Import panels; panels are not kept alive when hidden.
- Predefined Worlds tab — shows discovered installed Space Engineers templates from DS `Content/CustomWorlds`, `Content/QuickStarts`, and `Content/Scenarios`, with search, Refresh, source/category display, and per-row Add buttons. The table uses `installed-world-template-*` classes so the left action column stays fixed and the source path truncates inside narrow docked containers.
- Predefined Worlds tab — shows discovered installed Space Engineers templates from DS `Content/CustomWorlds`, `Content/QuickStarts`, and `Content/Scenarios`, with search, Refresh, short relative source/category display, and per-row Add buttons. The table uses `installed-world-template-*` classes so the untitled left action column stays fixed and the source path truncates inside narrow docked containers.
- Custom Import tab — `MudTextField` controls for name, description, and source path with a "Browse" folder-picker button, plus Import (shows "Importing…" while `_importing`) and Clear buttons.
- Right panel (xl:7) — `MudTable<WorldTemplateRow>` with Clone/Delete actions, Size and Updated metadata, Name, and Description.
- **`WorldTemplateRow` (private sealed record)** — `(QuasarWorldTemplate Template, bool WorldExists, long FileSizeMb)`.
Expand All @@ -35,4 +35,4 @@ Routable page at `/world-templates` for managing reusable Space Engineers world
## Notes
- Directory size is recomputed inline on every render by walking all files, which can be slow for large templates.
- A missing world directory surfaces as a warning chip and disables Clone, but does not auto-remove the catalog entry.
- The predefined-world source column is intentionally ellipsized so long DS paths cannot push the Add button outside the docked import panel.
- The predefined-world source column displays `SourceDisplayPath` instead of the absolute source path and still ellipsizes so long nested scenario paths cannot push the Add button outside the docked import panel.
Loading
Loading