Skip to main content
Unofficial Guild Wars Reforged App — a fan project, not affiliated with ArenaNet or NCSoft

Our workarounds, and what each fix removes

What GWonMac does to the client today, why each workaround exists, what it costs us per client build, and exactly what we delete after each upstream fix.

This page is everything our host does to the client today: why each workaround exists, what it costs per client build, and exactly what each upstream fix lets us delete.

Ground rules

The downloaded artifact from ArenaNet stays canonical: we never change it on disk and never redistribute it. When the client is broken on this platform, the host derives a separate patched copy in memory, verifying the input by hash and the output by hash. If any check fails, the host serves the untouched official module — so a new client build degrades to "the feature is missing", never to a broken client.

Three states, one decision
Each build is certified, template-only, or uncertified, decided by one component. A build we don't recognise runs as the plain official client, with every workaround switched off.

Workaround 1 — the template file bridge

Removed by: UR-01, UR-02, UR-03, UR-04, and the probe part of UR-06.

The host appends five forwarder functions to the module and repoints the call sites of the four stub routines at them. Each forwarder calls the host through a marker value on a syscall import the module already has — no real call can produce that value. The host then does the file work in the Emscripten file system: create a directory tree, list a directory, derive a relative name, delete a file, and test for a file.

Cost per client build. The five routines have to be found again, with proof that their callers didn't change. This part is automated: a machine locates each routine by body bytes, signature, and caller set, then hashes the complete body of every caller the transform touches. If any of that changed, the transform refuses to run.

Deleted after the fix: all of it — five forwarders, five marker values, the host file-system bridge, and the whole certification table for this transform.

Workaround 2 — the entry name without an extension

Removed by: UR-05.

Our bridge returns each entry name without its extension, so the client calls Path::RemoveExtension on a name where the off-by-one can't fire.

Deleted after the fix: nothing on day one — removing an absent extension does nothing, so the workaround stays correct. It goes together with workaround 1.

Workaround 3 — the double-click flag

Removed by: UR-07.

The host appends one exported mutable i32 global to the module and splices three instructions into the mousedown callback. They store the global's value into the record word the client already carries to FrMouse, and the host writes the Chromium click count into that global on every trusted press. The transform adds no function and moves no index or table entry.

Cost per client build. The exact body hash of the mousedown callback, plus two byte offsets. A machine re-derives this; a human confirms it.

Deleted after the fix: the transform, its table, and the renderer code that writes the global.

An earlier workaround for the same defect synthesised a pair of touch taps. It delivered four clicks instead of two, so we deleted it — UR-07 has the measurement.

Workaround 4 — the native cursor

Removed by: nothing today. UR-12 would remove it.

The host adds a small companion component to the module. It reads the client's cursor state — the active art pointer, the texture chain, and the colour buffer — and publishes a 32 by 32 image into a region the host allocated; the host sets that image as the native cursor. The component imports no function and writes nothing the game owns. Every read goes through bounds-checked pointer chases, and every chase can fail safely.

Cost per client build. Our most expensive workaround: twelve memory addresses and two function indices. No machine can prove a memory address, so a human measures them on each build. Until that's done, the player gets the plain system pointer.

Deleted after UR-12: the twelve cursor addresses and the component's read path.

Workaround 5 — the pointer replay

Removed by: UR-09.

After a click that produced no cursor event, the host dispatches one synthetic out-and-back mousemove pair at the last click position; the physical pointer doesn't move. It retries every 150 ms for at most 2,500 ms. The replay can't start a click and can't leave the pointer at a new position. The host also synthesises pointer moves while the player rotates the camera, because the client steers from absolute coordinates — that path can't originate a click either.

Deleted after the fix: the whole retry path.

Workaround 6 — the heap warning and safe reload

Removed by: UR-10.

The host monitors the effective heap cap, warns while the player still has room to return somewhere safe, then offers a user-controlled reload. WASM memory capacity cannot shrink inside one process; reload starts a new heap.

Deleted after the fix: emergency reload guidance can go once ArenaNet's ownership policy is bounded and long content-diverse sessions remain well below the effective cap. The general crash-recovery path remains.

Research profile — paired 4 GB JS/WASM transform

Reduced or removed by: UR-10.

An off-by-default developer profile raises build 38797's WASM maximum to 4 GiB minus one page and transforms the matching generated JavaScript to normalize wasm32 pointers above 2 GiB. It is a paired transform: changing only the WASM maximum would hand negative pointer values to JavaScript and is refused.

The exact pair has passed ArenaNet-allocator qualification above 3 GiB and a live Electron string-glue test at 2,625 MiB. It remains a research profile, not a default player setting. All five variants the production chain can emit are hash-pinned. Before an experimental release it still needs packaged high-address gameplay coverage, system-memory testing, and explicit opt-in copy. Diagnostics record the effective mode and cap.

Cost per client build. Both official artifact hashes, every accepted predecessor hash, both derived hashes, and the generated-glue pointer audit. Any ArenaNet update disables the profile until it is re-certified.

Deleted after the fix: the whole transform and its certification table. A source-built 4 GB client already has the compiler's matching unsigned-pointer support; a bounded cache/lifecycle fix removes the reason for our emergency headroom.

What our workarounds never do

  • They never change the artifact on disk.
  • They never redistribute game files.
  • They never send credentials, account identifiers, or game traffic anywhere.
  • They never automate gameplay. The host does not choose a target, move a character, use a skill, or send chat.
  • They never run on a build that we have not certified.

The bottom line

Six of the twelve requests exist because of file handling; one exists because of a single unwritten byte. Fix UR-01 to UR-07 and three production workarounds disappear outright. A source-level UR-10 correction also avoids turning the research-only 4 GB profile into another permanent certification burden.

GWonMac

Play Guild Wars on your Mac. GWonMac runs ArenaNet's official client natively on Apple Silicon — free, open source, signed and notarized.

© GWonMac. All rights reserved.