Imported from EP93/forged-alliance-mac (
AGENTS.md). Install upstream withnpx skills add EP93/forged-alliance-mac. Copyright stays with the author.
Project instructions
Goal and current state
Build a free, straightforward Mac launcher for a licensed Steam copy of Supreme Commander: Forged Alliance, with smooth campaign play on Apple Silicon, Mac keyboard controls, visible FPS and simulation pace, and a repeatable large-army lab. Multiplayer is outside the current priority.
The launcher is native Swift/AppKit. The game is still a 32-bit Windows executable running through Wine and Rosetta. Its compiled Moho engine is not available here as complete open source; the licensed game exposes Lua, and FAF provides Lua improvements and individual binary patches. Do not describe an executable experiment as a native ARM port.
Read these before performance work:
README.md— working product, installation and controls.docs/testing.mdandevidence/README.md— evidence index and measurement rules.docs/local-environment.md— this Mac's paths, launch environment and recovery.docs/stress-tests.md,docs/profiling.md,docs/zoom-performance.md— results, failed approaches and current investigation.docs/engine-work.md— available source and engine boundaries.
Architecture
native/Runtime.swift: pinned downloads, checksums, isolated runtime, Steam readiness, asset installation and experimental engine preparation.native/main.swift: Mac setup/launch UI. Ordinary Play must use the original executable. The combat optimization is explicitly lab-only.assets/LaunchForgedAlliance: matching Wine/MSync environment and launch modes.assets/files.json: authoritative mapping of original project Lua to game paths. Keep Python and native installation behavior consistent.assets/controls.lua,benchmark.lua: controls and frame/simulation measurements.assets/stress*.lua,assets/stress/: offline over-cap arena, guarded by the arena flag and command-line mode; never spawn or destroy campaign armies.diagnostics/: developer probes and guarded, separate-output binary trials.
Work safely and continue autonomously
- Session handover: read
docs/handover/2026-09-16-session-cut.mdfirst. The owner wants more code work and fewer game restarts. Batch code changes, unit checks and evidence review before a small, planned set of live launches. Performance testing was explicitly deferred at this cut; do not restart it until the user resumes that part of the work. - Local implementation, read-only research, reversible experiments, screenshots and game testing are authorized. Complete useful work without repeatedly asking for permission. Ask only for genuinely missing information or new consequential scope such as publication, purchasing, accepting legal terms or deleting data.
- Check current git and process state. Preserve unrelated work, Steam login, saves, preferences and the working prefix. Exit the game gracefully before reconfiguring its installation. Never kill every Wine process to stop one game.
- For focused Lua debugging, a backed-up, project-owned module may be hot-reloaded in a paused game. Keep this separate from runtime/renderer/executable changes, update its cached installer asset too, and verify the actual import and behavior.
- Back up managed files before replacing them. Refuse unknown hooks, unknown executable hashes and existing experimental outputs. Never patch the installed original executable or overwrite the baseline saves.
- Prefer the Computer tool for UI. This user has explicitly authorized macOS screenshot/keyboard/mouse fallback for Wine windows that Computer cannot access.
- Use Apple Command Line Tools via
DEVELOPER_DIR=/Library/Developer/CommandLineTools; do not accept Xcode's licence. - Do not publish or push without authorization. Do not put game assets, saves, binaries, Wine prefixes, Steam account data or private raw logs in git or bundles. Preserve third-party notices for borrowed source. See evidence handling below.
Performance acceptance
- Keep the chosen visual quality fixed. Lower shadows, reduced LOD, resolution or effect removal are not solutions to the user's performance objective.
- Report FPS and simulation pace separately. At normal requested speed, 1.00x means one game second per real second. High FPS with slow simulation is a failure.
- Compare the same executable baseline, save/seed, army count, camera path, selection, graphics, power state and foreground state. Change one variable.
- Warm up; collect 30-second samples; repeat close comparisons. Record p95/p99 frame time, not just mean FPS. Keep paused rendering distinct from running sim.
- Do not attach samplers/debuggers during timed benchmarks. Their observations belong to separate profiling runs. Do not label a screenshot FPS a benchmark.
- Preserve failed, incomplete, confounded and outlier runs with their limitations. A microbenchmark speedup is not a game speedup. Automated input is not proof: inspect logs and visible behavior.
- Campaign saves and extended mission play remain separate validation gates from arena performance. A fast camera replay does not prove real mouse-wheel input latency. Never claim smoothness at every army size or on untested Macs.
Known results and pitfalls
- 500 artillery firing: original 5.28 FPS / 0.071x pace; isolated float-copy patch 21.31–21.33 FPS / 0.328–0.333x. Still too slow. No idle-crowd gain.
- Float-copy patch changes x87 NaN/exception semantics. Remains opt-in lab-only.
- Constructor-copy v2 increased repeated combat pace to 0.441–0.453x, with 19.59–21.25 FPS versus v1's 20.88–22.97 FPS / 0.329–0.333x in new AC runs. It is an isolated developer experiment, not a promoted campaign engine.
- FAF Lua-state call inlining did not improve that fixture. Allocation trials were confounded by lingering effects; no established gain.
- D9VK/Sarek repeatedly stalled for seconds in the game despite a fast tiny triangle probe. Replacing memset and changing cached buffers did not fix it.
- New exact-buffer tracing found Wine's forced executable-memory policy in the
stall path: an isolated, opt-in DEP diagnostic changed native mapped buffers
from RWX to RW and reduced all 20 observed clears to 0.05–1.02 ms. See
diagnostics/sarek-dep-diagnostic.mdanddocs/compatibility-investigation.md. This does not establish broad visual/campaign correctness or a default renderer; several FPS comparisons lost foreground focus and remain diagnostic. - The x87 copy hotspot belongs to particle-record copying; its thread role has not been established. Do not equate a Windows thread ID with the named Sim thread or static instruction counts with CPU time. Read the verified handover addendum before adopting its sidecar/runtime recommendations.
- Earlier shadows-off results are historical graphics comparisons, not accepted fixed-quality optimization. Read date/state caveats in each report.
Checks and evidence
Run relevant checks from this directory (fail on a failed prerequisite):
python3 -m unittest discover -s tests -v
zsh -n assets/LaunchForgedAlliance
DEVELOPER_DIR=/Library/Developer/CommandLineTools xcrun swiftc \
native/Runtime.swift tests/RuntimeChecks.swift -o /tmp/fa-runtime-checks
/tmp/fa-runtime-checks
git diff --check
Build with zsh scripts/build_native.sh /new/path/Preview.app; it refuses an
existing destination. Verify the bundle has only project assets and expected
resources, validate its ad-hoc signature, then smoke-test affected UI/game paths.
Stock Lua contains extensions to standard Lua; a stock Lua parser alone cannot
prove game compatibility. Check the actual game log for errors and behavior.
Keep portable data and methodology under docs/ and evidence/. Full local raw
files belong in gitignored evidence/private/, with SHA-256 inventory and source
references; the archive script deliberately avoids traversing prefixes. Store
new measurements with executable hash, exact fixture and quality state. Update
the evidence index and outstanding work when results change. Never fabricate
missing timings, run metadata, source availability or historical proof.