Contributing
🇪🇸 ¿Prefieres español? Lee la versión en español.
Thanks for wanting to help! This guide covers how to build the project and how to make the most common changes.
Requirements
- Windows, Linux or macOS (the UI uses Avalonia, which is cross-platform).
- .NET 9 SDK.
- (Only to build the installer) Inno Setup 6.
Build and run
git clone https://github.com/JuanP-G/MC-ServerLauncher.git
cd MC-ServerLauncher
dotnet run --project McServerLauncher
Build the documentation site locally (this page):
dotnet tool install -g docfx # first time only
.\docs\build-docs.ps1 # builds and serves at http://localhost:8080
Conventions
- Code comments and identifiers are in English. User-facing text is not hard-coded — it goes through the localization system (see below).
- Keep the MVVM split: logic in
Services/, bindable state/commands inViewModels/, only thin code-behind inViews/. - No absolute machine paths. Use
Environment.GetFolderPath(...)and%APPDATA%. - Public types/members get an XML
///summary — that's what powers the API Reference.
How-to recipes
Add a language
- Copy
Resources/Strings.resxtoResources/Strings.<code>.resx(e.g.Strings.it.resx) and translate every<value>. - Add the code to
<SatelliteResourceLanguages>inMcServerLauncher.csproj. - Add it to the
Languageslist inMainViewModelso it shows in the sidebar selector.
Add a translatable text
- Add the same
<data name="MyKey">entry to all five.resxfiles with the translation. - Use it from XAML as
{loc:Loc MyKey}, or from code asLocalizer.Get("MyKey")(usestring.Format(Localizer.Get("MyKey"), arg)when it has{0}placeholders).
Add a server.properties setting to the visual editor
- Add the control + label/description in
Views/ServerConfigDialog.axaml(and bind it in its code-behind). - Read/write the key through
ServerPropertiesService.Read/Update, which preserves the rest of the file, comments and order.
Add a new dialog or service
- Dialog: create
Views/MyDialog.axaml+.axaml.csas a plain AvaloniaWindow, localize its texts with{loc:Loc ...}, and open it from a ViewModel command (seeWhatsNewDialogorPlayitApiKeyDialogas small examples). - Service: add a focused class in
Services/, keep it UI-free, and inject/instantiate it from the relevant ViewModel (see howServerViewModelcomposes its services).
Release a new version
- Bump
<Version>inMcServerLauncher/McServerLauncher.csproj— that's the single source of truth.publish.ps1reads it and passes it to Inno Setup. Keep the.issfallbackMyAppVersionaligned only so a direct/manual Inno Setup build doesn't produce a stale filename. - Add a what's-new entry so the update dialog has something to show:
- A new tuple at the top of
EntriesinServices/Changelog.cs(newest first), e.g.(new Version(1, 6, 0), "Whatsnew_1_6_0"). - The matching
Whatsnew_x_y_zkey in all five.resxfiles (Spanish text in the neutralStrings.resx, translations in the rest). See Add a translatable text above.
- A new tuple at the top of
- Run
publish.ps1. It publishes the self-containedwin-x64build, builds the Inno Setup installer indist/, and writesdist/SHA256SUMS.txtnext to it. - Create the release with both assets and bilingual notes:
gh release create vX.Y.Z dist/MC-ServerLauncher-Setup-X.Y.Z.exe dist/SHA256SUMS.txt- The in-app updater looks for the
.exeasset (so always attach it) and forSHA256SUMS.txt, which it uses to verify the installer before running it (UpdateService.CheckAsync). Verification is mandatory: without that file the updater refuses the silent install and just opens the release page — so never skip it.
- The in-app updater looks for the
Before changing how versions are numbered, check what the installed version can parse. A release whose tag has a shape older clients do not understand is invisible to them, however correct the new code is — and the code that understands it ships inside that release. It has happened twice: pre-releases were invisible before 1.10.1, and four-number tags before 1.10.3.1, because in both cases
UpdateServicehad to change to see them. The first release of a new shape always has to be handed over by hand; say so instead of promising an automatic update.
Numbering. A beta carries a fourth number that extends the stable it follows: after
1.10.3come1.10.3.1,1.10.3.2, and the finished work ships as the next stable (1.11.0). It is that way round on purpose — numbering betas after the version they lead to would make the stable sort below its own betas, stranding everyone who tested them.
- For a beta, publish it as a pre-release from its branch instead:
GitHub leaves pre-releases out ofgh release create vX.Y.Z --prerelease --target <branch> dist/MC-ServerLauncher-Setup-X.Y.Z.exe dist/SHA256SUMS.txt/releases/latest, so people on the stable line are never pushed onto one. From 1.10.1 the updater reads the release list instead, which is what makes a beta reachable at all — and it says it is a beta before the Update button is pressed. Anything older than 1.10.1 cannot see betas at all, so a first beta after a stable has to be handed over by hand. - Publishing the release automatically triggers the Linux (
release-linux.yml) and macOS (release-macos.yml) workflows, which build and attach the.AppImageand the two.dmgs. Don't upload those by hand — just wait for the workflows to finish.
Documentation site (GitHub Pages)
The API Reference + these articles are published automatically to GitHub Pages by
.github/workflows/docs.yml on every push to main. The site is generated with DocFX from the
/// comments and the markdown in docs/.
One-time setup by the repo owner: enable Pages in Settings → Pages → Source: “GitHub Actions”. After that, every push updates the site automatically.