Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
44756fe
test(windows): build an isolated update baseline
Mat4m0 Aug 31, 2026
dd9e48d
test(windows): prove signed update recovery and rollback
Mat4m0 Aug 31, 2026
4ad087a
test(platform): share installed update baselines
Mat4m0 Aug 31, 2026
4bf385d
test(linux): prove Flatpak update recovery and rollback
Mat4m0 Aug 31, 2026
c58c40a
test(windows): launch Crashpad with desktop handles
Mat4m0 Aug 31, 2026
1f6391a
test(linux): qualify desktop secret providers
Mat4m0 Aug 31, 2026
efec6ec
docs(platform): explain cross-platform installation
Mat4m0 Aug 31, 2026
8effe2b
docs(updates): describe explicit launcher restart
Mat4m0 Aug 31, 2026
c81e1df
test(windows): retain startup failure evidence
Mat4m0 Aug 31, 2026
9b1d6e2
test(windows): continue after empty event logs
Mat4m0 Aug 31, 2026
09cad03
test(linux): force recovery update fetch
Mat4m0 Aug 31, 2026
cf6d935
build(windows): bundle the native C++ runtime
Mat4m0 Aug 31, 2026
b969352
test(windows): isolate Squirrel first run
Mat4m0 Aug 31, 2026
a1d3eed
test(windows): seed data before installer launch
Mat4m0 Aug 31, 2026
708095b
test(windows): verify embedded ASAR integrity
Mat4m0 Aug 31, 2026
c88f4da
test(windows): pass first-run path without shell parsing
Mat4m0 Aug 31, 2026
1b1dbaf
test(windows): isolate rollback first run
Mat4m0 Aug 31, 2026
0d52d44
fix(windows): remove local crash reporting
Mat4m0 Aug 31, 2026
7e3bfbb
test(windows): isolate instrumented crash reporting
Mat4m0 Aug 31, 2026
cec2d5d
test(windows): preserve desktop process shape
Mat4m0 Aug 31, 2026
91b927b
test(windows): run desktop probe after UI checks
Mat4m0 Aug 31, 2026
cd225ee
test(windows): retain installed failure evidence
Mat4m0 Aug 31, 2026
826143c
test(windows): initialize qualification debugging after Crashpad
Mat4m0 Aug 31, 2026
3659fa1
test(windows): close Squirrel first run cleanly
Mat4m0 Aug 31, 2026
804bd46
test(windows): use a fixed DevTools port
Mat4m0 Aug 31, 2026
3360e0e
test(windows): capture installed renderer failures
Mat4m0 Aug 31, 2026
3457cef
fix(windows): bind native addons to Electron
Mat4m0 Aug 31, 2026
ee0994f
fix(windows): include delay-load types safely
Mat4m0 Aug 31, 2026
ea85414
test(windows): acknowledge cached client frames
Mat4m0 Aug 31, 2026
eccb854
test(windows): remove temporary crash capture
Mat4m0 Aug 31, 2026
0e952cc
test(linux): acknowledge cached client frames
Mat4m0 Aug 31, 2026
951a110
test(game): wait for stable crash fallback state
Mat4m0 Aug 31, 2026
b84d0c5
ci(linux): seed desktop portal activation environment
Mat4m0 Aug 31, 2026
010ac65
test(linux): enforce portal activation order
Mat4m0 Aug 31, 2026
f5a599a
test(input): focus the owned game before pointer lock
Mat4m0 Aug 31, 2026
db643e1
test(linux): qualify portal secrets on KDE
Mat4m0 Aug 31, 2026
fcab017
test(linux): select the Secret portal on KDE
Mat4m0 Aug 31, 2026
b66e0a8
test(electron): allow loaded runners to finish startup
Mat4m0 Aug 31, 2026
1dc73ff
ci(linux): allow manual Flatpak qualification
Mat4m0 Aug 31, 2026
56bf642
test(input): finish renderer startup before activation
Mat4m0 Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 79 additions & 3 deletions .github/workflows/linux-flatpak-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
pull_request:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read
Expand All @@ -24,29 +25,45 @@ jobs:
- name: Install build and Flatpak dependencies
run: |
sudo apt-get update
sudo apt-get install -y flatpak flatpak-builder g++ libglib2.0-dev gnupg xvfb
sudo apt-get install -y flatpak flatpak-builder g++ libglib2.0-dev gnupg ostree xvfb
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak install --user -y flathub org.freedesktop.Platform//25.08 org.freedesktop.Sdk//25.08 org.electronjs.Electron2.BaseApp//25.08
- name: Install the pinned Rust toolchain
run: rustup toolchain install
- run: pnpm install --frozen-lockfile
- run: pnpm run check
- name: Build the immediate prior package for update qualification
run: node --import ./scripts/ts-hook.mjs scripts/linux-update-fixture.ts "$RUNNER_TEMP/linux-baseline-package"
- name: Build the release-shaped Electron package
run: pnpm package
env:
GW_PACKAGE_INTENT: release
- name: Build and install a GPG-verified local repository
- name: Build two GPG-verified repository commits and install the baseline
shell: bash
run: |
export GNUPGHOME="$RUNNER_TEMP/flatpak-ci-gpg"
mkdir -m 700 "$GNUPGHOME"
gpg --batch --passphrase '' --quick-generate-key 'gwonmac CI qualification <ci@example.invalid>' ed25519 sign 1d
key_id="$(gpg --batch --with-colons --list-secret-keys | awk -F: '$1 == "fpr" { print $10; exit }')"
mv "out/Guild Wars Reforged-linux-x64" "$RUNNER_TEMP/linux-candidate-package"
cp -a "$RUNNER_TEMP/linux-baseline-package" "out/Guild Wars Reforged-linux-x64"
flatpak-builder --user --disable-rofiles-fuse --force-clean --repo=flatpak-baseline-repo --gpg-sign="$key_id" --gpg-homedir="$GNUPGHOME" flatpak-build packaging/linux/io.github.mat4m0.gwonmac.yml
flatpak build-update-repo --gpg-sign="$key_id" --gpg-homedir="$GNUPGHOME" flatpak-baseline-repo
baseline_commit="$(ostree --repo=flatpak-baseline-repo rev-parse app/io.github.mat4m0.gwonmac/x86_64/master)"
cp -a flatpak-baseline-repo flatpak-repo
rm -rf "out/Guild Wars Reforged-linux-x64"
mv "$RUNNER_TEMP/linux-candidate-package" "out/Guild Wars Reforged-linux-x64"
flatpak-builder --user --disable-rofiles-fuse --force-clean --repo=flatpak-repo --gpg-sign="$key_id" --gpg-homedir="$GNUPGHOME" flatpak-build packaging/linux/io.github.mat4m0.gwonmac.yml
flatpak build-update-repo --gpg-sign="$key_id" --gpg-homedir="$GNUPGHOME" flatpak-repo
candidate_commit="$(ostree --repo=flatpak-repo rev-parse app/io.github.mat4m0.gwonmac/x86_64/master)"
test "$baseline_commit" != "$candidate_commit"
gpg --batch --export "$key_id" > "$RUNNER_TEMP/flatpak-ci-public.gpg"
flatpak remote-add --user --gpg-import="$RUNNER_TEMP/flatpak-ci-public.gpg" gwonmac-ci "file://$GITHUB_WORKSPACE/flatpak-repo"
flatpak remote-add --user --gpg-import="$RUNNER_TEMP/flatpak-ci-public.gpg" gwonmac-ci "file://$GITHUB_WORKSPACE/flatpak-baseline-repo"
flatpak install --user -y gwonmac-ci io.github.mat4m0.gwonmac
echo "GW_LINUX_BASELINE_COMMIT=$baseline_commit" >> "$GITHUB_ENV"
echo "GW_LINUX_CANDIDATE_COMMIT=$candidate_commit" >> "$GITHUB_ENV"
echo "GW_LINUX_QUALIFICATION_REMOTE=gwonmac-ci" >> "$GITHUB_ENV"
echo "GW_LINUX_QUALIFICATION_REMOTE_URL=file://$GITHUB_WORKSPACE/flatpak-repo" >> "$GITHUB_ENV"
- name: Qualify the installed Xwayland package
# Start the session bus inside the display so portal services activated
# by D-Bus inherit DISPLAY instead of starting headless.
Expand Down Expand Up @@ -98,3 +115,62 @@ jobs:
done
test -S "$XDG_RUNTIME_DIR/wayland-gwonmac"
dbus-run-session -- env WAYLAND_DISPLAY=wayland-gwonmac XDG_SESSION_TYPE=wayland GW_LINUX_NATIVE_WAYLAND=1 pnpm test:linux-installed

desktop-secrets:
needs: installed-xwayland
strategy:
fail-fast: false
matrix:
desktop: [gnome, kde]
runs-on: ubuntu-24.04
timeout-minutes: 45
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@d15e628ca66d93ee5f352c71671a7bc6a97af5c9 # v6.0.8
with:
version: 11.13.1
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24.18.0
cache: pnpm
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: linux-flatpak-qualification-repo
path: flatpak-repo
- name: Install GNOME portal and secret service
if: matrix.desktop == 'gnome'
run: |
sudo apt-get update
sudo apt-get install -y flatpak xvfb dbus-x11 gnome-keyring xdg-desktop-portal xdg-desktop-portal-gnome
- name: Install KDE portal and secret service
if: matrix.desktop == 'kde'
run: |
sudo apt-get update
# Noble's KWallet 5 predates its Secret portal backend. GNOME Keyring
# supplies that standard interface for this KDE integration gate;
# current KWallet 6 supplies the same interface on supported systems.
sudo apt-get install -y flatpak xvfb dbus-x11 plasma-workspace xdg-desktop-portal xdg-desktop-portal-kde kwalletmanager gnome-keyring
mkdir -p "$HOME/.config/xdg-desktop-portal"
printf '[preferred]\ndefault=kde;gtk;\norg.freedesktop.impl.portal.Secret=gnome-keyring;\n' > "$HOME/.config/xdg-desktop-portal/kde-portals.conf"
- name: Install the exact GPG-verified package
run: |
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak install --user -y flathub org.freedesktop.Platform//25.08
flatpak remote-add --user --gpg-import=flatpak-repo/qualification-public.gpg gwonmac-desktop "file://$GITHUB_WORKSPACE/flatpak-repo"
flatpak install --user -y gwonmac-desktop io.github.mat4m0.gwonmac
- run: pnpm install --frozen-lockfile
- name: Qualify encrypted profile secrets on the desktop
shell: bash
env:
DESKTOP: ${{ matrix.desktop }}
GW_LINUX_SECRET_QUALIFICATION: "1"
run: |
desktop="${DESKTOP^^}"
xvfb-run -a env \
XDG_CURRENT_DESKTOP="$desktop" \
XDG_SESSION_DESKTOP="$desktop" \
XDG_SESSION_TYPE=x11 \
dbus-run-session -- bash -euc '
eval "$(printf "\n" | gnome-keyring-daemon --unlock --components=secrets)"
pnpm test:linux-installed
'
20 changes: 13 additions & 7 deletions .github/workflows/linux-signed-qualification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ jobs:
if: matrix.desktop == 'kde'
run: |
sudo apt-get update
sudo apt-get install -y flatpak flatpak-builder g++ libglib2.0-dev gnupg xvfb dbus-x11 plasma-workspace xdg-desktop-portal xdg-desktop-portal-kde kwalletmanager
# Noble's KWallet 5 predates its Secret portal backend. GNOME Keyring
# supplies that standard interface for this KDE integration gate;
# current KWallet 6 supplies the same interface on supported systems.
sudo apt-get install -y flatpak flatpak-builder g++ libglib2.0-dev gnupg xvfb dbus-x11 plasma-workspace xdg-desktop-portal xdg-desktop-portal-kde kwalletmanager gnome-keyring
mkdir -p "$HOME/.config/xdg-desktop-portal"
printf '[preferred]\ndefault=kde;gtk;\norg.freedesktop.impl.portal.Secret=gnome-keyring;\n' > "$HOME/.config/xdg-desktop-portal/kde-portals.conf"
- name: Install Flatpak runtimes
run: |
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
Expand Down Expand Up @@ -69,11 +74,12 @@ jobs:
DESKTOP: ${{ matrix.desktop }}
GW_LINUX_SECRET_QUALIFICATION: "1"
run: |
xvfb-run -a dbus-run-session -- bash -euc '
export XDG_CURRENT_DESKTOP="${DESKTOP^^}"
export XDG_SESSION_TYPE=x11
if [ "$DESKTOP" = gnome ]; then
eval "$(printf "\n" | gnome-keyring-daemon --unlock --components=secrets)"
fi
desktop="${DESKTOP^^}"
xvfb-run -a env \
XDG_CURRENT_DESKTOP="$desktop" \
XDG_SESSION_DESKTOP="$desktop" \
XDG_SESSION_TYPE=x11 \
dbus-run-session -- bash -euc '
eval "$(printf "\n" | gnome-keyring-daemon --unlock --components=secrets)"
pnpm test:linux-installed
'
8 changes: 8 additions & 0 deletions .github/workflows/portable-native-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,11 @@ jobs:
- name: Qualify the installed Windows package
if: runner.os == 'Windows'
run: pnpm test:windows-installed
- name: Retain installed Windows failure evidence
if: failure() && runner.os == 'Windows'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: windows-installed-failure
path: ~/AppData/Local/Guild Wars Reforged/logs
if-no-files-found: warn
retention-days: 3
9 changes: 8 additions & 1 deletion .github/workflows/windows-signed-qualification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ jobs:
[Convert]::FromBase64String($env:WINDOWS_SIGNING_PFX_BASE64)
)
"WINDOWS_CERTIFICATE_FILE=$certificate" | Out-File $env:GITHUB_ENV -Append
- name: Build the signed package
- name: Build the signed update baseline
env:
GW_PACKAGE_INTENT: release
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_SIGNING_PASSWORD }}
run: node --import ./scripts/ts-hook.mjs scripts/windows-update-fixture.ts "$env:RUNNER_TEMP/windows-update-baseline"
- name: Build the signed candidate package
env:
GW_PACKAGE_INTENT: release
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_SIGNING_PASSWORD }}
Expand All @@ -58,6 +63,8 @@ jobs:
- name: Qualify signed install, replacement, and credentials
env:
GW_WINDOWS_SIGNED_QUALIFICATION: "1"
GW_WINDOWS_BASELINE_FEED: ${{ runner.temp }}\windows-update-baseline
GW_WINDOWS_CANDIDATE_FEED: ${{ github.workspace }}\out\make\squirrel.windows\x64
run: pnpm test:windows-installed
- name: Remove the temporary signing certificate
if: always()
Expand Down
55 changes: 30 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<h1 align="center">gwonmac</h1>

<p align="center">
<strong>Guild Wars Reforged for macOS</strong><br>
Play ArenaNet's official Guild Wars client finally on an Apple Silicon Mac in high resolution and FPS.
<strong>Guild Wars Reforged for macOS, Windows, and Linux</strong><br>
Run ArenaNet's official Guild Wars client through one profile-based desktop launcher.
</p>

<p align="center">
Expand All @@ -24,50 +24,53 @@

## What gwonmac does

gwonmac hosts ArenaNet's official WebAssembly client (what is used in the mobile app) in a sandboxed macOS app.
You do not need Windows, Wine, VMWare or Crossover.
gwonmac hosts ArenaNet's official WebAssembly client in a sandboxed desktop
application. Windows and Linux run the client natively; macOS does not need
Wine, a virtual machine, or CrossOver.

The app provides:

- native Apple Silicon packaging;
- high FPS on full retina resolutions;
- notarized by Apple (checked for Malware);
- native Apple Silicon, Windows x64, and Linux x86_64 packaging;
- high-resolution rendering;
- platform-native package verification and saved-login storage;
- verified downloads from ArenaNet;
- ArenaNet and Steam sign-in;
- build and team management;
- optional Build Management, Quick Travel, and Xunlai Storage Tools;

## Requirements

- An Apple Silicon Mac.
- Apple Silicon macOS, Windows x64, or Linux x86_64 with Flatpak.
- A Guild Wars account.
- An internet connection for the first download and online play.

You can buy Guild Wars from the [official store](https://store.guildwars.com/en-us).

## Install

1. Open the [Releases page](https://github.com/Mat4m0/gwonmac/releases) or go to https://gwonmac.com/download
2. Download the latest Stable `.dmg` file.
3. Open the file.
4. Move **Guild Wars Reforged.app** to **Applications**.
5. Open the app from **Applications**.
1. Open the [Releases page](https://github.com/Mat4m0/gwonmac/releases) or go to https://gwonmac.com/download.
2. Choose the package published for your platform: macOS DMG, Windows Setup,
or the Linux Flatpak repository instructions.
3. Install it through the normal system installer or Flatpak software center.
4. Open **Guild Wars Reforged**.

Stable releases are signed with Developer ID and notarized by Apple. The
Releases page also provides checksums, an SBOM, and build attestations. See
[Verify a release](docs/release-verification.md) if you want to inspect them.
Published Stable packages use the platform's verification path: Developer ID
and Apple notarization, Windows Authenticode, or a signed Flatpak repository.
Release availability can differ by platform while qualification is in progress.
The Releases page also provides checksums, an SBOM, and build attestations. See
[Verify a release](docs/release-verification.md) for the exact checks.

## Start the game

Guild Wars starts as soon as the required data is ready, then downloads the
rest of the game in the background while you play. You can see progress or
pause the download in **Settings → Game Data**. The
pause the download in **Settings → Game files**. The
[user guide](docs/user-guide.md) explains sign-in, updates, Tools, recovery,
and local data.

## Privacy and safety

- The Mac app sends no gwonmac telemetry.
- Diagnostics stay on your Mac until you attach an export to a report.
- The app sends no gwonmac telemetry.
- Diagnostics stay on your device until you attach an export to a report.
- The diagnostics system does not record credentials, packet contents,
cookies, request bodies, or local paths.
- Provisioned builds can store saved login in Apple's device-only Data
Expand All @@ -85,20 +88,22 @@ Stable is the default update track. You can choose Beta in Settings. Stable,
Beta, and release-candidate builds use the same app identity and local profile.
Alpha builds are not public update candidates.

An update found during the launch check installs before play unless you choose
**Play Without Updating**. An update downloaded later waits for a restart.
Application updates never block Play. The launcher offers a restart after an
update is ready, so running game windows remain under the player's control.
The app never performs an automatic downgrade.

Application updates and ArenaNet game updates are separate systems. See the
[user guide](docs/user-guide.md#updates) for player instructions.

## Build from source

You need macOS on Apple Silicon, Xcode Command Line Tools, Node.js 22.19 or
newer, pnpm 11, and Rust through rustup.
You need Node.js 22.19 or newer, pnpm 11, Rust through rustup, and the native
compiler toolchain for the target platform. macOS builds require Apple Silicon
and Xcode Command Line Tools; Windows builds require x64 MSVC; Linux builds
require the x86_64 GLib development headers. The supported commands and exact
CI toolchains are in the [development workflow](docs/development-workflow.md).

```bash
xcode-select --install
corepack enable
pnpm install --frozen-lockfile
pnpm exec playwright install chromium
Expand Down
9 changes: 9 additions & 0 deletions scripts/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ export function nativeBuildSteps(platform, architecture) {
if (architecture !== "x64") {
throw new Error(`unsupported Windows build architecture: ${architecture}`);
}
// The installed package must not depend on a separately installed Visual
// C++ Redistributable. Both shipped binaries therefore carry the static
// runtime selected by /MT.
return [
[
"lib.exe",
Expand All @@ -162,19 +165,24 @@ export function nativeBuildSteps(platform, architecture) {
"/nologo",
"/std:c++20",
"/O2",
"/MT",
"/EHsc",
"/LD",
"/DNAPI_VERSION=8",
"/Inode_modules/node-api-headers/include",
"/Fobuild\\native\\",
"/Fdbuild/native/windows-host.pdb",
"src/native/windows-host/host.cpp",
"src/native/windows-host/win-delay-load-hook.cpp",
"Advapi32.lib",
"Shell32.lib",
"Ole32.lib",
"Wintrust.lib",
"build/native/node.lib",
"/Fe:build/native/windows-host.node",
"/link",
"/DELAYLOAD:NODE.EXE",
"Delayimp.lib",
],
],
[
Expand All @@ -183,6 +191,7 @@ export function nativeBuildSteps(platform, architecture) {
"/nologo",
"/std:c++20",
"/O2",
"/MT",
"/EHsc",
"/Isrc/native/gw-dat",
"/Fobuild\\native\\",
Expand Down
Loading