- Point docs, clone examples, launcher package/repo defaults, and GitHub Actions repository guards at HighSocietyRaiding/Fractured (Gitea). - Add db_world update: stackable 200 for item classes 3 (gems), 7 (trade goods), and 5 (reagents) where stack size was below 200. Co-authored-by: Cursor <cursoragent@cursor.com>
19 KiB
Fractured Launcher (Electron)
Windows and Linux (AppImage) launcher with no extra console window, native Browse folder dialog, Gitea or GitHub release assets + GitHub repo file sync, realmlist, optional auth, Play, and auto-update (via electron-updater). This is the only supported client launcher in this repo.
Requirements
- Node.js 20+ (includes npm)
Run from source
cd tools/fractured-launcher-electron
npm install
npm start
On first run, launcher.json is created: dev — next to the app in this folder; Windows packaged — beside the .exe; Linux AppImage / macOS packaged — under Electron app.getPath('userData') (typically under ~/.config/, folder name from the app; AppImage mount is read-only so config cannot live beside the binary).
Where patches download from
- Recommended (self-hosted Gitea): set
gitea.base_url,gitea.owner,gitea.repoinlauncher.json(seedefault-launcher.json). Players needGITEA_TOKEN(or the env name ingitea.token_env) if the Gitea repo is private — same trade-off as any private host (per-player token, SSO proxy, or a read-only deploy token you accept distributing). - Fallback: if
gitea.base_urlis empty,from_releaseuses the GitHub Releases API againstgithub.owner/github.repo(defaults to thisFracturedrepo for non-release paths), with optionalGITHUB_TOKENfor private assets.
Build Windows installers
npm install
npm run pack:win
Produces under dist/:
| Artifact | Purpose |
|---|---|
Fractured-Launcher-${version}-Setup.exe (NSIS) |
Recommended for players — supports seamless auto-update and restart. |
Fractured-Launcher-${version}-Windows-Portable.exe |
No installer; players replace the file manually. Auto-update is less reliable than NSIS. |
Build Linux AppImage
cd tools/fractured-launcher-electron
npm install
npm run pack:linux
Produces dist/Fractured-Launcher-${version}-Linux-x86_64.AppImage. Same lib/baked-gitea-channel.js and default-launcher.json as Windows; run on Linux (or use Fractured launcher CI / Sync release to Gitea, which upload this file to Gitea with the Windows installers).
Quick local test (avoids tag snapshot / CI):
- Linux: from repo root,
bash tools/fractured-launcher-electron/scripts/manual-pack-linux.sh→dist/*.AppImage. - Windows: on a Windows machine,
cd tools/fractured-launcher-electron,npm ci,npm run pack:win→dist/*.exe.
Hardcoded Gitea channel (non-token)
lib/baked-gitea-channel.js exports base_url, owner, repo, release_tag. Set those strings once in the repo (same values you use for CI upload — not secret). At runtime config-store merges them into gitea.* so launcher.json does not need those fields; GITEA_TOKEN (or gitea.token_env) is still only for private Gitea. Leave a field '' in the baked file to fall back to default-launcher.json / user launcher.json for that key.
npm run pack:win is plain electron-builder — no inject step, no extra JSON beside the app.
Auto-update behaviour
- Packaged builds only (
npm run pack:winoutput). Innpm startdev mode, update checks are skipped (button still explains that). - No implicit GitHub feed: the app does not guess
package.json→repositoryanymore. Without configuration you get a clear “skipped” message instead of a 404 on a private repo. - Configured feeds (first match wins):
update_feed_url/LAUNCHER_UPDATE_URL(genericlatest.yml); orgiteablock filled in +GITEA_TOKENwhen the instance is private (resolves…/releases/download/{tag}/); orGITHUB_TOKEN+github.owner/github.repofor private GitHub releases only. - ~5 seconds after launch, then every 6 hours, the app checks when a feed is configured.
- When a download finishes, a dialog offers Restart now (calls
quitAndInstall) or Later. - Manual check: button Check launcher updates in the UI.
Where launcher updates are hosted
npm run publish:win runs electron-builder with --publish never — artifacts stay in dist/; CI uploads them to Gitea when you publish a GitHub release. For ad-hoc uploads, use scripts/upload-release-to-gitea.sh. For launcher auto-update, prefer:
- Set
update_feed_url(orLAUNCHER_UPDATE_URL) to a generic HTTPS base URL wherelatest.ymland the installer files are hosted (often the same Gitea release attachment URLs pattern your reverse proxy exposes), or - Keep publishing to a GitHub release only for
latest.yml+ installers if you accept that small metadata/binary channel there.
Private GitHub updater: set GH_TOKEN / GITHUB_TOKEN / github.token_env as documented in lib/auto-update.js behaviour.
Generic feed: optional Bearer token via the same token envs if your static host checks Authorization.
Publishing a new launcher version
- Bump
versioninpackage.jsononmain(or your release branch) and merge. - Create a GitHub release (tag + attach patches /
Wow.exeif needed) and click Publish — Sync release to Gitea builds Windows + Linux launcher artifacts and mirrors everything to Gitea. - Local check:
npm run pack:win(on Windows) ornpm run pack:linux/scripts/manual-pack-linux.sh, thenscripts/upload-release-to-gitea.shwith the sameGITEA_*env vars as CI if you need a manual upload.
Sync to Gitea (patches + launcher binaries)
CI workflow Sync release to Gitea (.github/workflows/gitea-release-sync.yml) runs on every published GitHub release on this repo:
- Triggers on release published on
HighSocietyRaiding/Fractured(or workflow_dispatch with a tag). - Builds Windows (NSIS + portable) and Linux (AppImage) in parallel, each using
tools/fractured-launcher-electronfrom the default branch (overlaid onto the tag checkout), so older release tags never ship a launcher missing newlib/*.jsfiles. - Downloads all assets attached to that GitHub release (MPQs, patched
Wow.exe, etc.). - Merges with the built launcher artifacts and uploads to a Gitea release with the same tag (existing attachments on that Gitea release are replaced). Launcher installers attached on GitHub (
Fractured-Launcher*, case-insensitive) are not merged — the CI build from the default branch is the only source of launcher binaries, so an old installer on the GitHub release cannot “stick” on Gitea next to a newer build.*.blockmapandbuilder-debug.ymlare omitted from the merge and from Gitea uploads.
GitHub Actions secrets (repository → Settings → Secrets and variables → Actions):
| Secret | Example |
|---|---|
GITEA_BASE_URL |
https://git.yourdomain.com (no trailing slash) |
GITEA_TOKEN |
Gitea personal access token with permission to manage releases and attachments on the target repo |
GITEA_OWNER |
Organization or username on Gitea |
GITEA_REPO |
Repository name — must already have at least one commit (Gitea returns HTTP 422 “repo is empty” for zero-commit repos; push e.g. a README on main or set GITEA_TARGET_REF to your default branch) |
Optional variable (Settings → Variables): GITEA_TARGET_REF — default branch/commitish used only when the workflow must create a new Gitea release and Gitea needs target_commitish (defaults to main in the upload script if unset).
Player launcher.json: packaged builds should already include gitea.base_url / owner / repo from the bake step above. Players only need to set GITEA_TOKEN (or your token_env) if the Gitea repo is private. To point at another instance, edit gitea in launcher.json:
"gitea": {
"base_url": "https://git.yourdomain.com",
"owner": "myorg",
"repo": "fractured-patches",
"release_tag": "latest",
"token_env": "GITEA_TOKEN"
}
Manual upload: bash scripts/upload-release-to-gitea.sh /path/to/files v1.0.0 with the same env vars as CI.
Legacy “bridge” after changing baked-gitea-channel.js: Players still using the old Gitea URL only receive launcher updates from that host. Build Windows + Linux installers (e.g. download Fractured launcher CI artifacts, or run npm run pack:win / npm run pack:linux), put dist/ contents in one folder if needed, then:
export GITEA_BASE_URL=http://your-old-host:port # legacy base, no trailing slash
export GITEA_TOKEN=... GITEA_OWNER=Dawnsorrow GITEA_REPO=Fractured-Distro
bash tools/fractured-launcher-electron/scripts/gitea-replace-launcher-only.sh \
tools/fractured-launcher-electron/dist latest
That script deletes only Fractured-Launcher*, latest.yml (only if you supply a new latest.yml in dist/), latest-linux.yml (only if supplied), *.blockmap, and builder-debug.yml on the release, then uploads the new files — Wow.exe, MPQs, and patch-manifest.json are left alone. If you only built Linux locally, merge Windows CI dist/ files into the same folder first so latest.yml is not removed without a replacement. Use release tag latest if that is what release_tag points at.
Sync did not run / Gitea unchanged — checklist
- Git tag ≠ GitHub Release — Only Releases (published on the GitHub Releases page) trigger this workflow. If your teammate only
git push --tags, create a Release from that tag and click Publish (or run Actions → Sync release to Gitea → Run workflow and enter the tag). - Manual run: tag vs title — Run workflow must receive the git tag (e.g.
v0.7.11-paragon-…), copied from the release page’s tag badge. Pasting the release title (long line with spaces/parentheses) breaksgit fetchwithinvalid refspec. - Draft release — Must click Publish release; drafts do not mirror.
- Workflow on default branch — GitHub runs
releaseworkflows from the default branch (e.g.main). Ensure.github/workflows/gitea-release-sync.ymlis merged there. - Repo name guard — Jobs use
if: github.repository == 'HighSocietyRaiding/Fractured'. Forks or renames must change that line or runs are skipped (GitHub Actions only; canonical clone is Gitea). - Secrets —
GITEA_BASE_URL,GITEA_TOKEN,GITEA_OWNER,GITEA_REPOmust be set under Settings → Secrets and variables → Actions. A failed “Upload to Gitea” step usually prints which is missing. - Actions tab — Open the latest Sync release to Gitea run; a red build-electron (old tag without
package-lock.json, etc.) or Upload to Gitea step shows the real error. - HTTP 422
repo is empty— The Gitea repo has no commits yet. Push any initial commit (e.g. Add README in the Gitea web UI, orgit pushtomain). Optionally setGITEA_TARGET_REFto match your real default branch if it is notmain. From this repo you can runscripts/bootstrap-gitea-repo.sh(see script header forGITEA_*env or pass the HTTPS/SSH clone URL as the first argument). sync Wow.exe: fetch failed— Often HTTPS/TLS to Gitea; usehttp://…inlib/baked-gitea-channel.jsif you only serve plain HTTP, or fix certs /NODE_EXTRA_CA_CERTS. EnsureWow-patched.exeexists on the release (release_tag:latestvs pinned). Errors include the failing URL when possible.- Wine + Windows portable — If the folder picker returns
/home/..., the launcher maps it toZ:\home\...(Wine’s Unix root).Wow.exeis matched case-insensitively for Linux-backed folders. Re-save the WoW folder after upgrading if validation still fails. EBUSY/ file locked on Windows — The client (or antivirus) may keep.MPQfiles open. The launcher retries for a short window and downloads the new file before replacing the old one; if sync still fails, exit WoW (and any tool previewing that folder) and run Download updates again..bak-*clutter — When Download updates finishes without error, the launcher removes matching*.bak-YYYYMMDD-HHmmssfiles from earlier runs (same pattern it uses when replacing files). Failed syncs do not delete backups.- Gitea still shows an old launcher version — The sync workflow overlays
tools/fractured-launcher-electronfrom the default branch, sopackage.jsonthere defines the built version. Ensure launcher changes are merged tomainbefore publishing the GitHub release (or re-run Actions → Sync release to Gitea after merging). Previously, Fractured-Launcher* files attached on the GitHub release were merged too, which could leave two installer versions on Gitea; those assets are now skipped in favor of CI-only builds. - Migrating
baked-gitea-channel.jsto a new host — Publishgitea-replace-launcher-only.sh(see Manual upload above) on the old Gitealatestrelease so auto-update still works until clients move to the new URL.
Private Gitea token for players
Do not embed a shared admin PAT in a shipped launcher.json. Prefer read-only tokens scoped to one repo, short-lived tokens, or a small auth service that redirects to signed URLs.
Release asset names must match files[].source when from_release: true. Use release_tag: "latest" or a pinned tag matching both GitHub and Gitea.
Patch versions (same filenames, different bytes)
The launcher does not read Git commits. For turn-key updates when asset names stay fixed (e.g. Wow-patched.exe — add more files entries for any extra MPQs you ship):
- Ship
patch-manifest.jsonnext to those files on every release (Gitea/GitHub attachment). It lists aversionlabel (any string you bump per release, e.g.v0.9.0-client) and asha256perfiles[].sourcename. - With
patch_manifest.enabled: true (default indefault-launcher.json), Download updates first fetches the manifest from the same release channel. If the files already on disk match those checksums, the player sees “already match build … (nothing to download)” — no redundant downloads. - After a real download, the launcher re-hashes installed files and compares to the manifest; mismatch → clear error. It also writes
.fractured/patch-state.jsonunder the WoW folder so the UI can show “Installed client files: …”.
If patch-manifest.json is missing on a release, the launcher falls back to always downloading all configured files (same as before).
Generate the manifest when you cut a release (paths are your local patch binaries):
cd /path/to/staging
node tools/fractured-launcher-electron/scripts/generate-patch-manifest.js v0.9.0-client Wow-patched.exe > patch-manifest.json
Attach patch-manifest.json together with the MPQ/exe to the GitHub release (CI sync copies it to Gitea with everything else).
CI
Workflow Fractured launcher CI (.github/workflows/fractured-launcher-ci.yml) runs on pushes/PRs under tools/fractured-launcher-electron/: Windows (npm run pack:win) and Linux (npm run pack:linux) jobs, each electron-builder … --publish never. Actions → Fractured launcher CI → Run workflow runs it manually.
Sync release to Gitea (.github/workflows/gitea-release-sync.yml) uses the same pack commands. If you see GH_TOKEN / GitHubPublisher errors in logs, the job is almost certainly an old Re-run failed jobs — open Actions → Sync release to Gitea → Run workflow, enter the tag, and start a new run instead.
Config
Schema is defined by default-launcher.json (shipped in the app; first run copies to launcher.json — beside the Windows exe, or under userData on Linux/macOS packaged builds):
game_dir: WoW 3.3.5a root (containsWow.exe).update_feed_url: optional generic HTTPS base for launcher auto-update.launcher_updates_from_github: defaultfalse. Only whentruewill aGITHUB_TOKEN(orgithub.token_env) enable electron-updater’s GitHub provider againstgithub.owner/github.repo. Leavefalsewhen launcher binaries andlatest.ymllive on Gitea (usegitea+ token instead) so a stray GitHub token does not produce “No published versions on GitHub”.gitea:base_url,owner,repo,release_tag,token_env— whenbase_urlis set (and owner/repo set),from_releasedownloads and (with token if needed) the generic updater feed use Gitea. Required for players if your CI mirrors patches/launchers to Gitea only.github: used for non-release repo paths (from_release: false) and for GitHubfrom_releasewhengitea.base_urlis empty.patch_manifest:enabled,source(defaultpatch-manifest.json),from_release— checksum-based skip + verify (see above).files: default[]. Download updates resolves what to pull in order: (1) non-emptyfilesif you set explicitsource→destpairs; (2) else each key inpatch-manifest.jsonon the release (recommended); (3) else release attachments except launcher artifacts (Fractured-Launcher*,*.blockmap,latest*.yml,.AppImage,patch-manifest.json):.MPQ→Data/enUS/<name>.MPQ(extension forced to.MPQcaps for client compatibility), one.exe→launch.exe. Multiple.exeattachments require a manifest. LegacyWow-patched.exeentries are removed when merginglauncher.json.realmlist,auth,launch(**exe**,args). OnlyData/enUS/realmlist.wtfis written: anyrealmlist.pathsentry that is not the enUS file is ignored (soenGBis never created). On Linux, Play never runsWow.exeas a native process (that yields EACCES). Uselaunch.linux_wrapper(default["wine"]) so the launcher runs e.g.wine /path/Wow.exe…args, or setlaunch.linux_steam_urito a Steam URI (e.g.steam://rungameid/…for a non-Steam game shortcut — the number is shown in Steam’s shortcut properties). Optionallinux_steam_binarydefaults tosteam; for Flatpak Steam uselinux_steam_spawn:["flatpak", "run", "com.valvesoftware.Steam"](the URI is appended as the last argument). After a successful Download updates run, the launcher deletes prior*.bak-YYYYMMDD-HHmmssbackup files it created under the WoW folder.