Compare commits

...
Sign in to create a new pull request.

801 commits

Author SHA1 Message Date
Pat Hartl
f08fd24a14 Release notes for 2.1.9
All checks were successful
Publish Documentation Updates / dispatch (push) Successful in 9s
2026-07-27 20:09:00 -05:00
Pat Hartl
da94ff8438 Fix detection of primary display's resolution on some Linux multi-display configurations
Some checks failed
LANCommander Release / prep (push) Failing after 49s
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_packager (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
2026-07-27 00:26:12 -05:00
Pat Hartl
41bcfa908d Unify path resolution server-wide 2026-07-24 18:00:32 -05:00
Pat Hartl
2ca1db535c Fix app path resolution for server, add better tests 2026-07-24 17:35:58 -05:00
Pat Hartl
3eae04abdd Better handle bypass execution policy for scripts 2026-07-24 00:57:29 -05:00
Pat Hartl
2b10b55b38 Create abstraction around elevated process launching for better testing 2026-07-23 21:31:47 -05:00
Pat Hartl
eb5abc2c01 Expose internal bits of SDK to test project 2026-07-21 18:14:22 -05:00
Pat Hartl
9e7dc978df Increase performance of depot queries 2026-07-21 18:01:44 -05:00
Pat Hartl
4acf5936c5 Add shorter timeout for server notifications 2026-07-21 18:01:34 -05:00
Pat Hartl
a5b36e1e66 Fix SDK tests 2026-07-21 17:45:34 -05:00
Pat Hartl
e23a5c3eca Add tests for installing to an existing game install 2026-07-21 17:45:17 -05:00
Pat Hartl
a5ab9aba58 Update SharpCompress 2026-07-21 17:22:06 -05:00
Pat Hartl
28d640c888 Fix installation of wine32 and winetricks 2026-07-17 21:30:05 -05:00
Pat Hartl
d37bc13255 v2.1.8 release notes
Some checks failed
LANCommander Release / prep (push) Failing after 17s
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_packager (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
2026-07-08 18:59:36 -05:00
Pat Hartl
f31925e399 Add runtime platform flag to actions, scripts, and save paths 2026-07-08 02:15:54 -05:00
Pat Hartl
36bc769211 Move offline mode button to the titlebar 2026-07-07 01:53:38 -05:00
Pat Hartl
ec8af3f268 Log any version mismatch 2026-07-07 01:53:22 -05:00
Pat Hartl
f538ae4c81 Handle version mismatching 2026-07-07 01:50:25 -05:00
Pat Hartl
6c6a0245c6 Load images from local when in offline mode 2026-07-07 01:49:46 -05:00
Pat Hartl
04dff90a63 Convert description editor to markdown editor 2026-07-06 04:24:42 -05:00
Pat Hartl
331ebf6155 Improvements to Page functionality
Fixes various issues with page functionality, adds better menus for adding/deleting pages, changes slugs to PascalCase.
2026-07-06 02:11:36 -05:00
Pat Hartl
833535c1d9 Persist datatable sort 2026-07-05 16:25:48 -05:00
Pat Hartl
09a78d9da1 Fix enumeration of script snippets 2026-07-05 16:24:58 -05:00
Pat Hartl
99569f65c0 Fix PowerShell module loading in launcher
When running Windows, set execution policy to bypass. Load modules and trap exceptions that might be thrown. This outputs a warning message per PS module instead of full script execution failing.
2026-07-05 14:43:02 -05:00
Pat Hartl
08f5b5f76d Release notes for v2.1.7
Some checks failed
LANCommander Release / prep (push) Failing after 17s
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_packager (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
2026-07-05 02:03:11 -05:00
Pat Hartl
16495770ba Load target view's data before transitioning 2026-07-05 01:29:56 -05:00
Pat Hartl
545b5b1f43 Default to klling process if there's no spawned window 2026-07-04 15:07:13 -05:00
Pat Hartl
c292979010 Fix persisted data table page sizes 2026-07-04 14:51:11 -05:00
Pat Hartl
6e954ff5b8 Log exceptions from save upload failures 2026-07-04 14:27:00 -05:00
Pat Hartl
6bcbc0e9e5 Ensure game is updated when save path is added/updated/deleted 2026-07-04 14:26:25 -05:00
Pat Hartl
38db011de8 Show library before it's fully imported
Loads the user's library from the /Library/Games endpoint and displays it immediately. This route returns a full data set for the user's games. Images are rendered directly from the server. The import still runs in the background, preserving any offline functionality.
2026-07-04 03:33:13 -05:00
Pat Hartl
c26d9cfabb Add endpoint to list full game info for user library 2026-07-04 02:35:09 -05:00
Pat Hartl
b034736822 Avoid downloading depot images to disk, improve UI jank
Fixes #427
2026-07-04 00:14:33 -05:00
Pat Hartl
862f266275 Reorder components of game installations
Game installs now install items in the following order:
1. Base Game
2. Addons
3. Tools
4. Redistributables

Ref #414
2026-07-02 20:42:21 -05:00
Pat Hartl
e287d3ce8a Refresh game action bar after tool install
Ref #414
2026-07-02 20:34:22 -05:00
Pat Hartl
8d96acc98d Ensure tools are uninstalled before game is uninstalled
Ref #414
2026-07-02 20:21:08 -05:00
Pat Hartl
c07161a1a3 Ensure game cache is expired 2026-07-02 20:01:04 -05:00
Pat Hartl
6ccf53de21 Fix importing legacy LCX files with no save paths defined
Fixes #426
2026-07-02 18:14:46 -05:00
Pat Hartl
ee5c9017d5 Get list of installed addons for modify menu 2026-07-01 20:41:08 -05:00
Pat Hartl
04a715e9cb Fix install of games/addons that have no dependent games 2026-07-01 19:37:04 -05:00
Pat Hartl
7595f4d71c Fix inheritance
Some checks failed
LANCommander Release / prep (push) Failing after 14s
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_packager (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
2026-06-28 23:22:57 -05:00
Pat Hartl
5c1e011f0a Add release notes for v2.1.6 2026-06-28 23:17:08 -05:00
Pat Hartl
f87756243e Add support for defining PowerShell modules
Adds a new section "Scripting" in the server UI where PowerShell modules can be defined. These can be used for defining a library of functions that can be used in any script. These modules are automatically synced to the launcher and imported upon script execution.
2026-06-28 23:02:35 -05:00
Pat Hartl
5a80d1e605 Refactor connection string input into full database connection component
Fixes #256
2026-06-28 18:23:58 -05:00
Pat Hartl
69f8447cae Fix tests, add tests for user limits 2026-06-28 14:09:34 -05:00
Pat Hartl
fa1559c367 Add limit options for saves, user storage, and download speed
Implements #85, #84, #100
2026-06-28 14:09:02 -05:00
Pat Hartl
8fdb6f74cb Fix display of UTC times 2026-06-28 11:47:57 -05:00
Pat Hartl
68dece3f1b Preserve existing values in metadata lookup
Fixes #420
2026-06-27 23:19:50 -05:00
Pat Hartl
2f0e0daa3c Add "Select All" and "Load More" buttons to media grabber
Fixes #417
2026-06-27 23:13:11 -05:00
Pat Hartl
a6bf885340 Add dialog for creating users as admin
Fixes #419
2026-06-27 19:06:21 -05:00
Pat Hartl
171e7dcaf0 Invalidate profile cache when logging out
Fixes #422
2026-06-27 18:44:24 -05:00
Pat Hartl
e485ad1865 Fix reconciliation of library games based on user libraries enabled/disabled
Ref #423
2026-06-27 18:38:42 -05:00
Pat Hartl
4d32f958cc Additional gating of UI for user libraries
Ref #423
2026-06-27 18:18:55 -05:00
Pat Hartl
2910bc9476 If user libraries are disabled, hide Library from web UI
Ref #423
2026-06-27 18:14:22 -05:00
Pat Hartl
ecdfc6b4e4 Hide library/depot switcher if user libraries are disabled 2026-06-27 18:12:38 -05:00
Pat Hartl
8760b2d961 Default action ServerHost value to LANCommander server address 2026-06-27 17:59:17 -05:00
Pat Hartl
f53d5bfc4c Add "Always Install" option to tools
Ref #414
2026-06-27 12:11:17 -05:00
Pat Hartl
b21efc63d0 Track tools per game
Ref #414
2026-06-27 11:51:55 -05:00
Pat Hartl
fb0df46572 Uninstall tools when uninstalling game
Ref #414
2026-06-27 11:33:39 -05:00
Pat Hartl
f412f308a2 Fix creation of entities with many-to-many relationships
Ref #414
2026-06-27 11:28:39 -05:00
Pat Hartl
a778a56b18 2.1.5 release notes 2026-06-25 22:43:11 -05:00
Pat Hartl
d3a363bae5 Hide tools that have no archive from install overlay
Some checks failed
LANCommander Release / prep (push) Failing after 11s
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_packager (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
2026-06-25 22:26:59 -05:00
Pat Hartl
656fbfbcbb Fix game tools API request response 2026-06-25 22:04:57 -05:00
Pat Hartl
956c2c3b8a Fix exception thrown when uninstalling game 2026-06-25 22:04:37 -05:00
Pat Hartl
bd81e97037 Fix clickability of game in compact library list 2026-06-25 21:41:05 -05:00
Pat Hartl
84c0c5eff6 Fix updating of roles 2026-06-25 19:55:21 -05:00
Pat Hartl
0973c00f1e Fix removing games from library, consolidate flyout menu 2026-06-25 19:46:30 -05:00
Pat Hartl
33e6d0f4a3 Add missing endpoint for getting tools per game
Ref #414
2026-06-25 02:05:15 -05:00
Pat Hartl
a9f74ef78f Expose "Exit" button under profile dropdown
Fixes #416
2026-06-25 01:55:26 -05:00
Pat Hartl
2a64a621fc Tests for first time setup creation of administrator account 2026-06-25 01:51:42 -05:00
Pat Hartl
97dbcdc73a Ensure normalized username and email are populated
Fixes #415
2026-06-25 01:23:59 -05:00
Pat Hartl
e64439df66
Merge pull request #396 from aaronpowell/spike/ui-automated-tests
Spike: Playwright UI automated tests for server application
2026-06-25 00:20:31 -05:00
Pat Hartl
12d63c02a2 Stabilize Playwright smoke layer for CI
The remaining Playwright failures were all in interactions that depend on
the Blazor Server circuit being responsive in CI:

- Settings page access now navigates to /Settings/General directly instead
  of expanding the flaky nested Settings SubMenu flyout.
- Drop SettingsMenu_ShowsAllExpectedSubItems; the submenu-expansion is the
  unreliable interaction the bUnit migration exists to replace, and the
  Settings pages are now covered by bUnit component tests.
- Remove the GameImport UI tests (and unused GamesPage/OpenRCT2.lcx). The
  ChunkUploader modal never renders under the in-process test host, so they
  failed in every CI run; the Games list render is already covered by
  AdminNavigationTests.GamesPage_ShowsEmptyTable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-24 23:14:42 -05:00
Pat Hartl
004a254c37 Migrate flaky Playwright admin UI tests to bUnit
Replace the SignalR-circuit-race-prone Playwright component tests for
GameEdit, Settings, metadata, profile, roles and users with in-process
bUnit component tests. Keep a thin Playwright smoke layer for the true
E2E paths (login, admin navigation/routing, first-time setup, game
import) that bUnit cannot cover.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-24 20:45:21 -05:00
Pat Hartl
af63a20bd2 Add GameDetailView visual baselines from Linux CI render
Now that both GameDetailView layout tests render (INavigationService +
MultiplyConverter fixes), capture their deterministic CI screenshots as
baselines. Completes the launcher visual baseline regeneration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-24 18:48:19 -05:00
Pat Hartl
e76440c53e Fix GameDetailView visual tests failing to render
GameDetailViewModel resolves INavigationService from DI, and
GameDetailView.axaml references the MultiplyConverter declared in the
real App.axaml. Neither was present in the test harness, so both
GameDetailView layout tests threw before producing a screenshot.
Register the real NavigationService and mirror the MultiplyConverter
into TestApp so the views render. Baselines still need a fresh CI
capture since these tests never previously rendered.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-24 18:35:51 -05:00
Pat Hartl
99131c4b86 Regenerate launcher visual baselines from Linux CI render
Replaces 9 stale/corrupt baselines with fresh deterministic captures
from the CI Linux renderer. The three background views now render with
the random background disabled (ViewBackground seam); the remaining six
update corrupt mid-transition captures (e.g. overlapping Run/Stop text)
and cross-platform font differences.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-24 18:16:31 -05:00
Pat Hartl
860db8ff8f Make launcher view backgrounds deterministic in visual tests
LoginView, SplashView and ServerSelectionView each picked a random
full-screen background on load, so the visual-regression baselines could
never match — every run compared against a different photo and reported a
spurious 82-94% regression. Centralize the selection in ViewBackground and
let tests disable it, so captured screenshots and baselines are stable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-24 02:03:09 -05:00
Pat Hartl
6a2b5177b0 Use SQLite instead of EF InMemory for UI tests
The DataTable component issues relational queries (AsSplitQuery, Include,
and a translated punctuation-stripping search expression) that the EF
InMemory provider cannot translate. On CI this surfaced as a native stack
overflow in CountAsync that crashed the in-process server and cascaded
into widespread timeouts. A file-based SQLite database supports these
queries and concurrent connections, stabilizing the test run.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-24 00:39:44 -05:00
Pat Hartl
97c899f6f2 Generate PowerShell completions in ui_tests CI job
The Monaco editor's PowerShellCompletions.g.ts is gitignored and required
by the frontend webpack build. The in-build MSBuild target uses Windows
path separators that don't resolve on the Linux runner, so the ui_tests
job must generate the file explicitly first — mirroring the server build
jobs which already do this.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-23 22:26:42 -05:00
Pat Hartl
f2a864b540 Update UI tests for post-merge changes from main
- Target net10.0 to match the now-net10 server project (and fix the
  Playwright browser-install path in the PR workflow accordingly).
- Dismiss the persistent "Import Ready" notification (added with main's
  background UploadTracker) before clicking Import; its Duration=0 toast
  overlays and intercepts the modal's Import button.
- Update General settings assertions: "Database Provider" moved to the new
  dedicated /Settings/Database page, so assert on "Use SSL" instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-23 22:11:05 -05:00
Pat Hartl
20a2e0a1af Merge remote-tracking branch 'origin/main' into pr-396
# Conflicts:
#	.github/workflows/LANCommander.PR.yml
#	LANCommander.slnx
2026-06-23 21:13:19 -05:00
Pat Hartl
65c00fe9e1 Fix game import test: set file on InputFile directly
Replace the FileChooser approach with SetInputFilesAsync targeting the
ChunkUploader's hidden InputFile (matched by id prefix), which correctly
drives Blazor's OnChange and enables the Upload button. Also wait on the
actual Upload button's enabled state instead of the first primary button
(which matched the always-enabled "Browse" button and passed spuriously).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-23 21:03:16 -05:00
Pat Hartl
9b5195f1d6 Use web-first Playwright assertions in UI tests
Replace non-waiting Assert.True(await locator.IsVisibleAsync()) checks with
auto-retrying Assertions.Expect(locator).ToBeVisibleAsync()/ToBeHiddenAsync()
to remove flakiness against Blazor's async rendering. Table-row/field page
objects (Metadata/Roles/Users/Profile) now expose ILocator helpers instead of
Task<bool>, so deletion checks wait for the element to disappear.

Also: rename ScreenshotHelper.CaptureIfFailedAsync to CaptureAsync (xUnit v2
cannot expose the test outcome to DisposeAsync, so it always captures the final
page state), drop the unused TestConstants.ServerPort/BaseUrl now that ports
bind dynamically, and fix IClassFixture doc comments to ICollectionFixture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-23 17:51:34 -05:00
Pat Hartl
46801dd468 Hide auth fields / buttons based on server auth settings
Some checks failed
LANCommander Release / prep (push) Failing after 12s
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_packager (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
2026-06-22 21:46:49 -05:00
Pat Hartl
b6e674e2eb Fix saving of authentication settings 2026-06-22 21:08:59 -05:00
Pat Hartl
42ca077416 Add auto redirect to provider to launcher 2026-06-22 21:08:48 -05:00
Pat Hartl
f5c5bbee70 v2.1.4 release notes 2026-06-22 19:45:57 -05:00
Pat Hartl
24eaa79885 Add tool to repack non-streamable ZIP archives
Adds a tool that checks archives to see if they use data descriptors instead of local entry headers for tracking entry size. It will also repack these archives if required.
2026-06-22 19:18:11 -05:00
Pat Hartl
062e59268b Archive documentation
Describes how archives should be created and common pitfalls that archiving tools can make.
2026-06-22 18:07:37 -05:00
Pat Hartl
a78978b10c Fix mapping for save endpoints 2026-06-22 17:09:50 -05:00
Pat Hartl
516465ad1a Add WebP support for media 2026-06-22 17:05:47 -05:00
Pat Hartl
9bc44500fc Update "Last Played" text every minute, localize 2026-06-21 23:33:52 -05:00
Pat Hartl
2e8e4ae3ae Allow selected local files for archive upload to be moved instead of copied
Fixes #413
2026-06-21 23:00:56 -05:00
Pat Hartl
6b2610a200 Rework Tool installation / action resolution
Amends the tool installation to use game install directory and correctly populate actions. Tools previously used an undocumented/unpopulated/inaccessible global install directory. This has been changed to use the game's install directory. Uninstall scripts are also now executed for tools when the game is uninstalled.

Fixes #414
2026-06-21 21:16:45 -05:00
Pat Hartl
23a0a14e57 Auth provider documentation 2026-06-21 02:04:53 -05:00
Pat Hartl
f1fa66b632 Improve advanced auth provider editing, add role claim mapping
- Added auto discovery of scopes and claim mappings to auth provider editor
- Added the ability to map claims to roles
- Auto-provision users logged in over external auth

Ref #411
2026-06-21 02:03:51 -05:00
Pat Hartl
0f03461f53 Add setting to auto redirect to external provider
When enabled, AutoRedirectToProvider will switch authentication challenges to redirect to the external auth provider instead of displaying the password login form. If more than one external provider is configured, a minimal external auth provider login page will be shown.

Ref #410
2026-06-21 01:59:15 -05:00
Pat Hartl
ca7601e218 Mutate user from UserManager queries
Fixes #412
2026-06-20 23:23:38 -05:00
Pat Hartl
000b12ca1c Fix error in launcher when tool actions are not defined 2026-06-20 04:26:04 -05:00
Pat Hartl
b27b1f0ad4 Only display notification when entire install chain is complete 2026-06-20 04:25:01 -05:00
Pat Hartl
54fd3ac0b7 Adjust styling for buttons, dropdowns 2026-06-20 04:24:20 -05:00
Pat Hartl
6e7bcccf45 Add more logging to archive extraction, allow customization of retry attempts on install 2026-06-20 03:33:29 -05:00
Pat Hartl
04a710e101 Add message indicating server needs to be restarted after applying changes to authentication providers 2026-06-19 22:41:37 -05:00
Pat Hartl
72bac8240a Run game started/game stopped server scripts as fire and forget 2026-06-19 22:41:15 -05:00
Pat Hartl
fa63e8e95a Add uninstall script to Redistributables 2026-06-19 20:37:13 -05:00
Pat Hartl
a8709cd05b Fix server game actions loading from server 2026-06-19 20:17:47 -05:00
Pat Hartl
0ed8a80fbb Only allow one instance of the launcher to run at a time
Restores launcher from the system tray on subsequent runs
2026-06-19 19:37:31 -05:00
Pat Hartl
fe8ef32058 Update SharpCompress to 0.49.1 2026-06-19 19:29:47 -05:00
Pat Hartl
5e59e1ca17 Filter non-standalone addons from library compact list 2026-06-18 01:24:15 -05:00
Pat Hartl
1cb30dda24 Fix icons, bundle appimage in normal workflows 2026-06-17 09:36:23 -05:00
Pat Hartl
d676e2e25c Don't swallow errors for HQ authentication 2026-06-17 02:49:16 -05:00
Pat Hartl
fa81d4da6e Generate .app for macOS 2026-06-17 02:45:29 -05:00
Pat Hartl
180fabac97 Generate Linux appimage 2026-06-17 02:45:20 -05:00
Pat Hartl
e231828dd8 Fix list of play sessions under game
Some checks failed
LANCommander Release / prep (push) Failing after 17s
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_packager (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
2026-06-16 01:07:22 -05:00
Pat Hartl
6b4ba041e9 v2.1.3 release notes 2026-06-16 01:07:08 -05:00
Pat Hartl
f17932da5c Include game and user, protect against null game 2026-06-15 23:19:05 -05:00
Pat Hartl
c1aca1572e Add server-side tool to optimize images 2026-06-15 21:45:14 -05:00
Pat Hartl
1ec905420f Add tool to delete long play sessions 2026-06-15 21:04:02 -05:00
Pat Hartl
f17afdd54b Populate working directory from PCGamingWiki save paths 2026-06-15 20:52:54 -05:00
Pat Hartl
68edf9b9f4 Download self-container build of yt-dlp 2026-06-15 20:44:38 -05:00
Pat Hartl
5a29943d44 Fix updating for relationships with stale/untracked entities 2026-06-15 20:25:09 -05:00
Pat Hartl
86e1d6075a Adjust styling in game description 2026-06-15 20:19:03 -05:00
Pat Hartl
99cf4cdc51 Launcher game details performance optimizations
- Changed scroll transition on video/screenshot carousel to use RenderTransform instead of margin to cut down on layout passes for each frame. This should result in a significant drop in CPU usage when scrolling.
- Pause playback of videos when scrolled out of view or window is no longer active.
- Removed additional InlineVideoPlayer controls from being created for each item in the media carousel, and instead only created for videos. This should result in a significant drop in RAM allocation when scrolling.
- Rework caching mechanism for covers to have an upper limit of allocated RAM.
- Only decode screenshots / covers at Hi DPI size in which they are displayed at. This should ensure crisp display while only allocating the amount of RAM needed for display.
2026-06-15 19:37:46 -05:00
Pat Hartl
1ceb569a20 Fix extra gap on compact list scrollbar 2026-06-14 23:50:31 -05:00
Pat Hartl
814d0b3395 Add "Allow Registration" setting to server
Fixes #408
2026-06-14 21:46:29 -05:00
Pat Hartl
9d98e8059a Close to system tray 2026-06-14 21:24:07 -05:00
Pat Hartl
07255ab552 Update Notify.NET, fix progress in taskbar 2026-06-14 19:40:47 -05:00
Pat Hartl
d9501aea4e Localize remaining time and install progress status 2026-06-14 18:20:13 -05:00
Pat Hartl
f7a036453f Remove dead code, fix loading of videos/screenshots blocking UI 2026-06-14 01:58:52 -05:00
Pat Hartl
8cac65232a Fix missing "Starting" text in play button when game is starting 2026-06-14 01:01:04 -05:00
Pat Hartl
ecaecd21bb Send keepalive while game is running
Sends a keepalive/heartbeat signal across RPC while a game is running. Server-side this enables the ability to manage play session state with realtime information instead of hoping that the client will stop the play session. Keepalives are tracked in server cache allowing for future potential horizontally-scaled server instances.
2026-06-14 00:53:45 -05:00
Pat Hartl
ceb0eeb36b Move server lifecycle management to ServerManager. Add autostop delay to servers. 2026-06-14 00:53:38 -05:00
Pat Hartl
c4fa0c7335 Handle Process killing via extension method w/ libc import on Linux 2026-06-14 00:53:31 -05:00
Pat Hartl
268d66eed5
Merge pull request #407 from LANCommander/refactor/database-service-scope
Refactor/database service scope
2026-06-13 01:46:29 -05:00
Pat Hartl
da87e786ff Regenerate PostgreSQL migrations 2026-06-13 01:36:03 -05:00
Pat Hartl
5ffabe8064 Merge branch 'main' into refactor/database-service-scope 2026-06-12 23:51:05 -05:00
Pat Hartl
056eda9fb2 Fix missing SteamGridDB API key throwing exception when adding game 2026-06-12 23:50:24 -05:00
Pat Hartl
ad8f4ddfbf Fix error thrown on first time setup when no storage locations exist 2026-06-12 23:49:04 -05:00
Pat Hartl
790311ac8d Various fixes for MySQL/MariaDB
- Regenerate migrations
- Fix identity database context lifetimes
- Fix rendering of edit components
- Move database settings to separate section
2026-06-12 23:48:11 -05:00
Pat Hartl
e9854cc722 Update 2.1.0 release notes with 2.1.2 details 2026-06-11 18:38:48 -05:00
Pat Hartl
3da723ec2f Add contributor headings 2026-06-11 18:38:35 -05:00
Pat Hartl
3944339daf 2.1.2 release notes, contributor grid component
Some checks failed
LANCommander Release / prep (push) Failing after 22s
LANCommander Release / build_release (push) Has been skipped
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_packager (push) Has been skipped
2026-06-11 18:14:43 -05:00
Pat Hartl
6722740b51 Merge branch 'main' of https://github.com/LANCommander/LANCommander 2026-06-11 17:54:24 -05:00
Pat Hartl
c4a4b98e95 Add fallback for ProcessTerminationMethod for Windows, use absolute pathing for kill in Linux/macOS 2026-06-11 17:54:19 -05:00
Pat Hartl
c3e4f47827
Merge pull request #406 from Mavyre/patch-1
Corrected Local server engine tracking
2026-06-11 17:11:25 -05:00
Mavyre
55ebc1ac74
Corrected Local server engine tracking 2026-06-11 23:04:52 +02:00
Pat Hartl
5733651e42 Make entire row in compact list clickable 2026-06-11 00:12:09 -05:00
Pat Hartl
2c2d16146f Persist items per page
Fixes #296
2026-06-10 23:42:16 -05:00
Pat Hartl
92d10dce3c Fix padding in metadata lookup dialog 2026-06-10 23:06:55 -05:00
Pat Hartl
4fd268d9c3 Check redistributables for updates 2026-06-10 21:51:12 -05:00
Pat Hartl
ac498d45ba Fix grabbing of token for HQ 2026-06-10 21:15:02 -05:00
Pat Hartl
c605c5e8d6 Refresh manifest and scripts on disk if no update exists 2026-06-10 20:39:50 -05:00
Pat Hartl
76cb9f9246 Also handle .ini files 2026-06-10 20:21:18 -05:00
Pat Hartl
ca713f6d3f Rework .reg importing in script editor
Script editor now supports drag and drop for .ps1, .reg, and other text files. .ps1 files will be handled by replacing the contents of the text editor. .reg files will be converted to PowerShell commands and inserted into the script editor. Any other text file will be inserted as an escaped string.
2026-06-10 20:19:06 -05:00
Pat Hartl
37caa8ab9b Put 2.1.1 patch notes into 2.1.0 2026-06-08 20:40:11 -05:00
Pat Hartl
fe0ce66ccb v2.1.1 update release notes 2026-06-08 20:00:55 -05:00
Pat Hartl
76d5375e35 Fix display of empty library view
Some checks failed
LANCommander Release / prep (push) Failing after 1m10s
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_packager (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
2026-06-08 19:53:18 -05:00
Pat Hartl
7e35dac126 Add registration form to launcher 2026-06-08 19:49:38 -05:00
Pat Hartl
c6382d8dd0 Dim title for non-installed games 2026-06-08 19:36:37 -05:00
Pat Hartl
f7972d6a48 Fix "Update" button, update process 2026-06-08 19:29:09 -05:00
Pat Hartl
62c03621a3 Rework update functionality as an additional download workflow 2026-06-08 19:00:56 -05:00
Pat Hartl
7d367b6453 Add message when there's no items in games list view or depot 2026-06-07 12:08:44 -05:00
Pat Hartl
74263fa12d Download media if it doesn't exist locally, add fallback if LibVLC doesn't load 2026-06-07 11:46:25 -05:00
Pat Hartl
ea37124f68 Fix bundling of libvlc on macOS/Windows 2026-06-07 11:45:02 -05:00
Pat Hartl
0ee143cc73 Only scroll focus when using keyboard/gamepad navigation
Some checks failed
LANCommander Release / prep (push) Failing after 29s
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_packager (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
2026-06-06 07:00:07 -05:00
Pat Hartl
cb6bbcab00 Only focus when gamepad has been activated 2026-06-06 06:59:19 -05:00
Pat Hartl
e74ef66bcd Fix innosetup download exe 2026-06-06 06:51:11 -05:00
Pat Hartl
7a4553c407 Only run SDK workflow on publish 2026-06-06 06:50:57 -05:00
Pat Hartl
d1b558621f Update repository documentation 2026-06-06 06:35:49 -05:00
Pat Hartl
19ac365d72 Fix WinGet workflow 2026-06-06 06:29:28 -05:00
Pat Hartl
72365cdc0f Fix SDK packaging 2026-06-06 05:58:24 -05:00
Pat Hartl
2497489041 Remove launcher "Avalonia Preview" text 2026-06-06 05:50:53 -05:00
Pat Hartl
b183e4b18c Remove old launcher, rename Avalonia launcher
Also adds ARM builds for Linux + Windows launcher
2026-06-06 05:49:24 -05:00
Pat Hartl
044981b999 Carousel for packager 2026-06-06 04:05:48 -05:00
Pat Hartl
dc14bf10cb Fix use of Text component 2026-06-06 03:30:49 -05:00
Pat Hartl
0503f4e3a5 v2.1.0 release notes 2026-06-06 03:24:16 -05:00
Pat Hartl
0b260f65ff Fix display of redist picker 2026-06-06 03:19:12 -05:00
Pat Hartl
9c4854b6ad Expand file picker fields 2026-06-06 02:58:43 -05:00
Pat Hartl
5e37efb3cf Fix uploader 2026-06-06 02:34:28 -05:00
Pat Hartl
6f71d4c2e0 Fix packager LCX upload 2026-06-04 22:03:18 -05:00
Pat Hartl
302cfba815 Fix HQ link not persisting across restarts and refresh after HQ account link 2026-06-04 20:42:51 -05:00
Pat Hartl
e685b4c045 Use default storage location if none specified 2026-06-04 01:37:00 -05:00
Pat Hartl
6fa2b3b56c Use correct route for uploading 2026-06-04 01:29:44 -05:00
Pat Hartl
113d8bd080 Add platforms to metadata search results 2026-06-04 01:29:31 -05:00
Pat Hartl
819cb28d17 Packager app improvements, metadata endpoints
- Add ability to connect to server
- Add uploading of finished package to server
- Add metadata lookup when connected to server
- Fix layout when specifying action
- Hide finish button at generate step
2026-06-03 19:43:45 -05:00
Pat Hartl
90d04d1e5e Fix silent errors thrown on upload, fix SDK chunk uploading 2026-06-03 19:28:27 -05:00
Pat Hartl
77bc776c37 Add provider dropdown to media grabber dialog 2026-06-01 02:16:06 -05:00
Pat Hartl
45aaa0ea8d Simplify game addition w/ metadata lookup
Adding a game now immediately provides a dialog to lookup a game through metadata providers. Selecting a result will create the game with the populated metadata. An additional option of automatically downloading art (icon, cover, background, logo) is also provided, though may be inaccurate and completely up to the results of the provider.
2026-06-01 02:15:53 -05:00
Pat Hartl
fe39e5bc45 Allow user to alter files before selecting for packaging 2026-05-31 22:41:40 -05:00
Pat Hartl
34bc139ff9 Make sure Generate LCX button is always visible 2026-05-31 22:41:13 -05:00
Pat Hartl
bb48805d5e Fix detected directory not being populated 2026-05-31 22:40:40 -05:00
Pat Hartl
70a74599c4 Rename Install-LatestArchive to Expand-LatestArchive. Add to redist + tools 2026-05-31 22:29:00 -05:00
Pat Hartl
4c89aa60fe Add cmdlet for extracting the latest archive for a game 2026-05-31 22:19:03 -05:00
Pat Hartl
3237fcdabd Cmdlet documentation 2026-05-31 22:18:12 -05:00
Pat Hartl
670975b647 Use version to bust browser cache for static assets 2026-05-31 16:26:32 -05:00
Pat Hartl
aef2cebc00 Avoid publishing :latest docker tag for prerelease 2026-05-31 16:19:49 -05:00
Pat Hartl
2a66ae0fc7 v2.1.0-rc5 release notes 2026-05-30 13:36:45 -05:00
Pat Hartl
a7a7f021e2 Fix docs list for packager 2026-05-30 13:17:11 -05:00
Pat Hartl
8917a2d79f Add package application for auto-building LCX files
Some checks failed
LANCommander SDK Release / prep (push) Failing after 59s
LANCommander SDK Release / publish (push) Has been skipped
LANCommander Release / prep (push) Failing after 1m12s
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_win_x64 (push) Has been skipped
LANCommander Release / build_packager (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
2026-05-30 13:10:01 -05:00
Pat Hartl
1191226dc0 Hide standalone expansions/mods from nested games 2026-05-29 02:37:08 -05:00
Pat Hartl
af3ee00a87 Fix missing nested game data
Fixes #284
2026-05-29 02:36:47 -05:00
Pat Hartl
bcdbfdbce3 Add HQ login process to first time setup 2026-05-29 02:19:05 -05:00
Pat Hartl
f88ef42c42 Normalize save paths to use / before regex matching
Fixes #266
2026-05-28 22:46:24 -05:00
Pat Hartl
4948dbc0d5 Add background uploading
Fixes #298
2026-05-28 22:26:24 -05:00
Pat Hartl
19a63491fc Add button to download current log 2026-05-28 20:44:35 -05:00
Pat Hartl
2faa334461 Fix log viewer under settings / logs
Fixes #393
2026-05-28 20:31:02 -05:00
Pat Hartl
df97b4f5f9 Fix user approval menu item
Fixes #405
2026-05-28 20:11:26 -05:00
Pat Hartl
eed09becf7 Fix archive processing for "Use Local File" 2026-05-28 19:54:58 -05:00
Pat Hartl
02750d6d62 Improve file manager UX
- Faster enumeration of large directories
- Add toggleable columns
- Add Created and Type columns
- Fix breadcrumbs
- Properly build left view tree with support for cross-platform roots and special folders
2026-05-28 19:54:33 -05:00
Pat Hartl
638942ff89 Fix "Use Local File" disabled button 2026-05-28 19:52:23 -05:00
Pat Hartl
f54eb5bcdd v2.1.0-rc4 Release Notes 2026-05-28 02:02:55 -05:00
Pat Hartl
ab22e0aeb0 Fix scrollbar being overlapped by titlebar
Some checks failed
LANCommander SDK Release / prep (push) Failing after 15s
LANCommander Release / prep (push) Failing after 1m3s
LANCommander SDK Release / publish (push) Has been skipped
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_win_x64 (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
2026-05-28 00:14:22 -05:00
Pat Hartl
a577d6f898 Update HQ package, fixes HQ lookups 2026-05-28 00:10:03 -05:00
Pat Hartl
5b93161910 Add big screen mode 2026-05-28 00:09:53 -05:00
Pat Hartl
4fde39a729 Publish prerelease SDK to NuGet. Switch to multi-target 2026-05-27 23:14:03 -05:00
Pat Hartl
52dd3f23ba Add "Package" button to archive page w/ dialog 2026-05-27 20:42:23 -05:00
Pat Hartl
81639e9fc2 Update PowerShell snippets 2026-05-26 20:37:29 -05:00
Pat Hartl
a67b1953f0 Register cmdlets via source generated extension 2026-05-26 20:36:25 -05:00
Pat Hartl
68b112d6ce Implement RunWrapper script execution for redistributables
RunWrapper scripts were defined as a script type but never executed. This adds full execution support during game launch, with proper game running state tracking and cancellation/stop support including child process cleanup.
2026-05-26 20:11:53 -05:00
Pat Hartl
5b3278685e Gamepad navigation tweaking for game details 2026-05-26 01:11:00 -05:00
Pat Hartl
6704b63cb6 Gamepad navigation tweaking for library list view 2026-05-26 00:49:30 -05:00
Pat Hartl
4ffa52cc2a Fix media download on import 2026-05-26 00:14:48 -05:00
Pat Hartl
3736a6174c Somewhat working gamepad controls 2026-05-26 00:14:13 -05:00
Pat Hartl
54dc7fd20c Fix icon downloading 2026-05-25 16:30:09 -05:00
Pat Hartl
c27496ec2f Add run wrapper script to list of available Redistributable scripts 2026-05-25 15:32:14 -05:00
Pat Hartl
c1c398ad2c Update notes
Some checks failed
LANCommander Release / prep (push) Failing after 1m3s
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_win_x64 (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
2026-05-25 04:38:58 -05:00
Pat Hartl
9d5d11f2c1 Fix Settings/Media 2026-05-25 04:35:46 -05:00
Pat Hartl
9849bf23f7 Fix check for newer saves on server 2026-05-25 04:16:51 -05:00
Pat Hartl
527d3abaf2 Add more save handling logging 2026-05-25 04:16:19 -05:00
Pat Hartl
be4626b779 Increase quality of rendered covers 2026-05-25 03:02:11 -05:00
Pat Hartl
a599e62ade Sync play sessions on import 2026-05-25 02:47:08 -05:00
Pat Hartl
2b3bd643f6 Action bar tweaks
- Hide "Download Size" stat when game is installed
- Correctly load play sessions to fix "Play Time" and "Last Played" stats
2026-05-25 02:32:49 -05:00
Pat Hartl
596bed55ae v2.1.0-rc3 release notes 2026-05-25 02:05:53 -05:00
Pat Hartl
56bc38b235 Fix shadow overlay on covers in carousels 2026-05-25 01:55:47 -05:00
Pat Hartl
667a89634e Fix bottom of covers in carousel being cut off on hover 2026-05-25 01:42:49 -05:00
Pat Hartl
aac2d46932 Fix display of playing/stop on play button when game is running 2026-05-25 01:38:50 -05:00
Pat Hartl
07521ec8f4 Add server discovery to Avalonia launcher 2026-05-25 01:34:40 -05:00
Pat Hartl
12f04167af Merge branch 'main' of https://github.com/LANCommander/LANCommander 2026-05-25 01:08:00 -05:00
Pat Hartl
1897fc50b9
Merge pull request #403 from akifreak/fix-beacon-discovery
Fix beacon discovery
2026-05-24 22:24:16 -05:00
Pat Hartl
65f13f9fcb Preserve Linux root when expanding path variables
Fixes #404
2026-05-24 18:55:57 -05:00
Pat Hartl
4965d2019b Fix redistributable/tool importing 2026-05-24 18:51:58 -05:00
Pat Hartl
1b473343a0 Rework list view in launcher
Redesigns the list view to look more like the classic library view from the previous launcher, with added carousels for Recently Played and Collections.
2026-05-24 16:36:32 -05:00
Pat Hartl
41c9e5a0b9 Launcher optimizations 2026-05-24 15:05:29 -05:00
Pat Hartl
783816e7da Fix launcher CI asset naming
Some checks failed
LANCommander Release / prep (push) Failing after 56s
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_win_x64 (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
2026-05-24 01:55:25 -05:00
Pat Hartl
e7415e8817 Rename assets 2026-05-24 01:05:19 -05:00
Pat Hartl
60b1bd4fbd v2.1.0-rc2 release notes 2026-05-24 01:00:47 -05:00
Pat Hartl
8a618706bd Add variable picker dialog 2026-05-24 00:42:10 -05:00
Pat Hartl
4d795eb449 Remove CLI project
Functionality is built into the Avalonia launcher
2026-05-24 00:10:23 -05:00
Pat Hartl
2bb95e8a12 Formatting 2026-05-24 00:09:13 -05:00
Pat Hartl
d8452efb5a Track redist files
Tracks files created in the game's install directory when the install script is run. This results in the ability to delete redist files when the game is uninstalled.
2026-05-23 22:59:56 -05:00
Pat Hartl
6e2e0542d6 Add full set of command line verbs 2026-05-23 22:58:10 -05:00
Pat Hartl
d2b6d65c93 Always run redist install if no detect script exists 2026-05-23 22:17:32 -05:00
Pat Hartl
a368b7f646 Filter out package scripts from responses 2026-05-23 22:13:58 -05:00
Pat Hartl
0ef795ac3d Fix write all scripts 2026-05-23 22:13:23 -05:00
Pat Hartl
dd4aa6b163 Fix unicode support for win9x 2026-05-23 21:07:28 -05:00
Pat Hartl
528356e9e4 Fix legacy launcher CI pathing 2026-05-23 20:47:12 -05:00
Pat Hartl
725ce079fd Update legacy launcher workflow 2026-05-23 20:43:26 -05:00
Pat Hartl
a5e68a99c3 Remove legacy crash test build 2026-05-23 20:42:52 -05:00
Pat Hartl
eb61c9c95d Merge branch 'feature/legacy-launcher' 2026-05-23 20:33:20 -05:00
Pat Hartl
856d7206ab Missing files for legacy launcher
Some checks failed
LANCommander.Launcher.Legacy / Build (Win9x / MinGW-w64 i686) (push) Has been cancelled
2026-05-23 20:33:04 -05:00
Pat Hartl
c3a9829b85 Visual changes
- Added backgrounds to login screen
- Fixed word wrapping app-wide
- Restyled back button in game details
- Combine Library/Depot button in footer
2026-05-23 20:32:21 -05:00
Pat Hartl
767ca4416d Add app icon. Fix plaer alias button 2026-05-23 20:09:31 -05:00
Pat Hartl
554f2e64a6 Only show standalone mods/expansions and main games in game lists 2026-05-23 19:49:49 -05:00
Pat Hartl
37a6f906f4 Fix rendering issues with window resizing 2026-05-23 19:44:08 -05:00
Pat Hartl
303c6079ac Window resizing 2026-05-23 19:32:12 -05:00
Pat Hartl
6d2c3ccc15 Merge branch 'feature/legacy-launcher' 2026-05-23 16:58:54 -05:00
Pat Hartl
d766f38813 Fix includes 2026-05-23 16:58:45 -05:00
Pat Hartl
443ed1a0c9 Feature parity / Win9x fixes
- Moved launcher data under Data directory
- Fixed initialization under Win9x
- Draw our own cursor
- Add logging throughout application
- Added settings screen
- Added miniz and sqlite vendor libs (with minor modifications to support 9x)
- Fix game extraction
- Cache media files under Data/Media directory
- Fix keyboard input
- Add scrollbars
- Add install options modal
- Add support for multiple install directories
2026-05-23 16:57:11 -05:00
Pat Hartl
74bb5ad1ec Added contributors section 2026-05-21 21:38:53 -05:00
Pat Hartl
6b9ec870a2 Download queue, SQLite, Settings.yml
- Added download queue
- Reimplemented settings using Settings.yml matching the schema of other launchers
- Added SQLite support and storing game information in database
- Improved image caching
- Add uninstall
2026-05-21 21:38:13 -05:00
Pat Hartl
8f41eb9536 Reworked legacy launcher with common library 2026-05-20 21:21:26 -05:00
Pat Hartl
18fc3c291b Updates to release notes 2026-05-19 18:17:29 -05:00
Pat Hartl
df2cf665a4 Updated documentation for v2.1.0-rc1 2026-05-19 01:42:28 -05:00
Pat Hartl
0d1ee04533 Debounce image loading/unloading, decode images at rendered dimension, cache some image loading
Some checks failed
LANCommander Release / build_launcher_avalonia_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_avalonia_win_x64 (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
LANCommander Release / prep (push) Failing after 22s
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
2026-05-18 20:07:54 -05:00
Pat Hartl
909d4878d4 Load library in background 2026-05-18 20:06:51 -05:00
Pat Hartl
50987309e2 Fix package icon reference 2026-05-18 18:46:21 -05:00
Pat Hartl
814c39860e Remove file committed with invalid backslash path 2026-05-18 18:13:55 -05:00
Pat Hartl
b52f83df1e Add repeatable options with list type 2026-05-18 22:04:03 +00:00
Pat Hartl
2c58c12ec6 Fix closing of upload dialog when file selected 2026-05-18 22:02:14 +00:00
Pat Hartl
4e17700e51 Alert when error starting install 2026-05-18 01:14:05 -05:00
Pat Hartl
fe90c007c9 Fix infinite loop in mapping for API 2026-05-17 21:45:29 -05:00
Pat Hartl
6575b2eb20 Fix games with tools not working in depot 2026-05-17 20:39:15 -05:00
Pat Hartl
8772b3a000 Add Discord rich presence 2026-05-17 20:38:50 -05:00
Pat Hartl
0200a6df5c Add package as a valid script type for redistributables 2026-05-17 13:53:59 -05:00
Pat Hartl
c5d6d88c0b Disable NetBeauty on ARM64 2026-05-17 13:08:01 -05:00
Pat Hartl
37dec439ae Remove Photino launcher from CI 2026-05-17 12:56:43 -05:00
akifreak
f6fe755277 Refresh server list UI and clean up event subscription
Call StateHasChanged when a new server is discovered so the list
updates immediately. Unsubscribe from BeaconClient.OnBeaconResponse
in Dispose to prevent callbacks firing after the component is torn down.
2026-05-17 09:53:44 +02:00
akifreak
d6ea7936b1 Forward probe beacon responses to BeaconClient event 2026-05-17 09:53:44 +02:00
akifreak
ae144043f3 Harden DiscoveryProbe receive callback error handling
Split the single try/catch into three isolated blocks so that:
- ObjectDisposedException on EndReceiveFrom stops the loop cleanly
- Other socket errors still re-arm the socket to keep listening
- Non-JSON packets (e.g. loopback of our own probe) are silently
  dropped without killing the receive loop
2026-05-17 09:53:44 +02:00
akifreak
8569b56ffe Use socket directly for broadcast send in DiscoveryProbe
Switch SendAsync from UdpClient.SendAsync to Socket.SendTo, and enable
the broadcast socket option before binding. The UdpClient path was not
reliably sending to broadcast addresses on all platforms.
2026-05-17 09:46:29 +02:00
Pat Hartl
97fdb68899 Add display names to option choices 2026-05-17 01:21:05 -05:00
Pat Hartl
c7f0423a8c Run ARM64 builds on ARM64 runners 2026-05-17 00:24:20 -05:00
Pat Hartl
fa5cc99511 Merge branch 'feature/redistributable-options' 2026-05-17 00:03:10 -05:00
Pat Hartl
38bd25513e Add Redistributable options to script editor 2026-05-16 19:57:59 -05:00
Pat Hartl
e84c8a9c7a Ensure that redistributable options persist in export/import 2026-05-16 19:38:43 -05:00
Pat Hartl
4fce064dd4 Update option documentation, add run wrapper script docs 2026-05-16 19:29:24 -05:00
Pat Hartl
15bc84016a Add UI for editing option schema
- Moves Redistributables for games to separate panel
- Add option override for games
- Add Monaco completions for option schema editing
- Add UI for editing options for a redistributable
- Add config importing to translate from INI, JSON, XML, etc to redist option schema
2026-05-16 19:28:04 -05:00
Pat Hartl
e5a8d39bf3 Replace INI parsing 2026-05-16 18:14:41 -05:00
Pat Hartl
c53a99b0ac Fix generated migrations 2026-05-16 17:16:16 -05:00
Pat Hartl
f3cdc4b5ab Better feature layout for HQ 2026-05-16 17:15:54 -05:00
Pat Hartl
9f37dc7b56 Adjust external ID layout 2026-05-16 17:15:40 -05:00
Pat Hartl
639aa2cede RunWrapper script type option 2026-05-15 00:23:57 -05:00
Pat Hartl
ecdc5f9ce3 Add options to redistributables
Options are defined schema that the redistributable will allow a game to customize / override. This will be useful for install scripts on a redistributable. It opens up the opportunity to define a redistributable for something such as dgVoodoo and override options on a per-game basis without having to edit the config manually in the game's scripts.
2026-05-15 00:14:46 -05:00
github-actions[bot]
c23c89187d Refresh launcher visual baselines from CI run 25893538204 2026-05-15 00:33:01 +00:00
Pat Hartl
1f60ccdeca Test workflows 2026-05-14 19:27:23 -05:00
Pat Hartl
8aa119e53a Tests for Avalonia launcher 2026-05-14 19:26:04 -05:00
Pat Hartl
fa294bec4d Merge remote-tracking branch 'origin/main' 2026-05-14 17:23:20 -05:00
Pat Hartl
be19fcf1bc WIP 2.1.0-rc1 release notes 2026-05-14 17:20:43 -05:00
Pat Hartl
e56371cd7c Add support for external IDs beyond IGDB 2026-05-14 00:30:45 -05:00
Pat Hartl
5ad55faa5a Add support for tools 2026-05-14 00:29:48 -05:00
Pat Hartl
52679c538a Inital Win9x launcher commit 2026-05-13 02:00:14 +00:00
Pat Hartl
90220b69ac Fix Avalonia token redemption for SSO 2026-05-12 19:57:43 -05:00
Pat Hartl
ace6783de8 Code formatting 2026-05-12 17:47:03 -05:00
Pat Hartl
2a98a85de0 Fix OIDC auth handling, add external provider support to Avalonia launcher 2026-05-12 17:21:53 -05:00
Pat Hartl
ccf4c5f57e Allow logging in via token redemption 2026-05-11 22:29:41 -05:00
Pat Hartl
19459f767b Include all cmdlets in completion 2026-05-11 04:06:51 -05:00
Pat Hartl
62b8bf332f Use correct storage location for packaging 2026-05-11 04:06:32 -05:00
Pat Hartl
59e64bb672 Fix link 2026-05-11 04:06:16 -05:00
Pat Hartl
32e0bfd3b6 Fix state changes when linking to HQ 2026-05-10 21:38:55 -05:00
Pat Hartl
36444d39e9 Add completions for built-in PowerShell cmdlets 2026-05-10 20:56:17 -05:00
Pat Hartl
b5e93cbb88 Fix completion rendering 2026-05-10 19:53:41 -05:00
Pat Hartl
885032547b Improve script editor with context-aware completions, validation, and UX
- Filter variable completions and hover by script type so only relevant variables appear (e.g. $AllocatedKey only in KeyChange scripts)
- Add inline validation warnings for package scripts missing New-Package and for variables used outside their valid script type
- Extend debug console to support tools and redistributables, add stop button and elapsed time display
- Disable minimap, enable bracket pair colorization and word wrap
- Add snippet insertion via Monaco snippet controller with selection replacement support
- Add script templates that auto-populate on new script creation
- Add Variables dropdown in editor toolbar based on current script type
- Change Add Script button to type-selection dropdown, auto-populate script name from type, remove Type field from editor dialog
- Move Requires Admin checkbox to toolbar row, move toolbar and editor outside Form to prevent dropdown clicks from closing the modal
- Set MaskClosable=false on script editor modals so Monaco autocomplete clicks don't dismiss the dialog
2026-05-10 19:33:48 -05:00
Pat Hartl
302667e544 Remove hard-coded variable snippets 2026-05-10 19:22:26 -05:00
Pat Hartl
a256ea6406 Log script errors, avoid silent failures in package scripts 2026-05-10 12:14:32 -05:00
Pat Hartl
b3813d7c3c Add cmdlet for specifying new package details 2026-05-10 12:13:43 -05:00
Pat Hartl
4729304ee4 Cache media entries, add skeletons for carousels 2026-05-10 01:44:23 -05:00
Pat Hartl
fcfdd2cfd8 Dispose previous search debounce cancellation token 2026-05-09 19:42:59 -05:00
Pat Hartl
e5815a7eb8 Implement IDisposable for chat 2026-05-09 19:42:41 -05:00
Pat Hartl
16cf4dbcce Share HTTP client across server pings 2026-05-09 19:42:09 -05:00
Pat Hartl
823b543519 Dispose cancellation token after game stopped 2026-05-09 19:41:52 -05:00
Pat Hartl
7116b2e523 Dispose HTTP client on provider dispose 2026-05-09 19:41:24 -05:00
Pat Hartl
d873526833 Dispose RpcClient 2026-05-09 19:40:58 -05:00
Pat Hartl
520cf66038 Share HTTP client across sessions 2026-05-09 19:40:49 -05:00
Pat Hartl
a92104703b Clean up video players when end reached 2026-05-09 19:40:18 -05:00
Pat Hartl
0d8e20e9c1 Generate PowerShell completions in CI before package 2026-05-08 09:41:08 -05:00
Pat Hartl
8bf9895b51 Load screenshots / videos separately in the carousel 2026-05-08 02:28:24 -05:00
Pat Hartl
402af57f79 Enhance PowerShell completions with generated type definitions 2026-05-08 02:13:36 -05:00
Pat Hartl
59795ff222 Add code completion for built-in cmdlets and variables 2026-05-08 02:02:33 -05:00
Pat Hartl
c1f0f4c7db Cleanup import files after completion 2026-05-07 23:40:17 -05:00
Pat Hartl
0e978dabc3 Fix orphaned files tool 2026-05-07 23:39:58 -05:00
Pat Hartl
2ca08fdec5 Fix archives not downloading for redists 2026-05-07 23:39:46 -05:00
Pat Hartl
92f863246d Don't delete redist files 2026-05-07 23:39:28 -05:00
Pat Hartl
70557e4a9b Fix script working directory for games 2026-05-07 23:12:15 -05:00
Pat Hartl
879885c640 Add alert message if action failed to start 2026-05-07 23:12:00 -05:00
Pat Hartl
04fbc12615 Fix SteamCMD login when SteamGuard is enabled 2026-05-07 21:01:01 -05:00
Pat Hartl
e5b2a7173c Fix Steam API model parsing 2026-05-07 17:54:45 -05:00
Pat Hartl
640f5f00b8 Fix SteamDeckCompatibility deserialization 2026-05-07 02:23:56 -05:00
Pat Hartl
8d25b20c62 Add LANCommander HQ metadata/media provider 2026-05-07 01:11:41 -05:00
Pat Hartl
ba95896716 Support grabbing animated images from SteamGridDb 2026-05-01 02:20:24 -05:00
Pat Hartl
63ac3d1361 Automatically convert APNG to video 2026-05-01 01:14:13 -05:00
Pat Hartl
7e72df0651 Fix media grabbing 2026-05-01 01:10:58 -05:00
Pat Hartl
1efbde33d7 Allow multiple uploading for screenshots/videos. Add support for animated covers. 2026-04-30 23:47:15 -05:00
Pat Hartl
5b434daf8a Render description as markdown 2026-04-30 02:53:19 -05:00
Pat Hartl
82a41ed031 Add lightbox control for screenshots, videos, and manuals 2026-04-30 02:39:12 -05:00
Pat Hartl
f0b85de93f Add multiple select support for media grabber 2026-04-30 02:16:17 -05:00
Pat Hartl
8563f0b291 Merge remote-tracking branch 'origin/main' 2026-04-28 01:50:11 -05:00
Pat Hartl
232f86ba85 Refactor launcher game importing
- Game importing happens on a separate thread to avoid UI blocking
- Server requests per-game are done in parallel to cut down on import times
- Media downloading is done in parallel
- Library import progress shown in title bar
- Enabled WAL mode for SQLite for better performance
- Fix potential issues with deferred imports causing infinite loops or lockups
2026-04-27 22:45:02 -05:00
Pat Hartl
71de97e04f FIx preview page uploader. Add progress when downloading videos. Fix large video downloads. 2026-04-27 17:58:48 -05:00
Pat Hartl
509241770e Merge remote-tracking branch 'origin/main' 2026-04-26 13:17:07 -05:00
Pat Hartl
f9abb08141 Stream video using new endpoint, adjust video overlay UI 2026-04-26 02:14:45 -05:00
Pat Hartl
f03b8da874 Merge branch 'main' of https://github.com/LANCommander/LANCommander 2026-04-26 01:19:36 -05:00
Pat Hartl
d2da56ad11 Add streaming endpoint for media 2026-04-26 01:19:31 -05:00
Pat Hartl
8de94803e3 Bundle libvlc in Linux builds 2026-04-25 23:42:05 -05:00
Pat Hartl
e448ce69fb Fix missing RatingService 2026-04-25 14:44:12 -05:00
Pat Hartl
b677328a25 Fix missing depot service 2026-04-25 13:59:24 -05:00
Pat Hartl
f05a32c907 Add ffmpeg/yt-dlp checks / installers in settings 2026-04-24 02:26:09 -05:00
Pat Hartl
7d862e5dc5 Ensure yt-dlp and ffmpeg are installed via Docker 2026-04-24 02:06:30 -05:00
Pat Hartl
3fe6bea7e5 Added "Preview" edit view, flesh out media picker
- Added sort order to media for screenshots and videos
- Added new "Preview" pane to game editor. Simulates the game details view in the launcher and allows for in-place media upload / grabbing
- Added abstractions for media grabbing
- Added subtitle to media picker dialog to indicate which services provide which assets
- Added preview for videos
- Made media searching faster overall
2026-04-24 02:05:56 -05:00
Pat Hartl
4362e7c22f Add package versions 2026-04-23 09:35:45 -05:00
Pat Hartl
9f8ce4895a WIP video and screenshot carousel in game details 2026-04-23 03:06:27 -05:00
Pat Hartl
4c5d126d73 Show game icon in footer progress 2026-04-23 03:05:45 -05:00
Pat Hartl
adfb593cde Add "Modify" option to game action bar dropdown 2026-04-23 03:05:34 -05:00
Pat Hartl
f49905983f Fix installs getting locked after install, uninstall, then install again 2026-04-22 22:43:21 -05:00
Pat Hartl
8cf758c88d Rework install dialog 2026-04-22 20:14:56 -05:00
Pat Hartl
f2be3a094f Implement cancel button as IconButton 2026-04-22 20:14:40 -05:00
Pat Hartl
a1aba863c8 Implement proper route navigation in Avalonia application
- Introduces transitions in between views
- Adds proper routing with history stack
- Replaces hacky "go back" navigation code with routing navigation
- Filters/scroll position are maintained on navigation
2026-04-22 19:37:44 -05:00
Pat Hartl
3f2b8e32d9 Add transfer speed chart to download queue items, arrange task list as timeline, restyle queue. 2026-04-22 01:54:07 -05:00
Pat Hartl
194f219e68 Add settings for notifications 2026-04-21 18:31:14 -05:00
Pat Hartl
692187b7e4 Add play/view in library buttons on completed downloads 2026-04-19 23:19:11 -05:00
Pat Hartl
e40885e946 Merge branch 'main' into refactor/install-workflow 2026-04-19 12:55:52 -05:00
Pat Hartl
0f35c69ace Move cover to its own component 2026-04-19 12:55:23 -05:00
Pat Hartl
24df629352 Put download queue item into its own component 2026-04-19 12:54:55 -05:00
Pat Hartl
b6e1860ce8 Fix git checkout for nightly release 2026-04-18 19:13:26 -05:00
Pat Hartl
1053c1aa6f Remove all assets on nightly release 2026-04-18 18:56:12 -05:00
Pat Hartl
538d6b4676 Fix download queue progress 2026-04-18 18:55:28 -05:00
Pat Hartl
b134c09f4b Silence debug logs for HTTP client and EF 2026-04-17 23:52:40 -05:00
Pat Hartl
9a7cadf1ca Add better logging for installs 2026-04-17 23:52:22 -05:00
Pat Hartl
0f65f79c92 Fix mapping for data model Game -> manifest Game 2026-04-17 23:51:41 -05:00
Pat Hartl
eaaaf39b8d Refactor install process to use major/minor task queue
Tasks are broken down into major and minor tasks. Major tasks are like install game, redistributable, or tool. These are reported as individual tasks in the download queue. Minor tasks are running of a script, downloading saves, extracting/downloading, etc.
2026-04-17 22:24:47 -05:00
Pat Hartl
31dc9e4d3a Add verify files option to game dropdown 2026-04-17 20:35:44 -05:00
Pat Hartl
5b9b63cf09 Fix built output to .NET 10 2026-04-12 20:18:44 -05:00
Pat Hartl
b8cba6b024 Use observable collection for chat messages 2026-04-12 20:00:44 -05:00
Pat Hartl
415c804f7f Add missing method implementation 2026-04-12 20:00:18 -05:00
Pat Hartl
0ae9cb127b Merge branch 'main' of https://github.com/LANCommander/LANCommander 2026-04-12 19:59:56 -05:00
Pat Hartl
07ec314370 Fix .NET 10 build 2026-04-12 19:59:53 -05:00
Pat Hartl
c48bf31db1 Made allocation method on key nullable, added manual assign key dialog, reworked how the next available key is chosen 2026-04-12 19:58:47 -05:00
Pat Hartl
8fb5779070 Add migration for ratings 2026-04-12 19:55:43 -05:00
Pat Hartl
6013cd8cdf Fix download status message when downloading 2026-04-12 00:19:08 -05:00
Pat Hartl
5a96d58b68 Update install status after install 2026-04-12 00:18:44 -05:00
Pat Hartl
ce85bf4531 Load manuals and actions after install 2026-04-12 00:18:26 -05:00
Pat Hartl
063e8c0580
Merge pull request #400 from MasterMNB/main
Avalonia Launcher GameDetailView Back Button should now be more usable.
2026-04-11 01:40:48 -05:00
MasterMNB
2813e682dd Change of the background color to same color of title bar.
And class name to Glass.Dark, plus a fix in the ColorPalette.axaml.
2026-04-11 01:02:21 +02:00
MasterMNB
bd5b053a59 Avalonia Launcher GameDetailView Back Button should now be more usable. 2026-04-10 21:34:56 +02:00
Pat Hartl
7f801cde22 Bump CLI to 10.0 2026-04-10 02:22:47 -05:00
Pat Hartl
f9863043d1 Bump to .NET 10 2026-04-10 02:19:50 -05:00
Pat Hartl
afa20bee72 Merge branch 'avalonia-launcher' 2026-04-10 01:52:20 -05:00
Pat Hartl
c62a8f105d Nightly builds for Avalonia launcher 2026-04-10 01:01:38 -05:00
Pat Hartl
c9fb738a21 Update to .NET 10 2026-04-10 00:54:43 -05:00
Pat Hartl
0e612faf3e Include multiplayer modes in depot results 2026-04-09 21:50:58 -05:00
Pat Hartl
540e498ecc Fix filtering, hide installation status in action bar, add install status to download queue progress, reorganize detail view 2026-04-09 21:50:28 -05:00
Pat Hartl
3c17c4ac8d Adjust download progress positioning, fix icon 2026-04-08 18:08:06 -05:00
Pat Hartl
91a56c5d3b Fix uninstall -> reinstall not working correctly 2026-04-08 18:07:32 -05:00
Pat Hartl
3494c48cc0 Fix library button, default to library view 2026-04-08 17:56:13 -05:00
Pat Hartl
a90b45f2ee Make add to library button square 2026-04-08 17:55:50 -05:00
Pat Hartl
2f42ab7c76
Merge pull request #398 from akifreak/fix-game-archive-upload
Improve game archive upload
2026-04-07 23:42:07 -05:00
Pat Hartl
354c92851a Fix server selection 2026-04-07 23:12:41 -05:00
Pat Hartl
caee4ca4e0 Separate views for library vs depot 2026-04-07 23:12:22 -05:00
Pat Hartl
0ab6ee5262 Basic chat window 2026-04-07 23:10:45 -05:00
Pat Hartl
319b762fd7 Error message in server selection 2026-04-07 23:08:16 -05:00
Pat Hartl
5debc68697 Restyle detail view, move metadata to right side under cover 2026-04-07 23:07:51 -05:00
Pat Hartl
a6b0b1e90e Adjust contents of empty in download queue 2026-04-07 23:07:19 -05:00
Pat Hartl
bb08c56012 Style IconButton, remove library section of game details in favor of single button 2026-04-07 23:07:06 -05:00
Pat Hartl
b5b13a34c1 Style badge 2026-04-07 23:06:19 -05:00
Pat Hartl
5d2e271e59 Add IconButton control 2026-04-07 23:05:48 -05:00
Pat Hartl
777333c46b Make LC controls default namespace 2026-04-07 23:05:36 -05:00
Pat Hartl
ae0f67869c Fix clipping with carousel 2026-04-05 01:32:25 -05:00
Pat Hartl
6b61c28e39 Fix in library icons 2026-04-04 17:53:51 -05:00
Pat Hartl
fe307e4b7e Fix depot search layout 2026-04-04 17:53:41 -05:00
Pat Hartl
1a5c74aea4 Standardize class names 2026-04-04 17:53:26 -05:00
Pat Hartl
1fab7324f3 Fix alignment of carousel controls, styling 2026-04-04 17:52:52 -05:00
Pat Hartl
95f376f9b2 Fix icon color, rendering 2026-04-04 17:52:29 -05:00
akifreak
80bb5d59ee Replace var with const/let and remove unused variable in ChunkUploader
- var request and var response in Init() → const (never reassigned,
  var is function-scoped and hoisted which is undesirable in class methods)
- let formData in UploadChunk() → const (never reassigned)
- chunkResponse in UploadChunk() removed (assigned but never read)
- let start in GetChunks() → const (never reassigned)
- var percent in UpdateProgressBar() → const (never reassigned)
2026-04-04 15:05:06 +02:00
akifreak
7dbb1b7619 Remove unused UploadInitResponse import from ChunkUploader 2026-04-04 15:05:06 +02:00
akifreak
171c409ccc Pass correct archive ID to OnArchiveUploaded callback
Archive is the local form model used for version/changelog input.
On a new upload Archive.Id is Guid.Empty, so callers received an
empty GUID instead of the real database ID. Pass uploadedArchive.Id,
which is the record fetched from the database after the upload
completes.
2026-04-04 15:05:06 +02:00
akifreak
8254a212c3 Reset Uploading flag in finally block after Start()
If ChunkUploader.Start() threw a JS interop exception Uploading was
left as true permanently, disabling the Upload and Cancel buttons
for the rest of the session without any visible error. Using a
finally block ensures the flag is reset whether the call succeeds,
throws, or is cancelled.
2026-04-04 15:05:06 +02:00
akifreak
d61b8b2ae4 Fix FileMode.Append conflicting with explicit fs.Position seek
FileMode.Append forces all writes to the end of the file, making
fs.Position assignments ineffective. This works by accident on a
fresh sequential upload but will silently corrupt the file on any
retry, since chunks are re-sent from their original offset but land
at the current end instead. Use FileMode.OpenOrCreate so the seek
to chunk.Start is honoured.
2026-04-04 15:05:06 +02:00
akifreak
c37be528ca Require administrator authorization on upload endpoints
/api/Upload/Init and /api/Upload/Chunk had no authorization policy,
meaning any unauthenticated request could create archive database
records and write arbitrary data to server storage. Every other
endpoint group in the application requires authorization; apply
the same administrator role requirement here.
2026-04-04 15:05:06 +02:00
akifreak
4065232e61 Null-guard progress bar DOM elements in UpdateProgressBar
document.querySelector() can return null if the elements are not
yet in the DOM. Dereferencing null would throw inside the axios
onUploadProgress callback, which is caught by UploadChunk's catch
and re-thrown as a string - causing the whole upload to fail
silently with an undefined message.
2026-04-04 15:05:06 +02:00
akifreak
9882f14d04 Fix undefined error message when UploadChunk throws a string
UploadChunk throws a template literal string, not an Error object.
Accessing .message on a string primitive returns undefined, so
JSOnUploadError was always called with undefined and the server log
showed no useful detail. Use String(ex) as fallback so the message
is always populated.
2026-04-04 15:05:06 +02:00
akifreak
a439cf5638 Propagate init net failure instead of swallowing
The original catch block was simply swalloing errors instead of
propagating them upwards.
When the /api/Upload/Init request failed it set this.Key = null, logged
to the console, and returned normally. From the caller's perspective
Init() had succeeded.

Start() in the Blazor component then called Upload() immediately after,
which proceeded with this.Key = null. Every chunk was sent to the server
with a null key, the server returned 400 for each one, each UploadChunk
call threw, and the whole upload failed - but only after sending every
chunk, and only with the opaque "Error uploading chunk N/M" message
rather than anything pointing at the actual cause.

By removing the catch, an axios error in Init() propagates up through
the JS interop call InvokeVoidAsync("Init", ...) in Start(). That throws
a JSException in C#, Start() exits immediately before Upload() is ever
called, and the finally block resets Uploading so the UI recovers. The
user gets a failure where it actually happened.
2026-04-04 15:02:46 +02:00
Pat Hartl
422d4c041f Use discovered server address 2026-04-03 20:09:29 -05:00
Pat Hartl
83cf1389d1 Add new depot view with carousels for browsing 2026-04-03 19:28:31 -05:00
Pat Hartl
c7d426aa85 Include full Phosphor icon library 2026-04-03 19:27:37 -05:00
Pat Hartl
0f042f7cf9 Show grid image in inline image if exists 2026-04-03 19:26:10 -05:00
Pat Hartl
5f3621dfb3 Add Steam video and screenshot downloads 2026-04-03 19:24:49 -05:00
Pat Hartl
73ab3edf0f Add Avalonia debug tools. Update Notify.NET 2026-04-03 19:24:30 -05:00
Pat Hartl
3bf6ea86f5 Add "Verifying Files" install status 2026-04-03 19:23:52 -05:00
Pat Hartl
a8e0e7f820 Code cleanup 2026-04-03 19:23:30 -05:00
Pat Hartl
7deb435e98 Initial support for grid image, screenshots, video 2026-04-03 19:22:26 -05:00
Pat Hartl
5ae9c88c0f Handle elevated script execution 2026-04-03 19:20:40 -05:00
akifreak
910504b7df Remove leftover debugger statement from ChunkUploader.Init 2026-04-03 21:22:22 +02:00
akifreak
a7bd41ff9d Fix game archive upload in web ui
bundle.js is built by webpack as an ES module (output.library.type:
'module'), ending with named exports such as:

  export { Be as ChunkUploader, s as InfiniteScroll, ... }

All three inclusion sites (App.razor, _Layout.cshtml,
ScriptLoader.razor) loaded it via a plain <script> tag, which is for
classic scripts only and does not support the 'export' keyword.

Edge throws a visible SyntaxError and uploading does not work.

Fixed by adding type="module" to the bundle.js script tag in all three
locations.
2026-04-03 21:21:40 +02:00
Pat Hartl
bcfa9768fc Add ratings to DB. Add popular/backlog games to depot. 2026-04-01 23:13:33 -05:00
Pat Hartl
fc1e643ac6 Fix logo sizing 2026-04-01 01:39:54 -05:00
Pat Hartl
3efd0de707 Fix text colors 2026-04-01 01:39:40 -05:00
Pat Hartl
8f1084764c Add indeterminate progress bar for login status message 2026-04-01 01:12:53 -05:00
Pat Hartl
42cdf5fef8 Adjust styling of inputs, login/server select views 2026-04-01 01:09:30 -05:00
Pat Hartl
fd29bc0f01 Theme / UI tweaking, localization, notifications
- Started adding localization resources
- Implemented notifications using Notify.NET
- Reworked many control styling to use overall app theme instead of controlling them individually
- Adjusted display of play button in different states
- Fixed cover in download queue"
- Added play actions overlay
- Fixed grouped view reference to bitmap converter
2026-03-31 21:19:47 -05:00
Pat Hartl
38eb9368ee Fix cover box shadow, moved cover button to separate component 2026-03-29 00:53:06 -05:00
Pat Hartl
39ac37709d Add hot reload, Avalonia dev tools 2026-03-28 21:12:33 -05:00
Pat Hartl
f486b48c8d Split depot/library layouts to views, add advanced options to filter 2026-03-28 21:12:04 -05:00
Pat Hartl
42732ce733 Increase opacity of title bar 2026-03-28 21:11:11 -05:00
Pat Hartl
fc1a1f1df3 Make install overlay full height/width 2026-03-28 21:10:56 -05:00
Pat Hartl
222492a24b Continue to tweak UI, add notifications, partial add gamepad support
- Add backgrounds for login / splash
- Fix logo to use uncut version
- Update titlebar icon
- WIP implementation of notifying on install complete/fail with cross-platform support
- WIP support for gamepads using SDL2
- Add lancommander:// protocol support for navigating to a specific game (currently used for notifications)
- WIP support for updating taskbar progress
- Don't show "in library" checkmark in library view
- Change back button text based on active view
- Adjust styling of download queue
- Add grouping by first letter + index control
- Change titlebar title based on view selected
- Adjust how titlebar overlaps main content
- Make chips clickable in game details
- Add overlay for game install options
- Add resizing to window
2026-03-28 16:59:33 -05:00
Pat Hartl
5dd1f7b54a WIP visual testing 2026-03-27 19:00:26 -04:00
Pat Hartl
4f3b3efb96 Normalize file paths when writing text files 2026-03-27 19:00:03 -04:00
Pat Hartl
46ba19558f Fix display resolution variable injection for Linux 2026-03-27 18:59:44 -04:00
Pat Hartl
81a6a3a0fc Rework views / interactions
- Added a horizontal and list view for depot/library
- Games only show covers (with fallback cover)
- Added install addon / directory select dialog
- Fixed download queue
- Added footer somewhat matching Blazor launcher
- Temporarily removed sidebar
- Implemented custom window chrome
- Added profile button
- Reworked game details to better match Blazor launcher
- Added grouping by genre, collection, or first letter
2026-03-27 18:59:17 -04:00
Pat Hartl
939e0b3313 Fix links throughout documentation 2026-03-23 00:50:06 -05:00
Aaron Powell
025d12e1ca Show loading screen during login-to-shell transition
After login succeeds, switch to the splash view with a 'Loading library...'
message and progress bar before running ShellViewModel.InitializeAsync().
This gives immediate visual feedback instead of leaving the user staring
at the login screen wondering if the app has frozen.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-21 15:18:23 +11:00
Aaron Powell
2da5aa0aa4 Fix token lost after IOptionsMonitor cache invalidation
TokenProvider.GetToken() was reading solely from settingsProvider.CurrentValue,
which rebuilds from the YAML file after configRefresher.RefreshAsync() calls
OnReload(). Since the settings file save is debounced by 1 second, the rebuilt
Settings object has a null token.

Fix: TokenProvider now keeps a local _cachedToken field that is set immediately
during SetToken() and used as the primary source in GetToken(), falling back
to the settings provider only for tokens loaded from disk (e.g., app restart
with stored credentials).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-21 15:12:57 +11:00
Aaron Powell
f8bc4edd18 Fix auth token race condition causing NullReferenceException after login
ApiRequestBuilder.UseAuthenticationToken() was reading the token eagerly
at construction time (line 22 initializer). After login, configRefresher.RefreshAsync()
calls OnReload() which invalidates the IOptionsMonitor cache. The next
CurrentValue access creates a new Settings from the YAML file, which hasn't
been persisted yet (1-second debounce), so the token is null.

Fix: read the token lazily from tokenProvider at the point of use instead
of capturing it at construction time. Remove the now-unused _token field.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-21 15:09:55 +11:00
Aaron Powell
73e310f349 Fix sidebar state issues on first login
- Initialize ShellViewModel fully before switching CurrentView in all paths
  (splash→shell, login→shell, offline→shell). Previously the UI rendered
  with uninitialized child ViewModels causing race conditions.
- Hide download queue panel entirely when no items (removes confusing
  'No active downloads' panel and empty header on first login)
- Collapsed download progress only shows when there's an active download

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-21 14:56:43 +11:00
Aaron Powell
a83528d743 Fix SDK API changes after merge from main
- Replace SDK.Client.Games with GameClient (resolved from DI directly)
- Rename ScriptClient methods to Game_ prefix: Game_RunInstallScriptAsync,
  Game_RunUninstallScriptAsync, Game_RunNameChangeScriptAsync,
  Game_RunKeyChangeScriptAsync

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-21 14:49:45 +11:00
Aaron Powell
3115ed4ea3 Merge branch 'main' into avalonia-launcher
# Conflicts:
#	Directory.Packages.props
2026-03-21 14:15:00 +11:00
Pat Hartl
0d7b2176d0 Update documentation 2026-03-20 01:20:24 -05:00
Pat Hartl
966e049c7e Fix installation of redistributables
Some checks failed
LANCommander SDK Release / prep (push) Failing after 14s
LANCommander SDK Release / publish (push) Has been skipped
LANCommander Release / prep (push) Failing after 29s
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
2026-03-20 01:09:29 -05:00
Pat Hartl
dd99da6857 Fix missing docs 2026-03-20 00:41:28 -05:00
Pat Hartl
032ca4a313 v2.0.2 release notes 2026-03-20 00:06:04 -05:00
Pat Hartl
7cc857bcc8 Fix script editor bindings, update BlazorMonaco 2026-03-20 00:01:12 -05:00
Pat Hartl
7a7c154d49 Increased web socket message size. Fixes issue where changes in script editor for large scripts wouldn't take. 2026-03-19 23:58:06 -05:00
Pat Hartl
4a11e0af01 Fix launcher to only show actions pulled from server for installed games 2026-03-19 23:57:05 -05:00
Pat Hartl
582b244d4c Add downloads to releases 2026-03-18 01:43:10 -05:00
Pat Hartl
4b82dfe577 Add latest tag
Some checks failed
LANCommander SDK Release / prep (push) Failing after 10s
LANCommander SDK Release / publish (push) Has been skipped
LANCommander Release / prep (push) Failing after 18s
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
2026-03-15 18:20:22 -05:00
Pat Hartl
be804b0910 v2.0.1 release notes 2026-03-15 17:49:09 -05:00
Pat Hartl
6276823f86 Fix variables on action run, populate server variables when retrieving actions 2026-03-15 17:15:50 -05:00
Pat Hartl
fef88f024d Fix play button state when starting game 2026-03-15 17:07:07 -05:00
Pat Hartl
a9d32c6bef Open debug window for elevated scripts 2026-03-15 17:06:37 -05:00
Pat Hartl
830f34b19d Restore lobby actions 2026-03-13 01:38:58 -05:00
Pat Hartl
1c2b068001 Fix regex for checking if script requires admin 2026-03-13 01:33:18 -05:00
Pat Hartl
4a0e0115ec Fix saving scripts on Games, Tools, Redistributables 2026-03-13 00:45:21 -05:00
Pat Hartl
36a0f3aee0 Fix long game start delays if no save exists on server 2026-03-11 23:59:21 -05:00
Pat Hartl
4fc488d90a Refresh tracked servers per engine on update/add 2026-03-11 23:27:33 -05:00
Pat Hartl
2d27c2db65 Fix saving script actual contents 2026-03-11 20:12:09 -05:00
Pat Hartl
9ad5a236af Fix script downloading, code cleanup
Scripts are now grabbed from a separate request. This ensures that the contents of the scripts themselves don't muddy up the manifest model. This also opens up the potential to allow clients to redownload scripts without redownloading the entire game/tool/redist.
2026-03-11 19:36:24 -05:00
Pat Hartl
85e707b7a3 Fix form rendering logic for remote server credentials 2026-03-11 01:44:08 -05:00
Pat Hartl
8914da3d28 Fix form for adding remote server 2026-03-10 19:10:14 -05:00
Pat Hartl
e068db25c4 Fix execution of elevated scripts 2026-03-10 18:31:54 -05:00
Pat Hartl
da0d1d6d7a Replace custom admin directive with PowerShell standard directive 2026-03-10 18:15:35 -05:00
Pat Hartl
5a91475dd8 Fix remote host migration 2026-03-10 02:08:06 -05:00
Pat Hartl
2e46c6ecc5 Launcher: Only import games since last import 2026-03-09 22:12:57 -05:00
Pat Hartl
4817c00939 Update SharpCompress, move to async extraction, add logging per entry on fail 2026-03-09 22:12:09 -05:00
Pat Hartl
06a787bb78 Fix binding for default role 2026-03-08 15:39:37 -05:00
Pat Hartl
2e3d999eba Fix use of alert param 2026-03-08 15:12:12 -05:00
Pat Hartl
7cfaeb1255 Fix use of color 2026-03-08 14:59:11 -05:00
Pat Hartl
23678107e8 Fix downloading updates to user avatar 2026-03-08 14:50:42 -05:00
Pat Hartl
d5cd784593 Fix launching of games to not be blocked too much by loading actions via server 2026-03-08 14:39:15 -05:00
Pat Hartl
f661a7a8e7 Fix play button state not updating when install state changes 2026-03-08 14:27:56 -05:00
Pat Hartl
53b9064418 Fix saving of new server scripts 2026-03-08 14:10:41 -05:00
Pat Hartl
9e322e5e42 Associate addon with base game on import 2026-03-08 14:03:58 -05:00
Pat Hartl
115c9b02f3 Fix media thumbnails not loading after new media added 2026-03-08 13:56:51 -05:00
Pat Hartl
de38e609a5 Add remote LANCommander server installs as viable server engine 2026-03-08 13:49:30 -05:00
Aaron Powell
77b0f04e4d Fix file upload: use FileChooser API for Blazor InputFile compatibility
Blazor Server's InputFile component doesn't reliably process synthetic
FileList objects from Playwright's SetInputFilesAsync on headless Linux.
Switch to clicking the upload label to trigger the native file dialog,
then intercept it with Playwright's FileChooser API. This triggers a
real browser file selection that Blazor always recognizes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 12:56:49 +11:00
Aaron Powell
0c3ed7cb77 Capture screenshots on test completion for CI diagnostics
- Add ScreenshotHelper that captures full-page screenshots for every test
- Screenshots are named with the full test display name for easy correlation
- Save to SCREENSHOT_DIR env var (CI) or bin/Screenshots/ (local)
- Add ITestOutputHelper to all 10 test classes for test name extraction
- Screenshots are included in the existing ui-test-results artifact upload

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 12:33:49 +11:00
Aaron Powell
952af65b2c Fix CI: decouple GameEditTests from import, add test-reporter
- GameEditTests now use a game seeded via the service layer in
  ConfiguredServerFixture instead of importing via the UI.
  This eliminates the Blazor InputFile compatibility issue on Linux CI.
- GamesPage.ImportGameAsync uses SetInputFilesAsync with retry and
  manual change event dispatch for Blazor InputFile compatibility.
- Add dorny/test-reporter to display test results in PR summary.
- Add checks/pull-requests write permissions for test-reporter.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 12:07:52 +11:00
Aaron Powell
951c1a08a0 Fix game import file upload for headless Linux CI
Use Playwright's FileChooser API instead of SetInputFilesAsync on the
hidden file input. AntDesign's Upload component JS interop doesn't
recognize files set directly on the hidden input in headless Linux,
leaving the Upload button disabled. The FileChooser approach intercepts
the native file dialog, which AntDesign processes normally.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 10:05:28 +11:00
Aaron Powell
f3ea535e0d Fix CI: add Node.js setup for UI project webpack build
The LANCommander.UI project runs 'npm run package' during build,
which requires Node.js and npm dependencies to be installed first.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 09:39:01 +11:00
Aaron Powell
373ad2464f Add UI tests job to PR workflow
Runs Playwright UI tests on ubuntu-latest with Chromium.
Only builds and tests the LANCommander.Server.UI.Tests project.
Uploads test results as artifacts for inspection on failure.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 09:30:34 +11:00
Aaron Powell
c856a51504 Fix stack overflow on test disposal with FreshServerFixture
- Refactor FirstTimeSetupTests to use FreshServerFixture via ICollectionFixture
  instead of creating its own UITestApplicationFactory per test
- Skip _realHost.Dispose() in UITestApplicationFactory since the DI container's
  deep dependency chain causes an uncatchable native StackOverflowException
- All 62 tests pass with exit code 0 (61 pass, 1 skip)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 08:41:11 +11:00
Aaron Powell
0bac5e7b8b Move test .lcx file into repo under TestData
Copy OpenRCT2.lcx into TestData/ and reference via AppContext.BaseDirectory
so tests don't depend on a user-specific Downloads path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 08:25:45 +11:00
Aaron Powell
39ebe66c73 Add game editing UI tests with page object
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 08:25:44 +11:00
Aaron Powell
fd0c15bf8a Fix full test suite: use ICollectionFixture and remove Hangfire
- Switch all test classes from IClassFixture<ConfiguredServerFixture> to
  [Collection("Server")] with ICollectionFixture so a single server instance
  is shared across all test classes (avoids static DatabaseContext.Provider conflicts)
- Put FirstTimeSetupTests in its own collection since it needs an unconfigured server
- Remove Hangfire hosted services in UITestApplicationFactory to prevent
  stack overflow during process shutdown (deep DI disposal chain)
- Add graceful shutdown handling with timeout in DisposeAsync
- All 54 tests pass, 1 skipped, clean exit code 0

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 08:25:44 +11:00
Aaron Powell
3e6c59e384 Add game import UI tests with page object and storage location seeding
- Create GamesPage page object with NavigateAsync, GetGameCountAsync,
  IsGameVisibleAsync, ImportGameAsync, and OpenGameEditAsync methods
- Create GameImportTests with 5 tests covering empty state, import
  button visibility, LCX file import, imported game visibility, and
  game edit navigation
- Seed default storage locations (Archive, Save, Media) in
  ConfiguredServerFixture to support import dialog initialization
- Handle hidden file input upload via Playwright SetInputFilesAsync
- Scope modal interactions to .ant-modal-wrap to avoid button
  ambiguity with the page toolbar

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 08:25:44 +11:00
Aaron Powell
3cc9e47461 Add user management UI tests with page object
- Create UsersPage page object with navigate, search, user visibility,
  roles check, and delete methods
- Add UserManagementTests with 5 tests: admin user visibility, search,
  roles display, new user registration, and user deletion
- Fix GamesPage.cs syntax error (unchecked is a C# keyword)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 08:25:44 +11:00
Aaron Powell
d9d6e7a3c9 Add Settings page UI tests
Create SettingsPage page object with navigation helpers for all settings
sub-pages and SettingsTests with 9 tests covering General form elements,
Users list, Roles list, Authentication, Archives, Media, Beacon, Updates,
and Appearance settings pages.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 08:25:44 +11:00
Aaron Powell
7a90eb1d48 Add metadata CRUD UI tests for Tags, Genres, and Platforms
Create a reusable MetadataPage page object that encapsulates navigation,
add/edit/delete operations, and item visibility checks for any metadata
type. Add MetadataTests with 8 tests covering:
- Tags: empty state, add, edit, delete
- Genres: add, delete
- Platforms: add, delete

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 08:25:44 +11:00
Aaron Powell
4f528e5378 Add role management UI tests
Add Playwright-based UI tests for the Roles management page including:
- RolesPage page object with navigation, add, delete, and visibility methods
- Tests verifying Administrator role visibility
- Tests for adding and deleting custom roles
- Test confirming Administrator role cannot be deleted

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 08:25:44 +11:00
Aaron Powell
5e3d740b83 Refactor UI tests to use WebApplicationFactory instead of dotnet run
Replace process-based server management (ServerManager) with ASP.NET Core's
WebApplicationFactory<Program> for proper integration testing:

- Add UITestApplicationFactory with dual-host pattern (real Kestrel + dummy TestServer)
  to work around .NET 9's hard-cast to TestServer in WebApplicationFactory
- Use in-memory EF Core database with dynamic port binding (IPAddress.Loopback:0)
- Stub IVersionProvider and IGitHubService to avoid external dependencies
- Programmatically create admin user in ConfiguredServerFixture via service layer
- Remove ServerManager.cs (no longer needed)
- Skip CompleteWizardAndLogin test (requires real DB file I/O)
- 18 tests pass, 1 skipped, ~28s runtime (down from ~101s)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 08:25:44 +11:00
Aaron Powell
94cfd6fe18 Spike: Playwright UI automated tests for server application
Add LANCommander.Server.UI.Tests project with 19 Playwright-based UI tests
covering three key scenarios:

- First-time setup flow (4 tests): Fresh server redirect, setup wizard steps,
  database provider selection, and complete wizard-to-login flow
- Login flow (5 tests): Unauthenticated redirect, page elements, valid/invalid
  credentials, and empty credential handling
- Admin navigation (10 tests): Dashboard, sidebar menu, Games, Redistributables,
  Tools, Servers, Issues, Files, Settings pages, and Settings submenu items

Infrastructure includes:
- ServerManager: Manages server process lifecycle with data directory backup/restore
- PlaywrightFixture: Browser lifecycle management (headless Chromium)
- ConfiguredServerFixture: Shared xUnit class fixture for tests needing a
  configured server (avoids restarting server per test)
- Page objects: LoginPage, FirstTimeSetupPage, AdminDashboardPage

Key technical decisions:
- Uses IClassFixture<T> pattern to share server instances across test classes
- Disables parallel execution (tests share port 1337)
- Uses element-based waits instead of URL-based waits for Blazor SSR reliability
- Uses role-based selectors for AntDesign components (no standard label/for)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-08 08:25:44 +11:00
Pat Hartl
8b3a63e9b5 Release notes for v2.0.0 2026-03-05 01:08:07 -06:00
Pat Hartl
b9098a52bd Fix cmdlet documentation title 2026-03-05 01:07:56 -06:00
Pat Hartl
99e8cf5d6d Fix release titles 2026-03-05 01:07:48 -06:00
Pat Hartl
45721ec47e Add documentation for 2.0.0-rc6 2026-03-05 00:41:23 -06:00
Pat Hartl
1f4ddafa8c Fix missing fields in metadata lookup for IGDB
Some checks failed
LANCommander SDK Release / prep (push) Failing after 7s
LANCommander SDK Release / publish (push) Has been skipped
LANCommander Release / prep (push) Failing after 24s
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
2026-03-04 23:54:44 -06:00
Pat Hartl
2a604eec74 Add snippets path input to settings 2026-03-04 23:49:57 -06:00
Pat Hartl
f318348438 Fix setting duplication 2026-03-04 23:46:17 -06:00
Pat Hartl
a18f9b9af9 Fix snippet lookup 2026-03-04 21:18:33 -06:00
Pat Hartl
5c750a84ea Fix ID used to grab info from PCGW 2026-03-04 19:23:16 -06:00
Pat Hartl
3de2f789d6 Fix spacing for download size in action bar 2026-03-04 19:13:07 -06:00
Pat Hartl
d417ce264e Fix downloading avatar 2026-03-04 19:03:57 -06:00
Pat Hartl
88297fdc01 Close install dialog on install 2026-03-04 19:03:10 -06:00
Pat Hartl
a875b2a258 Pass base game ID in manifest 2026-03-03 23:33:36 -06:00
Pat Hartl
99eeb4193f Rework metadata lookup to support multiple providers, merging 2026-03-03 22:29:41 -06:00
Pat Hartl
b8e9a8c8cd Fix redist/tool/server exports
Fixes various issues that may prevent redistributables, tools, or servers from being exported properly.
2026-02-28 17:51:30 -06:00
Pat Hartl
3b3da2980c Add tests for save packing 2026-02-24 00:42:39 -06:00
Pat Hartl
289e87c4aa Add ManifestHelper tests 2026-02-23 19:20:53 -06:00
Pat Hartl
d508f53c8c Add tests for install/helpers 2026-02-23 19:03:18 -06:00
Pat Hartl
ab300587c5 Increase buffer size for downloads, avoid throttling large responses via kestrel
Some checks failed
LANCommander Release / prep (push) Failing after 19s
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
2026-02-17 00:30:27 -06:00
Pat Hartl
d17c93a367 Avoid crashing if auth provider templates don't exist 2026-02-16 19:34:59 -06:00
Pat Hartl
2d888ef50b Rename private variables 2026-02-16 19:34:41 -06:00
Pat Hartl
b9daf08dea Fix passing of tool ID to uploader 2026-02-16 18:28:23 -06:00
Pat Hartl
7e732c73f7 Fix uploading for tool archives 2026-02-15 14:56:55 -06:00
Pat Hartl
3442900dc4 Add migration for tools 2026-02-12 18:58:40 -06:00
Pat Hartl
fadf516c51 Hide logo on settings page 2026-02-12 18:58:25 -06:00
Pat Hartl
46bbf85093 First pass of adding tool installation to launcher 2026-02-10 18:04:49 -06:00
Pat Hartl
4474164e9b Split ScriptClient into partials 2026-02-10 17:34:35 -06:00
Pat Hartl
2bdd1ed261 Fix alignment of action buttons in list pages 2026-02-08 16:09:51 -06:00
Pat Hartl
2066667c99 Fix export initialization for servers, redistributables, and tools 2026-02-08 15:46:18 -06:00
Pat Hartl
5c28d608aa Fix server header extra layout, remove more old export dropdowns 2026-02-08 15:11:28 -06:00
Pat Hartl
3202df776a Fix console/http path saving in servers. Remove old export dropdown. 2026-02-08 15:03:15 -06:00
Pat Hartl
120e14d40c Add Tools section to server app
Allows users to upload additional tools and tie them to games. This can be useful for software like map editors, trainers, etc.
2026-02-08 02:24:53 -06:00
Pat Hartl
f28e942a25 Fix redistributable archive editor 2026-02-08 02:20:19 -06:00
Pat Hartl
c97e116ca9 Add RC5 documentation 2026-02-07 22:24:19 -06:00
Pat Hartl
f31d730bf8 Fix chat window opening as library instead of chat
Some checks failed
LANCommander Release / prep (push) Failing after 15s
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
2026-02-07 19:37:04 -06:00
Pat Hartl
860e5298f9 Show library after first login 2026-02-07 19:19:42 -06:00
Pat Hartl
1b21c77fe5 Make ArchiveId nullable for file pickers 2026-02-07 19:19:33 -06:00
Pat Hartl
9694f97671 Fix save path editor crashing when no archive exists on game 2026-02-07 19:05:10 -06:00
Pat Hartl
322f05cb26 Use steamcmd.net for app info, remove extra Steam cmdlets 2026-02-07 18:58:03 -06:00
Pat Hartl
303f8720f4 ILogger implementation to log to PowerShell 2026-01-28 02:13:19 -06:00
Pat Hartl
87f7e2160c Remove DI from cmdlets, fix SteamCMD login, fix SteamCMD settings 2026-01-28 01:59:27 -06:00
Pat Hartl
16adb724dd Remove install queueing from SteamCMD 2026-01-26 19:43:56 -06:00
Pat Hartl
831e1f0aea Added release notes for v2.0.0-rc4 2026-01-25 18:34:14 -06:00
Pat Hartl
d4c47fe45d Redirect to root after logout
Some checks failed
LANCommander Release / prep (push) Failing after 17s
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
2026-01-25 17:46:19 -06:00
Pat Hartl
1c5de59904 Remove incomplete server-side chat from menu 2026-01-25 17:41:41 -06:00
Pat Hartl
7be1f54b9f Display loading indicator when opening export dialog 2026-01-25 17:41:01 -06:00
Pat Hartl
93fc5e4c65 Remove old export endpoint 2026-01-25 17:33:56 -06:00
Pat Hartl
1f000f5f5a Add name to export file 2026-01-25 17:33:39 -06:00
Pat Hartl
acf66a330a Remove old export endpoints 2026-01-25 17:33:24 -06:00
Pat Hartl
b570ff0713 Fix export button for servers and redistributables 2026-01-24 23:35:58 -06:00
Pat Hartl
9415a31707 Fix redistributable and server script editors, add packaging for redistributables 2026-01-24 23:35:39 -06:00
Pat Hartl
3dc5e8010d Fix file manager styling 2026-01-24 23:02:54 -06:00
Pat Hartl
b6602dcfbe Add tooltips for archive editor buttons, show loading spinner when browsing archive 2026-01-24 23:02:31 -06:00
Pat Hartl
6017a65ac9 Fix dependencies on SteamProfileEditor 2026-01-24 22:32:28 -06:00
Pat Hartl
406a92b104 Fix import issues with scripts, archives, and legacy manifests
- Scripts now import properly
- Archives now import properly when using non-absolute storage location paths
- Support legacy LCX files that have manifests with the name _manifest.yml
2026-01-24 22:32:12 -06:00
Pat Hartl
6809e3b0da Add package reference 2026-01-24 17:17:29 -06:00
Pat Hartl
017f945537 Use full names to fix generated DI code 2026-01-24 17:14:33 -06:00
Pat Hartl
eea42dd62e Add documentation for Steam cmdlets 2026-01-24 16:41:40 -06:00
Pat Hartl
c5506b440f Move Steam integrations to separate assembly, add cmdlets for various Steam actions 2026-01-24 16:37:07 -06:00
Pat Hartl
036ef8a8c4 Fix bad injection of removed Client 2026-01-24 16:01:45 -06:00
Pat Hartl
1936f50599 Remove singular Client service for DI clients 2026-01-24 15:32:40 -06:00
Pat Hartl
e6ec74c59c Adjust release documentation titles 2026-01-23 19:15:52 -06:00
Pat Hartl
9ffdcd07ee v2.0.0-rc3 documentation 2026-01-23 17:42:43 -06:00
Pat Hartl
a8efeb97e6 Better error handling for SteamCMD integration, add timeouts
Some checks failed
LANCommander Release / prep (push) Failing after 26s
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
2026-01-23 02:31:48 -06:00
Pat Hartl
8e42a00f5a Validate username is valid Steam username 2026-01-23 01:36:41 -06:00
Pat Hartl
f23cf3384f Auto detect SteamCMD path 2026-01-23 01:36:26 -06:00
Aaron Powell
e3b0fe11dd Add Avalonia launcher builds to PR workflow 2026-01-23 16:16:55 +11:00
Aaron Powell
c15801d064 Fix CanInstall property notification for IsInstalled/IsInstalling changes
Added NotifyPropertyChangedFor(nameof(CanInstall)) to IsInstalled and IsInstalling properties so the install button properly enables/disables when going online/offline
2026-01-23 16:05:53 +11:00
Aaron Powell
827629e68b Add offline mode support for Avalonia launcher
- Detect server unreachable at startup and enter offline mode if user was previously logged in
- Skip server import when offline, load library from local database only
- Add offline indicator bar in sidebar (orange warning with 'Go Online' button)
- Add manual 'Go Offline' button in sidebar footer
- Disable install button when offline (CanInstall property)
- Propagate offline state from ShellViewModel to Sidebar, GamesListViewModel, GameDetailViewModel, and GameActionBarViewModel
- TryGoOnlineAsync attempts to reconnect and re-authenticate when user clicks 'Go Online'
2026-01-23 16:00:15 +11:00
Aaron Powell
e87548d125 Replace LANCommander text with logo in sidebar header 2026-01-23 15:40:08 +11:00
Aaron Powell
1cf0191c46 Add LANCommander logo to splash screen
- Add Avalonia.Svg.Skia package for SVG support
- Copy logo.svg to Assets folder
- Update SplashView to display logo instead of text
2026-01-23 15:39:08 +11:00
Aaron Powell
8d53430ff8 Add script debugging console for Avalonia launcher
- Create ScriptConsoleControl for displaying script output and interactive input
- Create PowerShellConsoleWindow to host the script console
- Wire up ScriptDebugger events to console control for real-time output
- Add debug scripts menu to GameActionBarView (visible when EnableScriptDebugging=true)
- Scripts run via SDK's ScriptClient and stay interactive after completion
- Fix MediaImporter.ExistsAsync to check database record instead of file existence
- Remove Iciclecreek.Avalonia.Terminal dependency, revert Avalonia to 11.2.3
2026-01-23 15:05:59 +11:00
Aaron Powell
73cd83c8bd Add PDF viewer for game manuals
- Create custom PdfViewerControl using PDFtoImage library
- Replace AvaloniaPdfViewer (had DynamicData version conflict) with PDFtoImage
- Add page navigation and zoom (DPI) controls to PDF viewer
- Update GameActionBarView with proper MenuFlyout bindings for manuals
- Add OpenFirstManualCommand property for safe binding to first manual
2026-01-23 12:04:38 +11:00
Aaron Powell
f075ba5e0a Merge remote-tracking branch 'upstream' into avalonia-launcher 2026-01-23 10:00:20 +11:00
Pat Hartl
5d16b7fc82 Documentation dispatcher 2026-01-20 21:12:44 -06:00
Pat Hartl
fd282f6dda Trim documentation 2026-01-20 21:12:18 -06:00
Pat Hartl
676898608b Add basic PowerShell module for SteamCMD 2026-01-20 00:48:13 -06:00
Pat Hartl
fec743a630 Copy static assets on publish 2026-01-20 00:47:47 -06:00
Pat Hartl
f0ec242bc5 Set default snippets directory if blank 2026-01-20 00:46:53 -06:00
Pat Hartl
e777a061ea Persist SteamCMD credentials 2026-01-20 00:32:39 -06:00
Pat Hartl
60bbd6c22c Add documentation for v2.0.0-rc2 2026-01-19 17:37:14 -06:00
Aaron Powell
74f8bf11e4 ci: Add GitHub Actions workflow for Avalonia Launcher build
- Created LANCommander.Launcher.Avalonia.yml workflow for building cross-platform
- Builds for Linux x64, macOS ARM64/x64, Windows x64
- Uses single-file publish with native libraries included
- Updated LANCommander.Release.yml to include Avalonia launcher builds
- Artifacts are included in release draft
2026-01-20 10:08:26 +11:00
Pat Hartl
3936c58ff7 Show logo instead of auth form when loading on startup
Some checks failed
LANCommander Release / prep (push) Failing after 31s
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
2026-01-19 17:04:28 -06:00
Aaron Powell
cd480ac2e3 fix: Fix In Library filter and add library indicator to depot games
- Changed IsInLibrary() to IsInLibraryAsync() which checks database directly
- The sync version was checking Items collection which may not be populated
- Added 📚 badge overlay on game covers for games in user's library
- Badge appears in top-right corner of cover image
2026-01-20 10:02:34 +11:00
Pat Hartl
b33cc1fc54 Fix chat window opening 2026-01-19 16:54:31 -06:00
Aaron Powell
8d04aca485 chore: Add root Data folder to gitignore 2026-01-20 09:48:59 +11:00
Aaron Powell
5ce9230487 chore: Remove data files from tracking 2026-01-20 09:48:52 +11:00
Aaron Powell
a9020e4e4c feat: Add PDF manual viewer using AvaloniaPdfViewer
- Added AvaloniaPdfViewer NuGet package (0.0.2-pre) for PDF rendering
- Created ManualViewerWindow to display game manuals
- Created ManualViewerViewModel with title and file path binding
- Added ManualViewModel for managing manual items in the action bar
- Added LoadManuals() to GameActionBarViewModel to load available manuals
- Added manual button (📖) to GameActionBarView with dropdown for multiple manuals
- Manuals are detected from game media with MediaType.Manual
2026-01-20 09:48:42 +11:00
Aaron Powell
ad05fb2c1d fix: Update play stats on UI thread after game stops
- Use Dispatcher.UIThread to post property updates from timer callback
- Ensures PlayTime and LastPlayed update immediately when game closes
- Fixed thread synchronization for CheckRunningState and RefreshPlayStatsAsync
2026-01-20 09:40:23 +11:00
Aaron Powell
2b840a05c1 chore: Add Avalonia data folder to gitignore 2026-01-20 09:05:28 +11:00
Aaron Powell
74ecaacd4a chore: Remove data files from tracking 2026-01-20 09:05:09 +11:00
Aaron Powell
d6de9fb810 feat: Add SplitButton for multiple game actions and combined uninstall/remove
- Play button uses SplitButton when game has multiple actions
- Clicking dropdown shows all available actions to run
- Uninstall button combines with Remove from Library as dropdown option
- Added GameActionViewModel class to wrap actions with commands
- Added ShowSimplePlayButton computed property for visibility logic
2026-01-20 09:05:03 +11:00
Aaron Powell
eaffd30fb2 ui: Move action bar to top of game detail view 2026-01-20 08:44:39 +11:00
Aaron Powell
5c07c103b6 refactor: Extract GameActionBar into separate component 2026-01-20 08:39:19 +11:00
Aaron Powell
b92f218adb fix: Refresh game UI after install completes, move download queue to sidebar 2026-01-20 08:19:36 +11:00
Pat Hartl
f7aedc9287 Fix adding game to library from game details 2026-01-19 14:52:40 -06:00
Aaron Powell
04c62b9e89 feat: Add game action bar with play/stop, stats, and file browsing 2026-01-19 22:23:09 +11:00
Aaron Powell
7f64550602 feat: Add splash screen on startup while checking connection 2026-01-19 22:15:02 +11:00
Aaron Powell
b45944ebab fix: Use async database check for IsInLibrary status 2026-01-19 22:12:18 +11:00
Pat Hartl
8955a87cb9 Fix calculation of width of game details drawer 2026-01-19 03:28:58 -06:00
Aaron Powell
281bf14af3 feat: Add settings page with install directories, media, UI, and debug options 2026-01-19 18:34:33 +11:00
Aaron Powell
2a42da4ac0 refactor: Consolidate game action buttons and add uninstall 2026-01-19 18:23:12 +11:00
Aaron Powell
594c18cd91 feat: Add game installation with download queue panel 2026-01-19 18:10:56 +11:00
Aaron Powell
ac2ebeef29 feat: Add library management (add/remove game from library) 2026-01-19 17:55:32 +11:00
Aaron Powell
e3b3d0ab90 fix: Download cover images for all depot games, not just library games 2026-01-19 17:47:24 +11:00
Aaron Powell
b419e5971e fix: Download media for depot games when viewing details 2026-01-19 17:40:18 +11:00
Aaron Powell
52180edb31 feat: Change depot games to horizontal grid layout 2026-01-19 17:36:43 +11:00
Aaron Powell
53664e2751 feat: Add filtering and sorting to depot games list 2026-01-19 17:34:29 +11:00
Aaron Powell
f642ec5f2a fix: Add FilePathToBitmapConverter to load images without extensions 2026-01-19 16:42:15 +11:00
Aaron Powell
e719ad25bc feat: Show game covers instead of icons in games list 2026-01-19 15:34:05 +11:00
Aaron Powell
7d51fbb6ad refactor: Extract UI components for better organization
- Create ViewModels/Components/ and Views/Components/ folders
- Extract LibrarySidebarView and LibrarySidebarViewModel from ShellView
- Move LibraryItemViewModel to Components folder
- Move GameItemViewModel to Components folder
- Create GameBannerView component (not yet integrated)
- Create PageHeaderView component (not yet integrated)
- Simplify ShellView to use LibrarySidebarView component
- Update GamesListView to reference Components namespace

This improves code organization and makes it easier to navigate
the codebase as the UI grows.
2026-01-19 14:55:27 +11:00
Aaron Powell
10068c9672 Merge branch 'main' into avalonia-launcher 2026-01-19 14:37:00 +11:00
Aaron Powell
8cf2139ccf feat: Add Avalonia launcher PoC with cross-platform UI
- Create LANCommander.Launcher.Avalonia project using Avalonia MVVM
- Implement server selection, login, and game library views
- Add library sidebar showing user's games
- Add depot/games list view with game details
- Integrate with LANCommander.Launcher.Services for:
  - DepotService, LibraryService, ImportService, MediaService
  - SDK authentication and connection clients
- Fix async initialization pattern to prevent UI thread deadlocks
- Add ConfigureAwait(false) to ServerConfigurationProvider.RefreshAsync
- Add Microsoft.Extensions.Http package for HttpClient DI

Key features:
- Server discovery and connection
- User authentication with credential persistence
- Library import from server to local SQLite cache
- Game icons and banners via MediaService
- Navigation between library, depot, and game details
2026-01-19 14:24:49 +11:00
Pat Hartl
d7114a4aaa Fix JS initialization on server after module refactor 2026-01-18 19:36:43 -06:00
Pat Hartl
fddc5de2e7 Move IGDB and SteamGridDB auth settings to distinct pages 2026-01-18 19:36:24 -06:00
Pat Hartl
7ac0943d02 Merge branch 'main' of https://github.com/LANCommander/LANCommander 2026-01-18 18:56:04 -06:00
Pat Hartl
4df9cee47f Fix log viewer initialization
Ref #393
2026-01-18 18:56:01 -06:00
Pat Hartl
c96ded5610
Merge pull request #392 from vladimir-aubrecht/fix_crash_on_no_connection_when_disconnecting
Don't try to disconnect when there is no connection.
2026-01-18 04:43:44 -06:00
Pat Hartl
58b09ab378 Remove use of Client in InstallService 2026-01-18 04:40:18 -06:00
Pat Hartl
d5392af32d Fix game install when IGDB is null 2026-01-18 04:40:00 -06:00
Pat Hartl
f4625ab134 Fix library list not scrollable and display of selected game details 2026-01-18 04:22:11 -06:00
Pat Hartl
90bf93d955 Fix scrolling in depot 2026-01-18 04:15:16 -06:00
Pat Hartl
77ad9b9c53 Add component for better Not Found message 2026-01-18 02:09:07 -06:00
Pat Hartl
986f102bec Map export endpoints
Fixes issue where exporting a game brings you to a "not found" page
2026-01-18 01:46:35 -06:00
vladimir.aubrecht
eb782c29bb Don't try to disconnect when there is no connection. 2026-01-17 17:46:12 +01:00
Pat Hartl
6307f0351e Fix startup of launcher
Fixes #391
2026-01-16 22:43:10 -06:00
Pat Hartl
54a120f870 Fix assembly version parsing
Some checks failed
LANCommander Release / prep (push) Failing after 27s
LANCommander Release / build_server_osx_x64 (push) Has been skipped
LANCommander Release / build_server_win_arm64 (push) Has been skipped
LANCommander Release / build_server_win_x64 (push) Has been skipped
LANCommander Release / build_launcher_linux_arm64 (push) Has been skipped
LANCommander Release / build_launcher_linux_x64 (push) Has been skipped
LANCommander Release / build_launcher_osx_arm64 (push) Has been skipped
LANCommander Release / build_launcher_osx_x64 (push) Has been skipped
LANCommander Release / build_launcher_win_arm64 (push) Has been skipped
LANCommander Release / build_launcher_win_x64 (push) Has been skipped
LANCommander Release / build_release (push) Has been skipped
LANCommander Release / build_server_linux_arm64 (push) Has been skipped
LANCommander Release / build_server_linux_x64 (push) Has been skipped
LANCommander Release / build_server_osx_arm64 (push) Has been skipped
2026-01-15 01:57:02 -06:00
Pat Hartl
4649f4a657 Fix tag matching for release 2026-01-15 00:25:43 -06:00
Pat Hartl
b4e09090a7 Update release workflow 2026-01-15 00:23:01 -06:00
Pat Hartl
657dd746b8 Skip packaging if resulting path does not exist 2026-01-14 20:13:20 -06:00
Pat Hartl
df45af54f2 Basic docs for package script type 2026-01-14 20:12:41 -06:00
Pat Hartl
0ababa702b Fix Discord heading 2026-01-14 20:12:31 -06:00
Pat Hartl
47e7b79c4a Docker documentation 2026-01-14 19:55:11 -06:00
Pat Hartl
bc560ca5c5 Update README 2026-01-13 23:16:49 -06:00
Pat Hartl
3cc8e08014 Add release info for 2.0.0-rc1 2026-01-13 21:24:37 -06:00
Pat Hartl
06f3fc6a4e Fix ignore blocking release docs 2026-01-13 21:24:20 -06:00
Pat Hartl
b52cd15edf Fix arch for x64 in Docker 2026-01-13 21:17:07 -06:00
Pat Hartl
cc32f930e0 Merge branch 'main' of https://github.com/LANCommander/LANCommander 2026-01-13 20:53:07 -06:00
Pat Hartl
7799e8224b Nightly builds for ARM64 Docker
Ref #388, #92
2026-01-13 20:53:03 -06:00
Pat Hartl
81fc33b46f
Merge pull request #378 from aaronpowell/refactor/remove-window-service
Simplifying the launcher startup codebase
2026-01-13 20:38:14 -06:00
Pat Hartl
fe5a6a0181
Merge pull request #390 from aaronpowell/ci-tweak
Skipping nightly CI if it's not the lancommander main repo
2026-01-13 20:36:53 -06:00
Pat Hartl
61384c1c46 Remove old docs static assets 2026-01-12 00:37:31 -06:00
Pat Hartl
9e564300e5 Filter documentation publishing 2026-01-12 00:34:36 -06:00
Aaron Powell
513ee67d42 Skipping nightly CI if it's not the lancommander main repo 2026-01-12 17:19:31 +11:00
Aaron Powell
a8184f763e Merge branch 'main' into refactor/remove-window-service 2026-01-12 17:15:35 +11:00
Pat Hartl
e68048f4fe Docs: versioning, customize homepage and theme, add logo 2026-01-11 22:30:59 -06:00
Pat Hartl
a165c843e7 Change cache dependency path 2026-01-11 21:53:03 -06:00
Pat Hartl
536310dc6a Change working directory for docs workflow 2026-01-11 21:50:01 -06:00
Pat Hartl
41b48deff9
Rename workflow 2026-01-11 21:47:23 -06:00
Pat Hartl
7e22bc5460
Add GitHub Actions workflow for documentation
This workflow builds and deploys the LANCommander documentation to GitHub Pages.
2026-01-11 21:46:42 -06:00
Pat Hartl
d7c49c529d Remove docs workflow 2026-01-11 21:46:22 -06:00
Pat Hartl
f3720916e1
Merge pull request #389 from LANCommander/development
Initial implementation of chat
2026-01-11 21:29:47 -06:00
Pat Hartl
4086cbaeeb Update read status when sending message, scroll to bottom on new message, merge message into group if gap < 5 mins 2026-01-11 21:21:12 -06:00
Pat Hartl
abb808a8ad GitHub Pages workflow for documentation 2026-01-11 14:38:51 -06:00
Pat Hartl
66abc6e59e Implement infinite loading with chat thread
- Chat messages are now loaded based on scroll position
- Read status is now updated and set by last message read
- Reworked script importing by using ScriptProvider
2026-01-11 14:38:24 -06:00
Pat Hartl
a0c7bf63f0 Fix ability to add database migrations 2026-01-10 20:02:45 -06:00
Pat Hartl
8bf006b1a4 Refactor TS to classes, remove factory in place of static Create factory method, rework module importing 2026-01-07 18:25:17 -06:00
Pat Hartl
e9b3f14896 Use Terminal component for server PowerShell debugger 2026-01-04 00:03:31 -06:00
Pat Hartl
e86cf75df1 Stub implementation 2026-01-03 23:59:06 -06:00
Pat Hartl
fbcb02a863 Move useful terminal methods to component, remove old addon registration 2026-01-03 23:58:50 -06:00
Pat Hartl
eda3a8e7bc Fix infinite loader namespace 2026-01-03 23:22:35 -06:00
Pat Hartl
2d95541942 Remove overload for GetMessages 2026-01-03 23:22:25 -06:00
Pat Hartl
74f2551a7c Ensure init location exists on upload 2026-01-03 18:31:51 -06:00
Pat Hartl
9b515623df Extend XtermBlazor to load addons 2026-01-03 18:27:26 -06:00
Pat Hartl
e85eeb01db Fix bundling, JS interop initialization 2026-01-03 18:26:30 -06:00
Pat Hartl
bb1f3e6003 Update EasyMDE.Blazor 2026-01-03 16:03:57 -06:00
Pat Hartl
76b37fc46b Fix SplitPane factory 2026-01-03 15:57:01 -06:00
Pat Hartl
40871e24aa WIP infinite loader component 2026-01-03 15:53:17 -06:00
Pat Hartl
76aa51dc7a Extension method for creating interop types from factory pattern 2026-01-03 15:52:31 -06:00
Pat Hartl
7f21a1846f Refactor split pane as JS module 2026-01-03 15:52:09 -06:00
Pat Hartl
6021eca118 Refactor time provider as JS module 2026-01-03 15:51:47 -06:00
Pat Hartl
1e1fa1713d Refactor chunk uploader as JS module 2026-01-03 15:51:21 -06:00
Pat Hartl
81685f2da7 Style chat component 2026-01-02 02:10:42 -06:00
Pat Hartl
b54ef115ab Fix footer 2026-01-02 02:10:24 -06:00
Pat Hartl
0d86e27811 Fix depot SCSS import 2025-12-31 21:24:22 -06:00
Pat Hartl
9b705a757d Remove unused code 2025-12-31 21:24:12 -06:00
Pat Hartl
17a50d5497 Fix depot variables 2025-12-31 21:24:02 -06:00
Pat Hartl
4069ab93a2 Fix page namespacing 2025-12-31 20:09:32 -06:00
Pat Hartl
edb8af917d Fix package reference for imports 2025-12-31 20:05:20 -06:00
Pat Hartl
1bbe105adc Fix SDK SignalR implementation of IChatClient, registration of chat client 2025-12-31 20:01:37 -06:00
Pat Hartl
698a7523b6 Code cleanup
- Move all Launcher components to LANCommander.Launcher.UI namespace
- Move all SCSS files next to components (when possible)
- Refactor styles that control overflow of content into titlebar to MainWindow only
- Remove use in launcher UI of SDK.Client
2025-12-31 20:01:04 -06:00
Pat Hartl
fb9370b21e Fix missing StyleBuilder utility class 2025-12-30 18:11:20 -06:00
Pat Hartl
e1c1469614 Fix chat component rendering
- Load thread after creation
- Change thread list from DataList to Flex
- Move unread badge to right side of chat thread list item
- Fix avatar rerendering after another message is added to message group
- Add styling to thread list
- Change message input to support markdown and add markdown rendering for message contents
2025-12-30 18:08:20 -06:00
Pat Hartl
6bfaa7b9e6 Fix virtualized rendering of message groups 2025-12-30 18:03:57 -06:00
Pat Hartl
83ff6a8237 Separate SignalR chat client from RPC client. Create abstraction for getting server address. Implement server-side chat client. 2025-12-23 21:12:06 -06:00
Pat Hartl
77f0e47e47 Make sure list of participants is populated 2025-12-22 21:12:19 -06:00
Pat Hartl
fc397fc6e8 Add MouseArea component 2025-12-22 21:11:58 -06:00
Pat Hartl
a67d624a06 Merge branch 'main' into development
# Conflicts:
#	LANCommander.Server/Migrations/CombineSettingsYaml.cs
#	LANCommander.Server/Migrations/EncapsulateUserData.cs
2025-12-22 21:11:24 -06:00
Pat Hartl
52c3fd9e5c
Merge pull request #386 from aaronpowell/expanding-migrations
Evolving Migrations
2025-12-19 01:11:55 -06:00
Pat Hartl
9b3e154eaa
Merge pull request #385 from aaronpowell/download-files-from-new-data-path
Download endpoints get correct paths
2025-12-19 01:11:10 -06:00
Pat Hartl
f26a07b780
Merge pull request #387 from aaronpowell/launcher-startup
Launcher import fixes
2025-12-19 01:08:00 -06:00
Aaron Powell
216f34144b Adding more info to migration history 2025-12-19 16:01:14 +11:00
Pat Hartl
06936c1035
Merge pull request #384 from aaronpowell/media-error-logging
Ensuring media path is correct
2025-12-18 22:07:55 -06:00
Aaron Powell
aaba97caed Fixing to check for absolute paths 2025-12-19 14:51:06 +11:00
Aaron Powell
2f9cb9a9f2 checking if the path is an absolute path or not 2025-12-19 14:46:46 +11:00
Aaron Powell
869b7ea466 Fixing media importer and adding importer robustness
ImportContext wasn't being set on the importers which caused problems during the import process. Fixed that and added a bunch more logging around the place so we can get better tracing into what's happening through the import pipeline
2025-12-19 11:14:43 +11:00
Aaron Powell
ef12bc58c9 Missing service registration 2025-12-19 10:35:50 +11:00
Aaron Powell
b07ad7d9a9 Adding migration history tracking 2025-12-18 11:12:41 +11:00
Aaron Powell
8600bae9ec Overhauling the migrations to be a bit more explicit in the steps that they go through.
Changed the IMigration interface to have a series of steps, first a method to indicate that the migration should run, next some pre-checks (like is it in a container), and lastly the actual execution.

Next, the settings and folder migrations are broken into individual migrations, which means that it's a lot more obvious which ones run and which ones are skipped.
2025-12-18 10:57:26 +11:00
Aaron Powell
0e5a8c5be2 Ensuring that the download endpoints will receive correctly pathed folders for data directory
Also adding logging throughout the download endpoints
2025-12-17 22:52:28 +11:00
Aaron Powell
2d66c4c0b7 Some tidying up on the MediaService 2025-12-17 22:36:49 +11:00
Aaron Powell
d37623dbc0 Ensuring that the config/data directory is appended to the media path 2025-12-17 22:36:32 +11:00
Aaron Powell
c62c36772a Error handling, logging, and return types to help debugging 2025-12-17 22:35:28 +11:00
Pat Hartl
fc0fcdcb7c
Merge pull request #382 from aaronpowell/tags-input-fixes
tags input fixes
2025-12-17 00:34:58 -06:00
Pat Hartl
2ea51b7455 Use correct AppPaths methods 2025-12-17 00:33:43 -06:00
Pat Hartl
8ffca6d99f Add XML docs for AppPaths 2025-12-17 00:33:38 -06:00
Pat Hartl
463831817d
Merge pull request #383 from aaronpowell/updating-migration
Adding SQLite database migration step
2025-12-17 00:33:17 -06:00
Aaron Powell
e2b2654a61 Adding SQLite database migration step
Created a new abstraction for migrations that have to run against the file system, so that we don't need to do the check in each migration.

Adding the default DB name to the settings so it's got a const value.

Updated the Settings migrator to check a few more places
2025-12-17 17:09:36 +11:00
Aaron Powell
eee946c6e0 Updating some missing renames
Razor tooling doesn't detect property renames to update usages (known issue tracked in https://github.com/dotnet/razor/issues/7060)
2025-12-17 14:56:26 +11:00
Aaron Powell
5b0368bf7a Changing how null is detected and handled
Using the required keyword to indicate what is expected to be passed to the components in the stack.

Looking through the usage, only the Values property can come through as null, everything else is set to something that should never be nullable (and then, if it is, it should be considered a proper error to resolve).
2025-12-17 14:50:05 +11:00
Pat Hartl
2e6b187eb1 Move settings file 2025-12-16 20:41:26 -06:00
Pat Hartl
1f5be92565 Update paths for storage locations and various settings 2025-12-16 20:41:17 -06:00
Pat Hartl
ad8bbfaa7b
Merge pull request #381 from LANCommander/development
Config Directory Relocation
2025-12-16 19:46:37 -06:00
Pat Hartl
63f6583ab7 Fix default storage location paths 2025-12-16 19:35:36 -06:00
Pat Hartl
397919ff0a Don't move user data if running in container with unmounted volume 2025-12-16 19:35:21 -06:00
Pat Hartl
9d8f8cc238 Error handling for migrations 2025-12-16 19:13:14 -06:00
Pat Hartl
9bbac6e2fa Fix null values for tag inputs 2025-12-16 19:02:35 -06:00
Pat Hartl
4d9590360c Move files as well as directories 2025-12-16 18:53:38 -06:00
Pat Hartl
954b4bf265 Check for old nightly config 2025-12-16 18:53:25 -06:00
Pat Hartl
d786bdf6ac Update sample compose 2025-12-16 00:56:39 -06:00
Pat Hartl
a8ffbb601b Safely move contents to new data directory 2025-12-16 00:55:15 -06:00
Pat Hartl
f345b49263 Apply SemVer sort ordering 2025-12-16 00:20:48 -06:00
Pat Hartl
5b0abac404 Merge branch 'main' into development 2025-12-15 23:15:40 -06:00
Pat Hartl
7c1453923b Move documentation to Docusaurus 2025-12-14 17:01:10 -06:00
Pat Hartl
6b12814055
Merge pull request #380 from aaronpowell/server-images
Fixing cascading parameter for Includes on DataColumn
2025-12-13 00:30:08 -06:00
Pat Hartl
3774580b17
Merge pull request #379 from aaronpowell/git-ignore-updates
Ignoring some artifacts that we shouldn't include
2025-12-13 00:29:28 -06:00
Pat Hartl
5d5f93c3af
Merge pull request #377 from aaronpowell/dropping-serilog
Simplifying logging and making it work for the launcher
2025-12-13 00:28:43 -06:00
Pat Hartl
1679719ad4 Add application migrations 2025-12-13 00:27:54 -06:00
Pat Hartl
2c7a835efa Move clients out of Services directory 2025-12-13 00:26:43 -06:00
Aaron Powell
734f38d3aa Making it 'required' and removing the default to help the runtime resolve better 2025-12-13 16:50:52 +11:00
Aaron Powell
132a2bb4db Fixing cascading parameter for Includes on DataColumn 2025-12-13 16:46:30 +11:00
Aaron Powell
ff0cfc744e Ignoring some artifacts that we shouldn't include 2025-12-12 15:00:16 +11:00
Aaron Powell
a3b840ca36 refactor: Remove WindowService abstraction layer
- Removed WindowService.cs which was just a thin wrapper around PhotinoBlazor

- Removed WindowOptions, WindowType, and WindowRef models (unused or part of incomplete window tracking)

- Updated Program.cs to directly use PhotinoBlazorAppBuilder

- Service registration now done through direct extension method calls

- Window configuration uses existing extension methods in Startup/Window.cs

- Simplified initialization flow with clearer code structure

- All functionality preserved, just more transparent and maintainable
2025-12-12 11:27:13 +11:00
Pat Hartl
369f298aaf Expression bodies 2025-12-11 02:06:18 -06:00
Pat Hartl
962e227b03 Update snippet pathing 2025-12-11 02:06:04 -06:00
Pat Hartl
88301d9c79 Move config/user files to Data directory 2025-12-11 01:58:28 -06:00
Aaron Powell
2b3c2c560a Logging working from the launcher now 2025-12-11 16:32:35 +11:00
Aaron Powell
8c436b3d47 Consolidating to consistent logging across all apps 2025-12-11 16:18:33 +11:00
Aaron Powell
cb097f2809 Removing serilog packages from package list 2025-12-11 16:18:33 +11:00
Aaron Powell
52642ccb25 Removing serilog from server 2025-12-11 16:18:33 +11:00
Aaron Powell
e236927f54 Removing serilog from CLI launcher 2025-12-11 16:18:33 +11:00
Aaron Powell
22113f9ef2 Removing Serilig from the sdk 2025-12-11 16:18:33 +11:00
Aaron Powell
4cef475348 Removing Serilog from the launcher
Rely on the standard .NET logging system, so it integrates better with external libraries we might be logging through.
2025-12-11 16:18:33 +11:00
Pat Hartl
9a713045d1 Fix column rendering 2025-12-10 21:49:53 -06:00
Pat Hartl
ccb7bebd48 Merge branch 'main' into development 2025-12-10 21:49:20 -06:00
Pat Hartl
a06c97475c
Merge pull request #376 from aaronpowell/initialising-events
Fixing launcher crash on startup
2025-12-10 21:48:53 -06:00
Pat Hartl
ce183c8eb9 Implement debugger for packaging scripts 2025-12-10 21:46:46 -06:00
Pat Hartl
17fa802c19 Implementation of script debugger component with RPC via SignalR 2025-12-10 20:55:38 -06:00
Aaron Powell
0992d95bd1 Adding some logging into server connect/disconnect for debugging 2025-12-11 12:09:58 +11:00
Aaron Powell
e48db47603 Event handlers weren't initialised, causing null ref exception on launcher start 2025-12-11 11:57:35 +11:00
Pat Hartl
f242f66813 Fix display of system scripts table 2025-12-09 00:16:32 -06:00
Pat Hartl
20731f355a Fix certain taxonomies (tag, genre, platform) being removed from game on save
Fixes the action, custom field, multiplayer mode, and save path editors from clearing out taxonomies when saving. Also passes the Game/Server/Redistributable ID as a cascading parameter.
2025-12-08 00:51:32 -06:00
Pat Hartl
7bc0783360 Restore Developer/Publisher fields 2025-12-06 19:33:43 -06:00
Pat Hartl
f733fc274a Fix tag input 2025-12-06 19:28:04 -06:00
Pat Hartl
04f7a0a07a
Merge pull request #375 from LANCommander/development
Refactor Launcher Import
2025-12-05 21:49:19 -06:00
Pat Hartl
b93c0f6732
Merge pull request #374 from LANCommander/development
Refactored Importing / Legacy Game Importing
2025-12-05 14:40:12 -06:00
Pat Hartl
e91aca8292
Merge pull request #373 from aaronpowell/feature/pr-build-workflow
Add PR build workflow
2025-12-04 19:08:27 -06:00
Aaron Powell
8ae9120037 Using different patterns for tags 2025-12-05 11:27:59 +11:00
Aaron Powell
e77620fdd6 Using timestamp not SHA 2025-12-04 17:01:19 +11:00
Aaron Powell
0976f8ccd4 Removing duplicate package version 2025-12-04 16:54:35 +11:00
Aaron Powell
3bbb1fa3f3 fixing permissions 2025-12-04 16:51:04 +11:00
Aaron Powell
031ef039b8 Add PR build workflow 2025-12-04 16:46:12 +11:00
Pat Hartl
729dbf5811
Merge pull request #372 from aaronpowell/end-to-end-saves
Fixing end to end bug in Save uploading
2025-12-03 23:34:44 -06:00
Aaron Powell
5e305fc0b0 Endpoint for saves uploading was missing Game from it 2025-12-04 14:38:09 +11:00
Aaron Powell
d0f6782d3e Adding a file name when uploading
The file name is ignored by the server, but if you don't add one, you can't attach a file to the form
2025-12-04 13:35:27 +11:00
Pat Hartl
1ff1089049
Merge pull request #370 from aaronpowell/path-regex-handling
Path regex handling
2025-12-03 18:57:33 -06:00
Aaron Powell
ead5b3ef9e Cleanup warnings 2025-12-03 19:35:44 +11:00
Aaron Powell
96327b3e2f Fixing how regex paths are handled and removing dead code 2025-12-03 19:34:53 +11:00
Pat Hartl
9f6cb26134
Merge pull request #369 from aaronpowell/null-on-versioned-archive
Fixing null ref error with versioned endpoint
2025-12-01 23:36:16 -06:00
Aaron Powell
bc47a7f61c Using TypedResults properly to understand return types and putting some more diagnostics for readability 2025-12-02 16:28:54 +11:00
Aaron Powell
059fc7d907 Fixing null ref error with versioned endpoint 2025-12-02 16:18:58 +11:00
1551 changed files with 502318 additions and 25249 deletions

View file

@ -55,6 +55,9 @@ jobs:
with:
node-version: '20'
- name: Generate PowerShell Completions
run: dotnet run --project ./LANCommander.CompletionGenerator/LANCommander.CompletionGenerator.csproj -- ./LANCommander.UI/Components/MonacoCodeEditor/PowerShellCompletions.g.ts
# Parallel UI builds
- name: Build UI Components
run: |

View file

@ -56,6 +56,8 @@ jobs:
uses: actions/setup-node@v3.8.1
# UI
- name: Generate PowerShell Completions
run: dotnet run --project ./LANCommander.CompletionGenerator/LANCommander.CompletionGenerator.csproj -- ./LANCommander.UI/Components/MonacoCodeEditor/PowerShellCompletions.g.ts
- run: cd ./LANCommander.UI; npm install; npm run package
- run: cd ./LANCommander.Server; npm install

View file

@ -124,19 +124,6 @@ jobs:
build_platform: Windows
build_configuration: Debug
build_launcher_win_x64:
needs: [prep]
if: needs.prep.outputs.changed == 'true'
uses: ./.github/workflows/LANCommander.Launcher.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
version_semver: ${{ needs.prep.outputs.version_semver }}
version_tag: ${{ needs.prep.outputs.version_tag }}
build_runtime: win-x64
build_arch: x64
build_platform: Windows
build_configuration: Debug
# --------------------------------------------------------------------------
# 3) FINALIZE: if changes == 'true', gather artifacts + push Docker:development
# --------------------------------------------------------------------------
@ -145,7 +132,6 @@ jobs:
- prep
- build_server_linux_x64
- build_server_win_x64
- build_launcher_win_x64
if: needs.prep.outputs.changed == 'true'
runs-on: ubuntu-latest
steps:
@ -247,12 +233,6 @@ jobs:
name: LANCommander.Server-Windows-x64-v${{ needs.prep.outputs.version_tag }}.zip
path: artifacts
- name: Download Launcher Windows x64
uses: actions/download-artifact@v4
with:
name: LANCommander.Launcher-Windows-x64-v${{ needs.prep.outputs.version_tag }}.zip
path: artifacts
- name: Delete existing release assets
uses: dev-drprasad/delete-tag-and-release@v1.1
with:
@ -270,6 +250,5 @@ jobs:
files: |
artifacts/LANCommander.Server-Windows-x64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Server-Linux-x64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-Windows-x64-v${{ needs.prep.outputs.version_tag }}.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -0,0 +1,28 @@
name: Publish Documentation Updates
on:
push:
branches: [main]
paths:
- "LANCommander.Documentation/**"
jobs:
dispatch:
runs-on: ubuntu-latest
steps:
- name: Dispatch to Docusaurus repo
env:
DISPATCH_TOKEN: ${{ secrets.DOCUMENTATION_DISPATCH_TOKEN }}
run: |
curl -sS -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${DISPATCH_TOKEN}" \
https://api.github.com/repos/LANCommander/LANCommander.Documentation/dispatches \
-d '{
"event_type": "docs-sources-updated",
"client_payload": {
"repo": "'"${GITHUB_REPOSITORY}"'",
"sha": "'"${GITHUB_SHA}"'",
"ref": "'"${GITHUB_REF}"'"
}
}'

View file

@ -0,0 +1,125 @@
name: LANCommander.Launcher.Legacy
on:
push:
branches: [main, feature/legacy-launcher]
paths:
- 'LANCommander.Launcher.Legacy/**'
- 'LANCommander.SDK.Cpp/**'
- '.github/workflows/LANCommander.Launcher.Legacy.yml'
pull_request:
branches: [main]
paths:
- 'LANCommander.Launcher.Legacy/**'
- 'LANCommander.SDK.Cpp/**'
- '.github/workflows/LANCommander.Launcher.Legacy.yml'
workflow_dispatch:
jobs:
build-win9x:
name: Build (Win9x / MinGW-w64 i686)
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- name: Checkout
uses: actions/checkout@v4
# ---------------------------------------------------------------
# MSYS2 with MinGW32 (i686) toolchain
# ---------------------------------------------------------------
- name: Setup MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: MINGW32
update: false
install: >-
mingw-w64-i686-gcc
mingw-w64-i686-cmake
mingw-w64-i686-make
make
# ---------------------------------------------------------------
# Vendor dependencies (cJSON, miniz, Allegro 4 source)
# ---------------------------------------------------------------
- name: Download vendor dependencies
shell: pwsh
run: ./setup-vendor.ps1
# ---------------------------------------------------------------
# Build Allegro 4 from source (static, no addons)
# ---------------------------------------------------------------
- name: Build Allegro 4
run: |
ALLEGRO_SRC="LANCommander.Launcher.Legacy/vendor/allegro4/allegro5-4.4.3.1"
ALLEGRO_BUILD="LANCommander.Launcher.Legacy/build-allegro-win9x"
ALLEGRO_PREFIX="$(pwd)/LANCommander.Launcher.Legacy/allegro4-win9x"
cmake -S "$ALLEGRO_SRC" -B "$ALLEGRO_BUILD" \
-G "MinGW Makefiles" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="$ALLEGRO_PREFIX" \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DSHARED=OFF \
-DWANT_EXAMPLES=OFF \
-DWANT_TOOLS=OFF \
-DWANT_TESTS=OFF \
-DWANT_ALLEGROGL=OFF \
-DWANT_LOADPNG=OFF \
-DWANT_LOGG=OFF \
-DWANT_JPGALLEG=OFF \
-DWANT_FRAMEWORKS=OFF
mingw32-make -C "$ALLEGRO_BUILD" -j$(nproc)
mingw32-make -C "$ALLEGRO_BUILD" install
# ---------------------------------------------------------------
# Build the launcher
# ---------------------------------------------------------------
- name: Build launcher
run: |
LAUNCHER_DIR="LANCommander.Launcher.Legacy"
LAUNCHER_BUILD="$LAUNCHER_DIR/build-win9x"
ALLEGRO_PREFIX="$(pwd)/$LAUNCHER_DIR/allegro4-win9x"
cmake -S "$LAUNCHER_DIR" -B "$LAUNCHER_BUILD" \
-G "MinGW Makefiles" \
-DCMAKE_BUILD_TYPE=Release \
-DALLEGRO_STATIC=ON \
-DTARGET_WIN9X=ON \
-DALLEGRO_ROOT="$ALLEGRO_PREFIX"
mingw32-make -C "$LAUNCHER_BUILD" -j$(nproc)
# ---------------------------------------------------------------
# Package
# ---------------------------------------------------------------
- name: Package artifacts
run: |
mkdir -p out-win9x
LAUNCHER_EXE=$(find LANCommander.Launcher.Legacy/build-win9x -name "launcher.exe" | head -1)
cp "$LAUNCHER_EXE" out-win9x/LANCommander.exe
strip out-win9x/LANCommander.exe
# Bundle GDI+ redistributable (MinGW CRT is statically linked)
if [ -f "$MINGW_PREFIX/bin/gdiplus.dll" ]; then
cp "$MINGW_PREFIX/bin/gdiplus.dll" out-win9x/
echo "Bundled: gdiplus.dll"
fi
# Show PE info for verification
objdump -p out-win9x/LANCommander.exe | grep -i "Version\|Subsystem" || true
echo ""
ls -lh out-win9x/
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: LANCommander-Legacy-Win9x
path: out-win9x/
if-no-files-found: error

View file

@ -0,0 +1,127 @@
name: LANCommander Launcher Tests — PR Visual Diff Comment
on:
workflow_call:
inputs:
pr_number:
description: 'Pull request number to comment on'
required: true
type: string
artifact_run_id:
description: 'Workflow run ID that produced the launcher-visual-artifacts artifact'
required: true
type: string
permissions:
contents: write # push diff PNGs to the gh-visual-diffs branch
pull-requests: write # post / update the comment
jobs:
comment:
runs-on: ubuntu-latest
steps:
- name: Checkout for branch push
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download visual artifacts
uses: actions/download-artifact@v4
with:
name: launcher-visual-artifacts
path: visual
run-id: ${{ inputs.artifact_run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Read regression manifest
id: manifest
run: |
if [ ! -f visual/regressions.json ]; then
echo "no manifest — assuming no visual run"
echo "count=0" >> "$GITHUB_OUTPUT"
exit 0
fi
count=$(jq '.regressed_count' visual/regressions.json)
echo "count=$count" >> "$GITHUB_OUTPUT"
- name: Skip when no regressions
if: steps.manifest.outputs.count == '0'
run: echo "No visual regressions; nothing to comment."
# Push diff/actual/baseline PNGs to an orphan branch so raw.githubusercontent.com
# URLs render inline in the PR comment. One folder per (PR, run).
- name: Push diffs to gh-visual-diffs branch
if: steps.manifest.outputs.count != '0'
env:
PR: ${{ inputs.pr_number }}
RUN: ${{ inputs.artifact_run_id }}
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Worktree off an orphan branch so we never touch the source tree.
if git ls-remote --exit-code --heads origin gh-visual-diffs >/dev/null; then
git fetch origin gh-visual-diffs
git worktree add /tmp/visual gh-visual-diffs
else
git worktree add --detach /tmp/visual
cd /tmp/visual
git checkout --orphan gh-visual-diffs
git rm -rf . 2>/dev/null || true
cd -
fi
DEST="/tmp/visual/pr-${PR}/run-${RUN}"
mkdir -p "$DEST"
cp -r visual/diffs "$DEST/" 2>/dev/null || true
cp -r visual/screenshots "$DEST/" 2>/dev/null || true
cp -r visual/baselines "$DEST/" 2>/dev/null || true
cd /tmp/visual
git add -A
git -c user.name=github-actions -c user.email=github-actions@github.com \
commit -m "Visual diffs for PR #${PR} run ${RUN}"
git push origin gh-visual-diffs
- name: Post / update PR comment
if: steps.manifest.outputs.count != '0'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR: ${{ inputs.pr_number }}
RUN: ${{ inputs.artifact_run_id }}
REPO: ${{ github.repository }}
MARKER: '<!-- launcher-visual-diff-comment -->'
run: |
set -euo pipefail
base="https://raw.githubusercontent.com/${REPO}/gh-visual-diffs/pr-${PR}/run-${RUN}"
body=$(mktemp)
{
echo "$MARKER"
echo "## :art: Launcher visual regressions"
echo
echo "$(jq '.regressed_count' visual/regressions.json) baseline(s) drifted in [run ${RUN}](https://github.com/${REPO}/actions/runs/${RUN})."
echo
jq -r '.regressions[] | .name' visual/regressions.json | while read -r name; do
echo "<details><summary><strong>${name}</strong></summary>"
echo
echo "| Baseline | Actual | Diff |"
echo "| --- | --- | --- |"
echo "| ![baseline](${base}/baselines/${name}.png) | ![actual](${base}/screenshots/${name}.png) | ![diff](${base}/diffs/${name}.diff.png) |"
echo
echo "</details>"
echo
done
echo "_If these changes are intentional, run the **Update Visual Baselines** workflow to refresh._"
} >> "$body"
# Find an existing comment with our marker; update it if present.
existing=$(gh api repos/${REPO}/issues/${PR}/comments --paginate \
--jq ".[] | select(.body | startswith(\"${MARKER}\")) | .id" | head -1)
if [ -n "$existing" ]; then
gh api -X PATCH "repos/${REPO}/issues/comments/${existing}" --field "body=@${body}"
else
gh api -X POST "repos/${REPO}/issues/${PR}/comments" --field "body=@${body}"
fi

View file

@ -0,0 +1,91 @@
name: LANCommander Launcher Tests — Update Visual Baselines
# Operator-triggered: re-renders every visual test on the CI host and commits the
# new screenshots over the committed Baselines/. Only runs when a human dispatches
# it from the Actions tab — never on push or PR — so accidental drift can't slip in.
on:
workflow_dispatch:
inputs:
branch:
description: 'Branch to commit refreshed baselines to'
required: true
type: string
default: 'main'
build_dotnet_version:
description: 'Build .NET Version'
required: false
type: string
default: '9.0.102'
permissions:
contents: write
jobs:
refresh:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.branch }}
submodules: true
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ inputs.build_dotnet_version }}
- name: Install rendering prereqs
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends \
libfontconfig1 libfreetype6 libice6 libsm6 libx11-6 libxcb1 libxext6
- name: Restore + build visual test project
run: |
dotnet restore LANCommander.Launcher.Tests/LANCommander.Launcher.Tests.csproj
dotnet build LANCommander.Launcher.Tests/LANCommander.Launcher.Tests.csproj -c Debug --no-restore
# Run visual tests, ignore failures (regressions are expected here — that's
# the whole point of refreshing). Then promote every captured screenshot
# to be the new baseline.
- name: Capture fresh screenshots
continue-on-error: true
run: |
dotnet test LANCommander.Launcher.Tests/LANCommander.Launcher.Tests.csproj \
-c Debug --no-build --no-restore \
--logger "console;verbosity=normal"
- name: Promote captures to baselines
run: |
set -euo pipefail
src=LANCommander.Launcher.Tests/bin/Debug/net10.0/Screenshots
dst=LANCommander.Launcher.Tests/Baselines
if [ ! -d "$src" ]; then
echo "::error::No screenshots captured at $src — visual tests didn't run."
exit 1
fi
mkdir -p "$dst"
cp -v "$src"/*.png "$dst"/
echo "## Updated baselines" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
ls -1 "$dst"/*.png | xargs -n1 basename | sed 's/^/- /' >> "$GITHUB_STEP_SUMMARY"
- name: Commit + push refreshed baselines
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
if git diff --quiet -- LANCommander.Launcher.Tests/Baselines; then
echo "Baselines unchanged — nothing to commit."
exit 0
fi
git add LANCommander.Launcher.Tests/Baselines
git commit -m "Refresh launcher visual baselines from CI run ${GITHUB_RUN_ID}"
git push origin HEAD:${{ inputs.branch }}

View file

@ -0,0 +1,170 @@
name: LANCommander Launcher Tests
on:
workflow_call:
inputs:
build_dotnet_version:
description: 'Build .NET Version'
required: true
type: string
outputs:
visual_diff_count:
description: 'Number of visual baselines that regressed in this run'
value: ${{ jobs.test.outputs.visual_diff_count }}
workflow_dispatch:
inputs:
build_dotnet_version:
description: 'Build .NET Version'
required: false
type: string
default: '9.0.102'
permissions:
contents: read
jobs:
test:
name: Run Launcher Tests
runs-on: ubuntu-latest
outputs:
visual_diff_count: ${{ steps.summarize.outputs.visual_diff_count }}
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ inputs.build_dotnet_version }}
# Avalonia headless rendering uses Skia + the Inter font. Install
# font + GPU prereqs so screenshots render consistently.
- name: Install rendering prereqs
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends \
libfontconfig1 libfreetype6 libice6 libsm6 libx11-6 libxcb1 libxext6
- name: Restore
run: dotnet restore
# ---- Unit + integration test projects ---------------------------------
# Services.Tests and IntegrationTests build cleanly without the
# Server/UI npm+pwsh chain. Launcher.Tests likewise just needs Skia.
- name: Build test projects
run: |
dotnet build LANCommander.Launcher.Services.Tests/LANCommander.Launcher.Services.Tests.csproj -c Debug --no-restore
dotnet build LANCommander.Launcher.IntegrationTests/LANCommander.Launcher.IntegrationTests.csproj -c Debug --no-restore
dotnet build LANCommander.Launcher.Tests/LANCommander.Launcher.Tests.csproj -c Debug --no-restore
- name: Run Services unit tests
run: |
dotnet test LANCommander.Launcher.Services.Tests/LANCommander.Launcher.Services.Tests.csproj \
-c Debug --no-build --no-restore \
--logger "trx;LogFileName=services.trx" \
--logger "console;verbosity=normal" \
--results-directory test-results \
--collect:"XPlat Code Coverage"
- name: Run integration tests
run: |
dotnet test LANCommander.Launcher.IntegrationTests/LANCommander.Launcher.IntegrationTests.csproj \
-c Debug --no-build --no-restore \
--logger "trx;LogFileName=integration.trx" \
--logger "console;verbosity=normal" \
--results-directory test-results \
--collect:"XPlat Code Coverage"
# Visual tests are continued-on-error so a baseline regression doesn't
# mask any earlier failure and so we always reach the artifact-upload
# + summary steps below. The summary step re-asserts the failure for CI.
- name: Run visual tests
id: visual_tests
continue-on-error: true
run: |
dotnet test LANCommander.Launcher.Tests/LANCommander.Launcher.Tests.csproj \
-c Debug --no-build --no-restore \
--logger "trx;LogFileName=visual.trx" \
--logger "console;verbosity=normal" \
--results-directory test-results
# ---- Visual artifact collection --------------------------------------
# Aggregate every screenshot + diff PNG into top-level folders so the
# PR-comment job downstream can find them by predictable name.
- name: Collect visual artifacts
if: always()
run: |
mkdir -p visual/screenshots visual/diffs visual/baselines
cp -r LANCommander.Launcher.Tests/bin/Debug/net10.0/Screenshots/. visual/screenshots/ 2>/dev/null || true
cp -r LANCommander.Launcher.Tests/bin/Debug/net10.0/Diffs/. visual/diffs/ 2>/dev/null || true
cp -r LANCommander.Launcher.Tests/Baselines/. visual/baselines/ 2>/dev/null || true
- name: Summarize visual diffs
id: summarize
if: always()
run: |
shopt -s nullglob
diffs=(visual/diffs/*.diff.png)
count=${#diffs[@]}
echo "visual_diff_count=$count" >> "$GITHUB_OUTPUT"
echo "## Visual test summary" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "$count baseline(s) regressed." >> "$GITHUB_STEP_SUMMARY"
# JSON manifest the PR-comment job consumes
python3 - "$count" <<'PY' > visual/regressions.json
import json, os, sys
count = int(sys.argv[1])
diffs = sorted(os.listdir("visual/diffs")) if os.path.isdir("visual/diffs") else []
data = {
"regressed_count": count,
"regressions": [
{
"name": d.replace(".diff.png", ""),
"diff": f"visual/diffs/{d}",
"actual": f"visual/screenshots/{d.replace('.diff.png', '.png')}",
"baseline": f"visual/baselines/{d.replace('.diff.png', '.png')}",
}
for d in diffs
],
}
print(json.dumps(data, indent=2))
PY
cat visual/regressions.json
- name: Upload visual artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: launcher-visual-artifacts
path: |
visual/screenshots/**
visual/diffs/**
visual/baselines/**
visual/regressions.json
if-no-files-found: warn
retention-days: 14
- name: Upload TRX + coverage
if: always()
uses: actions/upload-artifact@v4
with:
name: launcher-test-results
path: test-results/**
if-no-files-found: warn
retention-days: 14
# Re-assert the visual test outcome so CI fails when baselines diverge,
# after artifacts are guaranteed uploaded.
- name: Fail if visual tests regressed
if: always() && steps.visual_tests.outcome == 'failure'
run: |
echo "::error::Visual tests reported regressions. See the launcher-visual-artifacts artifact."
exit 1

View file

@ -45,27 +45,18 @@ env:
jobs:
build:
runs-on: ubuntu-latest
runs-on: ${{ inputs.build_platform == 'Linux' && inputs.build_arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: frabert/replace-string-action@v2
name: Swap Path Backslashes
id: swap_path_backslashes
with:
string: '${{ github.workspace }}'
pattern: '\\'
replace-with: '/'
flags: g
# Checkout code
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: true
# .NET Setup and Caching
# .NET Setup
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
@ -74,64 +65,243 @@ jobs:
- name: Restore dependencies
run: dotnet restore --locked-mode
# Node.js Setup and Caching
- name: Setup Node.js
uses: actions/setup-node@v3.8.1
with:
node-version: '20'
- name: Install Node Packages
- name: Publish Launcher
run: |
npm install --prefix ./LANCommander.UI
npm install --prefix ./LANCommander.Launcher
# Strip leading 'v' if present
RAW_VERSION="${{ inputs.version_tag }}" # e.g. v2.0.0-rc1
SEMVER="${RAW_VERSION#v}" # 2.0.0-rc1
- name: Package Frontend
run: |
npm run package --prefix ./LANCommander.UI
npm run package --prefix ./LANCommander.Launcher
# Numeric part only for Assembly/FileVersion
NUMERIC="${SEMVER%%-*}" # 2.0.0
ASSEMBLY_VERSION="${NUMERIC}.0" # 2.0.0.0
# .NET builds
- name: Publish Components
echo "SEMVER=$SEMVER"
echo "ASSEMBLY_VERSION=$ASSEMBLY_VERSION"
dotnet publish "./LANCommander.Launcher/LANCommander.Launcher.csproj" \
-c "${{ inputs.build_configuration }}" \
--self-contained \
--runtime "${{ inputs.build_runtime }}" \
-p:Version="$SEMVER" \
-p:AssemblyVersion="$ASSEMBLY_VERSION" \
-p:FileVersion="$ASSEMBLY_VERSION" \
-p:InformationalVersion="$SEMVER" \
-p:PublishSingleFile=true \
-p:IncludeNativeLibrariesForSelfExtract=true \
-p:IncludeAllContentForSelfExtract=true \
-p:EnableCompressionInSingleFile=true \
-p:DebugType=embedded
- name: Bundle libvlc (Linux)
if: inputs.build_platform == 'Linux'
shell: bash
run: |
dotnet publish "./LANCommander.AutoUpdater/LANCommander.AutoUpdater.csproj" -c ${{ inputs.build_configuration }} --self-contained --runtime ${{ inputs.build_runtime }} -p:Version="${{ inputs.version_tag }}" -p:AssemblyVersion="${{ inputs.version_semver }}"
dotnet publish "./LANCommander.Launcher/LANCommander.Launcher.csproj" -c ${{ inputs.build_configuration }} --self-contained --runtime ${{ inputs.build_runtime }} -p:Version="${{ inputs.version_tag }}" -p:AssemblyVersion="${{ inputs.version_semver }}"
dotnet publish "./LANCommander.Launcher.CLI/LANCommander.Launcher.CLI.csproj" -c ${{ inputs.build_configuration }} --self-contained --runtime ${{ inputs.build_runtime }} -p:Version="${{ inputs.version_tag }}" -p:AssemblyVersion="${{ inputs.version_semver }}"
PUBLISH_DIR="LANCommander.Launcher/bin/${{ inputs.build_configuration }}/net10.0/${{ inputs.build_runtime }}/publish"
VLC_DIR="$PUBLISH_DIR/libvlc/${{ inputs.build_runtime }}"
mkdir -p "$VLC_DIR"
if [ "${{ inputs.build_arch }}" = "arm64" ]; then
# Enable arm64 multiarch and add Ubuntu Ports repository so apt can
# download arm64 packages on this x64 runner.
sudo dpkg --add-architecture arm64
CODENAME=$(lsb_release -cs)
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports ${CODENAME} main restricted universe" \
| sudo tee /etc/apt/sources.list.d/ubuntu-ports-arm64.list > /dev/null
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports ${CODENAME}-updates main restricted universe" \
| sudo tee -a /etc/apt/sources.list.d/ubuntu-ports-arm64.list > /dev/null
sudo apt-get update -qq
sudo apt-get download libvlc5:arm64 libvlccore9:arm64 vlc-plugin-base:arm64
mkdir -p vlc-extracted
for deb in libvlc5_*arm64.deb libvlccore9_*arm64.deb vlc-plugin-base_*arm64.deb; do
dpkg -x "$deb" vlc-extracted/
done
LIB_DIR="vlc-extracted/usr/lib/aarch64-linux-gnu"
PLUGINS_SRC="$LIB_DIR/vlc/plugins"
else
sudo apt-get install -y --no-install-recommends libvlc5 libvlccore9 vlc-plugin-base
LIB_DIR="/usr/lib/x86_64-linux-gnu"
PLUGINS_SRC="$LIB_DIR/vlc/plugins"
fi
# Copy the two core shared libraries, dereferencing symlinks so their
# content is preserved correctly when zipped.
for lib in libvlc.so.5 libvlccore.so.9; do
src=$(find "$LIB_DIR" -maxdepth 1 -name "${lib}*" | sort | head -1)
if [ -n "$src" ]; then
cp -L "$src" "$VLC_DIR/$lib"
echo "Bundled: $lib"
else
echo "WARNING: $lib not found in $LIB_DIR" >&2
fi
done
# Copy VLC plugins (vlc-plugin-base covers common codecs and demuxers)
if [ -d "$PLUGINS_SRC" ]; then
mkdir -p "$VLC_DIR/plugins"
cp -rL "$PLUGINS_SRC/." "$VLC_DIR/plugins/"
echo "Bundled $(find "$VLC_DIR/plugins" -name "*.so" | wc -l) plugin(s)"
fi
- name: Bundle and Clean
shell: pwsh
run: |
Copy-Item -Force -Recurse -Verbose LANCommander.AutoUpdater/bin/${{ inputs.build_configuration }}/net9.0/${{ inputs.build_runtime }}/publish/* LANCommander.Launcher/bin/${{ inputs.build_configuration }}/net9.0/${{ inputs.build_runtime }}/publish/
Copy-Item -Force -Recurse -Verbose LANCommander.Launcher.CLI/bin/${{ inputs.build_configuration }}/net9.0/${{ inputs.build_runtime }}/publish/* LANCommander.Launcher/bin/${{ inputs.build_configuration }}/net9.0/${{ inputs.build_runtime }}/publish/
# Remove unnecessary files in a single operation
$BasePath = "LANCommander.Launcher/bin/${{ inputs.build_configuration }}/net10.0/${{ inputs.build_runtime }}/publish"
# Remove unnecessary files
$PathsToRemove = @(
'wwwroot/_content/BootstrapBlazor.PdfReader/compat',
'wwwroot/_content/BootstrapBlazor.PdfReader/2.*',
'wwwroot/_content/BootstrapBlazor.PdfReader/build/pdf.sandbox.js',
'wwwroot/_content/BootstrapBlazor.PdfReader/build/*.map',
'wwwroot/_content/BootstrapBlazor.PdfReader/web/*.map',
'wwwroot/_content/AntDesign/less',
'wwwroot/_content/BlazorMonaco/lib/monaco-editor/min-maps',
'wwwroot/Identity/lib/bootstrap',
'LANCommander.ico',
'LANCommanderDark.ico',
'package-lock.json',
'package.json',
'*.pdb',
'hostfxr.dll.bak',
'Libraries/locales'
'*.pdb'
)
$BasePath = "LANCommander.Launcher/bin/${{ inputs.build_configuration }}/net9.0/${{ inputs.build_runtime }}/publish"
foreach ($path in $PathsToRemove) {
Remove-Item -Recurse -Force -ErrorAction Continue "$BasePath/$path"
}
- name: Bundle macOS .app
if: inputs.build_platform == 'macOS'
shell: bash
run: |
set -euo pipefail
# Strip leading 'v' and reduce to numeric x.y.z for the bundle version keys.
RAW_VERSION="${{ inputs.version_tag }}"
SEMVER="${RAW_VERSION#v}"
NUMERIC="${SEMVER%%-*}"
APP_NAME="LANCommander Launcher"
EXECUTABLE="LANCommander.Launcher"
BUNDLE_ID="app.lancommander.launcher"
ICON_SRC="LANCommander.Launcher/Assets/icon.icns"
PUBLISH_DIR="LANCommander.Launcher/bin/${{ inputs.build_configuration }}/net10.0/${{ inputs.build_runtime }}/publish"
APP_DIR="${EXECUTABLE}.app"
# Lay out the bundle skeleton.
mkdir -p "$APP_DIR/Contents/MacOS" "$APP_DIR/Contents/Resources"
cp -a "$PUBLISH_DIR/." "$APP_DIR/Contents/MacOS/"
cp "$ICON_SRC" "$APP_DIR/Contents/Resources/AppIcon.icns"
# Write Info.plist.
cat > "$APP_DIR/Contents/Info.plist" <<PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>${APP_NAME}</string>
<key>CFBundleDisplayName</key>
<string>${APP_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${BUNDLE_ID}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE}</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleShortVersionString</key>
<string>${NUMERIC}</string>
<key>CFBundleVersion</key>
<string>${SEMVER}</string>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>
PLIST
# Ensure the entrypoint is executable, then zip while preserving the
# permission bits (Compress-Archive drops the exec bit).
chmod +x "$APP_DIR/Contents/MacOS/${EXECUTABLE}"
zip -ry "LANCommander.Launcher-${{ inputs.build_platform }}-${{ inputs.build_arch }}-v${{ inputs.version_tag }}.zip" "$APP_DIR"
- name: Build AppImage (Linux)
if: inputs.build_platform == 'Linux'
shell: bash
run: |
set -euo pipefail
PUBLISH_DIR="LANCommander.Launcher/bin/${{ inputs.build_configuration }}/net10.0/${{ inputs.build_runtime }}/publish"
APP_NAME="LANCommander.Launcher"
APPDIR="AppDir"
# appimagetool / runtime architecture identifiers
if [ "${{ inputs.build_arch }}" = "arm64" ]; then
AI_ARCH="aarch64"
else
AI_ARCH="x86_64"
fi
export ARCH="$AI_ARCH"
# --- Assemble the AppDir ---------------------------------------------
rm -rf "$APPDIR"
mkdir -p "$APPDIR/usr/bin" "$APPDIR/usr/share/applications" \
"$APPDIR/usr/share/icons/hicolor/scalable/apps"
cp -r "$PUBLISH_DIR/." "$APPDIR/usr/bin/"
chmod +x "$APPDIR/usr/bin/$APP_NAME"
# Icon (use the prebuilt 256x256 project icon)
ICON_SRC="LANCommander.Launcher/Assets/icon.png"
ICON_DIR="$APPDIR/usr/share/icons/hicolor/256x256/apps"
mkdir -p "$ICON_DIR"
cp "$ICON_SRC" "$ICON_DIR/lancommander.png"
cp "$ICON_DIR/lancommander.png" "$APPDIR/lancommander.png"
ln -sf lancommander.png "$APPDIR/.DirIcon"
# Desktop entry
cat > "$APPDIR/usr/share/applications/lancommander.desktop" <<'EOF'
[Desktop Entry]
Type=Application
Name=LANCommander Launcher
Comment=LANCommander Launcher
Exec=LANCommander.Launcher
Icon=lancommander
Categories=Game;
Terminal=false
EOF
cp "$APPDIR/usr/share/applications/lancommander.desktop" "$APPDIR/lancommander.desktop"
# AppRun entry point
cat > "$APPDIR/AppRun" <<'EOF'
#!/bin/bash
HERE="$(dirname "$(readlink -f "${0}")")"
export PATH="${HERE}/usr/bin:${PATH}"
export LD_LIBRARY_PATH="${HERE}/usr/bin:${LD_LIBRARY_PATH:-}"
exec "${HERE}/usr/bin/LANCommander.Launcher" "$@"
EOF
chmod +x "$APPDIR/AppRun"
# --- Fetch appimagetool ----------------------------------------------
TOOL_URL="https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${AI_ARCH}.AppImage"
curl -L -o appimagetool "$TOOL_URL"
chmod +x appimagetool
# --- Build the AppImage ----------------------------------------------
OUT="LANCommander.Launcher-Linux-${{ inputs.build_arch }}-v${{ inputs.version_tag }}.AppImage"
./appimagetool --appimage-extract-and-run "$APPDIR" "$OUT"
echo "Produced $OUT"
- name: Upload AppImage Artifact (Linux)
if: inputs.build_platform == 'Linux'
uses: actions/upload-artifact@v4
with:
path: LANCommander.Launcher-Linux-${{ inputs.build_arch }}-v${{ inputs.version_tag }}.AppImage
name: LANCommander.Launcher-Linux-${{ inputs.build_arch }}-v${{ inputs.version_tag }}.AppImage
- name: Compress Build Output
if: inputs.build_platform != 'macOS'
shell: pwsh
run: |
$compress = @{
Path = "LANCommander.Launcher/bin/${{ inputs.build_configuration }}/net9.0/${{ inputs.build_runtime }}/publish/*"
Path = "LANCommander.Launcher/bin/${{ inputs.build_configuration }}/net10.0/${{ inputs.build_runtime }}/publish/*"
DestinationPath = "LANCommander.Launcher-${{ inputs.build_platform }}-${{ inputs.build_arch }}-v${{ inputs.version_tag }}.zip"
CompressionLevel = "Fastest"
}
@ -141,4 +311,4 @@ jobs:
uses: actions/upload-artifact@v4
with:
path: LANCommander.Launcher-${{ inputs.build_platform }}-${{ inputs.build_arch }}-v${{ inputs.version_tag }}.zip
name: LANCommander.Launcher-${{ inputs.build_platform }}-${{ inputs.build_arch }}-v${{ inputs.version_tag }}.zip
name: LANCommander.Launcher-${{ inputs.build_platform }}-${{ inputs.build_arch }}-v${{ inputs.version_tag }}.zip

View file

@ -29,7 +29,7 @@ jobs:
version_semver: ${{ steps.set_version.outputs.VERSION_SEMVER }}
version_tag: ${{ steps.set_version.outputs.VERSION_TAG }}
changed: ${{ steps.check_diff.outputs.changed }}
build_dotnet_version: 9.0.102
build_dotnet_version: 10.0.100
steps:
- name: Check out code
uses: actions/checkout@v4
@ -101,7 +101,7 @@ jobs:
# --------------------------------------------------------------------------
build_server_linux_arm64:
needs: [prep]
if: needs.prep.outputs.changed == 'true'
if: needs.prep.outputs.changed == 'true' && github.repository_owner == 'LANCommander'
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -114,7 +114,7 @@ jobs:
build_server_linux_x64:
needs: [prep]
if: needs.prep.outputs.changed == 'true'
if: needs.prep.outputs.changed == 'true' && github.repository_owner == 'LANCommander'
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -127,7 +127,7 @@ jobs:
build_server_osx_arm64:
needs: [prep]
if: needs.prep.outputs.changed == 'true'
if: needs.prep.outputs.changed == 'true' && github.repository_owner == 'LANCommander'
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -140,7 +140,7 @@ jobs:
build_server_osx_x64:
needs: [prep]
if: needs.prep.outputs.changed == 'true'
if: needs.prep.outputs.changed == 'true' && github.repository_owner == 'LANCommander'
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -153,7 +153,7 @@ jobs:
build_server_win_arm64:
needs: [prep]
if: needs.prep.outputs.changed == 'true'
if: needs.prep.outputs.changed == 'true' && github.repository_owner == 'LANCommander'
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -166,7 +166,7 @@ jobs:
build_server_win_x64:
needs: [prep]
if: needs.prep.outputs.changed == 'true'
if: needs.prep.outputs.changed == 'true' && github.repository_owner == 'LANCommander'
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -179,7 +179,7 @@ jobs:
build_launcher_linux_arm64:
needs: [prep]
if: needs.prep.outputs.changed == 'true'
if: needs.prep.outputs.changed == 'true' && github.repository_owner == 'LANCommander'
uses: ./.github/workflows/LANCommander.Launcher.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -192,7 +192,7 @@ jobs:
build_launcher_linux_x64:
needs: [prep]
if: needs.prep.outputs.changed == 'true'
if: needs.prep.outputs.changed == 'true' && github.repository_owner == 'LANCommander'
uses: ./.github/workflows/LANCommander.Launcher.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -205,7 +205,7 @@ jobs:
build_launcher_osx_arm64:
needs: [prep]
if: needs.prep.outputs.changed == 'true'
if: needs.prep.outputs.changed == 'true' && github.repository_owner == 'LANCommander'
uses: ./.github/workflows/LANCommander.Launcher.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -218,7 +218,7 @@ jobs:
build_launcher_osx_x64:
needs: [prep]
if: needs.prep.outputs.changed == 'true'
if: needs.prep.outputs.changed == 'true' && github.repository_owner == 'LANCommander'
uses: ./.github/workflows/LANCommander.Launcher.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -231,7 +231,7 @@ jobs:
build_launcher_win_arm64:
needs: [prep]
if: needs.prep.outputs.changed == 'true'
if: needs.prep.outputs.changed == 'true' && github.repository_owner == 'LANCommander'
uses: ./.github/workflows/LANCommander.Launcher.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -244,7 +244,7 @@ jobs:
build_launcher_win_x64:
needs: [prep]
if: needs.prep.outputs.changed == 'true'
if: needs.prep.outputs.changed == 'true' && github.repository_owner == 'LANCommander'
uses: ./.github/workflows/LANCommander.Launcher.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -267,13 +267,7 @@ jobs:
- build_server_osx_x64
- build_server_win_arm64
- build_server_win_x64
- build_launcher_linux_arm64
- build_launcher_linux_x64
- build_launcher_osx_arm64
- build_launcher_osx_x64
- build_launcher_win_arm64
- build_launcher_win_x64
if: needs.prep.outputs.changed == 'true'
if: needs.prep.outputs.changed == 'true' && github.repository_owner == 'LANCommander'
runs-on: ubuntu-latest
steps:
# 3c) Build and push Docker image with tag "nightly"
@ -302,23 +296,29 @@ jobs:
name: LANCommander.Server-Linux-x64-v${{ needs.prep.outputs.version_tag }}.zip
path: ./
- name: Extract Server Artifacts
- name: Download Server arm64 Artifacts
uses: actions/download-artifact@v4
with:
name: LANCommander.Server-Linux-arm64-v${{ needs.prep.outputs.version_tag }}.zip
path: ./
- name: Extract Server Artifacts x64
run: |
mkdir -p ./LANCommander.Server/published
unzip ./LANCommander.Server-Linux-x64-v${{ needs.prep.outputs.version_tag }}.zip -d ./LANCommander.Server/published
mkdir -p ./LANCommander.Server/published-amd64
unzip ./LANCommander.Server-Linux-x64-v${{ needs.prep.outputs.version_tag }}.zip -d ./LANCommander.Server/published-amd64
# - name: Download Server arm64 Artifacts
# uses: actions/download-artifact@v4
# with:
# name: LANCommander.Server-Linux-arm64-v${{ needs.prep.outputs.version_tag }}.zip
# path: ./published
- name: Extract Server Artifacts arm64
run: |
mkdir -p ./LANCommander.Server/published-arm64
unzip ./LANCommander.Server-Linux-arm64-v${{ needs.prep.outputs.version_tag }}.zip -d ./LANCommander.Server/published-arm64
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
# - name: Set up QEMU
# uses: docker/setup-qemu-action@v2
- name: Setup buildx
uses: docker/setup-buildx-action@v3
with:
platforms: linux/amd64
platforms: linux/amd64,linux/arm64
- name: Build and push Docker image
id: push
@ -327,7 +327,7 @@ jobs:
context: ./LANCommander.Server
file: ./LANCommander.Server/Dockerfile
push: true
platforms: linux/amd64
platforms: linux/amd64,linux/arm64
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}:nightly
${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}:v${{ needs.prep.outputs.version_tag }}
@ -359,7 +359,16 @@ jobs:
needs:
- prep
- publish_docker_image
- build_launcher_linux_arm64
- build_launcher_linux_x64
- build_launcher_osx_arm64
- build_launcher_osx_x64
- build_launcher_win_arm64
- build_launcher_win_x64
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Create Temp Directory
run: mkdir -p artifacts
@ -434,33 +443,40 @@ jobs:
with:
name: LANCommander.Launcher-Windows-x64-v${{ needs.prep.outputs.version_tag }}.zip
path: artifacts
- name: Delete existing release assets
uses: dev-drprasad/delete-tag-and-release@v1.1
- name: Download Launcher Linux ARM64 AppImage
uses: actions/download-artifact@v4
with:
tag_name: nightly
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Create nightly release
uses: softprops/action-gh-release@v2
name: LANCommander.Launcher-Linux-arm64-v${{ needs.prep.outputs.version_tag }}.AppImage
path: artifacts
- name: Download Launcher Linux x64 AppImage
uses: actions/download-artifact@v4
with:
tag_name: nightly
name: Nightly Build v${{ needs.prep.outputs.version_tag }}
draft: false
prerelease: true
generate_release_notes: true
body: This is the latest nightly build. These builds are generated automatically and should be considered unstable.
files: |
artifacts/LANCommander.Server-Windows-arm64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Server-Windows-x64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Server-Linux-x64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Server-Linux-arm64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Server-macOS-arm64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Server-macOS-x64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-Windows-arm64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-Windows-x64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-Linux-x64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-Linux-arm64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-macOS-arm64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-macOS-x64-v${{ needs.prep.outputs.version_tag }}.zip
name: LANCommander.Launcher-Linux-x64-v${{ needs.prep.outputs.version_tag }}.AppImage
path: artifacts
- name: Create or update nightly release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Create the release if it doesn't exist, otherwise update its metadata
if gh release view nightly > /dev/null 2>&1; then
# Remove all existing assets so only this nightly's files remain
gh release view nightly --json assets -q '.assets[].name' | while read -r asset; do
gh release delete-asset nightly "$asset" -y
done
gh release edit nightly \
--prerelease \
--title "Nightly Build v${{ needs.prep.outputs.version_tag }}" \
--notes "This is the latest nightly build. These builds are generated automatically and should be considered unstable."
else
gh release create nightly \
--prerelease \
--title "Nightly Build v${{ needs.prep.outputs.version_tag }}" \
--notes "This is the latest nightly build. These builds are generated automatically and should be considered unstable."
fi
# Upload new artifacts
gh release upload nightly artifacts/*

229
.github/workflows/LANCommander.PR.yml vendored Normal file
View file

@ -0,0 +1,229 @@
name: LANCommander Pull Request
on:
pull_request:
types:
- opened
- synchronize
- reopened
- ready_for_review
workflow_dispatch:
permissions:
contents: write
packages: read
checks: write
pull-requests: write
jobs:
prep:
runs-on: ubuntu-latest
outputs:
version_semver: ${{ steps.set_version.outputs.version_semver }}
version_tag: ${{ steps.set_version.outputs.version_tag }}
build_dotnet_version: 9.0.102
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Determine build metadata
id: set_version
shell: bash
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
RUN_NUMBER: ${{ github.run_number }}
run: |
TIMESTAMP=$(date -u +"%Y%m%d%H%M")
TIME_COMPONENT=$(date -u +"%H%M")
TIME_COMPONENT=$((10#$TIME_COMPONENT))
if [ -n "$PR_NUMBER" ]; then
BUILD_COMPONENT=$((PR_NUMBER % 65535))
if [ "$BUILD_COMPONENT" -eq 0 ]; then
BUILD_COMPONENT=1
fi
VERSION_SEMVER="0.0.${BUILD_COMPONENT}.${TIME_COMPONENT}"
VERSION_TAG="0.0.${BUILD_COMPONENT}-pr.${PR_NUMBER}.${TIMESTAMP}"
else
BUILD_COMPONENT=$((RUN_NUMBER % 65535))
if [ "$BUILD_COMPONENT" -eq 0 ]; then
BUILD_COMPONENT=1
fi
VERSION_SEMVER="0.0.${BUILD_COMPONENT}.${TIME_COMPONENT}"
VERSION_TAG="0.0.${BUILD_COMPONENT}-ci.${RUN_NUMBER}.${TIMESTAMP}"
fi
echo "version_semver=$VERSION_SEMVER" >> $GITHUB_OUTPUT
echo "version_tag=$VERSION_TAG" >> $GITHUB_OUTPUT
ui_tests:
needs: [prep]
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ needs.prep.outputs.build_dotnet_version }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Node packages
run: |
npm install --prefix ./LANCommander.UI
npm install --prefix ./LANCommander.Server
# The Monaco editor's PowerShell completions are generated (gitignored) and
# required by the frontend webpack build. The in-build MSBuild target uses
# Windows-style paths, so generate explicitly here for the Linux runner.
- name: Generate PowerShell Completions
run: dotnet run --project ./LANCommander.CompletionGenerator/LANCommander.CompletionGenerator.csproj -- ./LANCommander.UI/Components/MonacoCodeEditor/PowerShellCompletions.g.ts
- name: Restore dependencies
run: dotnet restore LANCommander.Server.UI.Tests
- name: Build test project
run: dotnet build LANCommander.Server.UI.Tests --no-restore --configuration Release
- name: Install Playwright browsers
run: pwsh LANCommander.Server.UI.Tests/bin/Release/net10.0/playwright.ps1 install --with-deps chromium
- name: Run UI tests
run: dotnet test LANCommander.Server.UI.Tests --no-build --configuration Release --logger "trx;LogFileName=ui-test-results.trx" --results-directory ./TestResults
env:
SCREENSHOT_DIR: ${{ github.workspace }}/TestResults/Screenshots
- name: Test report
if: always()
uses: dorny/test-reporter@v1
with:
name: UI Test Results
path: ./TestResults/ui-test-results.trx
reporter: dotnet-trx
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: ui-test-results
path: ./TestResults
retention-days: 7
build_server_linux_arm64:
needs: [prep]
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
version_semver: ${{ needs.prep.outputs.version_semver }}
version_tag: ${{ needs.prep.outputs.version_tag }}
build_runtime: linux-arm64
build_arch: arm64
build_platform: Linux
build_configuration: Debug
build_server_linux_x64:
needs: [prep]
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
version_semver: ${{ needs.prep.outputs.version_semver }}
version_tag: ${{ needs.prep.outputs.version_tag }}
build_runtime: linux-x64
build_arch: x64
build_platform: Linux
build_configuration: Debug
build_server_osx_arm64:
needs: [prep]
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
version_semver: ${{ needs.prep.outputs.version_semver }}
version_tag: ${{ needs.prep.outputs.version_tag }}
build_runtime: osx-arm64
build_arch: arm64
build_platform: macOS
build_configuration: Debug
build_server_osx_x64:
needs: [prep]
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
version_semver: ${{ needs.prep.outputs.version_semver }}
version_tag: ${{ needs.prep.outputs.version_tag }}
build_runtime: osx-x64
build_arch: x64
build_platform: macOS
build_configuration: Debug
build_server_win_arm64:
needs: [prep]
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
version_semver: ${{ needs.prep.outputs.version_semver }}
version_tag: ${{ needs.prep.outputs.version_tag }}
build_runtime: win-arm64
build_arch: arm64
build_platform: Windows
build_configuration: Debug
build_server_win_x64:
needs: [prep]
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
version_semver: ${{ needs.prep.outputs.version_semver }}
version_tag: ${{ needs.prep.outputs.version_tag }}
build_runtime: win-x64
build_arch: x64
build_platform: Windows
build_configuration: Debug
build_launcher_linux_x64:
needs: [prep]
uses: ./.github/workflows/LANCommander.Launcher.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
version_semver: ${{ needs.prep.outputs.version_semver }}
version_tag: ${{ needs.prep.outputs.version_tag }}
build_runtime: linux-x64
build_arch: x64
build_platform: Linux
build_configuration: Debug
build_launcher_win_x64:
needs: [prep]
uses: ./.github/workflows/LANCommander.Launcher.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
version_semver: ${{ needs.prep.outputs.version_semver }}
version_tag: ${{ needs.prep.outputs.version_tag }}
build_runtime: win-x64
build_arch: x64
build_platform: Windows
build_configuration: Debug
launcher_tests:
needs: [prep]
uses: ./.github/workflows/LANCommander.Launcher.Tests.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
launcher_tests_pr_comment:
# Always run after the test job — even on failure — so visual regressions
# show up as a PR comment instead of being buried in the artifact.
needs: [launcher_tests]
if: always() && needs.launcher_tests.result != 'cancelled' && github.event.pull_request.number != null
uses: ./.github/workflows/LANCommander.Launcher.Tests.PRComment.yml
with:
pr_number: ${{ github.event.pull_request.number }}
artifact_run_id: ${{ github.run_id }}

View file

@ -0,0 +1,100 @@
name: LANCommander Packager Build
on:
workflow_dispatch:
workflow_call:
inputs:
version_semver:
description: "Semantic Version"
required: true
type: string
version_tag:
description: 'Version Tag'
required: true
type: string
build_dotnet_version:
description: 'Build .NET Version'
required: false
type: string
default: '10.0.x'
build_configuration:
description: 'Build Configuration (Debug/Release)'
required: false
type: string
default: 'Release'
permissions:
contents: write
env:
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/package
jobs:
build:
runs-on: windows-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ inputs.build_dotnet_version }}
- name: Restore dependencies
run: dotnet restore
- name: Publish Packager
shell: pwsh
run: |
# Strip leading 'v' if present
$RawVersion = "${{ inputs.version_tag }}"
$Semver = $RawVersion -replace '^v', ''
# Numeric part only for Assembly/FileVersion
$Numeric = ($Semver -split '-')[0]
$AssemblyVersion = "$Numeric.0"
Write-Host "SEMVER=$Semver"
Write-Host "ASSEMBLY_VERSION=$AssemblyVersion"
dotnet publish "./LANCommander.Packager/LANCommander.Packager.csproj" `
-c "${{ inputs.build_configuration }}" `
--self-contained `
--runtime win-x86 `
-p:Version="$Semver" `
-p:AssemblyVersion="$AssemblyVersion" `
-p:FileVersion="$AssemblyVersion" `
-p:InformationalVersion="$Semver" `
-p:PublishSingleFile=true `
-p:IncludeNativeLibrariesForSelfExtract=true `
-p:IncludeAllContentForSelfExtract=true `
-p:EnableCompressionInSingleFile=true `
-p:DebugType=embedded
- name: Clean
shell: pwsh
run: |
$BasePath = "LANCommander.Packager/bin/${{ inputs.build_configuration }}/net10.0/win-x86/publish"
Remove-Item -Recurse -Force -ErrorAction Continue "$BasePath/*.pdb"
- name: Compress Build Output
shell: pwsh
run: |
$compress = @{
Path = "LANCommander.Packager/bin/${{ inputs.build_configuration }}/net10.0/win-x86/publish/*"
DestinationPath = "LANCommander.Packager-Windows-x86-v${{ inputs.version_tag }}.zip"
CompressionLevel = "Fastest"
}
Compress-Archive @compress
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
path: LANCommander.Packager-Windows-x86-v${{ inputs.version_tag }}.zip
name: LANCommander.Packager-Windows-x86-v${{ inputs.version_tag }}.zip

View file

@ -3,7 +3,12 @@ name: LANCommander Release
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v*.*.*'
- 'v*.*.*-*'
env:
REGISTRY: docker.io
IMAGE_NAME: lancommander/lancommander
permissions:
contents: write
@ -11,18 +16,26 @@ permissions:
id-token: write
attestations: write
env:
REGISTRY: docker.io
IMAGE_NAME: lancommander/lancommander
jobs:
# --------------------------------------------------------------------------
# 1) PREP JOB: figure out the latest semver, build nightly version,
# check if there are commits since the last nightly tag. If none,
# skip the rest of the workflow.
# --------------------------------------------------------------------------
prep:
runs-on: ubuntu-latest
outputs:
version_tag: ${{ steps.trim_tag_ref.outputs.replaced }}
version_semver: ${{ steps.trim_tag_ref.outputs.replaced }}
is_prerelease: ${{ steps.check_prerelease.outputs.is_prerelease }}
build_dotnet_version: 9.0.102
steps:
- name: Check out code
uses: actions/checkout@v4
with:
# Ensure we get all tags so we can find the latest
fetch-depth: 0
- uses: frabert/replace-string-action@v2
name: Trim Tag Ref
id: trim_tag_ref
@ -31,9 +44,19 @@ jobs:
pattern: 'refs/tags/v'
replace-with: ''
- name: Check if pre-release
id: check_prerelease
run: |
VERSION="${{ steps.trim_tag_ref.outputs.replaced }}"
if [[ "$VERSION" == *-* ]]; then
echo "is_prerelease=true" >> $GITHUB_OUTPUT
else
echo "is_prerelease=false" >> $GITHUB_OUTPUT
fi
# Server
build_server_linux_arm64:
needs: prep
needs: [prep]
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -45,7 +68,7 @@ jobs:
build_configuration: Release
build_server_linux_x64:
needs: prep
needs: [prep]
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -57,7 +80,7 @@ jobs:
build_configuration: Release
build_server_osx_arm64:
needs: prep
needs: [prep]
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -69,7 +92,7 @@ jobs:
build_configuration: Release
build_server_osx_x64:
needs: prep
needs: [prep]
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -81,7 +104,7 @@ jobs:
build_configuration: Release
build_server_win_arm64:
needs: prep
needs: [prep]
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -93,7 +116,7 @@ jobs:
build_configuration: Release
build_server_win_x64:
needs: prep
needs: [prep]
uses: ./.github/workflows/LANCommander.Server.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -106,7 +129,7 @@ jobs:
# Launcher
build_launcher_linux_arm64:
needs: prep
needs: [prep]
uses: ./.github/workflows/LANCommander.Launcher.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -118,7 +141,7 @@ jobs:
build_configuration: Release
build_launcher_linux_x64:
needs: prep
needs: [prep]
uses: ./.github/workflows/LANCommander.Launcher.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -130,7 +153,7 @@ jobs:
build_configuration: Release
build_launcher_osx_arm64:
needs: prep
needs: [prep]
uses: ./.github/workflows/LANCommander.Launcher.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -142,7 +165,7 @@ jobs:
build_configuration: Release
build_launcher_osx_x64:
needs: prep
needs: [prep]
uses: ./.github/workflows/LANCommander.Launcher.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -154,7 +177,7 @@ jobs:
build_configuration: Release
build_launcher_win_arm64:
needs: prep
needs: [prep]
uses: ./.github/workflows/LANCommander.Launcher.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -166,7 +189,7 @@ jobs:
build_configuration: Release
build_launcher_win_x64:
needs: prep
needs: [prep]
uses: ./.github/workflows/LANCommander.Launcher.yml
with:
build_dotnet_version: ${{ needs.prep.outputs.build_dotnet_version }}
@ -177,6 +200,16 @@ jobs:
build_platform: Windows
build_configuration: Release
# Packager (Windows x86 only)
build_packager:
needs: [prep]
uses: ./.github/workflows/LANCommander.Packager.yml
with:
build_dotnet_version: '10.0.x'
version_semver: ${{ needs.prep.outputs.version_semver }}
version_tag: ${{ needs.prep.outputs.version_tag }}
build_configuration: Release
build_release:
runs-on: ubuntu-latest
needs:
@ -193,6 +226,7 @@ jobs:
- build_launcher_osx_x64
- build_launcher_win_arm64
- build_launcher_win_x64
- build_packager
steps:
- name: Create Temp Directory
@ -234,6 +268,7 @@ jobs:
name: LANCommander.Server-Windows-x64-v${{ needs.prep.outputs.version_tag }}.zip
path: artifacts
# Launcher artifacts
- name: Download Launcher Linux ARM64
uses: actions/download-artifact@v4
with:
@ -270,6 +305,24 @@ jobs:
name: LANCommander.Launcher-Windows-x64-v${{ needs.prep.outputs.version_tag }}.zip
path: artifacts
- name: Download Launcher Linux ARM64 AppImage
uses: actions/download-artifact@v4
with:
name: LANCommander.Launcher-Linux-arm64-v${{ needs.prep.outputs.version_tag }}.AppImage
path: artifacts
- name: Download Launcher Linux x64 AppImage
uses: actions/download-artifact@v4
with:
name: LANCommander.Launcher-Linux-x64-v${{ needs.prep.outputs.version_tag }}.AppImage
path: artifacts
- name: Download Packager Windows x86
uses: actions/download-artifact@v4
with:
name: LANCommander.Packager-Windows-x86-v${{ needs.prep.outputs.version_tag }}.zip
path: artifacts
- name: Debug - List Artifact Files
run: |
echo "Contents of ./artifacts:"
@ -281,6 +334,7 @@ jobs:
name: v${{ needs.prep.outputs.version_tag }}
generate_release_notes: true
draft: true
prerelease: ${{ needs.prep.outputs.is_prerelease == 'true' }}
files: |
artifacts/LANCommander.Server-Windows-arm64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Server-Windows-x64-v${{ needs.prep.outputs.version_tag }}.zip
@ -288,12 +342,15 @@ jobs:
artifacts/LANCommander.Server-Linux-arm64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Server-macOS-arm64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Server-macOS-x64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-Windows-arm64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-Windows-x64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-Linux-x64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-Linux-arm64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-Linux-x64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-macOS-arm64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-macOS-x64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-Windows-arm64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-Windows-x64-v${{ needs.prep.outputs.version_tag }}.zip
artifacts/LANCommander.Launcher-Linux-arm64-v${{ needs.prep.outputs.version_tag }}.AppImage
artifacts/LANCommander.Launcher-Linux-x64-v${{ needs.prep.outputs.version_tag }}.AppImage
artifacts/LANCommander.Packager-Windows-x86-v${{ needs.prep.outputs.version_tag }}.zip
- name: Checkout Repo for Docker build
uses: actions/checkout@v4
@ -325,18 +382,35 @@ jobs:
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}
# - name: Download Server arm64 Artifacts
# uses: actions/download-artifact@v4
# with:
# name: LANCommander.Server-Linux-arm64-v${{ needs.prep.outputs.version_tag }}.zip
# path: ./published
- name: Download Server x64 Artifacts
uses: actions/download-artifact@v4
with:
name: LANCommander.Server-Linux-x64-v${{ needs.prep.outputs.version_tag }}.zip
path: ./
- name: Download Server arm64 Artifacts
uses: actions/download-artifact@v4
with:
name: LANCommander.Server-Linux-arm64-v${{ needs.prep.outputs.version_tag }}.zip
path: ./
- name: Extract Server Artifacts x64
run: |
mkdir -p ./LANCommander.Server/published-amd64
unzip ./LANCommander.Server-Linux-x64-v${{ needs.prep.outputs.version_tag }}.zip -d ./LANCommander.Server/published-amd64
- name: Extract Server Artifacts arm64
run: |
mkdir -p ./LANCommander.Server/published-arm64
unzip ./LANCommander.Server-Linux-arm64-v${{ needs.prep.outputs.version_tag }}.zip -d ./LANCommander.Server/published-arm64
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
# - name: Set up QEMU
# uses: docker/setup-qemu-action@v2
- name: Setup buildx
uses: docker/setup-buildx-action@v3
with:
platforms: linux/amd64
platforms: linux/amd64,linux/arm64
- name: Build and push Docker image
id: push
@ -347,8 +421,8 @@ jobs:
push: true
platforms: linux/amd64
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}:latest
${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}:v${{ needs.prep.outputs.version_tag }}
${{ needs.prep.outputs.is_prerelease == 'true' && format('{0}/{1}:prerelease', env.REGISTRY, env.IMAGE_NAME) || format('{0}/{1}:latest', env.REGISTRY, env.IMAGE_NAME) }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

View file

@ -1,9 +1,8 @@
name: LANCommander SDK Release
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
release:
types: [published]
permissions:
contents: write
@ -13,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
version_tag: ${{ steps.trim_tag_ref.outputs.replaced }}
version_semver: ${{ steps.trim_tag_ref.outputs.replaced }}
version_semver: ${{ steps.extract_semver.outputs.replaced }}
steps:
- uses: frabert/replace-string-action@v2
name: Trim Tag Ref
@ -23,6 +22,14 @@ jobs:
pattern: 'refs/tags/v'
replace-with: ''
- uses: frabert/replace-string-action@v2
name: Extract SemVer (strip prerelease suffix for AssemblyVersion)
id: extract_semver
with:
string: '${{ steps.trim_tag_ref.outputs.replaced }}'
pattern: '-.*$'
replace-with: ''
publish:
needs: prep
runs-on: ubuntu-latest

View file

@ -45,7 +45,7 @@ env:
jobs:
build:
runs-on: ubuntu-latest
runs-on: ${{ inputs.build_platform == 'Linux' && inputs.build_arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -85,6 +85,9 @@ jobs:
npm install --prefix ./LANCommander.UI
npm install --prefix ./LANCommander.Server
- name: Generate PowerShell Completions
run: dotnet run --project ./LANCommander.CompletionGenerator/LANCommander.CompletionGenerator.csproj -- ./LANCommander.UI/Components/MonacoCodeEditor/PowerShellCompletions.g.ts
- name: Package Frontend
run: |
npm run package --prefix ./LANCommander.UI
@ -93,13 +96,47 @@ jobs:
# .NET builds
- name: Publish Updater and Server
run: |
dotnet publish "./LANCommander.AutoUpdater/LANCommander.AutoUpdater.csproj" -c ${{ inputs.build_configuration }} --self-contained --runtime ${{ inputs.build_runtime }} -p:Version="${{ inputs.version_tag }}" -p:AssemblyVersion="${{ inputs.version_semver }}"
dotnet publish "./LANCommander.Server/LANCommander.Server.csproj" -c ${{ inputs.build_configuration }} --self-contained --runtime ${{ inputs.build_runtime }} -p:Version="${{ inputs.version_tag }}" -p:AssemblyVersion="${{ inputs.version_semver }}"
# Strip leading 'v' if present
RAW_VERSION="${{ inputs.version_tag }}" # e.g. v2.0.0-rc1
SEMVER="${RAW_VERSION#v}" # 2.0.0-rc1
# Numeric part only for Assembly/FileVersion
NUMERIC="${SEMVER%%-*}" # 2.0.0
ASSEMBLY_VERSION="${NUMERIC}.0" # 2.0.0.0
echo "SEMVER=$SEMVER"
echo "ASSEMBLY_VERSION=$ASSEMBLY_VERSION"
dotnet publish "./LANCommander.AutoUpdater/LANCommander.AutoUpdater.csproj" \
-c "${{ inputs.build_configuration }}" \
--self-contained \
--runtime "${{ inputs.build_runtime }}" \
-p:Version="$SEMVER" \
-p:AssemblyVersion="$ASSEMBLY_VERSION" \
-p:FileVersion="$ASSEMBLY_VERSION" \
-p:InformationalVersion="$SEMVER"
# Disable NetBeauty on ARM64 runners — nbeauty2 only ships x64 native binaries
DISABLE_BEAUTY="False"
if [ "${{ inputs.build_arch }}" = "arm64" ] && [ "${{ inputs.build_platform }}" = "Linux" ]; then
DISABLE_BEAUTY="True"
fi
dotnet publish "./LANCommander.Server/LANCommander.Server.csproj" \
-c "${{ inputs.build_configuration }}" \
--self-contained \
--runtime "${{ inputs.build_runtime }}" \
-p:Version="$SEMVER" \
-p:AssemblyVersion="$ASSEMBLY_VERSION" \
-p:FileVersion="$ASSEMBLY_VERSION" \
-p:InformationalVersion="$SEMVER" \
-p:DisableBeauty="$DISABLE_BEAUTY"
- name: Bundle and Clean
shell: pwsh
run: |
Copy-Item -Force -Recurse -Verbose LANCommander.AutoUpdater/bin/${{ inputs.build_configuration }}/net9.0/${{ inputs.build_runtime }}/publish/* LANCommander.Server/bin/${{ inputs.build_configuration }}/net9.0/${{ inputs.build_runtime }}/publish/
Copy-Item -Force -Recurse -Verbose LANCommander.AutoUpdater/bin/${{ inputs.build_configuration }}/net10.0/${{ inputs.build_runtime }}/publish/* LANCommander.Server/bin/${{ inputs.build_configuration }}/net10.0/${{ inputs.build_runtime }}/publish/
# Remove unnecessary files in a single operation
$PathsToRemove = @(
@ -120,16 +157,78 @@ jobs:
'Libraries/locales'
)
$BasePath = "LANCommander.Server/bin/${{ inputs.build_configuration }}/net9.0/${{ inputs.build_runtime }}/publish"
$BasePath = "LANCommander.Server/bin/${{ inputs.build_configuration }}/net10.0/${{ inputs.build_runtime }}/publish"
foreach ($path in $PathsToRemove) {
Remove-Item -Recurse -Force -ErrorAction Continue "$BasePath/$path"
}
- name: Bundle macOS .app
if: inputs.build_platform == 'macOS'
shell: bash
run: |
set -euo pipefail
# Strip leading 'v' and reduce to numeric x.y.z for the bundle version keys.
RAW_VERSION="${{ inputs.version_tag }}"
SEMVER="${RAW_VERSION#v}"
NUMERIC="${SEMVER%%-*}"
APP_NAME="LANCommander Server"
EXECUTABLE="LANCommander.Server"
BUNDLE_ID="app.lancommander.server"
ICON_SRC="LANCommander.Server/icon.icns"
PUBLISH_DIR="LANCommander.Server/bin/${{ inputs.build_configuration }}/net10.0/${{ inputs.build_runtime }}/publish"
APP_DIR="${EXECUTABLE}.app"
# Lay out the bundle skeleton.
mkdir -p "$APP_DIR/Contents/MacOS" "$APP_DIR/Contents/Resources"
cp -a "$PUBLISH_DIR/." "$APP_DIR/Contents/MacOS/"
cp "$ICON_SRC" "$APP_DIR/Contents/Resources/AppIcon.icns"
# Write Info.plist.
cat > "$APP_DIR/Contents/Info.plist" <<PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>${APP_NAME}</string>
<key>CFBundleDisplayName</key>
<string>${APP_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${BUNDLE_ID}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE}</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleShortVersionString</key>
<string>${NUMERIC}</string>
<key>CFBundleVersion</key>
<string>${SEMVER}</string>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>
PLIST
# Ensure the entrypoint is executable, then zip while preserving the
# permission bits (Compress-Archive drops the exec bit).
chmod +x "$APP_DIR/Contents/MacOS/${EXECUTABLE}"
zip -ry "LANCommander.Server-${{ inputs.build_platform }}-${{ inputs.build_arch }}-v${{ inputs.version_tag }}.zip" "$APP_DIR"
- name: Compress Build Output
if: inputs.build_platform != 'macOS'
shell: pwsh
run: |
$compress = @{
Path = "LANCommander.Server/bin/${{ inputs.build_configuration }}/net9.0/${{ inputs.build_runtime }}/publish/*"
Path = "LANCommander.Server/bin/${{ inputs.build_configuration }}/net10.0/${{ inputs.build_runtime }}/publish/*"
DestinationPath = "LANCommander.Server-${{ inputs.build_platform }}-${{ inputs.build_arch }}-v${{ inputs.version_tag }}.zip"
CompressionLevel = "Fastest"
}

View file

@ -12,7 +12,7 @@ jobs:
arch: ['x64', 'arm64']
steps:
- uses: actions/checkout@v4
- name: Get version
id: get_version
shell: pwsh
@ -33,13 +33,19 @@ jobs:
# Install Inno Setup
- name: Install Inno Setup
run: |
curl -L -o innosetup.exe https://files.jrsoftware.org/is/6/innosetup-6.2.2.exe
curl -L -o innosetup.exe https://github.com/jrsoftware/issrc/releases/download/is-6_7_3/innosetup-6.7.3.exe
.\innosetup.exe /VERYSILENT /SUPPRESSMSGBOXES /NORESTART
shell: cmd
# Create Inno Setup script
- name: Create installer script
run: |
$appId = if ('${{ matrix.app }}' -eq 'Server') {
'2C58E237-1D69-42A0-B702-F995B75B8A5E'
} else {
'A3D4F8E1-7B2C-4E5D-9F1A-6C8B3D7E2F4A'
}
@"
#define MyAppName "LANCommander ${{ matrix.app }}"
#define MyAppVersion "${{ env.VERSION }}"
@ -49,7 +55,7 @@ jobs:
#define Architecture "${{ matrix.arch }}"
[Setup]
AppId={{$(New-Guid)}}
AppId={{$appId}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppPublisher={#MyAppPublisher}
@ -94,7 +100,6 @@ jobs:
strategy:
matrix:
app: ['Server', 'Launcher']
arch: ['x64', 'arm64']
steps:
- name: Get version
shell: pwsh
@ -103,8 +108,14 @@ jobs:
$version = $tag.TrimStart('v')
echo "VERSION=$version" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Install wingetcreate
shell: pwsh
run: |
Invoke-WebRequest -Uri "https://aka.ms/wingetcreate/latest" -OutFile wingetcreate.exe
- name: Submit package to Windows Package Manager Community Repository
run: |
$installerUrl = "https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/LANCommander.${{ matrix.app }}-${env:VERSION}-${{ matrix.arch }}-Setup.exe"
wingetcreate submit --token ${{ secrets.GITHUB_TOKEN }} --urls "$installerUrl" --version ${env:VERSION} LANCommander.LANCommander.${{ matrix.app }}.${{ matrix.arch }}
shell: pwsh
$x64Url = "https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/LANCommander.${{ matrix.app }}-${env:VERSION}-x64-Setup.exe"
$arm64Url = "https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/LANCommander.${{ matrix.app }}-${env:VERSION}-arm64-Setup.exe"
.\wingetcreate.exe update --submit --token "${{ secrets.WINGET_TOKEN }}" --urls $x64Url $arm64Url --version ${env:VERSION} LANCommander.${{ matrix.app }}
shell: pwsh

20
.gitignore vendored
View file

@ -17,10 +17,6 @@
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
@ -370,3 +366,19 @@ LANCommander.Server/wwwroot/css/app.css*
LANCommander.Launcher/wwwroot/css/app.css*
LANCommander.Launcher/wwwroot/css/main.js
LANCommander.Server/wwwroot/css/main.js
.aspire
LANCommander.Server/config/
LANCommander.UI/wwwroot/
LANCommander.UI/Components/MonacoCodeEditor/PowerShellCompletions.g.ts
LANCommander.Server/Data/
LANCommander.Launcher.Avalonia/Data/
Data/
# Visual test output — screenshots & diffs are CI artifacts, not committed.
# Baselines/ is committed; everything else is transient.
LANCommander.Launcher.Avalonia.Tests/Screenshots/
LANCommander.Launcher.Avalonia.Tests/Diffs/
visual-test-output/

94
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,94 @@
# Contributing to LANCommander
Thanks for your interest in contributing to LANCommander! This project is primarily developed by a single developer, so community contributions are greatly appreciated.
## Getting Started
### Prerequisites
- [.NET 10.0 SDK](https://dotnet.microsoft.com/download/dotnet/10.0)
- [Node.js](https://nodejs.org/) (for the server UI's TypeScript/SCSS assets)
- A code editor such as [Visual Studio](https://visualstudio.microsoft.com/), [Rider](https://www.jetbrains.com/rider/), or [VS Code](https://code.visualstudio.com/)
### Building the Project
1. Clone the repository:
```bash
git clone https://github.com/LANCommander/LANCommander.git
cd LANCommander
```
2. Restore dependencies:
```bash
dotnet restore
```
3. Build the server:
```bash
dotnet build LANCommander.Server
```
4. Build the launcher:
```bash
dotnet build LANCommander.Launcher
```
### Running Locally
To run the server in development mode:
```bash
dotnet run --project LANCommander.Server
```
The server will be available at `http://localhost:1337` by default.
## How to Contribute
### Reporting Bugs
Use the [GitHub Issues](https://github.com/LANCommander/LANCommander/issues) page with the bug report template. Include:
- Steps to reproduce the issue
- Expected vs. actual behavior
- Your OS and LANCommander version
- Relevant logs or screenshots
### Submitting Changes
1. Fork the repository
2. Create a feature branch from `main` (`git checkout -b my-feature`)
3. Make your changes
4. Test your changes locally
5. Commit with a clear, descriptive message
6. Push to your fork and open a Pull Request
### What to Work On
- Check [open issues](https://github.com/LANCommander/LANCommander/issues) for bugs or feature requests
- Documentation improvements are always welcome at our [documentation site](https://docs.lancommander.app/)
- Game packaging scripts and guides for the community
### Code Guidelines
- Follow existing code style and conventions in the project
- Keep PRs focused, one feature or fix per PR when possible
- Include screenshots in your PR if you're changing UI
## Project Structure
| Directory | Description |
|-----------|-------------|
| `LANCommander.Server` | ASP.NET Blazor web application (server/admin) |
| `LANCommander.Launcher` | Avalonia desktop client (launcher) |
| `LANCommander.Packager` | Game packaging tool |
| `LANCommander.SDK` | .NET SDK for building custom clients |
| `LANCommander.Server.Data` | Entity Framework data models and migrations |
| `LANCommander.Server.Services` | Server business logic |
| `LANCommander.Documentation` | Docusaurus documentation site |
## Community
- [Discord](https://discord.gg/vDEEWVt8EM): Best place for discussion, help, and sharing game packages
## License
By contributing to LANCommander, you agree that your contributions will be licensed under the [MIT License](LICENSE).

View file

@ -4,7 +4,17 @@
</PropertyGroup>
<ItemGroup Label="Aspire">
<PackageVersion Include="Aspire.Hosting.AppHost" Version="9.5.0" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.9" />
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.1.1" />
<PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" />
<PackageVersion Include="EasyMDE.Blazor" Version="1.0.5" />
<PackageVersion Include="HotAvalonia" Version="3.1.0" />
<PackageVersion Include="LiveChartsCore" Version="2.0.0-rc5.4" />
<PackageVersion Include="LiveChartsCore.SkiaSharpView" Version="2.0.0-rc5.4" />
<PackageVersion Include="LiveChartsCore.SkiaSharpView.Avalonia" Version="2.0.0-rc5.4" />
<PackageVersion Include="Markdig" Version="0.44.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Configuration" Version="9.0.8" />
<PackageVersion Include="Notify.NET" Version="1.1.0" />
<PackageVersion Include="Svrooij.PowerShell.DI" Version="1.3.4" />
</ItemGroup>
<ItemGroup Label="AutoMapper">
<PackageVersion Include="AutoMapper" Version="14.0.0" />
@ -14,7 +24,7 @@
<PackageVersion Include="AntDesign.Charts" Version="0.8.0" />
</ItemGroup>
<ItemGroup Label="Blazor">
<PackageVersion Include="BlazorMonaco" Version="3.3.0" />
<PackageVersion Include="BlazorMonaco" Version="3.4.0" />
<PackageVersion Include="BootstrapBlazor.PdfReader" Version="9.0.1" />
<PackageVersion Include="PSC.Blazor.Components.MarkdownEditor" Version="8.0.8" />
<PackageVersion Include="Razor.Templating.Core" Version="2.1.0" />
@ -27,8 +37,8 @@
<PackageVersion Include="HtmlAgilityPack" Version="1.11.72" />
<PackageVersion Include="Crc32.NET" Version="1.2.0" />
<PackageVersion Include="Emzi0767.NtfsDataStreams" Version="1.0.0" />
<PackageVersion Include="MadMilkman.Ini" Version="1.0.6" />
<PackageVersion Include="PeanutButter.INI" Version="3.0.396" />
<PackageVersion Include="Superpower" Version="3.1.0" />
<PackageVersion Include="RegParserDotNet" Version="1.1.1" />
<PackageVersion Include="nulastudio.NetBeauty" Version="2.1.4.6" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
@ -59,7 +69,9 @@
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.12.0" />
</ItemGroup>
<ItemGroup Label="Testing">
<PackageVersion Include="bunit" Version="1.40.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="Microsoft.Playwright" Version="1.51.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
<PackageVersion Include="Microsoft.TypeScript.MSBuild" Version="5.7.1" />
@ -75,28 +87,21 @@
</ItemGroup>
<ItemGroup Label="External">
<PackageVersion Include="CoreRCON" Version="5.0.5" />
<PackageVersion Include="LANCommander.HQ.SDK" Version="1.0.1" />
<PackageVersion Include="craftersmine.SteamGridDB.Net" Version="1.1.7" />
<PackageVersion Include="YoutubeExplode" Version="6.4.3" />
<PackageVersion Include="Docker.DotNet.Enhanced" Version="3.129.0" />
<PackageVersion Include="Facepunch.Steamworks" Version="2.3.3" />
<PackageVersion Include="IGDB" Version="6.1.0" />
<PackageVersion Include="IPXRelayDotNet" Version="1.1.3" />
<PackageVersion Include="LANCommander.Interposer" Version="1.0.7" />
<PackageVersion Include="Octokit" Version="14.0.0" />
<PackageVersion Include="RestSharp" Version="112.1.0" />
<PackageVersion Include="rix0rrr.BeaconLib" Version="1.0.2" />
<PackageVersion Include="Semver" Version="3.0.0" />
<PackageVersion Include="SharpCompress" Version="0.40.0" />
<PackageVersion Include="SharpCompress" Version="0.50.0" />
<PackageVersion Include="SteamWebAPI2" Version="4.4.1" />
</ItemGroup>
<ItemGroup Label="Logging">
<PackageVersion Include="Elastic.Serilog.Sinks" Version="8.12.3" />
<PackageVersion Include="Serilog" Version="4.3.0" />
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="9.0.0" />
<PackageVersion Include="Serilog.Sinks.AspNetCore.App.SignalR" Version="1.2.2" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.Seq" Version="9.0.0" />
</ItemGroup>
<ItemGroup Label="Entity Framework Core">
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.9" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.9" />
@ -119,13 +124,20 @@
<ItemGroup Label="Microsoft Extensions">
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.9" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.9" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.1" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.9" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.1" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.1" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="9.7.0" />
<PackageVersion Include="Microsoft.Extensions.Localization" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.1" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.9" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.1" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.9" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.9" />
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="9.4.0" />
</ItemGroup>
<ItemGroup Label="OpenTelemetry">
@ -135,7 +147,28 @@
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.12.0" />
</ItemGroup>
<ItemGroup Label="LibVLC">
<PackageVersion Include="LibVLCSharp" Version="3.8.5" />
<PackageVersion Include="LibVLCSharp.Avalonia" Version="3.8.5" />
<PackageVersion Include="VideoLAN.LibVLC.Windows" Version="3.0.23.1" />
<PackageVersion Include="VideoLAN.LibVLC.Mac" Version="3.1.3.1" />
</ItemGroup>
<ItemGroup Label="SDL">
<PackageVersion Include="ppy.SDL3-CS" Version="2026.520.0" />
</ItemGroup>
<ItemGroup Label="Photino">
<PackageVersion Include="AsyncImageLoader.Avalonia" Version="3.3.0" />
<PackageVersion Include="Avalonia" Version="11.2.3" />
<PackageVersion Include="Avalonia.Desktop" Version="11.2.3" />
<PackageVersion Include="Avalonia.Headless" Version="11.2.3" />
<PackageVersion Include="Avalonia.Headless.XUnit" Version="11.2.3" />
<PackageVersion Include="Avalonia.Themes.Fluent" Version="11.2.3" />
<PackageVersion Include="Avalonia.Fonts.Inter" Version="11.2.3" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.3" />
<PackageVersion Include="Avalonia.Controls.ItemsRepeater" Version="11.1.5" />
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.2.0.2" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageVersion Include="PDFtoImage" Version="4.1.1" />
<PackageVersion Include="Photino.Blazor" Version="4.0.13" />
<PackageVersion Include="Photino.Blazor.CustomWindow" Version="1.3.1" />
<PackageVersion Include="Photino.NET" Version="4.0.16" />
@ -143,12 +176,16 @@
<ItemGroup Label="System">
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.1" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.3.1" />
<PackageVersion Include="System.Linq.Async" Version="6.0.1" />
<PackageVersion Include="System.Management.Automation" Version="7.4.7" />
<PackageVersion Include="System.Text.Encodings.Web" Version="9.0.0" />
<PackageVersion Include="System.Text.Json" Version="9.0.1" />
</ItemGroup>
<ItemGroup Label="Caching">
<PackageVersion Include="ZiggyCreatures.FusionCache" Version="2.1.0" />
</ItemGroup>
<ItemGroup Label="Overlay">
<PackageVersion Include="Avalonia.Skia" Version="11.2.3" />
<PackageVersion Include="Reloaded.Hooks" Version="4.3.0" />
<PackageVersion Include="DNNE" Version="2.1.0" />
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
</ItemGroup>
</Project>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 647 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

View file

@ -4,7 +4,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>0899617b-b319-465d-aaaf-aac7ea333029</UserSecretsId>

View file

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishSingleFile>true</PublishSingleFile>

View file

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LANCommander.SDK\LANCommander.SDK.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,512 @@
using System.Management.Automation;
using System.Reflection;
using System.Text;
using System.Text.Json;
using LANCommander.SDK.PowerShell.Extensions;
if (args.Length == 0)
{
Console.Error.WriteLine("Usage: LANCommander.CompletionGenerator <output-path>");
return 1;
}
var outputPath = args[0];
var assembly = typeof(InitialSessionStateExtensions).Assembly;
var cmdletTypes = assembly.GetTypes()
.Where(t => t.GetCustomAttribute<CmdletAttribute>() != null)
.OrderBy(t => t.GetCustomAttribute<CmdletAttribute>()!.VerbName + "-" + t.GetCustomAttribute<CmdletAttribute>()!.NounName);
var sb = new StringBuilder();
sb.AppendLine("// Auto-generated by LANCommander.CompletionGenerator — do not edit manually");
sb.AppendLine();
sb.AppendLine("export interface CmdletParameter {");
sb.AppendLine(" name: string;");
sb.AppendLine(" type: string;");
sb.AppendLine(" mandatory: boolean;");
sb.AppendLine(" position: number | null;");
sb.AppendLine(" helpMessage: string | null;");
sb.AppendLine(" aliases: string[];");
sb.AppendLine("}");
sb.AppendLine();
sb.AppendLine("export interface CmdletDefinition {");
sb.AppendLine(" name: string;");
sb.AppendLine(" description: string | null;");
sb.AppendLine(" outputType: string | null;");
sb.AppendLine(" parameters: CmdletParameter[];");
sb.AppendLine("}");
sb.AppendLine();
sb.AppendLine("export const cmdlets: CmdletDefinition[] = [");
foreach (var type in cmdletTypes)
{
var cmdletAttr = type.GetCustomAttribute<CmdletAttribute>()!;
var name = $"{cmdletAttr.VerbName}-{cmdletAttr.NounName}";
var outputTypeAttr = type.GetCustomAttribute<OutputTypeAttribute>();
string? outputType = null;
if (outputTypeAttr?.Type?.Length > 0)
outputType = GetFriendlyTypeName(outputTypeAttr.Type[0].Type);
// Use XML doc summary if available via the Summary property pattern, otherwise use null
var descriptionLines = type.GetCustomAttributes()
.Where(a => a.GetType().Name == "DescriptionAttribute")
.Select(a => a.GetType().GetProperty("Description")?.GetValue(a)?.ToString())
.FirstOrDefault();
// Fall back to XML summary comment — not available via reflection, use class summary convention
string? description = descriptionLines;
sb.AppendLine(" {");
sb.AppendLine($" name: {JsonEncode(name)},");
sb.AppendLine($" description: {JsonEncode(description)},");
sb.AppendLine($" outputType: {JsonEncode(outputType)},");
sb.AppendLine(" parameters: [");
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.GetCustomAttribute<ParameterAttribute>() != null)
.OrderBy(p =>
{
var pa = p.GetCustomAttribute<ParameterAttribute>()!;
return pa.Position == int.MinValue ? int.MaxValue : pa.Position;
})
.ThenBy(p => p.Name);
foreach (var prop in properties)
{
var paramAttr = prop.GetCustomAttribute<ParameterAttribute>()!;
var aliasAttr = prop.GetCustomAttribute<AliasAttribute>();
var aliases = aliasAttr?.AliasNames?.ToArray() ?? Array.Empty<string>();
var position = paramAttr.Position == int.MinValue ? (int?)null : paramAttr.Position;
sb.AppendLine(" {");
sb.AppendLine($" name: {JsonEncode(prop.Name)},");
sb.AppendLine($" type: {JsonEncode(GetFriendlyTypeName(prop.PropertyType))},");
sb.AppendLine($" mandatory: {(paramAttr.Mandatory ? "true" : "false")},");
sb.AppendLine($" position: {(position.HasValue ? position.Value.ToString() : "null")},");
sb.AppendLine($" helpMessage: {JsonEncode(paramAttr.HelpMessage)},");
sb.AppendLine($" aliases: [{string.Join(", ", aliases.Select(a => JsonEncode(a)))}],");
sb.AppendLine(" },");
}
sb.AppendLine(" ],");
sb.AppendLine(" },");
}
sb.AppendLine("];");
sb.AppendLine();
// Generate built-in PowerShell cmdlet completions from core modules
var builtinCmdletNames = new HashSet<string>(cmdletTypes.Select(t =>
{
var a = t.GetCustomAttribute<CmdletAttribute>()!;
return $"{a.VerbName}-{a.NounName}";
}));
Console.WriteLine("Enumerating built-in PowerShell cmdlets...");
sb.AppendLine("export const builtinCmdlets: CmdletDefinition[] = [");
var builtinCount = 0;
try
{
// Use external pwsh process for full module metadata and help access
var psScript = @"
$commonParams = @(
'Verbose','Debug','ErrorAction','WarningAction','InformationAction',
'ErrorVariable','WarningVariable','InformationVariable','OutVariable',
'OutBuffer','PipelineVariable','ProgressAction','Confirm','WhatIf'
)
$modules = @(
'Microsoft.PowerShell.Management',
'Microsoft.PowerShell.Utility',
'Microsoft.PowerShell.Security',
'Microsoft.PowerShell.Archive'
)
$results = @()
# Get command names from core modules, then resolve each individually for full metadata
$commandNames = Get-Command -CommandType Cmdlet,Function -Module $modules -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name -Unique
foreach ($cmdName in $commandNames) {
$cmd = Get-Command $cmdName -ErrorAction SilentlyContinue
if (-not $cmd) { continue }
$synopsis = $null
try {
$h = Get-Help $cmdName -ErrorAction SilentlyContinue
if ($h -and $h.Synopsis) {
$s = $h.Synopsis.Trim()
# Skip if synopsis is just the cmdlet name, or looks like syntax (contains parameter notation)
if ($s -ne $cmdName -and $s -notmatch '\[[-<]' -and $s -notmatch '-\w+\s+<') {
$synopsis = $s
}
}
} catch {}
$outputType = $null
if ($cmd.OutputType -and $cmd.OutputType.Count -gt 0 -and $cmd.OutputType[0].Type) {
$outputType = $cmd.OutputType[0].Type.Name
}
$params = @()
if ($cmd.Parameters) {
foreach ($key in $cmd.Parameters.Keys) {
if ($key -in $commonParams) { continue }
$p = $cmd.Parameters[$key]
$pAttr = $p.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] } | Select-Object -First 1
$mandatory = $false
$position = $null
$helpMsg = $null
if ($pAttr) {
$mandatory = [bool]$pAttr.Mandatory
if ($pAttr.Position -ne [int]::MinValue) {
$position = $pAttr.Position
}
if ($pAttr.HelpMessage) {
$helpMsg = $pAttr.HelpMessage
}
}
$aliases = @($p.Aliases)
$params += @{
n = $key
t = $p.ParameterType.Name
m = $mandatory
pos = $position
h = $helpMsg
a = $aliases
}
}
}
$results += @{
name = $cmdName
desc = $synopsis
out = $outputType
params = $params
}
}
$results | ConvertTo-Json -Depth 4 -Compress
";
// Write the script to a temp file to avoid stdin encoding issues
var tempScript = Path.GetTempFileName() + ".ps1";
File.WriteAllText(tempScript, psScript, new UTF8Encoding(false));
var psi = new System.Diagnostics.ProcessStartInfo
{
FileName = "pwsh",
Arguments = $"-NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"{tempScript}\"",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
};
// Clear .NET SDK environment variables that can interfere with pwsh's module loading
foreach (var key in Environment.GetEnvironmentVariables().Keys.Cast<string>()
.Where(k => k.StartsWith("DOTNET_", StringComparison.OrdinalIgnoreCase) ||
k.StartsWith("MSBuild", StringComparison.OrdinalIgnoreCase)))
{
psi.Environment[key] = null;
}
using var process = System.Diagnostics.Process.Start(psi)!;
var json = process.StandardOutput.ReadToEnd();
var stderr = process.StandardError.ReadToEnd();
process.WaitForExit(120_000);
if (!string.IsNullOrWhiteSpace(stderr))
Console.Error.WriteLine($" pwsh stderr: {stderr.Trim()}");
try { File.Delete(tempScript); } catch { }
if (process.ExitCode != 0)
throw new Exception($"pwsh exited with code {process.ExitCode}");
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
// Handle both array and single-object responses
var elements = root.ValueKind == JsonValueKind.Array
? root.EnumerateArray().ToList()
: new List<JsonElement> { root };
foreach (var cmd in elements.OrderBy(e => e.GetProperty("name").GetString()))
{
var name = cmd.GetProperty("name").GetString()!;
if (builtinCmdletNames.Contains(name))
continue;
string? description = null;
if (cmd.TryGetProperty("desc", out var descEl) && descEl.ValueKind == JsonValueKind.String)
description = descEl.GetString();
string? outputTypeName = null;
if (cmd.TryGetProperty("out", out var outEl) && outEl.ValueKind == JsonValueKind.String)
outputTypeName = MapTypeName(outEl.GetString()!);
sb.AppendLine(" {");
sb.AppendLine($" name: {JsonEncode(name)},");
sb.AppendLine($" description: {JsonEncode(description)},");
sb.AppendLine($" outputType: {JsonEncode(outputTypeName)},");
sb.AppendLine(" parameters: [");
if (cmd.TryGetProperty("params", out var paramsEl) && paramsEl.ValueKind == JsonValueKind.Array)
{
// Sort: positional first, then alphabetical
var paramList = paramsEl.EnumerateArray().ToList();
paramList.Sort((a, b) =>
{
var posA = a.TryGetProperty("pos", out var pa) && pa.ValueKind == JsonValueKind.Number ? pa.GetInt32() : int.MaxValue;
var posB = b.TryGetProperty("pos", out var pb) && pb.ValueKind == JsonValueKind.Number ? pb.GetInt32() : int.MaxValue;
var cmp = posA.CompareTo(posB);
if (cmp != 0) return cmp;
return string.Compare(
a.GetProperty("n").GetString(),
b.GetProperty("n").GetString(),
StringComparison.Ordinal);
});
foreach (var param in paramList)
{
var paramName = param.GetProperty("n").GetString()!;
var typeName = MapTypeName(param.GetProperty("t").GetString() ?? "object");
var mandatory = param.TryGetProperty("m", out var mEl) && mEl.ValueKind == JsonValueKind.True;
string? posStr = "null";
if (param.TryGetProperty("pos", out var posEl) && posEl.ValueKind == JsonValueKind.Number)
posStr = posEl.GetInt32().ToString();
string? helpMessage = null;
if (param.TryGetProperty("h", out var hEl) && hEl.ValueKind == JsonValueKind.String)
helpMessage = hEl.GetString();
var aliases = new List<string>();
if (param.TryGetProperty("a", out var aEl) && aEl.ValueKind == JsonValueKind.Array)
{
foreach (var alias in aEl.EnumerateArray())
{
if (alias.ValueKind == JsonValueKind.String)
aliases.Add(alias.GetString()!);
}
}
sb.AppendLine(" {");
sb.AppendLine($" name: {JsonEncode(paramName)},");
sb.AppendLine($" type: {JsonEncode(typeName)},");
sb.AppendLine($" mandatory: {(mandatory ? "true" : "false")},");
sb.AppendLine($" position: {posStr},");
sb.AppendLine($" helpMessage: {JsonEncode(helpMessage)},");
sb.AppendLine($" aliases: [{string.Join(", ", aliases.Select(a => JsonEncode(a)))}],");
sb.AppendLine(" },");
}
}
sb.AppendLine(" ],");
sb.AppendLine(" },");
builtinCount++;
}
}
catch (Exception ex)
{
Console.Error.WriteLine($"Warning: Failed to enumerate built-in cmdlets: {ex.Message}");
Console.Error.WriteLine("Ensure 'pwsh' (PowerShell 7+) is installed and on PATH.");
}
sb.AppendLine("];");
sb.AppendLine();
// Generate type definitions for complex objects used as script variables
var variableTypes = new Dictionary<string, Type>
{
["GameManifest"] = typeof(LANCommander.SDK.Models.Manifest.Game),
["ToolManifest"] = typeof(LANCommander.SDK.Models.Manifest.Tool),
["RedistributableManifest"] = typeof(LANCommander.SDK.Models.Manifest.Redistributable),
["Server"] = typeof(LANCommander.SDK.Models.Server),
["Game"] = typeof(LANCommander.SDK.Models.Game),
["User"] = typeof(LANCommander.SDK.Models.User),
["Tool"] = typeof(LANCommander.SDK.Models.Tool),
["Redistributable"] = typeof(LANCommander.SDK.Models.Redistributable),
};
sb.AppendLine("export interface TypeProperty {");
sb.AppendLine(" name: string;");
sb.AppendLine(" type: string;");
sb.AppendLine("}");
sb.AppendLine();
sb.AppendLine("export interface TypeDefinition {");
sb.AppendLine(" name: string;");
sb.AppendLine(" properties: TypeProperty[];");
sb.AppendLine("}");
sb.AppendLine();
sb.AppendLine("export const variableTypes: TypeDefinition[] = [");
foreach (var (typeName, clrType) in variableTypes.OrderBy(kv => kv.Key))
{
sb.AppendLine(" {");
sb.AppendLine($" name: {JsonEncode(typeName)},");
sb.AppendLine(" properties: [");
var props = clrType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.CanRead)
.OrderBy(p => p.Name);
foreach (var prop in props)
{
sb.AppendLine(" {");
sb.AppendLine($" name: {JsonEncode(prop.Name)},");
sb.AppendLine($" type: {JsonEncode(GetPropertyTypeName(prop.PropertyType))},");
sb.AppendLine(" },");
}
sb.AppendLine(" ],");
sb.AppendLine(" },");
}
sb.AppendLine("];");
sb.AppendLine();
// Generate ScriptType enum values
sb.AppendLine("export const scriptTypeValues: string[] = [");
foreach (var value in Enum.GetNames<LANCommander.SDK.Enums.ScriptType>())
{
sb.AppendLine($" {JsonEncode(value)},");
}
sb.AppendLine("];");
var directory = Path.GetDirectoryName(outputPath);
if (!string.IsNullOrEmpty(directory))
Directory.CreateDirectory(directory);
File.WriteAllText(outputPath, sb.ToString(), Encoding.UTF8);
Console.WriteLine($"Generated completions for {cmdletTypes.Count()} LANCommander cmdlets, {builtinCount} built-in cmdlets, and {variableTypes.Count} variable types -> {outputPath}");
return 0;
static string JsonEncode(string? value)
{
if (value == null) return "null";
return JsonSerializer.Serialize(value);
}
static string GetPropertyTypeName(Type type)
{
var underlying = Nullable.GetUnderlyingType(type);
if (underlying != null)
return GetPropertyTypeName(underlying) + "?";
if (type.IsArray)
return GetPropertyTypeName(type.GetElementType()!) + "[]";
if (type.IsGenericType)
{
var genericDef = type.GetGenericTypeDefinition();
if (genericDef == typeof(IEnumerable<>) || genericDef == typeof(ICollection<>) ||
genericDef == typeof(List<>) || genericDef == typeof(IList<>))
{
var elementType = type.GetGenericArguments()[0];
return GetPropertyTypeName(elementType) + "[]";
}
}
// Check non-generic IEnumerable
if (type != typeof(string) && type.GetInterfaces().Any(i =>
i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
{
var elementType = type.GetInterfaces()
.First(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.GetGenericArguments()[0];
return GetPropertyTypeName(elementType) + "[]";
}
if (type == typeof(string)) return "string";
if (type == typeof(int) || type == typeof(long)) return "int";
if (type == typeof(uint)) return "uint";
if (type == typeof(double) || type == typeof(float)) return "double";
if (type == typeof(bool)) return "bool";
if (type == typeof(Guid)) return "Guid";
if (type == typeof(DateTime)) return "DateTime";
if (type == typeof(Uri)) return "Uri";
if (type.IsEnum) return type.Name;
return type.Name;
}
static string MapTypeName(string typeName) => typeName switch
{
"String" => "string",
"Int32" => "int",
"Int64" => "long",
"UInt32" => "uint",
"Double" => "double",
"Single" => "float",
"Boolean" => "bool",
"Byte" => "byte",
"Guid" => "Guid",
"Uri" => "Uri",
"DateTime" => "DateTime",
"Object" => "object",
"SwitchParameter" => "SwitchParameter",
"SecureString" => "SecureString",
"String[]" => "string[]",
"Int32[]" => "int[]",
"Object[]" => "object[]",
"Byte[]" => "byte[]",
"PSObject" => "object",
"PSObject[]" => "object[]",
"Hashtable" => "Hashtable",
"ScriptBlock" => "ScriptBlock",
"TimeSpan" => "TimeSpan",
"PSCredential" => "PSCredential",
_ => typeName,
};
static string GetFriendlyTypeName(Type? type)
{
if (type == null) return "object";
// Handle nullable types
var underlying = Nullable.GetUnderlyingType(type);
if (underlying != null)
return GetFriendlyTypeName(underlying) + "?";
// Handle arrays
if (type.IsArray)
return GetFriendlyTypeName(type.GetElementType()) + "[]";
// Handle SwitchParameter
if (type == typeof(SwitchParameter))
return "SwitchParameter";
// Handle common types
if (type == typeof(string)) return "string";
if (type == typeof(int)) return "int";
if (type == typeof(long)) return "long";
if (type == typeof(uint)) return "uint";
if (type == typeof(double)) return "double";
if (type == typeof(float)) return "float";
if (type == typeof(bool)) return "bool";
if (type == typeof(byte)) return "byte";
if (type == typeof(byte[])) return "byte[]";
if (type == typeof(Guid)) return "Guid";
if (type == typeof(Uri)) return "Uri";
if (type == typeof(object)) return "object";
// Handle SecureString
if (type == typeof(System.Security.SecureString))
return "SecureString";
return type.Name;
}

View file

@ -0,0 +1,82 @@
---
id: GettingStarted
sidebar_label: Getting Started
sidebar_position: 2
---
# Getting Started
This guide will walk you through setting up LANCommander from scratch — from installing the server to connecting your first client and adding games to your library.
---
## 1. Install the Server
The LANCommander server is available as pre-built binaries for Windows, Linux, and macOS (x86 and ARM), as well as a Docker container.
### Docker (Recommended)
The easiest way to get started is with Docker. See the [Docker deployment guide](/Server/Installation/Docker) for a full walkthrough, including a sample `docker-compose.yml`.
### Binary
1. Download the latest release for your platform from the [GitHub Releases page](https://github.com/LANCommander/LANCommander/releases).
2. Extract the archive to a directory of your choice (e.g. `C:\LANCommander` on Windows or `/opt/lancommander` on Linux).
3. Run the server executable:
- **Windows:** `LANCommander.Server.exe`
- **Linux / macOS:** `./LANCommander.Server`
4. The server will start and listen on port **1337** by default.
---
## 2. Initial Server Setup
Once the server is running, open a browser and navigate to `http://<server-address>:1337`.
On first launch you will be prompted to create an administrator account. Fill in a username and password, then click **Create**. These credentials will be used to log in to the server's web interface.
After creating your account you will be taken to the main dashboard where you can begin configuring your library.
---
## 3. Install the Launcher
The LANCommander launcher is the desktop client your users will use to browse, install, and play games.
1. Download the latest launcher release for your platform from the [GitHub Releases page](https://github.com/LANCommander/LANCommander/releases).
2. Extract and run the launcher executable.
---
## 4. Connect the Launcher to the Server
When the launcher opens for the first time you will be presented with a login screen.
1. Enter the address of your LANCommander server (e.g. `http://192.168.1.100:1337`).
- If the launcher is on the same network as the server and beaconing is enabled, the server will appear automatically in the **Discovered Servers** list.
2. Enter your username and password, then click **Login**.
- If you don't have an account yet, click **Register** to create one (requires registration to be enabled on the server).
3. After logging in, the launcher will sync your accessible game library from the server.
---
## 5. Add Games to the Library
Games are managed from the server's web interface.
1. Log in to the server at `http://<server-address>:1337`.
2. Navigate to **Games** in the sidebar.
3. Click **Add Game** and fill in the game's details (title, metadata, cover art, etc.).
4. Upload the game archive or point to an existing archive on disk.
5. Optionally configure [scripts](/Scripting/Overview) (install, uninstall, key change, etc.) for the game.
Once a game is added and made accessible to users, it will appear in the launcher after the next sync.
---
## Next Steps
- [Server Documentation](/Server/Overview) — detailed server configuration, redistributables, collections, and more
- [Launcher Documentation](/Launcher/Overview) — launcher features including the download queue, filtering, and script debugging
- [Scripting](/Scripting/Overview) — automate game setup with PowerShell scripts
- [SDK Documentation](/SDK/Overview) — integrate LANCommander into your own applications

View file

@ -0,0 +1,47 @@
# Launcher
## Overview
The official LANCommander launcher is a desktop application built in ASP.NET Blazor and was designed to be easy to use and familiar. By providing a custom launcher, LANCommander is able to provide a tight integration between the server and any authenticated client.
![Launcher Library View](./_Assets/Library.png)
## Logging In
When opening the launcher for the first time, users will be presented with an authentication screen. From here they will have to enter the LANCommander server address and their credentials. If the launcher is running on the same network as the server, the **Discovered Servers** pane will show any available servers (if beaconing is enabled. See [Server / Settings](/docs/Server/Settings) for more details).
![Launcher Login View](./_Assets/Logging%20In.png)
If the user does not have an account registered on the server, they can register either directly in-launcher by clicking the **Register** button, or the server's web UI.
After logging in, the launcher will immediately start syncing the user's accessible games from the server. Once the sync is complete the user is free to start installing any games listed.
## Changing the Install Location
By default, the launcher will install games to `C:\Games` on Windows. This can be changed in settings by hovering over your username in the top right and clicking **Settings** in the dropdown.
![Launcher Settings](./_Assets/Settings.png)
If you have more than one storage path specified for games, you will be asked upon install which path you would like to install the game to.
![Launcher Install Directory Location](./_Assets/Install%20Directory%20Location.png)
## Filtering Games
By clicking the filter icon in the bottom right, you can filter games based on specific criteria. This metadata is pulled in directly from the game's configuration on the server.
![Launcher Library Filter](./_Assets/Filter.png)
## Download Queue
The launcher has the ability to queue up multiple game installs by utilizing a download queue. Simply start installing multiple games and click the bottom bar to see the queue's current progress.
![Launcher Download Queue](./_Assets/Download%20Queue.png)
## Refreshing Games
If a new game is available or a user's list of accessible games has changed, click the import button at the top of the application next to the user's name to sync the list of games from the server.
![Launcher Refreshing Games](./_Assets/Refreshing%20Games.png)
## Script Debugging
Developing scripts for games is often a process of trial and error. When the setting **Enable Script Debugging** is enabled in the launcher, users will be given the ability to execute any configured scripts for a game without having to reinstall.
![Launcher Manual Script Execution](./_Assets/Manual%20Script%20Execution.png)
Additionally, a terminal will show at the bottom of the launcher after the script has executed. This is a limited PowerShell runspace and will allow for basic debugging of scripts and provides a history of the execution. Any variables that were passed into the script are also listed for visibility.
![Launcher Script Debugger](./_Assets/Script%20Debugger.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 558 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

View file

@ -0,0 +1,30 @@
---
sidebar_label: Overview
sidebar_position: 1
---
# Introduction
LANCommander is an open source digital game distribution platform. In essence, it's a ways to self-host your own game library ala Steam, GOG, Epic, etc.
Both the server and custom launcher applications are built using the ASP.NET Blazor web application framework. Binaries are provided for Windows, Linux, and macOS supporting both x86 and ARM architectures. The server also has a preconfigured Docker container for easier deployment.
The platform is designed to work on local networks and loads no assets from the internet when installing games from the launcher. It was originally developer to help assist deploying games at a LAN party where the local network was closed circuit and no internet access was permitted. The server can also be accessed publicly, though use of a reverse proxy or VPN is recommended.
# Development
Code can be viewed over at the project's [GitHub page](https://github.com/LANCommander/LANCommander). This is where new releases will be posted and also serves as the main issue tracker.
The Docker container is available over at [Docker Hub](https://hub.docker.com/r/lancommander/lancommander) and automatically gets updated with each version release through our CI/CD pipeline.
# Community
The community behind LANCommander is small, but extremely knowledgeable. Most support and troubleshooting questions stem from discussions in the official [Discord server](https://discord.gg/vDEEWVt8EM). There is also a forum within the server where users post freeware and shareware games that can be directly imported into your LANCommander server.
If you would like to support the project, there is a [Patreon page](https://patreon.com/LANCommander) available with a couple paid tiers as well as a free tier. This also serves as a general blog for the project with news about development.
# Installation and Use
This site serves as the main documentation platform for the project. As such, it is recommended to check out the following resources:
- [Getting Started](/GettingStarted)
- [Server](/Server/Overview)
- [Launcher](/Launcher/Overview)
- [Packager](/Packager/Overview)
- [Scripting](/Scripting/Overview)
- [SDK Documentation](/SDK/Overview)

View file

@ -0,0 +1,34 @@
---
sidebar_label: Getting Started
sidebar_position: 2
---
# Getting Started
## Requirements
- **Windows 10 or later** (x86 or x64)
- **Administrator privileges** - the Packager requires elevation to monitor installer processes via DLL injection
The Packager is distributed as a single 32-bit executable (`LANCommander.Packager.exe`). No installation is required.
## Download
Download the latest release from the [GitHub Releases page](https://github.com/LANCommander/LANCommander/releases). The Packager artifact is named `LANCommander.Packager-Windows-x86-v{VERSION}.zip`.
Extract the archive to a directory of your choice and run `LANCommander.Packager.exe`.
## Command-Line Usage
The Packager can optionally accept arguments to skip the initial file picker dialog:
```
LANCommander.Packager.exe [installer-path] [-o output-path]
```
| Argument | Description |
|:--------:|:------------|
| `installer-path` | Path to the installer executable to monitor |
| `-o`, `--output` | Path for the output `.lcx` file |
If no installer path is provided, a file picker dialog will appear on launch.

View file

@ -0,0 +1,47 @@
---
sidebar_label: LCX Package Format
sidebar_position: 4
---
# LCX Package Format
An `.LCX` file is a standard ZIP archive containing everything needed to install and configure a game through LANCommander. The Packager generates this format automatically, but understanding its structure is useful for troubleshooting or manual editing.
## Archive Structure
```
package.lcx (ZIP)
├── manifest.yaml # Game metadata (YAML)
├── Archives/
│ └── {guid} # Inner ZIP containing game files
└── Scripts/
├── {guid} # Install script (PowerShell)
└── {guid} # Uninstall script (PowerShell)
```
### manifest.yaml
The manifest is a YAML file describing the game's metadata, actions, archive references, and script references. It follows the LANCommander SDK's `Game` manifest schema and includes:
- **Title, Sort Title, Version, Description, Notes** - basic metadata
- **Released On, Singleplayer** - classification
- **Directory Name** - the expected install directory name
- **Actions** - launch configurations (name, executable path, arguments, primary flag)
- **Archives** - references to inner archive entries with compressed/uncompressed sizes
- **Scripts** - references to script entries with type (Install/Uninstall) and admin requirements
### Archives
The `Archives/` directory contains one or more inner ZIP files, each identified by a GUID. The inner archive holds the game files with paths relative to the install directory root.
### Scripts
The `Scripts/` directory contains PowerShell scripts identified by GUID. The Packager generates up to two scripts:
**Install Script** - Recreates registry keys and values captured during monitoring. If the Patch GameSpy option was enabled, it also includes an `Edit-PatchGameSpy` call. Scripts assume `$InstallDirectory` is available in the execution environment (provided by the launcher's PowerShell runtime).
**Uninstall Script** - Removes the registry keys and values that were created by the install script.
## Importing into LANCommander
`.LCX` packages can be imported directly through the LANCommander server's web interface. The server reads the manifest, extracts the archive and scripts, and creates the corresponding game entry with all metadata, actions, and scripts pre-configured.

View file

@ -0,0 +1,14 @@
---
sidebar_label: Overview
sidebar_position: 1
---
# Packager
The LANCommander Packager is a standalone Windows utility that automates the creation of `.LCX` game packages. It monitors a game installer as it runs, captures all file and registry changes, and guides you through a wizard to produce a ready-to-import package for your LANCommander server.
Instead of manually creating archives, writing install scripts, and filling out metadata by hand, the Packager handles all of this in a single guided workflow.
- [Getting Started](/Packager/Getting%20Started) - requirements, download, and command-line usage
- [Wizard Walkthrough](/Packager/Wizard) - step-by-step guide through the seven wizard stages
- [LCX Package Format](/Packager/LCX%20Format) - internal structure of `.LCX` files

View file

@ -0,0 +1,114 @@
---
sidebar_label: Wizard Walkthrough
sidebar_position: 3
---
# Wizard Walkthrough
The Packager walks you through seven steps to create a complete `.LCX` package. Each step is shown in the sidebar with a progress indicator.
---
## Step 1: Monitor Installer
After selecting an installer executable, the Packager launches it and monitors all file and registry activity using native DLL injection (Interposer). A real-time log displays captured events as the installer runs.
The Packager automatically:
- Detects the installer's architecture (32-bit or 64-bit) and injects the appropriate Interposer DLL
- Monitors child processes spawned by the installer
- Filters out writes to system directories (Windows, temp folders)
- Captures both file writes and registry key/value creation
Once the installer exits, the captured data is summarized in the status bar. Click **Next** to continue.
:::info
The log view continues to show captured events for reference. All diagnostic output is also written to `packager.log` in the application directory.
:::
---
## Step 2: Install Directory
The Packager analyzes the captured file writes to detect the game's installation directory. This is determined by finding the most common non-system directory among the written files.
If the detected directory is incorrect, click **Browse** to manually select the correct location. This directory becomes the root of the game archive.
---
## Step 3: Select Files
All files within the install directory are displayed in a tree view with checkboxes. By default, every file is selected.
- **Check/uncheck a directory** to toggle all files within it
- **Select All** / **Select None** buttons at the top for bulk operations
- The counter at the top shows how many files are currently selected
Files outside the install directory (if any were captured) are listed by their full paths. Only files that still exist on disk at this point are shown.
---
## Step 4: Registry Entries
All captured registry writes are displayed in a tree view organized by hive and key path. Entries are deduplicated so if the same key and value were written multiple times during installation, only one entry is shown.
Each leaf entry displays an indicator:
- **Green +** - the entry was created during installation
- **Yellow ~** - the entry was updated (written to an existing key)
Selected entries will be included in the auto-generated install and uninstall scripts. The install script recreates the registry keys and values; the uninstall script removes them.
---
## Step 5: Game Metadata
Enter basic information about the game. The title is pre-populated from the installer's filename.
| Field | Description |
|:------|:------------|
| **Title** | Display name of the game (required) |
| **Sort Title** | Optional override for alphabetical sorting |
| **Version** | Game version, defaults to `1.0` |
| **Released On** | Release date of the game |
| **Singleplayer** | Whether the game supports singleplayer |
| **Description** | A description of the game |
| **Notes** | Private notes (admin-only, not shown to users) |
---
## Step 6: Game Executable
The Packager scans your selected files for `.exe` files and filters out common installer/redistributable executables (e.g. `vcredist`, `dxsetup`, `setup`, `unins`). The remaining executables are displayed in a list.
Select the primary game executable. This is the file the launcher will run when the user clicks "Play". You can also customize:
| Field | Description |
|:------|:------------|
| **Action Name** | Label shown on the play button, defaults to `Play` |
| **Arguments** | Command-line arguments passed when launching |
---
## Step 7: Generate Package
Configure the output path for the `.LCX` file and optionally adjust packaging options before generating.
### Output Path
The default output path is based on the game title in the current working directory. Click **Browse** to choose a different location.
### Options
Expand the **Options** panel to configure additional settings:
| Option | Description |
|:-------|:------------|
| **Patch GameSpy** | Adds an `Edit-PatchGameSpy -Path $InstallDirectory` call to the install script. This scans the install directory for GameSpy references and patches them for OpenSpy compatibility. |
| **Compression Level** | Controls the trade-off between archive size and packaging speed. Options: Optimal (default), Fastest, No Compression, Smallest Size. |
| **Write Summary Log** | Writes a `.Package.log` file alongside the `.LCX` output documenting the source installer, selected files, registry entries, metadata, and options used. |
Click **Generate .LCX** to build the package. A progress bar shows the current stage:
1. Creating game files archive
2. Generating scripts
3. Writing manifest
On completion, the output path and file size are displayed.

View file

@ -0,0 +1,175 @@
---
title: 1.0.0
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
# Server
## Database Overhaul
For some big news, the server now supports MySQL and PostgreSQL as database providers! SQLite support is still available as well for anyone that wants to keep their setup simple.
In order to support multiple database providers, the data layer of the application has been rebuilt from the ground up. Due to the way that Blazor SSR handles database contexts, this was a necessary and arduous process and was the source of the most time put into this release. That being said, lazy loading in Entity Framework has been removed. This means that almost every query across the application has been optimized and will run faster while consuming fewer resources.
:::info
Support for MySQL and PostgreSQL should be considered experimental. Make sure to keep regular backups as there is a non-zero chance of data loss.
:::
#### Caching
Many areas of the application now utilize a cache where speed is paramount. Most of these improvements will be seen in the API and loading data from the server in the launcher. Redis support is also being looked at for taking one more step to allowing distributed server configurations.
#### Authentication
OAuth/OIDC identity providers can now be defined in Settings. No restrictions are imposed, and an "Account Link" functionality has been added to the web UI for existing users. Yes, you read that right, there's no SSO tax!
Some provider templates have been provided, but have not been fully vetted. Keep in mind that most IDPs require SSL for authentication. We'll talk about that a little later, but it's still recommend to use a reverse proxy or products like Cloudflare Zero Trust to provide a robust and secure solution.
### New Login / Registration Design
The login and registration pages have been redesigned to be a bit more attractive while supporting new authentication features. A new button for downloading the launcher has been added as well!
These downloads can be provided directly via the server if **Host Client Updates** is set under Settings / Launcher. Your supported architectures and platforms can be customized as well.
:::info
Support for launchers on Linux and macOS are still experimental
:::
## Logging
The available logging providers are now able to be fully configured from Settings / Logging. Support for remote logging with Seq or Elastic search has been added. For now this only is supported on the server, but in the future this information may be broadcasted to clients using the launcher in order to collect user logs if desired.
## Redistributable Scripting
Additional script types of Name Change, Before Start, and After Stop are now available to redistributables. These script types work the same as if they were defined for games, but they are shared with any game that has the redistributable as a dependency.
Redistributable archives will now be extracted to the game's `.lancommander` directory under its own directory matching its ID (e.g. `.lancommander\d3355f4a-870e-4964-a5a3-832a8d8a6b3f\Files`). These files are then cleaned up once install has completed.
## Game Custom Fields
Custom fields have been added to games and can be used in scripts (game and redistributable), actions (games and servers), and save paths. These are loaded like any other variable with their name set as-is. For example, if you wanted to have a game custom field of `AppId`, you could use it in a script as `$AppId` or in an action/save path as `{AppId}`.
## Server Process Tracking
Some game servers would fail to have their process tracked correctly. This has been revamped and the server's entire process tree will now be tracked. This should resolve any issue previously seen where a server could be started from LANCommander, and then would immediately stop while still actually running on the host machine.
## Datatables
Most datatables throughout the web UI have been reworked and should be faster, while supporting column customization. This is an area that will be continued to be improved upon with the potential for individual column searching and filtering being a possiblity in the future.
## Image Optimization
Images for media are now optimized on application start and upload. This results in faster downloads for the launcher. The following optimizations are currently implemented:
- PNGs with no alpha pixels are converted to JPG
- "Thumbnails" are generated with a JPG quality of 75
- "Thumbnails" are generated with a max width/height based on type. Refer to [Thumbnail Generation]() for more information.
## Multiple Upload Locations
An often-requested feature has been to allow for the uploading of archives to multiple locations on the host. This has now been implemented and is available for configuration in the First Time Setup or Settings / Archives.
Please note that in order to keep track of archives properly, a random GUID is still used as the uploaded archive's name. It is not recommended to alter these files once they've been uploaded.
## HTTPS Support
SSL support for Kestrel (the underlying HTTP server) can now be defined in Settings / General. Most OAuth/OIDC providers will require HTTPS for redirects.
## Stylesheets
The CSS for the web UI can now be customized under Settings / Appearance. A separate stylesheet only for Pages can also be defined.
## PowerShell Scripting
The following cmdlets have been added to the scripting engine:
- `Get-HorizontalFov`
- `Get-VerticalFov`
- `Out-PlayerAvatar`
Review the [Cmdlets](/Scripting/Cmdlets) documentation for more information and usage.
## Fixes and Minor Improvements
- Updated to .NET 9
- Text alignment in select inputs has been fixed
- Uploading large files with the chunk uploader should now be more reliable
- Archive file sizes will now calculate properly on upload
- Searched images can now be double clicked to select
- Login / registration pages have been reworked to support authentication providers
- Documentation and social icons have been added to the main menu
- Cookie policy can now be customized in Settings
- Fatal errors in the UI should now be caught without having to reload the page
- Scripts for installing the application as a service have been provided
## Breaking Changes
- In supporting multiple upload paths, configuration has moved from `Settings.yml` to the database. Your current path should have migrated on update, but you should probably double check it.
- Redistributable scripts will now execute out of the game's `.lancommander\<Redistributable ID>\Files` directory
# Launcher
## Depot and User Library
On start of the launcher, you'll probably notice that you have no games listed. Fear not! Your games are not gone, your library is just empty. By clicking on the "Depot" button you'll be able to browse and filter the games from the server. From here games can be installed directly or added to your library.
User libraries are tracked by the server and synced locally to the launcher. This also means that logging into the launcher on another machine will sync your user library. For admins, games in the depot will also be filtered based on accessible collections and role permissions so you can curate as much or as little as needed.
## Addon Installation
When installing a game that has expansions and/or mods defined, a new dialog will display allowing you to select what to install:
![Launcher Addon Installation](./_Assets/1.1.0%20-%20Addon%20Installation.png)
Keep in mind that this does not apply to standalone expansions or standalone mods. For a refresher on game types, check out [Server / Games](/Server/Games).
This dialog will show expected download and install sizes in addition to selectable install directories (if configured). Did you forget to select an addon when first installing? No worries! Under the game's context menu use the "Modify" option to display this dialog again. You can even move the game to another defined location if so desired.
## File Validation
A new tool for validating files has been added to the game context menu. This will compare the game files on your local install to contents of the game's archive on the server. Individual files can be selected for restore. File validation on install has now been improved to increase download speed in the case valid existing files are found on disk. This is fairly basic at the moment, but should hopefully help to fix some broken game installs.
![Launcher File Validation](./_Assets/1.1.0%20-%20File%20Validation.png)
## Authentication / Offline Mode
The main authentication form has been redesigned to allow for OAuth/OIDC authentication providers defined by the server. As is customary with these sorts of implementations, you will first have to put in your server address (or select from beaconing servers) and then you will be presented with all login options.
Offline mode has been revamped and should work more consistently. The launcher will actively monitor its connection to the server and automatically turn on offline mode if the connection is lost.
## Importing
Speed improvements and optimizations have been made to importing to drastically reduce the time to do a full import. Hovering over the import icon will also display an import status progress popup. Individual game information is cached server-side, so subsequent imports by other users should be faster.
## Save Management
A new dialog for save management is now accessible under the game's context menu. This can be used to download or delete specific saves from the server, or upload the current machine's save files to the server.
![Launcher Save Management](./_Assets/1.1.0%20-%20Save%20Management.png)
## Fixes and Minor Improvements
- Updated to .NET 9
- Redistributable installs will now show download progress
- Linux/macOS builds should now load assets correctly (non-Windows builds are still experimental)
- Game process tracking will now track the entire process tree
- Play Session recording has been fixed
- Download / install size is now displayed in the game's status bar
- Filtering, sorting, and grouping should be more reliable
- Progress will now be shown for in-progress installs of mods and expansions
- Game status should now be reflected properly in the main action button
- Server-defined actions should now display properly
- Total play session status displayed per game will now only show the authenticated user's total
- Loading game info on library list click should now be much faster
- Library image assets should now download from server's optimized image files, reducing download time and overall size
- A search field has been added to the library list and filter
- Library items can now be sorted
- Errors when searching for lobbies will now gracefully fail
- Fatal errors will no longer completely hang the UI
# Infrastructure
## Nightlies
Nightly builds are now available over at [GitHub](https://github.com/LANCommander/LANCommander/actions/workflows/LANCommander.Nightly.yml). Docker images are also available via [GitHub Packages](https://github.com/LANCommander/LANCommander/pkgs/container/lancommander).
## Docker Multi-Architecture Support
The Docker images pushed to Docker Hub now support Linux/ARM64! That's it!
## WinGet
An effort is being made to make the server and launcher available as WinGet packages with the names `LANCommander.Server` and `LANCommander.Launcher`. These packages are generated on the fly as part of the CI/CD pipeline, so the latest version should always be available.
# On Deck
There's a few large features being worked on / designed that will possibly make it into future builds as we kick off 2025:
- Docker support for game servers
- Game server config templates / structured forms (human-friendly UI for server configs!)
- VPN integrations
- P2P / distributed file transfers
- Script templates
- Friend list / chat
- Game overlay
- Rebuilt autoupdater
## Downloads
<ReleaseDownloads release="v1.1.0" />
# Community
Lastly, I'd like to shout out the community. This was LANCommander's biggest year of growth in users and features. We saw the introduction of a new launcher, the deprecation of the Playnite extension, and tons of features added along the way. Many of these came as suggestions from our community, and seeing all of your LAN parties has really shown that there's something to this little project.
There's one part of the community I'd like to give a shoutout to. A group of Aussies have banded together to establish a support network for men called **DadLAN**. They've been able to grow substantially over the past couple of years and have been making headlines down under. I really feel like they represent what LAN parties are all about and why they're so important to gaming culture. For more information, check out their site over at https://dadlan.au/!
I'd also like to take this moment to shill a bit and ask for any contribution you can spare. Whether it's contributing on [GitHub](https://github.com/LANCommander/LANCommander), helping out in the [Discord](https://discord.gg/vDEEWVt8EM), or, if you're able, subscribing to one of the paid [Patreon](https://www.patreon.com/c/LANCommander) tiers. Any amount of effort goes a long way towards bringing you this excellent piece of software!
Until next time, keep on being Bawlers!

View file

@ -0,0 +1,58 @@
---
title: 1.1.5
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
## Server
- Fixed an issue where the IPX relay would cause high CPU usage if started/stopped multiple times (#200)
- Fixed the autostart of the IPX relay (#188)
- Fixed save uploading (#190)
- Settings / Scripts are now saved properly (#194)
- Game servers will now be more responsive when using the start/stop control buttons (#198)
- Fixed importing of companies when importing metadata from IGDB (#201)
- Fixed importing of games from local files (#203)
- Fixed first time setup creation of administrator user (#206)
- Added graceful error handling for Depot (#210)
- Added additional paths to first time setup
- Fixed error when adding media (#216)
- Fixed update of tables on various entities (#218, #224)
- Updated Dockerfile to be based off Microsoft's official .NET base Docker images
- Administrator step is skipped on first time setup if one already exists
- Fixed user custom fields not being shown when editing a user
- Moved the "Restrict Games By Collection" switch to Settings / Library
- Users datatable is now searchable
- Fixed logging on IPX relay if enabled
- Fixed some areas where the Created By and Created On fields were not getting populated
- Added API menu item to Settings for OpenAPI/Scalar integration
- Reworked downloading of launcher artifacts from GitHub (note, this is still iffy. It's hard to test this until releases are actually posted. Downloading launchers for nightlies is still a WIP)
- Times are now rendered as local time based on the user's browser locale.
## Launcher
- New: The server address field will now also try other possible valid URLs if the address input is invalid or incomplete. See [#208](https://github.com/LANCommander/LANCommander/issues/208#issuecomment-2712374078) for more details
- Fixed: The "Manage Saves" dialog no longer crashes when opened (#207)
- The library list will now display a button to clear the filter instead of the "Browse Depot" button (#193)
- Fixed "Register" button (#214)
- Rebuilt save packing to be more reliable
## Nightlies
Nightly builds of both the launcher and server are now being hosted on GitHub under [action runs](https://github.com/LANCommander/LANCommander/actions/workflows/LANCommander.Nightly.yml). Docker builds are also being pushed to [GitHub Packages](https://github.com/LANCommander/LANCommander/pkgs/container/lancommander). As all nightlies go, these are unstable builds and may not work as expected.
## Breaking Changes
The mounting paths for the Docker container have changed in v1.1.5. Directories that were in `/config` now live at `/app/config`. Please update your bindings accordingly.
## WinGet Packages
LANCommander is now available to install via WinGet! These packages are included in our CI, so releases will get automatically pushed to the public Microsoft source.
![WindowsTerminal_lyR1c4jvrs](https://github.com/user-attachments/assets/f1e14dee-a79d-4dc3-b6dc-70488ee095c4)
## Downloads
<ReleaseDownloads release="v1.1.5" />
## New Contributors
* @simonmarklar made their first contribution in https://github.com/LANCommander/LANCommander/pull/192
* @eliaswen made their first contribution in https://github.com/LANCommander/LANCommander/pull/202
* @PGHJA991id12 made their first contribution in https://github.com/LANCommander/LANCommander/pull/222
**Full Changelog**: https://github.com/LANCommander/LANCommander/compare/v1.1.4...v1.1.5

View file

@ -0,0 +1,119 @@
---
title: 2.0.0-rc1
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
# LANCommander 2.0.0-rc1 Release Notes
LANCommander 2.0.0 is up there as one of the largest updates in the project's history. This release represents over nine months of development, with more individual contributors than previous versions. The jump from 1.x to 2.x reflects substantial architectural changes, new features, and significant improvements in the server, launcher, and SDK.
Additionally, documentation has now been moved to Docusaurus and is being hosted on GitHub Pages. Documentation can now live in the same repository as the rest of the application, ensuring that documentation is versioned and up to date.
## Breaking Changes
### Settings
Settings have previously been implemented with custom code parsing the `Settings.yml` file. They are now implemented using standard .NET configuration practices.
A migration has been put in place to adapt to the new format, but as with any migration some settings may not carry over from previous installs.
### Import/Export
Importing and exporting has been rebuilt from the ground up. An effort to allow the import of legacy LCX files has been made, but this may vary based on the age of the export file.
### User Data Paths
In previous versions of LANCommander, user data files/directories such as the database, settings, media, and uploads were thrown into the same directory as the main executable (for both server and launcher). In 2.0.0 a new directory called "Data" will be created and used for all user data. This change should make it easier to create backups and map Docker volumes.
A migration has been written to assist with this change, but as always make sure you backup your data before upgrading. **For Docker users**: Before updating to 2.0.0, make sure you map `/app/Data` _with_ your volumes from previous versions. The migration will do its best attempt at discovering if this directory is mapped before moving any data.
### Logging
Previous versions have relied on Serilog to provide logging for both the server and launcher. An effort is being made to update this to more modern practices by integrating OpenTelemetry. As of right now, logging support for Seq and ElasticSearch is non-functional.
### SDK Changes
The SDK has been refactored to utilize dependency injection instead of relying on a monolithic singleton client. I'm not aware of any other projects using the SDK, but it's worth noting at this time.
## Major Changes / features
### Import/Export
As mentioned, importing and exporting has been rebuilt from the ground up, and as such the manifest format for LCX files has gone through some significant changes. Ultimately, this change reflects a closer parity between export files and the database schema, which should allow for extensibility in the future.
The core concept that drove this rewrite is **selective importing/exporting**. Now when you go to export or import a game, server, or redistributable you may choose which data you want to import or export. This includes metadata, archives, media, and user data such as save games or play sessions. This should make it easier to share only the data you want or to selectively migrate from one system to another.
### Launcher Authentication/Stability
A large effort was put into 2.0.0 to make the launcher more stable. A core issue has always been the authentication flow. While this still can be improved upon, the core changes put in should reduce the amount of soft-locking that could previously happen within the launcher.
### Chat
A bare-bones chat system has been added to the platform. As of right now this probably isn't entirely useful, but there's a good base for improvements in future versions.
### Packaging Scripts
Games now support a new script type called **Package**. These scripts are designed to run on the server to package up a game automatically. Package scripts are run on a recurring schedule, so they can be used to keep games up to date. Refer to the scripting documentation for more details.
### SteamCMD
The server now supports a basic integration with SteamCMD. Currently, you can authenticate to multiple Steam profiles. This pairs great with packaging scripts to keep your games up to date!
### Docker
The Docker image has been updated to support the new user data pathing. In addition, scripts have been added to allow the installation of both SteamCMD and WINE. These may be useful for writing packaging scripts or hosting dedicated servers in Docker-hosted environments.
For Docker Compose users, refer to the [sample docker-compose.yml](https://github.com/LANCommander/LANCommander/blob/v2.0.0-rc1/LANCommander.Server/docker-compose.yml) file in the repository.
## Bug Fixes / Misc Improvements
As if there weren't already enough changes in this release, here's a list of notable bug fixes that have been made in 2.0.0:
- Game process tracking is now more reliable and less prone to memory leaks
- Downloads now buffer correctly and file writes should happen asynchronously
- Metadata and libraries are now caching more effectively, resulting in faster response times
- API requests are now being prioritized over downloads. Users should see an increase in responsiveness while other users are downloading from the server.
- Game saves should now be more reliable on both upload and download
- Launcher authentication state should now be more reliable
- An unresponsive server should no longer cause fatal errors when trying to launch games
- The discovery beacon has been rebuilt from the ground up and should no longer cause hard crashes
- Thumbnail sizes can now be customized in settings
- IGDB metadata importing should now be more consistent
- Taxonomy metadata such as genre or tags is no longer being lost when updating other game information
## Downloads
<ReleaseDownloads release="v2.0.0-rc1" />
## Contributors
As mentioned previously, this update had the most outside contributions over any other version. A big thanks to the following contributors and their pull requests:
[aaronpowell](https://github.com/aaronpowell)
- [#390](https://github.com/LANCommander/LANCommander/pull/390) Skipping nightly CI if it's not the lancommander main repo
- [#387](https://github.com/LANCommander/LANCommander/pull/387) Launcher import fixes
- [#386](https://github.com/LANCommander/LANCommander/pull/386) Evolving Migrations
- [#385](https://github.com/LANCommander/LANCommander/pull/385) Download endpoints get correct paths
- [#384](https://github.com/LANCommander/LANCommander/pull/384) Ensuring media path is correct
- [#383](https://github.com/LANCommander/LANCommander/pull/383) Adding SQLite database migration step
- [#382](https://github.com/LANCommander/LANCommander/pull/382) tags input fixes
[Mavyre](https://github.com/Mavyre)
- [#337](https://github.com/LANCommander/LANCommander/pull/337) Corrected Auto-updater
- [#336](https://github.com/LANCommander/LANCommander/pull/336) Added redistributables to Games API and Games manifest
- [#335](https://github.com/LANCommander/LANCommander/pull/335) Fixing scripts not saved anymore in Launcher
- [#334](https://github.com/LANCommander/LANCommander/pull/334) Save Games no longer shared between user
- [#327](https://github.com/LANCommander/LANCommander/pull/327) Corrected routes for Play Sessions tracking
[RattleSN4K3](https://github.com/RattleSN4K3)
- [#322](https://github.com/LANCommander/LANCommander/pull/322) Re-implement registry save paths game save creation in Launcher
- [#321](https://github.com/LANCommander/LANCommander/pull/321) Fix game end lag caused by generating game save
- [#319](https://github.com/LANCommander/LANCommander/pull/319) Fix editing collection and other metadata pages
- [#318](https://github.com/LANCommander/LANCommander/pull/318) Fix generating of thumbnails on importing games
- [#316](https://github.com/LANCommander/LANCommander/pull/316) Error messaging for login/register via Launcher
- [#308](https://github.com/LANCommander/LANCommander/pull/308) Bypass password policy on first-time-setup for administrator creation
- [#307](https://github.com/LANCommander/LANCommander/pull/307) Launcher: Fix connecting to hostname:port Uri
- [#306](https://github.com/LANCommander/LANCommander/pull/306) Launcher: Rework on offline mode and connection timeout
- [#305](https://github.com/LANCommander/LANCommander/pull/305) Launcher: Configure logger with log level from debug settings
- [#303](https://github.com/LANCommander/LANCommander/pull/303) Fix context menu items not triggering on click
- [#291](https://github.com/LANCommander/LANCommander/pull/291) Library changes to addon installation
- [#290](https://github.com/LANCommander/LANCommander/pull/290) Update archive's file sizes for a specific archive/game
- [#289](https://github.com/LANCommander/LANCommander/pull/289) Depot game changes to update view
- [#287](https://github.com/LANCommander/LANCommander/pull/287) Remove games via dialog for dependent games
- [#286](https://github.com/LANCommander/LANCommander/pull/286) Fix crash of base game removal of user Library
- [#283](https://github.com/LANCommander/LANCommander/pull/283) Fix Avatar uploading
- [#282](https://github.com/LANCommander/LANCommander/pull/282) Fix packaging and uploading save games by Launcher
- [#281](https://github.com/LANCommander/LANCommander/pull/281) Fix storing several entities (such as Gamesaves) properly
- [#277](https://github.com/LANCommander/LANCommander/pull/277) Fix importing tags on Lookup from IGDB
- [#272](https://github.com/LANCommander/LANCommander/pull/272) Standalone mods/expansions as Base Game
- [#271](https://github.com/LANCommander/LANCommander/pull/271) Fix changing base game of a game
- [#269](https://github.com/LANCommander/LANCommander/pull/269) Option to delete selected Media entries
- [#268](https://github.com/LANCommander/LANCommander/pull/268) Expiring Archives cache after recalculating/updating archives
- [#260](https://github.com/LANCommander/LANCommander/pull/260) Utilizing MadMilkman.INI for ini parsing
- [#257](https://github.com/LANCommander/LANCommander/pull/257) Fix "Name Change Script" from context menu nulling username in game files

View file

@ -0,0 +1,25 @@
---
title: 2.0.0-rc2
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
# LANCommander 2.0.0-rc2 Release Notes
This page only covers updates/fixes included in v2.0.0-rc2. For more information on other v2.0.0 changes, head over to the [v2.0.0-rc1 release notes](/Releases/2.0.0-rc1).
## Bug Fixes / Misc Improvements
- Launcher now starts properly on fresh installs
- Exporting a game no longer redirects you to a 404 page
- Added a better not found page for the admin UI
- Fixed scrolling in depot
- Fixed scrolling in library list / selected game details
- Fixed log viewer installation in admin UI
- Moved IGDB and SteamGridDB credential settings under "Integrations" in admin UI
- Fixed width of game details in depot
- Fixed "Add to Library" button in depot
- Fixed opening of chat window
- Display logo instead of authentication form when starting launcher
## Downloads
<ReleaseDownloads release="v2.0.0-rc2" />

View file

@ -0,0 +1,21 @@
---
title: 2.0.0-rc3
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
# LANCommander 2.0.0-rc3 Release Notes
This page only covers updates/fixes included in v2.0.0-rc3. For more information on other v2.0.0 changes, head over to previous releases:
- [v2.0.0-rc1](/Releases/2.0.0-rc1)
- [v2.0.0-rc2](/Releases/2.0.0-rc2)
## Bug Fixes / Misc Improvements
- SteamCMD credentials are now persisted when enabling SteamCMD in the Docker container
- Fix default path for scripting snippets
- Added basic PowerShell module for SteamCMD
- Auto-detect SteamCMD path if possible when loading the Steam integration page under settings
- Better error handling/reporting when working with SteamCMD
## Downloads
<ReleaseDownloads release="v2.0.0-rc3" />

View file

@ -0,0 +1,27 @@
---
title: 2.0.0-rc4
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
# LANCommander 2.0.0-rc4 Release Notes
This page only covers updates/fixes included in v2.0.0-rc4. For more information on other v2.0.0 changes, head over to previous releases:
- [v2.0.0-rc1](/Releases/2.0.0-rc1)
- [v2.0.0-rc2](/Releases/2.0.0-rc2)
- [v2.0.0-rc3](/Releases/2.0.0-rc3)
## Bug Fixes / Misc Improvements
- Added PowerShell cmdlets for various SteamCMD/Steam store functions. See [Cmdlets](/Scripting/Cmdlets) for more information.
- Fix import issues with scripts, archives, and even-older legacy LCX files
- Added tooltips for archive editor buttons
- Fixed file manager styling
- Fixed redistributable and server script editors
- Added packaging scripts for redistributables
- Fixed export button for redistributables and servers
- Exports now download with a useful name
- Incomplete chat component for web UI has been temporarily disabled
- Fixed login redirecting to logout after logout
## Downloads
<ReleaseDownloads release="v2.0.0-rc4" />

View file

@ -0,0 +1,34 @@
---
title: 2.0.0-rc5
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
# LANCommander 2.0.0-rc5 Release Notes
This page only covers updates/fixes included in v2.0.0-rc5. For more information on other v2.0.0 changes, head over to previous releases:
- [v2.0.0-rc1](/Releases/2.0.0-rc1)
- [v2.0.0-rc2](/Releases/2.0.0-rc2)
- [v2.0.0-rc3](/Releases/2.0.0-rc3)
- [v2.0.0-rc4](/Releases/2.0.0-rc4)
## Bug Fixes / Misc Improvements
- Reworked Steam cmdlets, review the [documentation](/Scripting/Cmdlets) for a list of available cmdlets.
- Added better logging for PowerShell scripts
- Fixed the save editor so it no longer throws an error if there are no archives available for a game.
- Fixed first time login on launcher being stuck at loading screen.
- Fix chat window opening with library view instead of chat.
## Game Servers
An effort is being made to create Docker images to assist in hosting dedicated servers for various games. A list of these containers can be found under [Game Servers](/GameServers).
These servers should still be considered a work in progress. They will eventually get better integration into the platform. The aim is to provide a set of base containers and remove as much work as possible for each individual game. The current aim is to implement:
- OverlayFS to allow multiple servers to share the same base files
- A built in HTTP server (via nginx) for FastDL support
- Documentation for each image, including useful configuration of the game server itself
## Documentation
This documentation site now resides under our [LANCommander.Documentation](https://github.com/LANCommander/LANCommander.Documentation) GitHub repository, with the actual contents being fed from the organization's other repositories. Any contribution to the documentation can be submitted to the child repositories. They will automatically be published to the documentation repo on merge.
## Downloads
<ReleaseDownloads release="v2.0.0-rc5" />

View file

@ -0,0 +1,30 @@
---
title: 2.0.0-rc6
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
# LANCommander 2.0.0-rc6 Release Notes
This page only covers updates/fixes included in v2.0.0-rc6. For more information on other v2.0.0 changes, head over to previous releases:
- [v2.0.0-rc1](/Releases/2.0.0-rc1)
- [v2.0.0-rc2](/Releases/2.0.0-rc2)
- [v2.0.0-rc3](/Releases/2.0.0-rc3)
- [v2.0.0-rc4](/Releases/2.0.0-rc4)
- [v2.0.0-rc5](/Releases/2.0.0-rc5)
## Bug Fixes / Misc Improvements
- Fixed the archive editor in redistributables
- Fixed console/HTTP path editing in servers
- Removed old export methods from servers, redistributables
- Fixed export initialization for servers and redistributables
- Fixed alignment of action buttons in list pages
- Fixed logo being shown on launcher settings page, blocking certain controls
- Fixed missing auth provider templates crashing the server in some cases
- Increased buffer size for downloads and avoid throttling large responses. This should result in some faster download speeds across the board.
## Tools
LANCommander now supports the ability to manage tools that may be useful for games. This feature is intended to allow admins to upload applications such as map editors, save editors, trainers, alternate configuration tools, etc. Tools can be tied to games and will be available to select upon install of the game in the launcher. This feature is still very new and experimental.
## Downloads
<ReleaseDownloads release="v2.0.0-rc6" />

View file

@ -0,0 +1,132 @@
---
title: 2.0.0
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
# LANCommander 2.0.0 Release Notes
LANCommander 2.0.0 is up there as one of the largest updates in the project's history. This release represents over ten months of development, with more individual contributors than previous versions. The jump from 1.x to 2.x reflects substantial architectural changes, new features, and significant improvements in the server, launcher, and SDK.
Additionally, documentation has now been moved to Docusaurus and is being hosted on GitHub Pages. Documentation can now live in the same repository as the rest of the application, ensuring that documentation is versioned and up to date.
## Breaking Changes
### Settings
Settings have previously been implemented with custom code parsing the `Settings.yml` file. They are now implemented using standard .NET configuration practices.
A migration has been put in place to adapt to the new format, but as with any migration some settings may not carry over from previous installs.
### Import/Export
Importing and exporting has been rebuilt from the ground up. An effort to allow the import of legacy LCX files has been made, but this may vary based on the age of the export file.
### User Data Paths
In previous versions of LANCommander, user data files/directories such as the database, settings, media, and uploads were thrown into the same directory as the main executable (for both server and launcher). In 2.0.0 a new directory called "Data" will be created and used for all user data. This change should make it easier to create backups and map Docker volumes.
A migration has been written to assist with this change, but as always make sure you backup your data before upgrading. **For Docker users**: Before updating to 2.0.0, make sure you map `/app/Data` _with_ your volumes from previous versions. The migration will do its best attempt at discovering if this directory is mapped before moving any data.
### Logging
Previous versions have relied on Serilog to provide logging for both the server and launcher. An effort is being made to update this to more modern practices by integrating OpenTelemetry. As of right now, logging support for Seq and ElasticSearch is non-functional.
### SDK Changes
The SDK has been refactored to utilize dependency injection instead of relying on a monolithic singleton client. I'm not aware of any other projects using the SDK, but it's worth noting at this time.
## Major Changes / features
### Import/Export
As mentioned, importing and exporting has been rebuilt from the ground up, and as such the manifest format for LCX files has gone through some significant changes. Ultimately, this change reflects a closer parity between export files and the database schema, which should allow for extensibility in the future.
The core concept that drove this rewrite is **selective importing/exporting**. Now when you go to export or import a game, server, or redistributable you may choose which data you want to import or export. This includes metadata, archives, media, and user data such as save games or play sessions. This should make it easier to share only the data you want or to selectively migrate from one system to another.
### Launcher Authentication/Stability
A large effort was put into 2.0.0 to make the launcher more stable. A core issue has always been the authentication flow. While this still can be improved upon, the core changes put in should reduce the amount of soft-locking that could previously happen within the launcher.
### Chat
A bare-bones chat system has been added to the platform. As of right now this probably isn't entirely useful, but there's a good base for improvements in future versions.
### Packaging Scripts
Games and redistributables now support a new script type called **Package**. These scripts are designed to run on the server to package up a game/redistributable automatically. Package scripts are run on a recurring schedule, so they can be used to keep things up to date. Refer to the scripting documentation for more details.
### SteamCMD
The server now supports a basic integration with SteamCMD. Currently, you can authenticate to multiple Steam profiles. This pairs great with packaging scripts to keep your games up to date!
### Docker
The Docker image has been updated to support the new user data pathing. In addition, scripts have been added to allow the installation of both SteamCMD and WINE. These may be useful for writing packaging scripts or hosting dedicated servers in Docker-hosted environments.
For Docker Compose users, refer to the [sample docker-compose.yml](https://github.com/LANCommander/LANCommander/blob/v2.0.0-rc1/LANCommander.Server/docker-compose.yml) file in the repository.
### Metadata Retrieval
In previous versions, metadata retrieval was only possible through IGDB and you had to accept whatever it gave you. This has been retooled and you now have the option of importing metadata from either IGDB or PCGamingWiki. Once you select the correct game, you are also able to select which data you would like to pull. This is built on the back of the new import functionality, so it should be more stable as well! The framework has also been laid out where it should be easier to integrate other metadata sources in the future.
## Bug Fixes / Misc Improvements
As if there weren't already enough changes in this release, here's a list of notable bug fixes that have been made in 2.0.0:
- Game process tracking is now more reliable and less prone to memory leaks
- Downloads now buffer correctly and file writes should happen asynchronously. This should increase download speeds while preventing long load times in the web UI.
- Metadata and libraries are now caching more effectively, resulting in faster response times
- API requests are now being prioritized over downloads. Users should see an increase in responsiveness while other users are downloading from the server.
- Game saves should now be more reliable on both upload and download
- Launcher authentication state should now be more reliable
- An unresponsive server should no longer cause fatal errors when trying to launch games
- The discovery beacon has been rebuilt from the ground up and should no longer cause hard crashes
- Thumbnail sizes can now be customized in settings
- IGDB metadata importing should now be more consistent
- Taxonomy metadata such as genre or tags is no longer being lost when updating other game information
## Downloads
<ReleaseDownloads release="v2.0.0" />
## Contributors
As mentioned previously, this update had the most outside contributions over any other version. A big thanks to the following contributors and their pull requests:
[aaronpowell](https://github.com/aaronpowell)
- [#390](https://github.com/LANCommander/LANCommander/pull/390) Skipping nightly CI if it's not the lancommander main repo
- [#387](https://github.com/LANCommander/LANCommander/pull/387) Launcher import fixes
- [#386](https://github.com/LANCommander/LANCommander/pull/386) Evolving Migrations
- [#385](https://github.com/LANCommander/LANCommander/pull/385) Download endpoints get correct paths
- [#384](https://github.com/LANCommander/LANCommander/pull/384) Ensuring media path is correct
- [#383](https://github.com/LANCommander/LANCommander/pull/383) Adding SQLite database migration step
- [#382](https://github.com/LANCommander/LANCommander/pull/382) tags input fixes
[Mavyre](https://github.com/Mavyre)
- [#337](https://github.com/LANCommander/LANCommander/pull/337) Corrected Auto-updater
- [#336](https://github.com/LANCommander/LANCommander/pull/336) Added redistributables to Games API and Games manifest
- [#335](https://github.com/LANCommander/LANCommander/pull/335) Fixing scripts not saved anymore in Launcher
- [#334](https://github.com/LANCommander/LANCommander/pull/334) Save Games no longer shared between user
- [#327](https://github.com/LANCommander/LANCommander/pull/327) Corrected routes for Play Sessions tracking
[RattleSN4K3](https://github.com/RattleSN4K3)
- [#322](https://github.com/LANCommander/LANCommander/pull/322) Re-implement registry save paths game save creation in Launcher
- [#321](https://github.com/LANCommander/LANCommander/pull/321) Fix game end lag caused by generating game save
- [#319](https://github.com/LANCommander/LANCommander/pull/319) Fix editing collection and other metadata pages
- [#318](https://github.com/LANCommander/LANCommander/pull/318) Fix generating of thumbnails on importing games
- [#316](https://github.com/LANCommander/LANCommander/pull/316) Error messaging for login/register via Launcher
- [#308](https://github.com/LANCommander/LANCommander/pull/308) Bypass password policy on first-time-setup for administrator creation
- [#307](https://github.com/LANCommander/LANCommander/pull/307) Launcher: Fix connecting to hostname:port Uri
- [#306](https://github.com/LANCommander/LANCommander/pull/306) Launcher: Rework on offline mode and connection timeout
- [#305](https://github.com/LANCommander/LANCommander/pull/305) Launcher: Configure logger with log level from debug settings
- [#303](https://github.com/LANCommander/LANCommander/pull/303) Fix context menu items not triggering on click
- [#291](https://github.com/LANCommander/LANCommander/pull/291) Library changes to addon installation
- [#290](https://github.com/LANCommander/LANCommander/pull/290) Update archive's file sizes for a specific archive/game
- [#289](https://github.com/LANCommander/LANCommander/pull/289) Depot game changes to update view
- [#287](https://github.com/LANCommander/LANCommander/pull/287) Remove games via dialog for dependent games
- [#286](https://github.com/LANCommander/LANCommander/pull/286) Fix crash of base game removal of user Library
- [#283](https://github.com/LANCommander/LANCommander/pull/283) Fix Avatar uploading
- [#282](https://github.com/LANCommander/LANCommander/pull/282) Fix packaging and uploading save games by Launcher
- [#281](https://github.com/LANCommander/LANCommander/pull/281) Fix storing several entities (such as Gamesaves) properly
- [#277](https://github.com/LANCommander/LANCommander/pull/277) Fix importing tags on Lookup from IGDB
- [#272](https://github.com/LANCommander/LANCommander/pull/272) Standalone mods/expansions as Base Game
- [#271](https://github.com/LANCommander/LANCommander/pull/271) Fix changing base game of a game
- [#269](https://github.com/LANCommander/LANCommander/pull/269) Option to delete selected Media entries
- [#268](https://github.com/LANCommander/LANCommander/pull/268) Expiring Archives cache after recalculating/updating archives
- [#260](https://github.com/LANCommander/LANCommander/pull/260) Utilizing MadMilkman.INI for ini parsing
- [#257](https://github.com/LANCommander/LANCommander/pull/257) Fix "Name Change Script" from context menu nulling username in game files
More contributions are always welcome! If you would like to know how to help and have any of these skills, please reach out via Discord!
- Documentation maintenance
- ASP.NET Blazor development
- Discord moderation
- Docker image maintenance (we need more game servers!)
- Localization translation
- Avalonia development
And as always with a shameless plug, donations are welcome. Currently the best way to support the project is to [sign up over at Patreon](https://www.patreon.com/LANCommander). There's currently no special tiers for higher members other than getting a special "Bawler" role in the Discord and any locked posts are still accessible via the free tier.

View file

@ -0,0 +1,32 @@
---
title: 2.0.1
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
# LANCommander 2.0.1 Release Notes
## New Features
### Remote Server Engine
A new "Remote" server engine has been added as a way to link multiple LANCommander servers together. This can be configured from Settings -> Servers. Once configured / authenticated, servers defined in one instance can reference a server in the other. For example, if your main instance is hosted on a Linux host, you can add servers from another LANCommander instance running on a Windows host. All of the standard autostart / launcher integration features apply as well!
## Bug Fixes / Misc Improvements
- Script saving has been fixed across the board
- Media thumbnails should be loaded after new media is added via the media editor
- Imported addons will now be linked to their base game upon import, if the base game already exists
- The launcher's play button should now report correct state when games are starting/running
- Games should now take less time to start
- Avatar downloading in the launcher has been fixed
- The dropdown for changing default role now correctly shows the selected value
- Games larger than 10GB will now extract properly
- UI stutter during game extraction should be reduced
- The time to import games on the launcher should now be substantially reduced
- Admin scripts written to disk now follow the standard PowerShell RunAsAdministrator directive
- Admin scripts now execute properly and can be debugged in the launcher
- An issue causing scripts not to save properly upon download has been fixed
- Lobby actions for games have had their functionality restored in the launcher
- Server variables (`{ServerHost}`, `{ServerPort}`) should now be populated correctly when starting an action
## Downloads
<ReleaseDownloads release="v2.0.1" />

View file

@ -0,0 +1,17 @@
---
title: 2.0.2
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
# LANCommander 2.0.2 Release Notes
## Bug Fixes / Misc Improvements
- Fixed an issue where large scripts could not be added or updated
- Fixed actions dialog in launcher to only show actions for installed addons
- Reworked the script editor to ensure changes are being captured reliably
- Fixed installation of redistributables
## Downloads
<ReleaseDownloads release="v2.0.2" />

View file

@ -0,0 +1,195 @@
---
title: 2.1.0-rc1
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
import ScreenshotCarousel from '@site/src/components/ScreenshotCarousel';
import ContributorGrid from '@site/src/components/ContributorGrid';
# LANCommander 2.1.0 Release Candidate 1 Release Notes
## Breaking Changes
### .NET 10
LANCommander has been upgraded from .NET 8 to .NET 10. This should be a seamless transition as all LANCommander binaries are self-contained and include the appropriate .NET runtime. However, if you are running an older version of Windows that does not support .NET 10, you may encounter issues running this version of LANCommander. The minimum supported Windows version is now Windows 10 version 1809.
## Launching Into the Future
To kick things off, LANCommander 2.1.0 introduces a new launcher. As a recap, the previous launcher was built using .NET Blazor and wrapped into a Webview2 chrome using the open source project [Photino](https://www.tryphotino.io/). This decision was originally made in an effort to maintain a somewhat shared codebase with the server web UI.
Unfortunately, over time limitations of the platform made it apparent that a change had to be made. Community member [aaronpowell](https://github.com/aaronpowell) stepped up and built out a proof of concept for a launcher using Avalonia, an open-source native cross-platform UI framework for .NET. The potential was immediately clear and the decision was made to jump in head first. The following months saw the project refocus on build a new launcher with a major emphasis on performance, reliability, design, and feature parity.
The result is, well, a project that went off a little off the rails in the best possible fashion:
<ScreenshotCarousel screenshots={[
{ src: require('./_Assets/2.1.0 - Launcher.jpg').default, alt: 'Launcher', label: 'Launcher', caption: 'The brand new Avalonia-based launcher' },
{ src: require('./_Assets/2.1.0 - Shelf.jpg').default, alt: 'Shelf View', label: 'Shelf View', caption: 'Browse your library in the new shelf layout' },
{ src: require('./_Assets/2.1.0 - Depot.jpg').default, alt: 'Depot', label: 'Depot', caption: 'The redesigned depot with categorization carousels' },
{ src: require('./_Assets/2.1.0 - Screenshot.jpg').default, alt: 'Game Details', label: 'Game Details', caption: 'Screenshots and videos displayed right in the detail view' },
{ src: require('./_Assets/2.1.0 - Downloads.png').default, alt: 'Download Queue', label: 'Download Queue', caption: 'Track every step of the installation process' },
]} />
I think the screenshots speak for themselves. Okay, not quite, there's a lot of fun stuff in this build so let's dive right in!
### User Library
Previous versions of the launcher attempted to bring the art of a game to the forefront. These views are where most players spend their time interacting with the application. However, everyone's tastes are different. As such the library now contains three main views; cover grid, list, and shelf.
![Shelf View](./_Assets/2.1.0%20-%20Shelf.jpg)
### Game Details
Browsing the user library is great and all, but the detail view is where a game really gets to shine. Metadata has shifted to the right-hand side, descriptions now support more complex formatting with Markdown rendering support, and _finally_ videos and screenshots are displayed in a carousel. A lightbox viewer lets you zoom in on screenshots, watch videos, and even read game manuals right from the detail view thanks to the new built-in PDF viewer.
![Game Screenshot Detail](./_Assets/2.1.0%20-%20Screenshot.jpg)
This is really just the start of something new. Expect this part of the launcher to be built out more in the future with better publishing tools and metadata expansion.
### Depot
The depot has been overhauled too, probably more than any other part of the launcher. Previous iterations have been _fine_, but with large game sets it was hard to find just what you needed. The new depot puts a focus on categorization while maintaining granular filtering tools to get you exactly where you need to go.
![Depot](./_Assets/2.1.0%20-%20Depot.jpg)
As you can see, this section more than any other benefits from high quality metadata. We'll get into that later, but first let's talk about game installation.
### Download Queue
Game installation and downloading has been the hardest component to develop in the entire LANCommander platform. And rightly so! Without game installation, all you get is a fancy web UI and a pretty launcher that can't launch any games.
The LANCommander launcher is essentially a UI wrapper for the LANCommander SDK. The SDK is a .NET library that provides all of the useful bits of communicating with a LANCommander server install. Authentication, script execution, save management, game installation and so much more are provided through this library. In previous iterations, the SDK had a very strict execution plan for installing games. Often times this would lead to deadlocks and outright failures.
To resolve this in 2.1.0, the game install process has moved to a two-layer queuing system. The first layer of the queue manages what actually enters the download queue. This could be a game, redistributable, tool, or addon. The second layer handles any subprocessing step such as executing the different script types, save downloading, existing file verification, etc. The result is a system that is more transparent and reliable.
This queuing system has been exposed somewhat in the download queue:
![Download Queue](./_Assets/2.1.0%20-%20Downloads.png)
Each entry in the download queue will now report each step of the process during a game installation. In addition to looking a bit less mystical, this should give users an idea of what _exactly_ is happening during a game installation.
### Discord Rich Presence
The launcher now integrates with Discord Rich Presence. When you're playing a game through LANCommander, your Discord status will update to show what you're playing. By default this will show as LANCommander with the game name as the status. If the game is mapped to the correct Discord ID (via external IDs in the server), then the game's official Discord assets will be used for a more polished presentation.
### Offline Mode
Previous attempts at an offline mode in the launcher were pretty clunky and unreliable. In 2.1.0, the launcher now has a proper offline mode that allows you to launch previously installed games even when the server is unreachable.
### Single Sign-On
SSO was supported in the previous launcher, but it was a bit of an afterthought and not super reliable. In the new launcher it's been given more attention and should work as expected.
### Chat
As with the last version, the chat functionality remains pretty barebones. This is an area that will get more attention in the future, but it _is_ currently available in the new launcher. It might even work a little better.
## Server Improvements
This release should also be pretty exciting for server admins. With a new launcher with shiny new features brings a set of tools to manage them.
### LANCommander HQ
A new metadata provider has been added: LANCommander HQ. This gives server admins another option for sourcing game metadata and media alongside IGDB and SteamGridDB. In addition, support for external IDs beyond IGDB has been introduced, making it easier to cross-reference games across multiple platforms and services. To get started with the HQ, link your Discord account under "Integrations / HQ" in the server settings. Existing metadata providers such as IGDB and SteamGridDB are still available as well, so no functionality has been lost from previous versions.
### Preview Tool
A new "Preview" editing view has been added to the server for managing game art. Inspired by the launcher's presentation, this tool gives admins a better sense of how their media will look in the launcher before publishing. Screenshots and videos can now be uploaded in bulk, and the media grabber supports selecting multiple assets at once. Animated covers are now supported as well, APNG images grabbed from SteamGridDB are automatically converted to video and should display in the new launcher.
![Preview Tool](./_Assets/2.1.0%20-%20Server%20-%20Preview.png)
### Script Editor Improvements
The script editor has received a significant upgrade. Context-aware code completions now suggest built-in PowerShell cmdlets, LANCommander-specific variables, and generated type definitions. Validation has been improved, and script errors are now logged rather than silently swallowed. A new `Set-GameDetails` cmdlet has been added for specifying package details from within scripts.
### Redistributable Options
Redistributables now support configurable options. Options can be defined with a schema editor, given display names, and are preserved across import/export. Assigning redistributables to a game can now be accessed under the "Redistributables" section of the game editor. When selected, a redistributable with options will render as an easy-to-use form and per-game overrides can be defined.
![Redistributable Options](./_Assets/2.1.0%20-%20Server%20-%20Redistributable%20Options.png)
A new **RunWrapper** script type has been added as well, giving admins more control over how redistributables and games are executed. This should enhance redistributables to cover more use cases such as emulators, launchers, and compatiblity shims like Proton or dgVoodoo.
### Key Management
Key allocation has been improved. The allocation method on keys is now nullable, and a new manual key assignment dialog has been added. The logic for choosing the next available key has also been reworked.
### Media & Tooling
Server settings now include checks for ffmpeg and yt-dlp availability, with automatic installation support in Docker environments. A new streaming endpoint for media has been added, improving video playback in the launcher. Automatic downloads for Steam screenshots have been added as well. YouTube video support has been added to the media grabber, and ffmpeg is now used for video processing tasks such as APNG conversion and thumbnail generation.
## LANCommander HQ
Last but far from least, this release coincides with the launch of [LANCommander HQ](https://hq.lancommander.com), a new metadata provider and community hub for LANCommander users. The HQ aims to provide a centralized platform for game metadata and media across multiple providers, while also normalizing and enhancing that data with a focus on the needs of LANCommander users. There's a bit to cover, so let's dive in!
### Metadata
LANCommander HQ aggregates metadata from multiple sources including IGDB, SteamGridDB, and user contributions. This data is then normalized and enhanced with a focus on the needs of LANCommander users. There are many metadata providers out there, and unfortunately they all have their own unique schemas and data quality issues. The HQ's goal is to provide a single source of truth for game metadata that can be consumed by LANCommander servers and launchers.
There are a lot of exciting features in the works for the HQ, but for this release the focus is on building out the core infrastructure and integrating with the new launcher. Expect to see more features and improvements in the future as the HQ continues to evolve.
### Community
The main features of the HQ are currently focused on metadata, but there is the potential to expand it into a broader community hub for LANCommander users. This could include an area to share game configurations, scripts, and other resources. Additionally, there are some content moderation tools in place that should allow the HQ to host user-generated content and provide the best possible experience for all users. If you have a knack for curation and want to help out, there will likely be opportunities to get involved in the future!
### Tiering / Subscriptions
The HQ operates on a tiered subscription model. Currently there are only two tiers: Basic (Free) and Premium ($5/month). The Basic tier provides access to the core metadata features of the HQ, while the Premium tier provides access to additional features such as enhanced metadata, relaxed rate limiting, and more. More information will be provided in the near future and will be provided on this site under the [LANCommander HQ](/hq) section. All paid subscriptions on the HQ are handled by Stripe, so every transaction is secure and your personal information is protected.
This subscription model is primarily in place to help cover the costs of running the HQ while also providing a sustainable path forward for continued development of LANCommander itself. This entire project is developed and maintained by essentially one person with some help from the community here and there. While this is a labor of love, there are real costs associated with running the infrastructure for the HQ and continued development of LANCommander. The subscription model is designed to be as accessible as possible while also providing a way for users to support the project if they find value in it.
As such, this will probably mean that the Patreon page will be going away in the near future. For existing patrons, it is recommended to switch over to the new subscription model on the HQ as it provides a more direct way to support the project and access the benefits of the HQ _today_.
## Contributions
This release includes contributions from the following community members:
**@aaronpowell | PR [#395](https://github.com/LANCommander/LANCommander/pull/395): Avalonia Launcher**
A substantial 42-commit, 6,000-line PR that bootstrapped the cross-platform Avalonia launcher. Aaron built out the core application structure, service wiring, and async initialization, and then built out the UI with filtered depot browsing and grid layout, a download queue, install/uninstall flows, a settings page, PDF manual viewer, game action bars, and a full CI pipeline for Windows, Linux, and macOS builds. A lot of the foundation that the current launcher sits on came from this PR.
**@akifreak | PR [#398](https://github.com/LANCommander/LANCommander/pull/398): Fix Game Archive Upload**
Discovered that archive uploads through the web UI were silently broken after upgrading to v2.0.2, traced the problem through the JavaScript bundle, and submitted 11 targeted fixes rather than a single bug report. The fixes covered ES module loading, null-guards on DOM elements, a FileMode.Append seek conflict, incorrect archive ID passing, and error propagation throughout the upload pipeline. The uploading process should feel much more stable now!
**@MasterMNB | PR [#400](https://github.com/LANCommander/LANCommander/pull/400): Avalonia Launcher Back Button**
Improved the visibility and styling of the back button in the `GameDetailView`, which was previously hard to distinguish from the background. It's a small change, but it's a real usability improvement that makes navigation feel more intentional.
## Changelog
**Launcher**
- Added: Brand new launcher built on Avalonia
- Added: Screenshots and videos in game detail carousel with lightbox viewer
- Added: Redesigned depot with categorization carousels and advanced filtering
- Added: Tools support
- Added: Download transfer speed graph
- Added: OS notifications upon game installation completion
- Added: Discord Rich Presence integration
- Added: Offline mode for playing games without server connectivity
- Added: SSO/OIDC external authentication provider support
- Added: Built-in PDF viewer for game manuals
- Added: Splash screen on startup
- Added: Verify files option in game dropdown
- Added: Markdown rendering for game descriptions
- Changed: Refactored game/redistributable/tool install process to two-layer queue system
- Changed: Imports should be faster and execute in the background without causing major UI hiccups
- Changed: Improved memory management with proper disposal of HTTP clients, RPC clients, and cancellation tokens
- Fixed: Installing games that already have files on disk will show the verification process during install
- Fixed: Logging in and logging out should no longer cause a hung UI
- Fixed: Install-uninstall-reinstall cycle no longer locks the game
- Fixed: Authentication token race condition causing crashes after login
**Server**
- Added: LANCommander HQ metadata provider support
- Added: Support for external IDs beyond IGDB
- Added: New launcher-inspired "Preview" tool for managing art
- Added: Context-aware script editor with code completions, validation, and PowerShell type definitions
- Added: Redistributable options with schema editor and display names
- Added: RunWrapper script type
- Added: Package versioning for archives
- Added: Bulk upload for screenshots and videos
- Added: Multiple select support in media grabber
- Added: Animated cover support (APNG auto-conversion to video)
- Added: Steam screenshot downloads
- Added: YouTube video support in media grabber
- Added: Media streaming endpoint
- Added: ffmpeg/yt-dlp availability checks and auto-install in Docker
- Added: Manual key assignment dialog and reworked key allocation
- Added: `Set-GameDetails` cmdlet for package scripts
- Fixed: Media uploads should now behave as expected
- Fixed: Game archive uploads in web UI
- Fixed: Orphaned files tool
- Fixed: Redistributable archive downloads
- Fixed: Script working directory for games
- Fixed: SteamCMD login when SteamGuard is enabled
- Fixed: Import file cleanup after completion
**Infrastructure**
- Upgraded to .NET 10
- Added ARM64 build support
- Removed legacy Photino launcher from CI
- Added nightly builds for Avalonia launcher
## Downloads
<ReleaseDownloads release="v2.1.0-rc1" />
## Contributors
<ContributorGrid from="v2.0.2" to="v2.1.0-rc1" />

View file

@ -0,0 +1,99 @@
---
title: 2.1.0-rc2
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
import ScreenshotCarousel from '@site/src/components/ScreenshotCarousel';
import ContributorGrid from '@site/src/components/ContributorGrid';
# LANCommander 2.1.0 Release Candidate 2 Release Notes
## Variable Picker Dialog
A new variable picker dialog has been added to the action editor and save path editor in the server UI. When editing action arguments, working directories, or save paths, a new button opens a dialog that lets you browse and insert LANCommander variables (`{InstallDir}`, `{DisplayWidth}`, etc.), environment variables (`%APPDATA%`, `%LOCALAPPDATA%`, etc.), and special folder paths (`%MyDocuments%`, `%Desktop%`, etc.) with a single click. No more needing to remember exact variable names or syntax.
## Redistributable Improvements
### File Tracking and Cleanup
Redistributable files installed into a game's directory are now tracked. When a game is uninstalled, any files that were extracted by redistributables are cleaned up alongside the game's own files. This prevents leftover redistributable artifacts from lingering in the install directory after uninstallation.
### Detect Install Script Behavior
The redistributable install detection logic has been updated. If a redistributable does not have a detect install script defined, the install script will now always run rather than being skipped. This ensures that redistributables without detection scripts are reliably installed every time, which is the expected behavior for most simple redistributable configurations.
## Package Script Filtering
Package scripts are now filtered out of API responses for games, redistributables, and tools. These scripts are used internally during the packaging process and should not be sent to clients. This filtering is applied at both the endpoint and AutoMapper levels, ensuring that package scripts never leak to launchers or other API consumers.
## Launcher CLI Improvements
### Full Command Line Verb Support
The Avalonia launcher now supports the full set of CLI verbs: `RunScript`, `Install`, `Uninstall`, `Run`, `Sync`, `Import`, `Export`, `Upload`, `Login`, `Logout`, and `ChangeAlias`. Previously only `RunScript` was supported in headless mode. The `--help` and `--version` flags are also now recognized. Console logging has been added for headless execution.
### CLI Project Removal
The standalone `LANCommander.Launcher.CLI` project has been removed. All CLI functionality is now handled directly by the Avalonia launcher's headless mode, consolidating the codebase and eliminating the need for a separate CLI binary.
## C++ SDK
A new C++ SDK (`LANCommander.SDK.Cpp`) has been introduced. Written in C++14, it provides maximum compatibility from Windows 95 through modern platforms. The SDK includes 16 API clients covering authentication, games, library, saves, media, tools, depot, and more. It ships with two HTTP backend implementations (WinINet for Windows and libcurl for cross-platform) and uses vendored cJSON for zero mandatory external dependencies. This SDK is the foundation that powers the legacy launcher.
## Legacy Launcher
:::note A Fun Detour
The legacy launcher is a fun side project and detour from the main development track. It should not be regarded as a primary concern or a supported production launcher. Think of it as an experiment in seeing just how far back LANCommander's reach can extend.
:::
The legacy launcher is a native Win32 application written in C++ that targets Windows 9x (Windows 95/98/ME) and beyond. Built on Allegro 4 for rendering and GDI+ for image decoding (JPEG cover art, backgrounds, etc.), it provides a surprisingly full-featured LANCommander experience on vintage hardware. GDI+ is a requirement and must be available on the target system. For Windows 9x systems, this means installing the [GDI+ redistributable](https://www.microsoft.com/en-us/download/details.aspx?id=18909).
Features include:
- **Library and Depot browsing** with cover art grid
- **Game detail view** with metadata and screenshots
- **Download queue** with progress tracking
- **Login screen** with server authentication
- **Settings screen** with configurable server address
- **SQLite-backed local database** for offline game metadata
- **Custom window chrome** with a themed UI
- **Unicode support** for international character sets
<ScreenshotCarousel screenshots={[
{ src: require('./_Assets/2.1.0-rc2 - Legacy Launcher - Login.png').default, alt: 'Legacy Launcher Login', label: 'Login', caption: 'The login screen running on Windows 98' },
{ src: require('./_Assets/2.1.0-rc2 - Legacy Launcher - Library.png').default, alt: 'Legacy Launcher Library', label: 'Library', caption: 'Browsing the game library with cover art' },
{ src: require('./_Assets/2.1.0-rc2 - Legacy Launcher - Detail.png').default, alt: 'Legacy Launcher Detail', label: 'Game Detail', caption: 'Game detail view with metadata' },
{ src: require('./_Assets/2.1.0-rc2 - Legacy Launcher - Downloads.png').default, alt: 'Legacy Launcher Downloads', label: 'Downloads', caption: 'The download queue in action' },
{ src: require('./_Assets/2.1.0-rc2 - Legacy Launcher - Settings.png').default, alt: 'Legacy Launcher Settings', label: 'Settings', caption: 'Configuring server connection settings' },
]} />
## Changelog
**Server**
- Added: Variable picker dialog for action editor and save path editor
- Fixed: Package scripts are no longer exposed through API responses
- Fixed: Script helper now correctly maps Package and RunWrapper script filenames
**Launcher**
- Added: Full set of CLI verbs (Install, Uninstall, Run, Sync, Import, Export, Upload, Login, Logout, ChangeAlias)
- Added: Console logging for headless CLI execution
- Changed: Redistributable files are now tracked and cleaned up on uninstall
- Changed: Redistributables without detect install scripts now always run the install script
- Fixed: Script writing for all script types
- Removed: Standalone CLI project (functionality merged into Avalonia launcher)
**Legacy Launcher**
- Added: Complete native Win32 launcher targeting Windows 9x
- Added: Library and depot browsing with cover art grid
- Added: Game detail view with metadata display
- Added: Download queue with SQLite-backed local database
- Added: Settings screen with YAML configuration
- Added: Custom themed window chrome
- Added: Unicode support for Win9x targets
- Added: CI workflow for automated builds
**SDK**
- Added: C++ SDK (LANCommander.SDK.Cpp) with C++14 compatibility
- Added: WinINet and libcurl HTTP backend implementations
- Added: 16 API clients covering full LANCommander API surface
- Changed: Game list filtering now shows only standalone mods/expansions and main games
## Downloads
<ReleaseDownloads release="v2.1.0-rc2" />
## Contributors
<ContributorGrid from="v2.1.0-rc1" to="v2.1.0-rc2" />

View file

@ -0,0 +1,73 @@
---
title: 2.1.0-rc3
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
import ContributorGrid from '@site/src/components/ContributorGrid';
# LANCommander 2.1.0 Release Candidate 3 Release Notes
## Server Discovery
The Avalonia launcher now supports automatic server discovery using the beacon system. When connecting to a server, the launcher can discover LANCommander servers on the local network without needing to manually enter an address. This feature was already present in the legacy Photino launcher and has now been brought forward to the new Avalonia-based launcher.
## Launcher Improvements
### List View Rework
The list view in the launcher library has been reworked for a cleaner presentation and better usability. It is essentially a reimagination of the library view in the previous launcher.
### Carousel Fixes
Cover art in carousels has received several visual fixes. The shadow overlay on covers now renders correctly, and covers are no longer cut off at the bottom when hovered. These are small polish items that improve the overall look and feel of the depot and library views.
### Play Button State
The play button now correctly reflects the current state of a running game, showing the appropriate playing/stop indicator when a game is actively running.
### Performance Optimizations
General launcher optimizations have been made to improve responsiveness and reduce resource usage.
## Bug Fixes
### Redistributable and Tool Importing
Fixed an issue where importing redistributables and tools from an LCX file was not working correctly.
### Linux Path Expansion
Path variable expansion now correctly preserves the Linux root (`/`) prefix. Previously, expanding variables like `{InstallDir}` on Linux could strip the leading slash, resulting in incorrect paths.
## Contributions
This release includes contributions from the following community members:
**@akifreak | PR [#403](https://github.com/LANCommander/LANCommander/pull/403): Fix Beacon Discovery**
Back for another round of contributions, akifreak tracked down and fixed an issue where server discovery wasn't working reliably. In some cases the launcher was actually finding servers on the network but wasn't surfacing them in the UI. The fix spans the full discovery pipeline; making the probe socket more resilient to bad data, ensuring beacon responses actually reach the client, and refreshing the server list as soon as a new server is found.
## Changelog
**Launcher**
- Added: Automatic server discovery via beacon system
- Added: Play session sync on game import
- Added: Additional save handling logging
- Changed: Reworked list view layout
- Changed: Performance optimizations
- Changed: Increased cover art rendering quality
- Changed: Action bar layout and behavior adjustments
- Fixed: Shadow overlay on covers in carousels
- Fixed: Cover art clipping on hover in carousels
- Fixed: Play button now shows correct playing/stop state when a game is running
- Fixed: Linux path variable expansion now preserves root prefix
**Server**
- Fixed: Redistributable and tool importing
- Fixed: Media settings page failing to render
**SDK**
- Fixed: Discovery probe socket resilience against malformed data
- Fixed: Broadcast send reliability in DiscoveryProbe
- Fixed: Beacon responses now forwarded to BeaconClient event
- Fixed: Save client now checks for newer server saves before uploading
- Added: Logging in save packaging and upload pipeline
## Downloads
<ReleaseDownloads release="v2.1.0-rc3" />
## Contributors
<ContributorGrid from="v2.1.0-rc2" to="v2.1.0-rc3" />

View file

@ -0,0 +1,57 @@
---
title: 2.1.0-rc4
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
import ContributorGrid from '@site/src/components/ContributorGrid';
# LANCommander 2.1.0 Release Candidate 4 Release Notes
## Big Screen Mode
The Avalonia launcher now supports a big screen mode designed for use with TVs and handhelds. When enabled, the launcher switches to a fullscreen layout optimized for focused gaming. This pairs with the new gamepad navigation support to make LANCommander usable from the sofa or a handheld without a keyboard or mouse.
## Gamepad Navigation
Full gamepad support has been added to the Avalonia launcher. Controllers are detected and mapped using SDL2, and directional navigation has been implemented across the library views, carousels, game detail pages, and overlay dialogs. Focus management handles moving between UI elements naturally with a d-pad or analog stick, making it possible to browse, install, and launch games entirely with a controller. This feature is still a bit rough around the edges, but should improve in future releases.
## RunWrapper Scripts for Redistributables
RunWrapper scripts were previously defined as a script type but never actually executed. This release adds full execution support during game launch. RunWrapper scripts now run alongside the game process with proper state tracking, cancellation support, and child process cleanup when the game exits.
## Packaging Dialog
The "Package" button has been moved from individual game, redistributable, and tool edit pages to the archive editor. A new packaging dialog provides a consolidated view of the packaging process, making it easier to build and manage archives directly from the archive list.
## Bug Fixes
- Fixed scrollbar being overlapped by the titlebar in the launcher
- Fixed HQ metadata lookups by updating the HQ SDK dependency
- Fixed media downloads failing during game import
- Fixed icon downloading
- Fixed server save check incorrectly determining newer saves were available
## Changelog
**Launcher**
- Added: Big screen mode for fullscreen TV/controller use
- Added: Gamepad navigation across library, carousels, detail views, and overlays
- Fixed: Scrollbar overlapped by titlebar
- Fixed: Media download on import
- Fixed: Icon downloading
**Server**
- Added: Packaging dialog on archive editor
- Added: RunWrapper script type available for redistributable scripts
- Changed: Moved "Package" button from edit pages to archive editor
- Changed: Updated PowerShell code snippets
- Fixed: HQ metadata lookups
**SDK**
- Added: RunWrapper script execution for redistributables with process lifecycle management
- Changed: Multi-target framework support
- Changed: Source-generated cmdlet registration via new `CmdletRegistrationGenerator`
- Fixed: Save client check for newer saves on server
## Downloads
<ReleaseDownloads release="v2.1.0-rc4" />
## Contributors
<ContributorGrid from="v2.1.0-rc3" to="v2.1.0-rc4" />

View file

@ -0,0 +1,59 @@
---
title: 2.1.0-rc5
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
import ContributorGrid from '@site/src/components/ContributorGrid';
# LANCommander 2.1.0 Release Candidate 5 Release Notes
## LANCommander Packager
A new standalone Avalonia application, LANCommander Packager, has been added for building LCX package files outside of the server. The packager works by monitoring the files and registry entries created by a game's installer. You can then choose which files / registry keys are bundled into the final LCX file.
## Background Uploading
Archive uploads now continue in the background, allowing you to navigate away from the upload page without losing progress. An upload indicator in the sidebar tracks active uploads so you can continue working in other areas of the server while files transfer. This also applies to the import upload dialog.
## HQ Login in First Time Setup
The first time setup wizard now includes a step to log in to LANCommander HQ directly during initial configuration. This streamlines connecting to HQ for metadata and media lookups without needing to visit the settings page after setup is complete.
## File Manager Improvements
The server file manager has received several usability improvements including faster enumeration of large directories, toggleable columns, new Created and Type columns, fixed breadcrumb navigation, and a properly built directory tree with support for cross-platform roots and special folders.
## Bug Fixes
- Fixed standalone expansions and mods appearing in nested game lists (#284)
- Fixed missing nested game data
- Fixed save path matching failing on Windows due to backslash normalization (#266)
- Fixed log viewer not working under Settings / Logs (#393)
- Fixed user approval menu item not functioning (#405)
- Fixed archive processing when using "Use Local File"
- Fixed "Use Local File" button being incorrectly disabled
## Changelog
**Server**
- Added: Background uploading with sidebar progress indicator
- Added: HQ login step in first time setup wizard
- Added: Button to download current log file
- Changed: Improved file manager with faster directory enumeration, toggleable columns, and cross-platform directory tree
- Fixed: Log viewer under Settings / Logs (#393)
- Fixed: User approval menu item (#405)
- Fixed: Archive processing for "Use Local File"
- Fixed: "Use Local File" disabled button
- Fixed: Standalone expansions/mods shown in nested game lists (#284)
- Fixed: Missing nested game data
**Packager**
- Added: New standalone application for building LCX package files
- Added: CI workflow for building and releasing the packager
- Added: Documentation for packager usage and LCX format
**SDK**
- Fixed: Save path normalization to use forward slashes before regex matching (#266)
## Downloads
<ReleaseDownloads release="v2.1.0-rc5" />
## Contributors
<ContributorGrid from="v2.1.0-rc4" to="v2.1.0-rc5" />

View file

@ -0,0 +1,678 @@
---
title: 2.1.0
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
import ScreenshotCarousel from '@site/src/components/ScreenshotCarousel';
import ContributorGrid from '@site/src/components/ContributorGrid';
# LANCommander 2.1.0 Release Notes
:::tip Latest Version
This page covers the full LANCommander 2.1 series. The latest patch is **2.1.9** — see [Patch Updates](#patch-updates) below for what's changed since the initial release.
:::
LANCommander 2.1.0 is a landmark release that touches virtually every part of the platform. A brand new launcher built on Avalonia, a standalone packager application, a C++ SDK powering a legacy Win32 launcher, major server improvements, and the launch of LANCommander HQ all come together in what has been the most ambitious update cycle to date.
## Breaking Changes
### .NET 10
LANCommander has been upgraded from .NET 8 to .NET 10. This should be a seamless transition as all LANCommander binaries are self-contained and include the appropriate .NET runtime. However, if you are running an older version of Windows that does not support .NET 10, you may encounter issues running this version of LANCommander. The minimum supported Windows version is now Windows 10 version 1809.
## Launching Into the Future
To kick things off, LANCommander 2.1.0 introduces a new launcher. As a recap, the previous launcher was built using .NET Blazor and wrapped into a Webview2 chrome using the open source project [Photino](https://www.tryphotino.io/). This decision was originally made in an effort to maintain a somewhat shared codebase with the server web UI.
Unfortunately, over time limitations of the platform made it apparent that a change had to be made. Community member [aaronpowell](https://github.com/aaronpowell) stepped up and built out a proof of concept for a launcher using Avalonia, an open-source native cross-platform UI framework for .NET. The potential was immediately clear and the decision was made to jump in head first. The following months saw the project refocus on building a new launcher with a major emphasis on performance, reliability, design, and feature parity.
The result is, well, a project that went off a little off the rails in the best possible fashion:
<ScreenshotCarousel screenshots={[
{ src: require('./_Assets/2.1.0 - Launcher.jpg').default, alt: 'Launcher', label: 'Launcher', caption: 'The brand new Avalonia-based launcher' },
{ src: require('./_Assets/2.1.0 - Shelf.jpg').default, alt: 'Shelf View', label: 'Shelf View', caption: 'Browse your library in the new shelf layout' },
{ src: require('./_Assets/2.1.0 - Depot.jpg').default, alt: 'Depot', label: 'Depot', caption: 'The redesigned depot with categorization carousels' },
{ src: require('./_Assets/2.1.0 - Screenshot.jpg').default, alt: 'Game Details', label: 'Game Details', caption: 'Screenshots and videos displayed right in the detail view' },
{ src: require('./_Assets/2.1.0 - Downloads.png').default, alt: 'Download Queue', label: 'Download Queue', caption: 'Track every step of the installation process' },
]} />
I think the screenshots speak for themselves. Okay, not quite, there's a lot of fun stuff in this build so let's dive right in!
### User Library
Previous versions of the launcher attempted to bring the art of a game to the forefront. These views are where most players spend their time interacting with the application. However, everyone's tastes are different. As such the library now contains three main views; cover grid, list, and shelf.
![Shelf View](./_Assets/2.1.0%20-%20Shelf.jpg)
### Game Details
Browsing the user library is great and all, but the detail view is where a game really gets to shine. Metadata has shifted to the right-hand side, descriptions now support more complex formatting with Markdown rendering support, and _finally_ videos and screenshots are displayed in a carousel. A lightbox viewer lets you zoom in on screenshots, watch videos, and even read game manuals right from the detail view thanks to the new built-in PDF viewer.
![Game Screenshot Detail](./_Assets/2.1.0%20-%20Screenshot.jpg)
This is really just the start of something new. Expect this part of the launcher to be built out more in the future with better publishing tools and metadata expansion.
### Depot
The depot has been overhauled too, probably more than any other part of the launcher. Previous iterations have been _fine_, but with large game sets it was hard to find just what you needed. The new depot puts a focus on categorization while maintaining granular filtering tools to get you exactly where you need to go.
![Depot](./_Assets/2.1.0%20-%20Depot.jpg)
As you can see, this section more than any other benefits from high quality metadata. We'll get into that later, but first let's talk about game installation.
### Download Queue
Game installation and downloading has been the hardest component to develop in the entire LANCommander platform. And rightly so! Without game installation, all you get is a fancy web UI and a pretty launcher that can't launch any games.
The LANCommander launcher is essentially a UI wrapper for the LANCommander SDK. The SDK is a .NET library that provides all of the useful bits of communicating with a LANCommander server install. Authentication, script execution, save management, game installation and so much more are provided through this library. In previous iterations, the SDK had a very strict execution plan for installing games. Often times this would lead to deadlocks and outright failures.
To resolve this in 2.1.0, the game install process has moved to a two-layer queuing system. The first layer of the queue manages what actually enters the download queue. This could be a game, redistributable, tool, or addon. The second layer handles any subprocessing step such as executing the different script types, save downloading, existing file verification, etc. The result is a system that is more transparent and reliable.
This queuing system has been exposed somewhat in the download queue:
![Download Queue](./_Assets/2.1.0%20-%20Downloads.png)
Each entry in the download queue will now report each step of the process during a game installation. In addition to looking a bit less mystical, this should give users an idea of what _exactly_ is happening during a game installation.
### Big Screen Mode & Gamepad Navigation
The launcher now supports a big screen mode designed for use with TVs and handhelds. When enabled, the launcher switches to a fullscreen layout optimized for focused gaming. Gamepad support has been added as well. Controllers are detected and mapped using SDL2, and directional navigation has been implemented across the library views, carousels, game detail pages, and overlay dialogs. Focus management handles moving between UI elements naturally with a d-pad or analog stick, making it possible to browse, install, and launch games with a controller. This feature is pretty barebones for now, but adds a foundation for deeper integration in the future.
### Discord Rich Presence
The launcher now integrates with Discord Rich Presence. When you're playing a game through LANCommander, your Discord status will update to show what you're playing. By default this will show as LANCommander with the game name as the status. If the game is mapped to the correct Discord ID (via external IDs in the server), then the game's official Discord assets will be used for a more polished presentation.
### Offline Mode
Previous attempts at an offline mode in the launcher were pretty clunky and unreliable. In 2.1.0, the launcher now has a proper offline mode that allows you to launch previously installed games even when the server is unreachable.
### Command Line Interface
The Avalonia launcher now handles all CLI functionality directly, replacing the previous standalone CLI project. The full set of CLI verbs is supported: `RunScript`, `Install`, `Uninstall`, `Run`, `Sync`, `Import`, `Export`, `Upload`, `Login`, `Logout`, and `ChangeAlias`. The `--help` and `--version` flags are also recognized, and console logging has been added for headless execution.
### Single Sign-On
SSO was supported in the previous launcher, but it was a bit of an afterthought and not super reliable. In the new launcher it's been given more attention and should work as expected.
### Chat
As with the last version, the chat functionality remains pretty barebones. This is an area that will get more attention in the future, but it _is_ currently available in the new launcher. It might even work a little better.
## Server Improvements
This release should also be pretty exciting for server admins. With a new launcher and shiny new features brings a set of tools to manage them.
### LANCommander HQ
A new metadata provider has been added: LANCommander HQ. This gives server admins another option for sourcing game metadata and media alongside IGDB and SteamGridDB. In addition, support for external IDs beyond IGDB has been introduced, making it easier to cross-reference games across multiple platforms and services. To get started with the HQ, link your Discord account under "Integrations / HQ" in the server settings. The first time setup wizard now includes a step to log in to HQ directly during initial configuration, streamlining the onboarding experience. Existing metadata providers such as IGDB and SteamGridDB are still available as well, so no functionality has been lost from previous versions. We'll cover more about HQ later on in these notes.
### Preview Tool
A new "Preview" editing view has been added to the server for managing game art. Inspired by the launcher's presentation, this tool gives admins a better sense of how their media will look in the launcher before publishing. Screenshots and videos can now be uploaded in bulk, and the media grabber supports selecting multiple assets at once. Animated covers are now supported as well, APNG images grabbed from SteamGridDB are automatically converted to video and should display in the new launcher.
![Preview Tool](./_Assets/2.1.0%20-%20Server%20-%20Preview.png)
### Script Editor Improvements
The script editor has received a significant upgrade. Context-aware code completions now suggest built-in PowerShell cmdlets, LANCommander-specific variables, and generated type definitions. Validation has been improved, and script errors are now logged rather than silently swallowed. A new `Set-GameDetails` cmdlet has been added for specifying package details from within scripts.
### Variable Picker Dialog
A new variable picker dialog has been added to the action editor and save path editor. When editing action arguments, working directories, or save paths, a new button opens a dialog that lets you browse and insert LANCommander variables (`{InstallDir}`, `{DisplayWidth}`, etc.), environment variables (`%APPDATA%`, `%LOCALAPPDATA%`, etc.), and special folder paths (`%MyDocuments%`, `%Desktop%`, etc.) with a single click.
### Redistributable Options
Redistributables now support configurable options. Options can be defined with a schema editor, given display names, and are preserved across import/export. Assigning redistributables to a game can now be accessed under the "Redistributables" section of the game editor. When selected, a redistributable with options will render as an easy-to-use form and per-game overrides can be defined.
![Redistributable Options](./_Assets/2.1.0%20-%20Server%20-%20Redistributable%20Options.png)
A new **RunWrapper** script type has been added as well, giving admins more control over how redistributables and games are executed. RunWrapper scripts now run alongside the game process with proper state tracking, cancellation support, and child process cleanup when the game exits. This should enhance redistributables to cover more use cases such as emulators, launchers, and compatibility shims like Proton or dgVoodoo.
### Packaging Dialog
The "Package" button has been moved from individual game, redistributable, and tool edit pages to the archive editor. A new packaging dialog provides a consolidated view of the packaging process, making it easier to build and manage archives directly from the archive list.
### Background Uploading
Archive uploads now continue in the background, allowing you to navigate away from the upload page without losing progress. An upload indicator in the sidebar tracks active uploads so you can continue working in other areas of the server while files transfer. This also applies to the import upload dialog.
### File Manager Improvements
The server file manager has received several usability improvements including faster enumeration of large directories, toggleable columns, new Created and Type columns, fixed breadcrumb navigation, and a properly built directory tree with support for cross-platform roots and special folders.
### Key Management
Key allocation has been improved. The allocation method on keys is now nullable, and a new manual key assignment dialog has been added. The logic for choosing the next available key has also been reworked.
### Media & Tooling
Server settings now include checks for ffmpeg and yt-dlp availability, with automatic installation support in Docker environments. A new streaming endpoint for media has been added, improving video playback in the launcher. Automatic downloads for Steam screenshots have been added as well. YouTube video support has been added to the media grabber, and ffmpeg is now used for video processing tasks such as APNG conversion and thumbnail generation.
## LANCommander Packager
A new standalone Avalonia application, LANCommander Packager, has been added for building LCX package files outside of the server. The packager works by monitoring the files and registry entries created by a game's installer. You can then choose which files and registry keys are bundled into the final LCX file. If the tool is authenticated to a LANCommander server instance, the game can be directly uploaded to the server as well.
<ScreenshotCarousel screenshots={[
{ src: require('./_Assets/2.1.0 - Packager - Monitor.png').default, alt: 'Packager Monitor', label: 'Monitor', caption: 'The Packager application will monitor the files and registry entries created during installation' },
{ src: require('./_Assets/2.1.0 - Packager - Select Files.png').default, alt: 'Packager Select Files', label: 'Select Files', caption: 'Files can be individually selected to include in the final import file' },
{ src: require('./_Assets/2.1.0 - Packager - Registry.png').default, alt: 'Packager Registry Entries', label: 'Registry Entries', caption: 'Registry entries can be individually selected and will be included in install/uninstall scripts' },
{ src: require('./_Assets/2.1.0 - Packager - Metadata.png').default, alt: 'Packager Metadata', label: 'Metadata', caption: 'Basic metadata can be defined before packaging' },
{ src: require('./_Assets/2.1.0 - Packager - Metadata Lookup.png').default, alt: 'Packager Metadata Lookup', label: 'Metadata Lookup', caption: 'Metadata can also be pulled in from metadata providers' },
{ src: require('./_Assets/2.1.0 - Packager - Select Executable.png').default, alt: 'Packager Select Executable', label: 'Select Executable', caption: 'The primary action can be defined before packaging' },
{ src: require('./_Assets/2.1.0 - Packager - Generate Package.png').default, alt: 'Packager Generate Package', label: 'Generate Package', caption: 'The final package can be created as an .LCX file or uploaded directly to a server' },
]} />
## C++ SDK & Legacy Launcher
### C++ SDK
A new C++ SDK (`LANCommander.SDK.Cpp`) has been introduced. Written in C++14, it provides maximum compatibility from Windows 95 through modern platforms. The SDK includes 16 API clients covering authentication, games, library, saves, media, tools, depot, and more. It ships with two HTTP backend implementations (WinINet for Windows and libcurl for cross-platform) and uses vendored cJSON for zero mandatory external dependencies. This SDK is the foundation that powers the legacy launcher.
### Legacy Launcher
:::note A Fun Detour
The legacy launcher is a fun side project and detour from the main development track. It should not be regarded as a primary concern or a supported production launcher. Think of it as an experiment in seeing just how far back LANCommander's reach can extend.
:::
The legacy launcher is a native Win32 application written in C++ that targets Windows 9x (Windows 95/98/ME) and beyond. Built on Allegro 4 for rendering and GDI+ for image decoding (JPEG cover art, backgrounds, etc.), it provides a surprisingly full-featured LANCommander experience on vintage hardware. GDI+ is a requirement and must be available on the target system. For Windows 9x systems, this means installing the [GDI+ redistributable](https://www.microsoft.com/en-us/download/details.aspx?id=18909).
Features include:
- **Library and Depot browsing** with cover art grid
- **Game detail view** with metadata and screenshots
- **Download queue** with progress tracking
- **Login screen** with server authentication
- **Settings screen** with configurable server address
- **SQLite-backed local database** for offline game metadata
- **Custom window chrome** with a themed UI
- **Unicode support** for international character sets
<ScreenshotCarousel screenshots={[
{ src: require('./_Assets/2.1.0-rc2 - Legacy Launcher - Login.png').default, alt: 'Legacy Launcher Login', label: 'Login', caption: 'The login screen running on Windows 98' },
{ src: require('./_Assets/2.1.0-rc2 - Legacy Launcher - Library.png').default, alt: 'Legacy Launcher Library', label: 'Library', caption: 'Browsing the game library with cover art' },
{ src: require('./_Assets/2.1.0-rc2 - Legacy Launcher - Detail.png').default, alt: 'Legacy Launcher Detail', label: 'Game Detail', caption: 'Game detail view with metadata' },
{ src: require('./_Assets/2.1.0-rc2 - Legacy Launcher - Downloads.png').default, alt: 'Legacy Launcher Downloads', label: 'Downloads', caption: 'The download queue in action' },
{ src: require('./_Assets/2.1.0-rc2 - Legacy Launcher - Settings.png').default, alt: 'Legacy Launcher Settings', label: 'Settings', caption: 'Configuring server connection settings' },
]} />
## LANCommander HQ
Last but far from least, this release coincides with the launch of [LANCommander HQ](https://hq.lancommander.com), a new metadata provider and community hub for LANCommander users. The HQ aims to provide a centralized platform for game metadata and media across multiple providers, while also normalizing and enhancing that data with a focus on the needs of LANCommander users. There's a bit to cover, so let's dive in!
### Metadata
LANCommander HQ aggregates metadata from multiple sources including IGDB, SteamGridDB, and user contributions. This data is then normalized and enhanced with a focus on the needs of LANCommander users. There are many metadata providers out there, and unfortunately they all have their own unique schemas and data quality issues. The HQ's goal is to provide a single source of truth for game metadata that can be consumed by LANCommander servers and launchers.
There are a lot of exciting features in the works for the HQ, but for this release the focus is on building out the core infrastructure and integrating with the new launcher. Expect to see more features and improvements in the future as the HQ continues to evolve.
### Community
The main features of the HQ are currently focused on metadata, but there is the potential to expand it into a broader community hub for LANCommander users. This could include an area to share game configurations, scripts, and other resources. Additionally, there are some content moderation tools in place that should allow the HQ to host user-generated content and provide the best possible experience for all users. If you have a knack for curation and want to help out, there will likely be opportunities to get involved in the future!
### Tiering / Subscriptions
The HQ operates on a tiered subscription model. Currently there are only two tiers: Basic (Free) and Premium ($5/month). The Basic tier provides access to the core metadata features of the HQ, while the Premium tier provides access to additional features such as enhanced metadata, relaxed rate limiting, and more. More information will be provided in the near future and will be provided on this site under the [LANCommander HQ](/hq) section. All paid subscriptions on the HQ are handled by Stripe, so every transaction is secure and your personal information is protected.
This subscription model is primarily in place to help cover the costs of running the HQ while also providing a sustainable path forward for continued development of LANCommander itself. This entire project is developed and maintained by essentially one person with some help from the community here and there. While this is a labor of love, there are real costs associated with running the infrastructure for the HQ and continued development of LANCommander. The subscription model is designed to be as accessible as possible while also providing a way for users to support the project if they find value in it.
As such, this will probably mean that the Patreon page will be going away in the near future. For existing patrons, it is recommended to switch over to the new subscription model on the HQ as it provides a more direct way to support the project and access the benefits of the HQ _today_.
## Contributions
This release includes contributions from the following community members:
**@aaronpowell | PR [#395](https://github.com/LANCommander/LANCommander/pull/395): Avalonia Launcher**
A substantial 42-commit, 6,000-line PR that bootstrapped the cross-platform Avalonia launcher. Aaron built out the core application structure, service wiring, and async initialization, and then built out the UI with filtered depot browsing and grid layout, a download queue, install/uninstall flows, a settings page, PDF manual viewer, game action bars, and a full CI pipeline for Windows, Linux, and macOS builds. A lot of the foundation that the current launcher sits on came from this PR.
**@akifreak | PR [#398](https://github.com/LANCommander/LANCommander/pull/398): Fix Game Archive Upload**
Discovered that archive uploads through the web UI were silently broken after upgrading to v2.0.2, traced the problem through the JavaScript bundle, and submitted 11 targeted fixes rather than a single bug report. The fixes covered ES module loading, null-guards on DOM elements, a FileMode.Append seek conflict, incorrect archive ID passing, and error propagation throughout the upload pipeline. The uploading process should feel much more stable now!
**@akifreak | PR [#403](https://github.com/LANCommander/LANCommander/pull/403): Fix Beacon Discovery**
Back for another round of contributions, akifreak tracked down and fixed an issue where server discovery wasn't working reliably. In some cases the launcher was actually finding servers on the network but wasn't surfacing them in the UI. The fix spans the full discovery pipeline; making the probe socket more resilient to bad data, ensuring beacon responses actually reach the client, and refreshing the server list as soon as a new server is found.
**@MasterMNB | PR [#400](https://github.com/LANCommander/LANCommander/pull/400): Avalonia Launcher Back Button**
Improved the visibility and styling of the back button in the `GameDetailView`, which was previously hard to distinguish from the background. It's a small change, but it's a real usability improvement that makes navigation feel more intentional.
## Changelog
**Launcher**
- Added: Brand new launcher built on Avalonia
- Added: Screenshots and videos in game detail carousel with lightbox viewer
- Added: Redesigned depot with categorization carousels and advanced filtering
- Added: Tools support
- Added: Download transfer speed graph
- Added: OS notifications upon game installation completion
- Added: Discord Rich Presence integration
- Added: SSO/OIDC external authentication provider support
- Added: Built-in PDF viewer for game manuals
- Added: Splash screen on startup
- Added: Verify files option in game dropdown
- Added: Markdown rendering for game descriptions
- Added: Big screen mode for fullscreen TV/controller use
- Added: Gamepad navigation across library, carousels, detail views, and overlays
- Added: Full set of CLI verbs (Install, Uninstall, Run, Sync, Import, Export, Upload, Login, Logout, ChangeAlias)
- Added: Console logging for headless CLI execution
- Changed: Refactored game/redistributable/tool install process to two-layer queue system
- Changed: Imports should be faster and execute in the background without causing major UI hiccups
- Changed: Improved memory management with proper disposal of HTTP clients, RPC clients, and cancellation tokens
- Changed: Redistributable files are now tracked and cleaned up on uninstall
- Changed: Redistributables without detect install scripts now always run the install script
- Fixed: Installing games that already have files on disk will show the verification process during install
- Fixed: Linux path variable expansion now preserves root prefix
- Fixed: Script writing for all script types
- Removed: Standalone CLI project (functionality merged into Avalonia launcher)
**Server**
- Added: LANCommander HQ metadata provider support
- Added: Support for external IDs beyond IGDB
- Added: New launcher-inspired "Preview" tool for managing art
- Added: Context-aware script editor with code completions, validation, and PowerShell type definitions
- Added: Redistributable options with schema editor and display names
- Added: RunWrapper script type with full execution support
- Added: Package versioning for archives
- Added: Bulk upload for screenshots and videos
- Added: Multiple select support in media grabber
- Added: Animated cover support (APNG auto-conversion to video)
- Added: Steam screenshot downloads
- Added: YouTube video support in media grabber
- Added: Media streaming endpoint
- Added: ffmpeg/yt-dlp availability checks and auto-install in Docker
- Added: Manual key assignment dialog and reworked key allocation
- Added: `Set-GameDetails` cmdlet for package scripts
- Added: Variable picker dialog for action editor and save path editor
- Added: Packaging dialog on archive editor
- Added: RunWrapper script type available for redistributable scripts
- Added: Background uploading with sidebar progress indicator
- Added: HQ login step in first time setup wizard
- Added: Button to download current log file
- Changed: Moved "Package" button from edit pages to archive editor
- Changed: Updated PowerShell code snippets
- Changed: Improved file manager with faster directory enumeration, toggleable columns, and cross-platform directory tree
- Fixed: Media uploads should now behave as expected
- Fixed: Game archive uploads in web UI
- Fixed: Orphaned files tool
- Fixed: Redistributable archive downloads
- Fixed: Script working directory for games
- Fixed: SteamCMD login when SteamGuard is enabled
- Fixed: Import file cleanup after completion
- Fixed: Package scripts are no longer exposed through API responses
- Fixed: Script helper now correctly maps Package and RunWrapper script filenames
- Fixed: Redistributable and tool importing
- Fixed: Media settings page failing to render
- Fixed: HQ metadata lookups
- Fixed: Log viewer under Settings / Logs (#393)
- Fixed: User approval menu item (#405)
- Fixed: Archive processing for "Use Local File"
- Fixed: "Use Local File" disabled button
- Fixed: Standalone expansions/mods shown in nested game lists (#284)
- Fixed: Missing nested game data
**Packager**
- Added: New standalone application for building LCX package files
- Added: CI workflow for building and releasing the packager
**Legacy Launcher**
- Added: Complete native Win32 launcher targeting Windows 9x
- Added: Library and depot browsing with cover art grid
- Added: Game detail view with metadata display
- Added: Download queue with SQLite-backed local database
- Added: Settings screen with YAML configuration
- Added: Custom themed window chrome
- Added: Unicode support for Win9x targets
- Added: CI workflow for automated builds
**SDK**
- Added: C++ SDK (LANCommander.SDK.Cpp) with C++14 compatibility
- Added: WinINet and libcurl HTTP backend implementations
- Added: 16 API clients covering full LANCommander API surface
- Added: RunWrapper script execution for redistributables with process lifecycle management
- Added: Logging in save packaging and upload pipeline
- Changed: Game list filtering now shows only standalone mods/expansions and main games
- Changed: Multi-target framework support
- Changed: Source-generated cmdlet registration via new `CmdletRegistrationGenerator`
- Fixed: Discovery probe socket resilience against malformed data
- Fixed: Broadcast send reliability in DiscoveryProbe
- Fixed: Beacon responses now forwarded to BeaconClient event
- Fixed: Save client now checks for newer server saves before uploading
- Fixed: Save path normalization to use forward slashes before regex matching (#266)
**Infrastructure**
- Upgraded to .NET 10
- Added ARM64 build support
- Removed legacy Photino launcher from CI
- Added nightly builds for Avalonia launcher
## Patch Updates
### 2.1.1
<details>
<summary>View 2.1.1 patch notes</summary>
#### New Features
##### User Registration in Launcher
User registration has been added back into the launcher's login screen. This was a feature from the old launcher that was missed in the port to Avalonia.
##### Game Update Support
The Avalonia launcher's update functionality was only partially-implemented. This has been completed and moved over to the new installation workflow system. Additionally, updates for games can be ignored by clicking the dropdown next to the "Update" button and selecting "Play without updating".
#### Improvements
- Game titles are now dimmed in the library when not installed, making it easier to see what's ready to play at a glance
- The games list view and depot now display a helpful message when there are no items, instead of showing a blank screen
- The launcher now downloads media on demand if it doesn't exist locally, and gracefully falls back if LibVLC fails to load
#### Bug Fixes
- Fixed bundling of LibVLC on macOS and Windows
- Fixed the display of the empty library view
- Fixed game updating
<ReleaseDownloads release="v2.1.1" />
</details>
### 2.1.2
<details>
<summary>View 2.1.2 patch notes</summary>
#### New Features
##### Redistributable Update Checking
The launcher now checks if installed redistributables are up to date and will re-run them when updates are available on the server. This ensures games always have the correct runtime dependencies.
##### Drag and Drop File Import in Script Editor
The script editor now supports drag and drop for `.ps1`, `.reg`, `.ini`, and other text files. PowerShell scripts replace the editor contents, `.reg` files are automatically converted to PowerShell commands, and other text files are inserted as escaped strings.
##### Manifest and Script Refresh
When a game has no update available, the launcher will now refresh the manifest and scripts on disk. This ensures local files stay in sync with server-side changes that don't trigger a full update.
#### Improvements
- Entire row is now clickable in the compact library list view
- Items per page selection is now persisted in data tables
- Improved process termination handling with fallback for Windows and absolute pathing for `kill` on Linux/macOS
#### Bug Fixes
- Fixed padding in the metadata lookup dialog
- Fixed HQ token retrieval on the first-time setup and integrations settings pages
- Fixed local server engine tracking (contributed by @Mavyre)
<ReleaseDownloads release="v2.1.2" />
</details>
### 2.1.3
<details>
<summary>View 2.1.3 patch notes</summary>
#### New Features
##### Allow Registration Setting
Servers now have an "Allow Registration" setting to control whether new users can register accounts. Resolves #408.
##### Server Autostop Delay
Server lifecycle management has been moved to a dedicated `ServerManager`, and servers can now be configured with an autostop delay so they shut down after a period of inactivity. Autostart/autostop for servers should be much more reliable now.
##### Close to System Tray
The launcher can now be closed to the system tray instead of exiting, keeping it running in the background.
##### Image Optimization Tool
A new server-side tool optimizes stored images to reduce disk usage and increase performance when displayed in the launcher.
##### Play Session Rework
Play session tracking has been reworked to be more reliable. Play sessions are now tracked in realtime with the launcher sending keepalives to the server. If a session is detected as stale (e.g. from a crash or improper shutdown), it will be automatically closed and the playtime will be calculated up to that point. This should result in more accurate playtime tracking and prevent issues with sessions that never end.
If, for some reason, a session is improperly created with a suspiciously long duration, a new server-side tool can be used to clean up these sessions and recalculate playtime under Settings > Tools > **Long Play Session**.
##### Working Directory from PCGamingWiki
Save paths pulled from PCGamingWiki will now attempt to set a working directory automatically.
#### Breaking Changes
##### MySQL/PostgreSQL Database Engine Reinitialization
Previous versions of LANCommander had major issues with MySQL/MariaDB and PostgreSQL database engines that would end up softlocking the server when certain operations were performed. This was caused by connections to the database being disposed of prematurely, causing the data access layer to throw exceptions and fail to recover. This has largely been resolved and should result in a much more stable experience using these other database engines.
However, due to support for MySQL/PostgreSQL falling into neglect, many of the database migrations had become out of sync and would fail to apply correctly. To resolve this, all MySQL/PostgreSQL migrations have been regenerated. This will require deployments using MySQL/PostgreSQL to recreate the database. Normally this type of change would be reserved for a major release, but given the state of the MySQL/PostgreSQL support being completely broken, this change is necessary to provide a stable experience for users of these database engines. If you are using MySQL/PostgreSQL, please make sure to back up your data and be prepared to recreate your database when updating to this version.
#### Improvements
- Performance optimizations for game details were made in the launcher, fixing potentially high CPU usage and RAM allocation with games that have videos or screenshots in the media carousel.
- Video players in the media carousel now only play when in view and pause when scrolled out of view or when the window is inactive.
- Updated Notify.NET and fixed taskbar progress reporting
- Localized remaining time and install progress status
- Adjusted styling in the game description
- Removed dead code and fixed video/screenshot loading that could block the UI
- yt-dlp is now downloaded as a self-contained build removing the dependency on Python for Linux systems.
#### Bug Fixes
- Fixed updating one-to-many relationships on the server. This should resolve various issues with redistributables, tags, genres, and platforms not saving correctly.
- Fixed an extra gap on the compact list scrollbar in the launcher
- Fixed the missing "Starting" text in the play button when a game is launching
- Fixed an exception when adding a game without a SteamGridDB API key configured
- Fixed an error thrown during first-time setup when no storage locations exist
- Regenerated MySQL/MariaDB and PostgreSQL migrations
- Fixed an issue where the identity database context could be disposed of prematurely, causing various issues with user management and authentication when using other database engines
- Improved server process termination on Linux by directly calling `kill` via `libc`
<ReleaseDownloads release="v2.1.3" />
</details>
### 2.1.4
<details>
<summary>View 2.1.4 patch notes</summary>
#### New Features
##### External Auth Provider Enhancements
External authentication providers received a major overhaul. The provider editor now supports auto-discovery of scopes and claim mappings, and claims can be mapped to roles so users are automatically assigned the correct roles when logging in over external auth. Users logging in through an external provider are also auto-provisioned.
##### Auto Redirect to External Provider
A new "Auto Redirect to Provider" setting switches authentication challenges to redirect straight to your external auth provider instead of showing the password login form. If more than one external provider is configured, a minimal provider selection page is shown instead.
##### Repack Non-Streamable ZIP Archives
A new server-side tool in the archive editor can now be used to repack archives that were not created in a streamable format. More information about why this is an issue can be found under the [Archives](/Server/Archives) documentation page.
##### WebP Media Support
Media uploads now support the WebP image format.
##### Redistributable Uninstall Scripts
Redistributables can now define an uninstall script, allowing their runtime dependencies to be cleanly removed.
##### Single Instance Launcher
The launcher now ensures only a single instance can run at a time, preventing conflicts from accidentally opening multiple copies.
#### Improvements
- Local files selected for archive upload can now be moved instead of copied, saving disk space.
- Reworked tool installation and action resolution.
- Install notifications now only display once the entire install chain is complete.
- The "Last Played" text now updates every minute and has been localized.
- Added more logging to archive extraction and made the number of install retry attempts configurable.
- A message is now shown indicating the server needs to be restarted after changes are made to authentication providers.
- Game started/stopped server scripts now run as fire and forget and will not prevent servers from starting.
- Adjusted styling for buttons and dropdowns in the launcher.
- Updated SharpCompress to 0.49.1.
- macOS builds now generate a proper `.app` bundle, and Linux builds now produce an AppImage bundled into the normal workflows.
#### Bug Fixes
- Fixed some situations where saves were not being uploaded/downloaded.
- Fixed an error in the launcher when tool actions were not defined.
- Fixed server game actions not loading correctly from the server.
- Fixed non-standalone addons appearing in the library compact list.
- HQ authentication errors are no longer swallowed.
- Fixed user promotion / role assignment.
- Fixed application icons.
<ReleaseDownloads release="v2.1.4" />
</details>
### 2.1.5
<details>
<summary>View 2.1.5 patch notes</summary>
#### New Features
##### Auto Redirect to External Provider in the Launcher
The launcher now honors the server's "Auto Redirect to Provider" setting. When enabled, the login screen redirects straight to your external authentication provider, and the standard authentication fields and buttons are hidden based on the server's authentication settings.
##### Exit Button in Profile Dropdown
An "Exit" button is now available under the profile dropdown, making it easier to fully quit the launcher.
#### Improvements
- Consolidated the game flyout into a unified context menu for more consistent actions across the library views.
- Tools that have no archive are now hidden from the install overlay.
- Automated testing for various aspects of the server UI (thanks [aaronpowell](https://github.com/aaronpowell)!)
#### Bug Fixes
- Fixed removing games from user libraries using the launcher.
- Fixed tool installation.
- Fixed the clickability of games in the compact library list.
- Fixed updating of roles.
- Fixed saving of authentication settings.
- Fixed admin user creation in first time setup.
<ReleaseDownloads release="v2.1.5" />
</details>
### 2.1.6
<details>
<summary>View 2.1.6 patch notes</summary>
#### New Features
##### User and Role Limits
Roles and individual users can now be assigned limits for save storage, total user storage, and download speed. This gives admins finer control over resource usage on shared servers. Resolves [#85](https://github.com/LANCommander/LANCommander/issues/85), [#84](https://github.com/LANCommander/LANCommander/issues/84), and [#100](https://github.com/LANCommander/LANCommander/issues/).
##### PowerShell Modules
A new "Scripting" section has been added to the server UI where PowerShell modules can be defined. These act as a library of reusable functions that can be called from any script. Modules are automatically synced to the launcher and imported when scripts are executed.
##### Per-Game Tool Tracking
Tools are now tracked on a per-game basis. Tools can be marked to "Always Install", and any tools installed for a game are automatically uninstalled when that game is uninstalled.
##### Admin User Creation Dialog
Admins can now create new users directly from the server with a new user creation dialog. Fixes [#419](https://github.com/LANCommander/LANCommander/issues/419).
##### Database Connection Editor
The connection string input used in first-time setup and server settings has been refactored into a full database connection component, making it easier to configure database connections. Fixes [#256](https://github.com/LANCommander/LANCommander/issues/256).
#### Improvements
- Added "Select All" and "Load More" buttons to the media grabber. Fixes [#417](https://github.com/LANCommander/LANCommander/issues/417).
- Metadata lookups now preserve existing values instead of overwriting them. Fixes [#420](https://github.com/LANCommander/LANCommander/issues/420).
- The Library navigation and library/depot switcher are now hidden in both the web UI and launcher when user libraries are disabled.
- Action `ServerHost` values now default to the LANCommander server address.
#### Bug Fixes
- Fixed the display of UTC times.
- Fixed creation of entities with many-to-many relationships. Ref [#414](https://github.com/LANCommander/LANCommander/issues/414).
- Fixed reconciliation of library games based on whether user libraries are enabled or disabled. Ref [#423](https://github.com/LANCommander/LANCommander/issues/423).
- The profile cache is now invalidated when logging out. Fixes [#422](https://github.com/LANCommander/LANCommander/issues/422).
<ReleaseDownloads release="v2.1.6" />
</details>
### 2.1.7
<details>
<summary>View 2.1.7 patch notes</summary>
#### Improvements
- The launcher now loads your library from a new `/Library/Games` endpoint and displays it immediately, with images rendered directly from the server. The full import still runs in the background, so offline functionality is preserved while the library becomes usable much sooner.
- Depot images are now streamed from a remote image cache instead of being downloaded to disk, reducing UI jank. Fixes [#427](https://github.com/LANCommander/LANCommander/issues/427).
- Game installs and uninstalls are now more reliable: installs run in a consistent order (base game, addons, tools, then redistributables), tools are removed before their game is uninstalled, and the game action bar refreshes after a tool is installed. Ref [#414](https://github.com/LANCommander/LANCommander/issues/414).
- The modify menu now lists installed addons.
- Save upload failures are now logged instead of failing silently.
- A running game process is now terminated as a fallback when no spawned window is detected.
- View data is now loaded before transitioning, smoothing navigation.
#### Bug Fixes
- Save path changes on a game now take effect immediately instead of requiring a server restart. Games are correctly updated when a save path is added, updated, or deleted.
- Fixed installation of games and addons that have no dependent games.
- Fixed importing legacy LCX files that have no save paths defined. Fixes [#426](https://github.com/LANCommander/LANCommander/issues/426).
- Fixed persisted page sizes in data tables.
<ReleaseDownloads release="v2.1.7" />
</details>
### 2.1.8
<details>
<summary>View 2.1.8 patch notes</summary>
#### New Features
##### Runtime Platform Targeting
Actions, scripts, and save paths can now be scoped to a specific runtime platform (Windows, Linux, or macOS). This makes it possible to define platform-specific launch actions, install/uninstall scripts, and save paths on a single game without them conflicting across operating systems. A new `Get-Runtime` PowerShell cmdlet is also available for detecting the current platform from within scripts.
#### Improvements
- The offline mode button has been moved to the titlebar for easier access.
- The game description editor in the server UI is now a full Markdown editor.
- Page functionality has been improved with better menus for adding and deleting pages, and slugs are now generated in PascalCase.
- Client/server version mismatches are now detected and logged, making it easier to diagnose connection issues caused by version drift.
- Data table sort order is now persisted.
#### Bug Fixes
- Images now load from the local cache when the launcher is in offline mode.
- Fixed enumeration of script snippets.
- Fixed PowerShell module loading in the launcher. On Windows the execution policy is set to bypass, and module load failures are now trapped and reported as a warning per module instead of failing the entire script.
<ReleaseDownloads release="v2.1.8" />
</details>
### 2.1.9
<details>
<summary>View 2.1.9 patch notes</summary>
#### Improvements
- Path resolution has been unified across the server so that storage paths are resolved consistently everywhere. A migration aligns existing settings storage paths automatically on upgrade.
- Depot queries have been optimized for better performance.
- Server notifications now use a shorter timeout so a slow or unreachable server no longer holds up the launcher.
- Updated SharpCompress to the latest version. This should resolve most extraction issues for games with large archives.
#### Bug Fixes
- Fixed detection of the primary display's resolution on some Linux multi-display configurations.
- Fixed application path resolution on the server, correcting how saves, media, archives, and updates are located.
- Improved handling of the bypass execution policy for scripts.
- Fixed installation of wine32 and winetricks.
<ReleaseDownloads release="v2.1.9" />
</details>
## Downloads
<ReleaseDownloads release="v2.1.9" />
<details>
<summary>View 2.1.8 downloads</summary>
<ReleaseDownloads release="v2.1.8" />
</details>
<details>
<summary>View 2.1.7 downloads</summary>
<ReleaseDownloads release="v2.1.7" />
</details>
<details>
<summary>View 2.1.6 downloads</summary>
<ReleaseDownloads release="v2.1.6" />
</details>
<details>
<summary>View 2.1.5 downloads</summary>
<ReleaseDownloads release="v2.1.5" />
</details>
<details>
<summary>View 2.1.4 downloads</summary>
<ReleaseDownloads release="v2.1.4" />
</details>
<details>
<summary>View 2.1.3 downloads</summary>
<ReleaseDownloads release="v2.1.3" />
</details>
<details>
<summary>View 2.1.2 downloads</summary>
<ReleaseDownloads release="v2.1.2" />
</details>
<details>
<summary>View 2.1.1 downloads</summary>
<ReleaseDownloads release="v2.1.1" />
</details>
<details>
<summary>View 2.1.0 downloads</summary>
<ReleaseDownloads release="v2.1.0" />
</details>
## Contributors
<ContributorGrid from="v2.0.2" to="v2.1.9" />

View file

@ -0,0 +1,38 @@
---
title: 2.1.1
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
import ContributorGrid from '@site/src/components/ContributorGrid';
# LANCommander 2.1.1 Release Notes
:::info Full Release Notes
This is a patch release for the **2.1 series**. For the complete feature overview and all patch notes, visit the [LANCommander 2.1.0 release notes](./2.1.0).
:::
## New Features
### User Registration in Launcher
User registration has been added back into the launcher's login screen. This was a feature from the old launcher that was missed in the port to Avalonia.
### Game Update Support
The Avalonia launcher's update functionality was only partially-implemented. This has been completed and moved over to the new installation workflow system. Additionally, updates for games can be ignored by clicking the dropdown next to the "Update" button and selecting "Play without updating".
## Improvements
- Game titles are now dimmed in the library when not installed, making it easier to see what's ready to play at a glance
- The games list view and depot now display a helpful message when there are no items, instead of showing a blank screen
- The launcher now downloads media on demand if it doesn't exist locally, and gracefully falls back if LibVLC fails to load
## Bug Fixes
- Fixed bundling of LibVLC on macOS and Windows
- Fixed the display of the empty library view
- Fixed game updating
## Downloads
<ReleaseDownloads release="v2.1.1" />
## Contributors
<ContributorGrid from="v2.1.0" to="v2.1.1" />

View file

@ -0,0 +1,41 @@
---
title: 2.1.2
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
import ContributorGrid from '@site/src/components/ContributorGrid';
# LANCommander 2.1.2 Release Notes
:::info Full Release Notes
This is a patch release for the **2.1 series**. For the complete feature overview and all patch notes, visit the [LANCommander 2.1.0 release notes](./2.1.0).
:::
## New Features
### Redistributable Update Checking
The launcher now checks if installed redistributables are up to date and will re-run them when updates are available on the server. This ensures games always have the correct runtime dependencies.
### Drag and Drop File Import in Script Editor
The script editor now supports drag and drop for `.ps1`, `.reg`, `.ini`, and other text files. PowerShell scripts replace the editor contents, `.reg` files are automatically converted to PowerShell commands, and other text files are inserted as escaped strings.
### Manifest and Script Refresh
When a game has no update available, the launcher will now refresh the manifest and scripts on disk. This ensures local files stay in sync with server-side changes that don't trigger a full update.
## Improvements
- Entire row is now clickable in the compact library list view
- Items per page selection is now persisted in data tables
- Improved process termination handling with fallback for Windows and absolute pathing for `kill` on Linux/macOS
## Bug Fixes
- Fixed padding in the metadata lookup dialog
- Fixed HQ token retrieval on the first-time setup and integrations settings pages
- Fixed local server engine tracking (contributed by @Mavyre)
## Downloads
<ReleaseDownloads release="v2.1.2" />
## Contributors
<ContributorGrid from="v2.1.1" to="v2.1.2" />

View file

@ -0,0 +1,67 @@
---
title: 2.1.3
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
import ContributorGrid from '@site/src/components/ContributorGrid';
# LANCommander 2.1.3 Release Notes
:::info Full Release Notes
This is a patch release for the **2.1 series**. For the complete feature overview and all patch notes, visit the [LANCommander 2.1.0 release notes](./2.1.0).
:::
## New Features
### Allow Registration Setting
Servers now have an "Allow Registration" setting to control whether new users can register accounts. Resolves #408.
### Server Autostop Delay
Server lifecycle management has been moved to a dedicated `ServerManager`, and servers can now be configured with an autostop delay so they shut down after a period of inactivity. Autostart/autostop for servers should be much more reliable now.
### Close to System Tray
The launcher can now be closed to the system tray instead of exiting, keeping it running in the background.
### Image Optimization Tool
A new server-side tool optimizes stored images to reduce disk usage and increase performance when displayed in the launcher.
### Play Session Rework
Play session tracking has been reworked to be more reliable. Play sessions are now tracked in realtime with the launcher sending keepalives to the server. If a session is detected as stale (e.g. from a crash or improper shutdown), it will be automatically closed and the playtime will be calculated up to that point. This should result in more accurate playtime tracking and prevent issues with sessions that never end.
If, for some reason, a session is improperly created with a suspiciously long duration, a new server-side tool can be used to clean up these sessions and recalculate playtime under Settings > Tools > **Long Play Session**.
### Working Directory from PCGamingWiki
Save paths pulled from PCGamingWiki will now attempt to set a working directory automatically.
## Breaking Changes
### MySQL/PostgreSQL Database Engine Reinitialization
Previous versions of LANCommander had major issues with MySQL/MariaDB and PostgreSQL database engines that would end up softlocking the server when certain operations were performed. This was caused by connections to the database being disposed of prematurely, causing the data access layer to throw exceptions and fail to recover. This has largely been resolved and should result in a much more stable experience using these other database engines.
However, due to support for MySQL/PostgreSQL falling into neglect, many of the database migrations had become out of sync and would fail to apply correctly. To resolve this, all MySQL/PostgreSQL migrations have been regenerated. This will require deployments using MySQL/PostgreSQL to recreate the database. Normally this type of change would be reserved for a major release, but given the state of the MySQL/PostgreSQL support being completely broken, this change is necessary to provide a stable experience for users of these database engines. If you are using MySQL/PostgreSQL, please make sure to back up your data and be prepared to recreate your database when updating to this version.
## Improvements
- Performance optimizations for game details were made in the launcher, fixing potentially high CPU usage and RAM allocation with games that have videos or screenshots in the media carousel.
- Video players in the media carousel now only play when in view and pause when scrolled out of view or when the window is inactive.
- Updated Notify.NET and fixed taskbar progress reporting
- Localized remaining time and install progress status
- Adjusted styling in the game description
- Removed dead code and fixed video/screenshot loading that could block the UI
- yt-dlp is now downloaded as a self-contained build removing the dependency on Python for Linux systems.
## Bug Fixes
- Fixed updating one-to-many relationships on the server. This should resolve various issues with redistributables, tags, genres, and platforms not saving correctly.
- Fixed an extra gap on the compact list scrollbar in the launcher
- Fixed the missing "Starting" text in the play button when a game is launching
- Fixed an exception when adding a game without a SteamGridDB API key configured
- Fixed an error thrown during first-time setup when no storage locations exist
- Regenerated MySQL/MariaDB and PostgreSQL migrations
- Fixed an issue where the identity database context could be disposed of prematurely, causing various issues with user management and authentication when using other database engines
- Improved server process termination on Linux by directly calling `kill` via `libc`
## Downloads
<ReleaseDownloads release="v2.1.3" />
## Contributors
<ContributorGrid from="v2.1.2" to="v2.1.3" />

View file

@ -0,0 +1,61 @@
---
title: 2.1.4
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
import ContributorGrid from '@site/src/components/ContributorGrid';
# LANCommander 2.1.4 Release Notes
:::info Full Release Notes
This is a patch release for the **2.1 series**. For the complete feature overview and all patch notes, visit the [LANCommander 2.1.0 release notes](./2.1.0).
:::
## New Features
### External Auth Provider Enhancements
External authentication providers received a major overhaul. The provider editor now supports auto-discovery of scopes and claim mappings, and claims can be mapped to roles so users are automatically assigned the correct roles when logging in over external auth. Users logging in through an external provider are also auto-provisioned.
### Auto Redirect to External Provider
A new "Auto Redirect to Provider" setting switches authentication challenges to redirect straight to your external auth provider instead of showing the password login form. If more than one external provider is configured, a minimal provider selection page is shown instead.
### Repack Non-Streamable ZIP Archives
A new server-side tool in the archive editor can now be used to repack archives that were not created in a streamable format. More information about why this is an issue can be found under the [Archives](/Server/Archives) documentation page.
### WebP Media Support
Media uploads now support the WebP image format.
### Redistributable Uninstall Scripts
Redistributables can now define an uninstall script, allowing their runtime dependencies to be cleanly removed.
### Single Instance Launcher
The launcher now ensures only a single instance can run at a time, preventing conflicts from accidentally opening multiple copies.
## Improvements
- Local files selected for archive upload can now be moved instead of copied, saving disk space.
- Reworked tool installation and action resolution.
- Install notifications now only display once the entire install chain is complete.
- The "Last Played" text now updates every minute and has been localized.
- Added more logging to archive extraction and made the number of install retry attempts configurable.
- A message is now shown indicating the server needs to be restarted after changes are made to authentication providers.
- Game started/stopped server scripts now run as fire and forget and will not prevent servers from starting.
- Adjusted styling for buttons and dropdowns in the launcher.
- Updated SharpCompress to 0.49.1.
- macOS builds now generate a proper `.app` bundle, and Linux builds now produce an AppImage bundled into the normal workflows.
## Bug Fixes
- Fixed some situations where saves were not being uploaded/downloaded.
- Fixed an error in the launcher when tool actions were not defined.
- Fixed server game actions not loading correctly from the server.
- Fixed non-standalone addons appearing in the library compact list.
- HQ authentication errors are no longer swallowed.
- Fixed user promotion / role assignment.
- Fixed application icons.
## Downloads
<ReleaseDownloads release="v2.1.4" />
## Contributors
<ContributorGrid from="v2.1.3" to="v2.1.4" />

View file

@ -0,0 +1,41 @@
---
title: 2.1.5
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
import ContributorGrid from '@site/src/components/ContributorGrid';
# LANCommander 2.1.5 Release Notes
:::info Full Release Notes
This is a patch release for the **2.1 series**. For the complete feature overview and all patch notes, visit the [LANCommander 2.1.0 release notes](./2.1.0).
:::
## New Features
### Auto Redirect to External Provider in the Launcher
The launcher now honors the server's "Auto Redirect to Provider" setting. When enabled, the login screen redirects straight to your external authentication provider, and the standard authentication fields and buttons are hidden based on the server's authentication settings.
### Exit Button in Profile Dropdown
An "Exit" button is now available under the profile dropdown, making it easier to fully quit the launcher.
## Improvements
- Consolidated the game flyout into a unified context menu for more consistent actions across the library views.
- Tools that have no archive are now hidden from the install overlay.
- Automated testing for various aspects of the server UI (thanks [aaronpowell](https://github.com/aaronpowell)!)
## Bug Fixes
- Fixed removing games from user libraries using the launcher.
- Fixed tool installation.
- Fixed the clickability of games in the compact library list.
- Fixed updating of roles.
- Fixed saving of authentication settings.
- Fixed admin user creation in first time setup.
## Downloads
<ReleaseDownloads release="v2.1.5" />
## Contributors
<ContributorGrid from="v2.1.4" to="v2.1.5" />

View file

@ -0,0 +1,49 @@
---
title: 2.1.6
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
import ContributorGrid from '@site/src/components/ContributorGrid';
# LANCommander 2.1.6 Release Notes
:::info Full Release Notes
This is a patch release for the **2.1 series**. For the complete feature overview and all patch notes, visit the [LANCommander 2.1.0 release notes](./2.1.0).
:::
## New Features
### User and Role Limits
Roles and individual users can now be assigned limits for save storage, total user storage, and download speed. This gives admins finer control over resource usage on shared servers. Resolves [#85](https://github.com/LANCommander/LANCommander/issues/85), [#84](https://github.com/LANCommander/LANCommander/issues/84), and [#100](https://github.com/LANCommander/LANCommander/issues/).
### PowerShell Modules
A new "Scripting" section has been added to the server UI where PowerShell modules can be defined. These act as a library of reusable functions that can be called from any script. Modules are automatically synced to the launcher and imported when scripts are executed.
### Per-Game Tool Tracking
Tools are now tracked on a per-game basis. Tools can be marked to "Always Install", and any tools installed for a game are automatically uninstalled when that game is uninstalled.
### Admin User Creation Dialog
Admins can now create new users directly from the server with a new user creation dialog. Fixes [#419](https://github.com/LANCommander/LANCommander/issues/419).
### Database Connection Editor
The connection string input used in first-time setup and server settings has been refactored into a full database connection component, making it easier to configure database connections. Fixes [#256](https://github.com/LANCommander/LANCommander/issues/256).
## Improvements
- Added "Select All" and "Load More" buttons to the media grabber. Fixes [#417](https://github.com/LANCommander/LANCommander/issues/417).
- Metadata lookups now preserve existing values instead of overwriting them. Fixes [#420](https://github.com/LANCommander/LANCommander/issues/420).
- The Library navigation and library/depot switcher are now hidden in both the web UI and launcher when user libraries are disabled.
- Action `ServerHost` values now default to the LANCommander server address.
## Bug Fixes
- Fixed the display of UTC times.
- Fixed creation of entities with many-to-many relationships. Ref [#414](https://github.com/LANCommander/LANCommander/issues/414).
- Fixed reconciliation of library games based on whether user libraries are enabled or disabled. Ref [#423](https://github.com/LANCommander/LANCommander/issues/423).
- The profile cache is now invalidated when logging out. Fixes [#422](https://github.com/LANCommander/LANCommander/issues/422).
## Downloads
<ReleaseDownloads release="v2.1.6" />
## Contributors
<ContributorGrid from="v2.1.5" to="v2.1.6" />

View file

@ -0,0 +1,35 @@
---
title: 2.1.7
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
import ContributorGrid from '@site/src/components/ContributorGrid';
# LANCommander 2.1.7 Release Notes
:::info Full Release Notes
This is a patch release for the **2.1 series**. For the complete feature overview and all patch notes, visit the [LANCommander 2.1.0 release notes](./2.1.0).
:::
## Improvements
- The launcher now loads your library from a new `/Library/Games` endpoint and displays it immediately, with images rendered directly from the server. The full import still runs in the background, so offline functionality is preserved while the library becomes usable much sooner.
- Depot images are now streamed from a remote image cache instead of being downloaded to disk, reducing UI jank. Fixes [#427](https://github.com/LANCommander/LANCommander/issues/427).
- Game installs and uninstalls are now more reliable: installs run in a consistent order (base game, addons, tools, then redistributables), tools are removed before their game is uninstalled, and the game action bar refreshes after a tool is installed. Ref [#414](https://github.com/LANCommander/LANCommander/issues/414).
- The modify menu now lists installed addons.
- Save upload failures are now logged instead of failing silently.
- A running game process is now terminated as a fallback when no spawned window is detected.
- View data is now loaded before transitioning, smoothing navigation.
## Bug Fixes
- Save path changes on a game now take effect immediately instead of requiring a server restart. Games are correctly updated when a save path is added, updated, or deleted.
- Fixed installation of games and addons that have no dependent games.
- Fixed importing legacy LCX files that have no save paths defined. Fixes [#426](https://github.com/LANCommander/LANCommander/issues/426).
- Fixed persisted page sizes in data tables.
## Downloads
<ReleaseDownloads release="v2.1.7" />
## Contributors
<ContributorGrid from="v2.1.6" to="v2.1.7" />

View file

@ -0,0 +1,37 @@
---
title: 2.1.8
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
import ContributorGrid from '@site/src/components/ContributorGrid';
# LANCommander 2.1.8 Release Notes
:::info Full Release Notes
This is a patch release for the **2.1 series**. For the complete feature overview and all patch notes, visit the [LANCommander 2.1.0 release notes](./2.1.0).
:::
## New Features
### Runtime Platform Targeting
Actions, scripts, and save paths can now be scoped to a specific runtime platform (Windows, Linux, or macOS). This makes it possible to define platform-specific launch actions, install/uninstall scripts, and save paths on a single game without them conflicting across operating systems. A new `Get-Runtime` PowerShell cmdlet is also available for detecting the current platform from within scripts.
## Improvements
- The offline mode button has been moved to the titlebar for easier access.
- The game description editor in the server UI is now a full Markdown editor.
- Page functionality has been improved with better menus for adding and deleting pages, and slugs are now generated in PascalCase.
- Client/server version mismatches are now detected and logged, making it easier to diagnose connection issues caused by version drift.
- Data table sort order is now persisted.
## Bug Fixes
- Images now load from the local cache when the launcher is in offline mode.
- Fixed enumeration of script snippets.
- Fixed PowerShell module loading in the launcher. On Windows the execution policy is set to bypass, and module load failures are now trapped and reported as a warning per module instead of failing the entire script.
## Downloads
<ReleaseDownloads release="v2.1.8" />
## Contributors
<ContributorGrid from="v2.1.7" to="v2.1.8" />

View file

@ -0,0 +1,32 @@
---
title: 2.1.9
---
import ReleaseDownloads from '@site/src/components/ReleaseDownloads';
import ContributorGrid from '@site/src/components/ContributorGrid';
# LANCommander 2.1.9 Release Notes
:::info Full Release Notes
This is a patch release for the **2.1 series**. For the complete feature overview and all patch notes, visit the [LANCommander 2.1.0 release notes](./2.1.0).
:::
## Improvements
- Path resolution has been unified across the server so that storage paths are resolved consistently everywhere. A migration aligns existing settings storage paths automatically on upgrade.
- Depot queries have been optimized for better performance.
- Server notifications now use a shorter timeout so a slow or unreachable server no longer holds up the launcher.
- Updated SharpCompress to the latest version. This should resolve most extraction issues for games with large archives.
## Bug Fixes
- Fixed detection of the primary display's resolution on some Linux multi-display configurations.
- Fixed application path resolution on the server, correcting how saves, media, archives, and updates are located.
- Improved handling of the bypass execution policy for scripts.
- Fixed installation of wine32 and winetricks.
## Downloads
<ReleaseDownloads release="v2.1.9" />
## Contributors
<ContributorGrid from="v2.1.8" to="v2.1.9" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 921 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View file

@ -0,0 +1,14 @@
## Getting Started
The SDK relies on .NET dependency injection in order to expose services to the consuming application. Use of the SDK can be implemented by calling the extension method for registering the services:
```csharp
builder.Services.AddLANCommanderClient<Settings>();
```
The generic type parameter (here `Settings`) can be used to allow extension of settings by providing your own class that inherits from `LANCommander.SDK.Settings`.
## Configuration
Standard .NET configuration has been implemented in the SDK. By default, `IOptions<Settings>` can be used to retrieve any settings, while `SettingsProvider` is to be used for updating any settings. Any configuration that gets bound to the `Settings` class will be stored in a file called `Settings.yml` on update.
## Authentication
By injecting `LANCommander.SDK.AuthenticationClient`, you can authenticate against a LANCommander server:

View file

@ -0,0 +1,17 @@
---
sidebar_label: Overview
sidebar_position: 1
---
# Overview
The LANCommander SDK is a .NET 9 assembly that provides the heart of functionality for the launcher and some parts of the server application. The SDK is currently used for:
- Authentication to a LANCommander server
- Installation of games / redistributables
- Launching of games and play session tracking
- Media linking and downloading
- Game save syncing, including packing and extraction
- PowerShell scripting runtime and execution
- Updating a player's profile including alias and custom fields
- Submission of issues
- Game lobby scanning
- Chat client

View file

@ -0,0 +1,727 @@
---
title: Cmdlets
---
# Overview
Since there is a full PowerShell runtime built into LANCommander, there are a few custom cmdlets that have been added to simplify common tasks that may be needed when installing or configuring a game. This page covers the definition and use of these cmdlets.
## `Convert-AspectRatio`
Calculates a resolution for the desired aspect ratio using an input width and height in pixels.
### Syntax
```powershell
Convert-AspectRatio
-Width <int>
-Height <int>
-AspectRatio <double>
```
### Description
The `Convert-AspectRatio` cmdlet is most useful for calculating a resolution for a specific aspect ratio that will fit within a display by either using pillar or letter boxing. For example, some games may only support 4:3 displays and you may want to calculate the correct 4:3 resolution from your 16:9 display. This cmdlet is really useful when paired with `Get-PrimaryDisplay`.
### Example
```powershell
Convert-AspectRatio -Width 2560 -Height 1440 -AspectRatio (4 / 3)
# Returns <DisplayResolution>
Width : 1920
Height : 1440
```
## `ConvertTo-StringBytes`
Converts an input string into a byte array.
### Syntax
```powershell
ConvertTo-StringBytes
-Input <string>
-Utf16 <bool>
-BigEndian <bool>
-MaxLength <int>
-MinLength <int>
```
### Description
`ConvertTo-StringBytes` is extremely useful for patching strings in binary files. It will take any input string and convert it to a byte array. Length can be controlled using the `-MaxLength` and `-MinLength` parameters. Endianness can be set using `-BigEndian`. If the string must be UTF-16 (easily identifiable as characters separated by `0x00`), use `-Utf16`.
### Example
```powershell
ConvertTo-StringBytes -Input "Hello, world!" -Utf16 1
72 0 101 0 108 0 108 0 111 0 44 0 32 0 119 0 111 0 114 0 108 0 100 0 33 0
ConvertTo-StringBytes -Input "Hello, world!" -MaxLength
72 101 108 108 111
ConvertTo-StringBytes -Input "Hello" -MaxLength 10 -MinLength 10
72 101 108 108 111 0 0 0 0 0
ConvertTo-StringBytes -Input "Hello" -Utf16 1 -BigEndian 1
0 72 0 101 0 108 0 108 0 111
```
## `Edit-PatchBinary`
Patches binary files at a specified offset.
### Syntax
```powershell
Edit-PatchBinary
-Offset <long>
-Data <byte[]>
-FilePath <string>
-MaxLength <int>
-MinLength <int>
```
### Description
This cmdlet is useful when a binary file has to be patched at a specific offset. It can be extremely useful when paired with `ConvertTo-StringBytes` to update a player name in a binary file.
### Example
```powershell
$bytes = ConvertTo-StringBytes -Input "Master Chief" -Utf16 1 -MaxLength 16 -MinLength 16
Edit-PatchBinary -FilePath "$($env:LOCALAPPDATA)\Microsoft\Halo 2\Saved Games\S0000000\profile" -Offset 0x08 -Data $bytes
```
## `Get-GameManifest`
Parses a game's manifest YAML file from the specified install directory.
### Syntax
```powershell
Get-GameManifest
-Path <string>
```
### Description
Used to deserialize a game's manifest file (`Manifest.yml`) from the specified install directory. Returns the game manifest as an object.
### Examples
```powershell
$manifest = Get-GameManifest -Path "C:\Games\Age of Empires II - The Age of Kings"
Write-Host $manifest.Title
Age of Empires II: The Age of Kings
```
## `Get-PrimaryDisplay`
Gets the bounds of the machine's current primary display.
### Syntax
```powershell
Get-PrimaryDisplay
```
### Description
The `Get-PrimaryDisplay` cmdlet takes no parameters and will only return the bounds of the current primary display attached to the machine. This is highly useful in where you might want to automatically set the game's resolution to match the primary display's resolution.
### Example
```powershell
$Display = Get-PrimaryDisplay
Write-Host "$($Display.Width)x$($Display.Height) @ $($Display.RefreshRate)Hz"
1920x1080 @ 120Hz
```
## `Update-IniValue`
Updates the value of an INI file.
### Syntax
```powershell
Update-IniValue
-Section <string>
-Key <string>
-Value <string>
-FilePath <string>
-WrapValueInQuotes <bool> (optional)
```
### Description
`Update-IniValue` should be used when updating the values of an INI file. These files are typically used for configuring games and may be hard to edit using `Write-ReplaceContentInFile` and regular expressions. INI files are comprised of sections (text surrounded in square brackets, (`[Display]`), and key-value pairs (`Width=1024`).
### Example
```powershell
# Change the resolution
$Display = Get-PrimaryDisplay
Update-IniValue -Section "Display" -Key "Width" -Value "$($Display.Width)" -FilePath "$InstallDirectory\config.ini"
```
## `Write-GameManifest`
Serializes a `GameManifest` object and writes it to disk.
### Syntax
```powershell
Write-GameManifest
-Path <string>
-Manifest <LANCommander.SDK.GameManifest>
```
### Example
```powershell
$manifest = Get-GameManifest -Path "C:\Games\Age of Empires II - The Age of Kings"
$manifest.SortTitle = "Age of Empires 2"
Write-GameManifest -Path "C:\Games\Age of Empires II - The Age of Kings\.lancommander\$($manifest.Id)\Manifest.yml"
```
## `Write-ReplaceContentInFile`
Find and replace a string in a text file.
### Syntax
```powershell
Write-ReplaceContentInFile
-Pattern <string>
-Substitution <string>
-FilePath <string>
```
### Description
`Write-ReplaceContentInFile` can be used when you want to edit a text file and replace content. The `-Pattern` parameter accepts regular expressions.
### Example
```powershell
# Changes the player's multiplayer name in Call of Duty (2003)
Write-ReplaceContentInFile -Pattern '^seta name (.+)' -Substitution "seta name ""$NewPlayerAlias""" -FilePath "$InstallDirectory\Main\config_mp.cfg"
```
## `Get-UserCustomField`
Retrieves the value of a custom field from the user's profile from the server.
### Syntax
```powershell
Get-UserCustomField
-Name <string>
```
### Description
This cmdlet can be useful if you have a game that might require a persistent ID attached to your user. Often times games will assign a unique ID to a player upon creation of a profile, and that ID will be used on a server to keep track of stats, inventory, etc. The list of custom fields added to a user can be viewed under their profile in the server's web UI.
### Example
```powershell
Get-UserCustomField -Name "SteamId"
```
## `Get-RedistributableOptions`
Retrieves the resolved options for a redistributable assigned to a game.
### Syntax
```powershell
Get-RedistributableOptions
-Path <string>
-Id <Guid>
-Name <string>
```
### Description
The `Get-RedistributableOptions` cmdlet reads the game manifest and returns the resolved option values for the specified redistributable as a nested `PSObject`. Options are resolved in order: schema defaults, then per-game configured values. Nested options in the schema are represented as nested properties on the returned object.
This cmdlet is useful in [Install](/Scripting/Script Types/Install), [Before Start](/Scripting/Script Types/Before Start), and [Run Wrapper](/Scripting/Script Types/Run Wrapper) scripts where you need to access compatibility shim configuration.
### Example
```powershell
$options = Get-RedistributableOptions -Path $InstallDirectory -Id $GameManifest.Id -Name "umu-launcher"
# Access nested options
Write-Host $options.Game.GAMEID # "umu-default" or admin-configured value
Write-Host $options.Proton.PROTONPATH # "GE-Proton" or admin-configured value
# Use in a script
if ($options.Proton.PROTONPATH -eq "GE-Proton") {
Write-Host "Using default Proton version"
}
```
## `Update-UserCustomField`
Updates the value of a custom field on a users profile.
### Syntax
```powershell
Update-UserCustomField
-Name <string>
-Value <string>
```
### Description
The companion to `Get-UserCustomField`, this cmdlet lets you update or set the value of a custom field on a user's profile directly within your scripts. The most common use case is generating a new user ID on install, writing it to the game's configuration file, and then updating the custom field on the user's profile to store it for subsequent installs.
### Example
```powershell
Update-UserCustomField -Name "SteamId" -Value "34950494"
```
## `ConvertFrom-SerializedBase64`
Deserializes a Base64-encoded YAML string back into an object.
### Syntax
```powershell
ConvertFrom-SerializedBase64
-Input <string>
```
### Description
The `ConvertFrom-SerializedBase64` cmdlet takes a Base64-encoded string containing YAML-serialized data, decodes it, and deserializes it back into a PowerShell object. This is the companion to `ConvertTo-SerializedBase64` and is useful for reading data that was previously serialized and encoded for storage or transport. Accepts pipeline input.
### Example
```powershell
$data = ConvertFrom-SerializedBase64 -Input "TmFtZTogSGVsbG8="
# Or via pipeline
"TmFtZTogSGVsbG8=" | ConvertFrom-SerializedBase64
```
## `ConvertTo-SerializedBase64`
Serializes an object to YAML and encodes it as a Base64 string.
### Syntax
```powershell
ConvertTo-SerializedBase64
-Input <object>
```
### Description
The `ConvertTo-SerializedBase64` cmdlet takes any object, serializes it to YAML, and then encodes the result as a Base64 string. This is useful for storing or transmitting structured data in a compact, text-safe format. Accepts pipeline input.
### Example
```powershell
$obj = @{ Name = "Hello"; Value = 42 }
$encoded = ConvertTo-SerializedBase64 -Input $obj
# Or via pipeline
$obj | ConvertTo-SerializedBase64
```
## `Edit-PatchGameSpy`
Patches GameSpy master server references in game files to point to a replacement server.
### Syntax
```powershell
Edit-PatchGameSpy
-Path <string>
-Hostname <string> (optional, default: "openspy.net")
-PublicKey <string> (optional, default: OpenSpy public key)
-BinariesToPatch <string[]> (optional, default: "*.dll", "*.exe")
-TextFilesToPatch <string[]> (optional, default: "*.ini", "*.cfg", "*.conf")
```
### Description
The `Edit-PatchGameSpy` cmdlet scans a game's directory for binary and text files that reference GameSpy master servers and patches them to use a replacement server (OpenSpy by default). For binary files, it replaces the `gamespy.com` hostname and public key at the byte level. For text files, it handles Unreal Engine configuration patterns including UT99 and Unreal 2 master server list entries. The replacement hostname must be exactly 12 characters to match the original `gamespy.com` length.
### Example
```powershell
# Patch all GameSpy references to use OpenSpy (default)
Edit-PatchGameSpy -Path "$InstallDirectory"
# Patch with custom hostname and file patterns
Edit-PatchGameSpy -Path "$InstallDirectory" -Hostname "openspy.net" -BinariesToPatch "*.dll","*.exe","*.so"
```
## `Get-HorizontalFov`
Calculates a horizontal field of view scaled for the current display's aspect ratio.
### Syntax
```powershell
Get-HorizontalFov
-Width <int> (optional, defaults to primary display width)
-Height <int> (optional, defaults to primary display height)
-BaseFov <int> (optional, default: 90)
```
### Description
The `Get-HorizontalFov` cmdlet calculates a scaled horizontal field of view based on the display's aspect ratio relative to a 4:3 baseline. Many older games use a default 90-degree horizontal FOV designed for 4:3 displays. This cmdlet computes the correct horizontal FOV for wider aspect ratios so that the visible area matches what was intended. If `-Width` and `-Height` are not specified, the primary display's resolution is used automatically.
### Example
```powershell
# Get FOV for the current display with default 90-degree base
$fov = Get-HorizontalFov
Write-Host "Horizontal FOV: $fov"
# Get FOV for a specific resolution with a custom base FOV
$fov = Get-HorizontalFov -Width 2560 -Height 1440 -BaseFov 90
Write-Host "Horizontal FOV: $fov" # Returns 106
```
## `Get-VerticalFov`
Calculates a vertical field of view scaled for the current display's aspect ratio.
### Syntax
```powershell
Get-VerticalFov
-Width <int> (optional, defaults to primary display width)
-Height <int> (optional, defaults to primary display height)
-BaseFov <int> (optional, default: 75)
```
### Description
The `Get-VerticalFov` cmdlet calculates a scaled vertical field of view based on the display's aspect ratio relative to a 4:3 baseline. Some games use vertical FOV for their configuration. This cmdlet computes the correct vertical FOV for wider aspect ratios. If `-Width` and `-Height` are not specified, the primary display's resolution is used automatically.
### Example
```powershell
# Get vertical FOV for the current display
$fov = Get-VerticalFov
Write-Host "Vertical FOV: $fov"
# Get vertical FOV for a specific resolution
$fov = Get-VerticalFov -Width 1920 -Height 1080 -BaseFov 75
Write-Host "Vertical FOV: $fov" # Returns 59
```
## `Get-SanitizedPath`
Removes invalid filename characters from a path string.
### Syntax
```powershell
Get-SanitizedPath
-Path <string>
```
### Description
The `Get-SanitizedPath` cmdlet strips invalid filename characters from the provided path string. This is useful when constructing file paths from user input or game titles that may contain characters not allowed in file names. Accepts pipeline input.
### Example
```powershell
$clean = Get-SanitizedPath -Path "Game: The Sequel?"
Write-Host $clean # Returns "Game The Sequel"
# Or via pipeline
"Game: The Sequel?" | Get-SanitizedPath
```
## `Expand-LatestArchive`
Downloads and extracts the latest archive for a game, redistributable, or tool.
### Syntax
```powershell
Expand-LatestArchive
-DestinationPath <string> (optional, defaults to current directory)
-GameId <Guid> (optional, resolved from $GameManifest or $Game)
-RedistributableId <Guid> (optional, resolved from $Redistributable)
-ToolId <Guid> (optional, resolved from $Tool)
```
### Description
The `Expand-LatestArchive` cmdlet extracts the latest archive for a game, redistributable, or tool. When run in a server-side packaging context (where `$LatestArchivePath` is set), it reads directly from the local archive file. When run in a client-side context, it downloads the archive from the LANCommander server via the API. The target ID is automatically resolved from context variables (`$Redistributable`, `$Tool`, `$GameManifest`, or `$Game`) if not explicitly provided. Returns a `DirectoryInfo` object pointing to the extraction destination.
### Example
```powershell
# Extract to the current directory using the context variable
Expand-LatestArchive
# Extract to a specific directory
Expand-LatestArchive -DestinationPath "C:\Games\MyGame"
# Extract a specific game's archive
Expand-LatestArchive -GameId "a1b2c3d4-e5f6-7890-abcd-ef1234567890" -DestinationPath "C:\Staging"
# Extract a redistributable's archive
Expand-LatestArchive -RedistributableId "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
# Extract a tool's archive
Expand-LatestArchive -ToolId "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
```
## `New-Package`
Creates a package result object for the packaging pipeline.
### Syntax
```powershell
New-Package
-Path <string>
-Version <string>
-Changelog <string> (optional)
```
### Description
The `New-Package` cmdlet creates a `Package` object with the specified path, version, and optional changelog, and sets it as the script's return value. This cmdlet is used in [Package](/Scripting/Script Types/Package) scripts to define the output of the packaging process. The `-Path` parameter should point to the directory containing the package contents to be archived.
### Example
```powershell
# Create a package from a build output directory
New-Package -Path "C:\Build\Output" -Version "1.0.0" -Changelog "Initial release"
# Minimal usage
New-Package -Path "$BuildDirectory" -Version "2.1.0"
```
## `Out-PlayerAvatar`
Downloads the current player's avatar from the server.
### Syntax
```powershell
Out-PlayerAvatar
```
### Description
The `Out-PlayerAvatar` cmdlet retrieves the current authenticated player's avatar from the LANCommander server. Returns the avatar file path as a string. This cmdlet takes no parameters and must be run within a LANCommander script context where the player is authenticated.
### Example
```powershell
$avatarPath = Out-PlayerAvatar
Write-Host "Avatar saved to: $avatarPath"
```
# Steam-Related Cmdlets
The following cmdlets provide functionality for interacting with SteamCMD and the Steam Store API. These cmdlets enable you to install Steam games, manage SteamCMD profiles, search for games, and retrieve Steam assets.
# Connection Management
## `Connect-SteamCmd`
Connects to SteamCMD with the specified username and optional password.
### Syntax
```powershell
Connect-SteamCmd
-Username <string>
-Password <SecureString> (optional)
```
### Description
The `Connect-SteamCmd` cmdlet authenticates with SteamCMD using the provided username and optional password. This is required before installing Steam content that requires authentication. Returns a `SteamCmdStatus` object indicating the connection result.
### Example
```powershell
$securePassword = ConvertTo-SecureString "mypassword" -AsPlainText -Force
Connect-SteamCmd -Username "myusername" -Password $securePassword
```
## `Disconnect-SteamCmd`
Disconnects from SteamCMD for the specified username.
### Syntax
```powershell
Disconnect-SteamCmd
-Username <string>
```
### Description
The `Disconnect-SteamCmd` cmdlet logs out the specified username from SteamCMD. Returns a `SteamCmdStatus` object indicating the logout result.
### Example
```powershell
Disconnect-SteamCmd -Username "myusername"
```
## `Get-SteamCmdConnectionStatus`
Gets the connection status for a SteamCMD username.
### Syntax
```powershell
Get-SteamCmdConnectionStatus
-Username <string>
```
### Description
The `Get-SteamCmdConnectionStatus` cmdlet retrieves the current connection status for the specified username. Returns a `SteamCmdConnectionStatus` object containing information about whether the user is connected and authenticated.
### Example
```powershell
$status = Get-SteamCmdConnectionStatus -Username "myusername"
Write-Host "Connected: $($status.IsConnected)"
```
# SteamCMD Configuration
## `Get-SteamCmdPath`
Gets the path to the SteamCMD executable.
### Syntax
```powershell
Get-SteamCmdPath
```
### Description
The `Get-SteamCmdPath` cmdlet attempts to auto-detect the SteamCMD executable path on the system. Returns the path as a string if found, or nothing if SteamCMD is not detected.
### Example
```powershell
$steamCmdPath = Get-SteamCmdPath
if ($steamCmdPath) {
Write-Host "SteamCMD found at: $steamCmdPath"
}
```
## `Get-SteamCmdProfile`
Gets a SteamCMD profile for the specified username.
### Syntax
```powershell
Get-SteamCmdProfile
-Username <string>
```
### Description
The `Get-SteamCmdProfile` cmdlet retrieves the SteamCMD profile configuration for the specified username. Returns a `SteamCmdProfile` object containing the username and install directory, or nothing if the profile doesn't exist.
### Example
```powershell
$profile = Get-SteamCmdProfile -Username "myusername"
if ($profile) {
Write-Host "Install Directory: $($profile.InstallDirectory)"
}
```
## `Get-SteamCmdProfiles`
Gets all SteamCMD profiles.
### Syntax
```powershell
Get-SteamCmdProfiles
```
### Description
The `Get-SteamCmdProfiles` cmdlet retrieves all configured SteamCMD profiles. Returns a collection of `SteamCmdProfile` objects.
### Example
```powershell
$profiles = Get-SteamCmdProfiles
foreach ($profile in $profiles) {
Write-Host "$($profile.Username): $($profile.InstallDirectory)"
}
```
## `Set-SteamCmdProfile`
Creates or updates a SteamCMD profile.
### Syntax
```powershell
Set-SteamCmdProfile
-Username <string>
-InstallDirectory <string>
```
### Description
The `Set-SteamCmdProfile` cmdlet creates or updates a SteamCMD profile with the specified username and install directory. This profile is used to store SteamCMD configuration settings.
### Example
```powershell
Set-SteamCmdProfile -Username "myusername" -InstallDirectory "C:\Steam\Content"
```
## `Remove-SteamCmdProfile`
Removes a SteamCMD profile.
### Syntax
```powershell
Remove-SteamCmdProfile
-Username <string>
```
### Description
The `Remove-SteamCmdProfile` cmdlet deletes the SteamCMD profile for the specified username.
### Example
```powershell
Remove-SteamCmdProfile -Username "myusername"
```
# Steam Content Installation
## `Install-SteamContent`
Installs Steam content (game, DLC, etc.) using SteamCMD.
### Syntax
```powershell
Install-SteamContent
-AppId <uint>
-InstallDirectory <string>
-Username <string> (optional)
```
### Description
The `Install-SteamContent` cmdlet queues an installation job to download and install Steam content using SteamCMD. The `AppId` parameter specifies the Steam App ID to install, and `InstallDirectory` is where the content will be installed. If `Username` is provided, it will use that profile's authentication. Returns a `SteamCmdInstallJob` object that can be used to track the installation progress.
### Example
```powershell
$job = Install-SteamContent -AppId 730 -InstallDirectory "C:\Games\Counter-Strike 2" -Username "myusername"
Write-Host "Installation job started: $($job.Id)"
```
## `Remove-SteamContent`
Removes Steam content from the specified install directory.
### Syntax
```powershell
Remove-SteamContent
-InstallDirectory <string>
```
### Description
The `Remove-SteamContent` cmdlet removes Steam content from the specified installation directory. Returns a `SteamCmdStatus` object indicating the result of the operation.
### Example
```powershell
Remove-SteamContent -InstallDirectory "C:\Games\Counter-Strike 2"
```
# Steam Store
## `Search-SteamGames`
Searches for games on the Steam Store.
### Syntax
```powershell
Search-SteamGames
-Keyword <string>
```
### Description
The `Search-SteamGames` cmdlet searches the Steam Store for games matching the specified keyword. Returns a collection of `GameSearchResult` objects containing the game name and App ID.
### Example
```powershell
$results = Search-SteamGames -Keyword "Counter-Strike"
foreach ($result in $results) {
Write-Host "$($result.Name) - App ID: $($result.AppId)"
}
```
## `Get-SteamWebAssetUri`
Gets the URI for a Steam web asset (logo, header, etc.).
### Syntax
```powershell
Get-SteamWebAssetUri
-AppId <int>
-WebAssetType <WebAssetType>
```
### Description
The `Get-SteamWebAssetUri` cmdlet returns the URI for a specific web asset type for the given Steam App ID. The `WebAssetType` parameter accepts one of the following values:
- `Capsule` - Small capsule image (231x87)
- `CapsuleLarge` - Large capsule image (616x353)
- `Header` - Header image
- `HeroCapsule` - Hero capsule image
- `LibraryCover` - Library cover image (600x900)
- `LibraryHeader` - Library header image
- `LibraryHero` - Library hero image
- `Logo` - Game logo (PNG)
Returns a `Uri` object.
### Example
```powershell
$logoUri = Get-SteamWebAssetUri -AppId 730 -WebAssetType Logo
Write-Host "Logo URL: $logoUri"
```
## `Get-SteamAppInfo`
Gets app details from the Steam Store (no API key required) and the **changenumber** (build ID) and last updated time from Steam via SteamKit2 (PICS).
### Syntax
```powershell
Get-SteamAppInfo
-AppId <uint>
```
### Description
The `Get-SteamAppInfo` cmdlet returns a `SteamAppInfo` object with name, short description, release date, developers, publishers, and store URL from the public Steam Store `appdetails` endpoint. It also connects to Steam via SteamKit2 (PICS) to fill **LastChangenumber** and **LastUpdated** for the public branch—no Web API key required. If SteamKit2 cannot connect or the app cannot be queried, those two properties are null.
### Example
```powershell
$info = Get-SteamAppInfo -AppId 413150
if ($info) {
Write-Host "Name: $($info.Name)"
Write-Host "Description: $($info.Description)"
Write-Host "Release date: $($info.ReleaseDate)"
Write-Host "Developer: $($info.Developer)"
}
```

View file

@ -0,0 +1,13 @@
---
sidebar_label: Overview
sidebar_position: 1
---
# Scripting
LANCommander includes a full PowerShell runtime that enables powerful automation for game installation, configuration, and management. Scripts are attached to games and executed by the launcher at key points in the game's lifecycle.
Scripts have access to a set of custom cmdlets that simplify common tasks such as patching binary files, reading display information, updating INI files, and syncing player profile data with the server.
import DocCardList from '@theme/DocCardList';
<DocCardList />

Some files were not shown because too many files have changed in this diff Show more