CoOp:Anything
게임을 검색해 협동 플레이 가능한 모드·단계·품질·커뮤니티 평점을 확인합니다
저장소 정보는 아직 수집하지 않았습니다.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
Project Reclaimer is a standalone launcher for Halo 3 multiplayer on OSX that does NOT use wine
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Runs Project Reclaimer (the community Halo 3 client built on Halo: The
Master Chief Collection's halo3.dll) on an Apple Silicon Mac without Wine, CrossOver or a VM.
shim/build/peload is a from-scratch Win64 PE loader that runs under Rosetta 2. It loads the Reclaimer
client and MCC's halo3.dll directly and implements the Windows APIs they use, including:
Playable, starting from Reclaimer's own menu (Halo 3 menu scene with Server Browser, Campaign, Host Game, Customize, Settings, Credits):
On an M4 the menu is up about 17 s after launch, a hosted match loads in ~6 s, and gameplay averages ~85–120 fps depending on the map and scene. The window title shows the elapsed time, the load time and the live frame rate (never more than the screen's refresh rate; when the engine runs faster, e.g. on loading screens, its own rate is shown beside it).
Known gaps:
softwareupdate --install-rosetta --agree-to-license) and
macOS 13 or newer (the binaries are built for 13.0). Developed and tested on an M4 running macOS 26; 16 GB of memory
or more is recommended (a 100-player game on a big Workshop map uses ~10 GB for the game and 1-8 GB for the GL helper).project-reclaimer-client-v0.9.23.exe from
https://github.com/ProjectReclaimer/project-reclaimer-releases/releases (SHA-256
ceef542ac17980d178e281ebf510b4aaaf28ef650a288f1d68e8250731ad7e70; bash shim/tools/client-update.sh fetches the
newest and checks it). The loader finds the few client code paths it patches by byte signature (with a variant per
compiled shape: shim/docs/patch-addresses.md), so other builds usually work too; v0.9.22, v0.9.20, v0.9.18 and v0.9.13
are also tested. A new release that moves one of them is ported with the scripts in shim/docs/porting.md. Servers admit only their own client version, so update when a new one comes out.
Servers and players move to each new release within hours (they refuse older clients: "This server runs Project
Reclaimer 0.9.19 and you have 0.9.18"). The launchers use the newest client/project-reclaimer-client-v*.exe, so dropping
a new release in client/ is enough to try it; a client code path whose signature changed only switches its feature
off (the launch log's [sigpatch] / [stats] lines say what was found).
bash shim/tools/client-update.sh downloads the newest client release, checks it against the release's SHA256SUMS.txt
and puts it there.No game files are included in this repository or its releases.
tar xzf ProjectReclaimerOSXNative-*-macos.tar.gz
Then clear the download quarantine so macOS lets the loader and Reclaimer.app (in the bundle's top folder) run:
xattr -dr com.apple.quarantine ProjectReclaimerOSXNative-*-macos
(Without this, macOS refuses to open the app: it is signed ad hoc, not by an Apple Developer ID.)bash shim/build.sh. This also needs
the three VC++ runtime DLLs (ucrtbase.dll, vcruntime140.dll, msvcp140.dll) in shim/crt/;
the release bundle contains them.STEAM_USER=<name> for password + Steam Guard). The files land in
~/Library/Application Support/ReclaimerMac/prefix/drive_c/Program Files/Steam/steamapps/common/Halo The Master Chief Collection.
./reclaimer-mac.sh download
(Only download is needed; that script's other commands are for an older Wine-based setup.)bash shim/tools/client-update.sh puts the newest release in client/ (SHA-256 checked), or
point RECLAIMER_EXE=/path/to/exe at one. The first launch of a new client version builds its Forge asset
library (about 2.5 minutes, "BUILDING ASSET LIBRARY"); later launches reach the menu in ~20 s.If one Mac is already set up, you can move everything to another Apple Silicon Mac on a USB stick: no Steam login
or DepotDownloader download on the second Mac. The stick needs about 31 GB free (25 GB with --minimal) plus your
downloaded Workshop mods (--no-workshop leaves them out); format it APFS or Mac OS Extended so it can hold files over 4 GB.
On the Mac that already plays (from the repository folder):
bash shim/tools/make-usb.sh /Volumes/RECLAIMER
That copies, into ReclaimerMac/ on the stick, the loader and its native GL helper, the launch scripts and map tools,
the VC++ runtime DLLs, the newest Reclaimer client exe, your MCC files (the whole MCC folder; --minimal copies only what
Project Reclaimer reads), your downloaded Workshop mods, the client's caches and fonts (the Forge asset cache, so the second
Mac skips the 2.5-minute first-launch build) and the stats page's map pictures, walking graphs and heatmaps. Add
--with-profile to bring your Reclaimer profile (nickname and identity) along -- then don't play on both Macs at once.
Short on space on the other Mac? The installer takes options (--dry-run shows what one needs first):
| Option | Copies | Leaves out |
|---|---|---|
| (none) | ~44 GB | nothing |
--no-movies | ~40 GB | MCC's cutscene movies (not used) |
--no-workshop | ~29 GB | the Workshop mods (a modded server's mods download when you join it) |
--minimal | ~25 GB | both |
--keep-on-stick | ~15 GB | the 10 GB MCC pak, the movies and the mods stay on the stick, linked: keep it plugged in to play |
The Halo 3 campaign maps and MCC's pak can't be left out: the client refuses to start without the campaign maps and its
engine worker stops without the pak (it reads a font from it). Free space is counted as Finder does (purgeable space
included); --skip-space-check goes on anyway.
Updating a stick: run the same command again. Only what changed is copied (new loader binaries, a new client exe,
new or updated Workshop mods...); unchanged MCC files are skipped, older client exes are removed from the stick, and each
part lists the files it copied. --no-game skips even checking the MCC files. Then run install.sh on the other Mac again:
it also copies only what changed.
Just a new build (the usual case after a fix): bash shim/tools/make-usb.sh /Volumes/RECLAIMER --update copies only the
loader, GL helper, scripts and client exe (seconds), and on the other Mac install.sh --update copies only those too --
no maps, Workshop mods or caches are looked at.
On the other Mac, plug in the stick and run:
bash /Volumes/RECLAIMER/ReclaimerMac/install.sh
It checks for Rosetta 2, then copies the game files to the same place ./reclaimer-mac.sh download would
(~/Library/Application Support/ReclaimerMac/prefix/drive_c/Program Files/Steam/steamapps/common/Halo The Master Chief Collection) and the loader to ~/ReclaimerMac (pass another folder as an argument to change that). Then:
cd ~/ReclaimerMac && bash shim/play.sh
Both scripts are resumable -- run them again if a copy was interrupted. The files are your own game copy, for your own Macs; don't share the stick.
Double-click Reclaimer.app. It is in the release bundle's top folder; shim/build.sh makes it in a checkout and
install.sh (USB stick) makes it in the install folder. To have it in Applications (Launchpad / Spotlight / the Dock):
bash shim/tools/make-app.sh --applications
The app starts shim/play.sh without a Terminal window, logging to /tmp/reclaimer_launcher.log (the game's log is still
/tmp/reclaimer_play.log). While the game runs, clicking it again only shows a notification. It runs the folder it sits in;
the copy in Applications remembers the folder it was made from (run make-app.sh --applications again after moving it).
On a Desktop / Documents / Downloads folder macOS asks once whether Reclaimer may access it: allow it.
The app is a tiny signed launcher (shim/src/launcher.c) plus the script Contents/Resources/launch.sh; make-app.sh
signs it ad hoc and verifies the signature. To sign with one of your own identities instead
(security find-identity -v -p codesigning lists them):
RECLAIMER_SIGN_IDENTITY="Apple Development: <you>" bash shim/tools/make-app.sh --applications
From Terminal it is the same as:
bash shim/play.sh
From a source checkout you can build and launch in one go:
bash shim/build.sh && bash shim/play.sh
Startup sequence:
starting... 0:07).menu loaded in N s in the title)./tmp/reclaimer_play.log.To jump straight into a server, give its address: RECLAIMER_JOIN=host:port bash shim/play.sh.
bash shim/tools/servers.sh lists the busiest servers (64-127 players) with their addresses and the mods
each one needs; shim/docs/mods.md explains mods, including downloading Workshop-only
ones with DepotDownloader.
RECLAIMER_DIRECT=1 bash shim/play.sh skips the menu and goes straight into a local Guardian match
(about 12 s). In that mode Reclaimer's in-game UI doesn't run, so the scoreboard has no names or scores.
Quit by closing the window, or Ctrl-C in the terminal. If a launch is interrupted, clear leftovers:
pkill -9 -f build/peload
The first launch builds and caches the game's shaders as effects first appear (brief hitches). Later launches pre-build them during the loading screen.
Project Reclaimer is client-server: when you join a server, all your game traffic, voice included, is one encrypted QUIC connection to that server, which relays it (the stats page's Packets window shows it: in a match the client has a single UDP peer, the server). So:
players lists name, player ID and address;
banip bans one). That is inherent to connecting to it.discover /
servers.sh do the same.api.github.com, presumably the
update check), and Workshop / Steam downloads. What the status query sends and receives, field by field:
shim/docs/server-status.md -- {"status":2} out, the server's name / map / mode / player count back; no player ID
or name is sent.Checked on the wire (2026-10-25, the packet log on a hidden client joining a public 60-player Big Team Battle server
through a join link, ~3.5 minutes in the match): before the match, TCP to GitHub, to the community stats service (port
8790: ranks; what the client asks it was not traced) and to three discovery masters on port 49175 (the server lists,
~105 KB each), and a QUIC status query to every listed game server in ~3 s (343 servers on 136 machines in the
recording, 317 answering; the recorder dropped part of that burst, and discover listed 383 servers at the time); in
the match, UDP with the joined server only -- not one datagram from another address while six players talked (voice
relayed), and all of halo3's 542 session messages went to or came from the server. The session list held up to 64
players and no address. All of the client's internet sockets were in the one process the packet log watches (lsof).
bash shim/tools/privacy-check.sh <host:port> repeats the check (it joins that server with a hidden test client).
Another internet connection changes what all of the above see. A commercial VPN may be refused: servers can refuse VPN and
datacenter addresses (block_vpn, on in the server configuration dedicated init writes, with lists of VPN providers and
datacenters; an admin can let one player ID through); a different home or mobile connection is not affected.
shim/target.sh starts a second copy of the client as a stationary target: it hosts a hidden Online game
on Valhalla by itself (scripted clicks in its own window) and stands in the match. It has its own profile
("Target"), no controller, keyboard, mouse or audio, no window and no Dock icon (it runs in the background,
capped at 30 fps since nobody sees its frames), so it doesn't touch your input. Then join it:
bash shim/target.sh
RECLAIMER_JOIN=127.0.0.1:11774 bash shim/play.sh
The target is in its match about 50 s after starting; TARGET_MAP=<name> picks another map, and
bash shim/target.sh stop stops it (play.sh leaves a running target alone). TARGET_SHOW=1 gives it a
window (minimized in the Dock; add TARGET_MINIMIZE=0 to keep it on screen).
Any client can run that way: PELOAD_HIDE_WINDOW=1 (no window, no Dock icon) and PELOAD_MAX_FPS=<n> (frame
cap; 30 by default with a hidden window, 0 = uncapped).
Project Reclaimer's headless dedicated server runs too (no window, in the background):
bash shim/dedicated.sh # start it
RECLAIMER_JOIN=127.0.0.1:49190 bash shim/play.sh # join it
bash shim/dedicated.sh status # what it is playing, players, ready
bash shim/dedicated.sh stop # stop it
The first start creates the server folder (C:\ReclaimerTestServer in the loader's prefix; DED_FOLDER='C:\<name>'
for another) with a local-only dedicated.toml: Valhalla Slayer on UDP 49190, hidden, not listed on any master, no
UPnP. The server waits in its lobby until a player joins, then starts the match. If the very first join after
starting it times out, join again. To host for other people, edit dedicated.toml (its options are documented in
dedicated.toml.example next to it) -- that makes the server public, so only do it on purpose. play.sh leaves a
running server alone. Logs: /tmp/reclaimer_dedicated.log and /tmp/reclaimer_dedicated_<pid>.log.
Options go in front of the command, e.g. PELOAD_INVERT_PITCH=0 bash shim/play.sh:
| Variable | Effect |
|---|---|
PELOAD_INVERT_PITCH=0 | normal (non-inverted) look |
PELOAD_AUDIO=0 | no sound |
PELOAD_VSYNC=1 | vsync the window (off by default) |
RECLAIMER_EXE=<path> | use the client exe at this path |
RECLAIMER_JOIN=host:port | join that server as soon as the menu is up, e.g. RECLAIMER_JOIN=203.0.113.7:11774 |
RECLAIMER_DIRECT=1 | skip the menu: straight into a local Guardian match (no scoreboard text) |
PELOAD_HITBOXES=1 | draw the players' real hitboxes (each player's collision model, which is what shots hit) posed on their live skeletons: enemies red with the head yellow, teammates blue. An object whose collision model can't be read gets nothing (no stand-in shapes). The stats page's overlay panel (Hitboxes row) switches it during play (any launch) |
PELOAD_HB_TRACERS=1 | projectile tracers: grenades/rockets/plasma as trails, bullets as lines from muzzle to impact; the panel's Tracers row |
PELOAD_HB_INFO=1 | player info above each player: shield and health %, weapon in hand / other weapon, grenades, active camo, in a vehicle; the panel's Player info row |
PELOAD_HB_ITEMS=1 | weapons and equipment lying on the map, by name; the panel's Items row |
PELOAD_HB_TEAMS=enemies | start every overlay feature showing only enemies (friends: only teammates); the panel sets it per feature (Show) |
PELOAD_STATS_HTTP=<port> | live game stats page; on by default at http://127.0.0.1:8765/, =0 turns it off (details below) |
PELOAD_HB_VEHICLES=1 | vehicle hitboxes (every vehicle, turret and mounted gun): cyan, with their weak spots yellow (damage there alone destroys it: the Wraith's back hatch), orange (breaking it exposes the driver: Wraith cockpit, Scorpion hatch) or magenta (breaking it disables a gun or the steering: Ghost wings, Warthog front fenders, Mongoose wheels); every vehicle's result is listed in ; off by default, the panel's row |
play.sh serves what the game knows about the match on http://127.0.0.1:8765/, updated 10 times a second
(PELOAD_STATS_HTTP=<port> for another port, =0 off; PELOAD_STATS_ADDR=<ip> to listen on another address, e.g. to
watch from another device). The page, top to bottom: Game rules; the in-game overlay controls, Tools and Record (left)
beside the death heatmap with the player details under it (right); Connected; the event feed and chat; the players table:
Screenshots: a live ~60-player public match, player names and chat anonymized by the page's demo mode (?demo=1).
Game rules (at the very top) -- the players in the game and the server's slots ("players 40 / 60 slots (20 free)": the limit the client got from the server when it joined; the game's own limit when you host), the running game's options (rounds, respawn, map, powerups, score and points) and every trait set with what it changes: everyone, after spawning, each powerup, and the mode's roles (leader, flag carrier, infected, last survivor, ...).
In-game overlay panel (the function keys of earlier versions are gone; PELOAD_HB_FKEYS=1 brings them back): a
row per feature -- Hitboxes (the players'), Vehicles (with chips for the three kinds of weak spots), Tracers, Player info,
Items, Kill/safe zones (a chip per kind: kill zones, safe zones, soft-kill and push-back ceilings, slip surfaces, invisible
walls). Each
row has its own on / off switch and its own settings: Show All / Enemies / Friends (a player's team; a vehicle's is
whoever sits in it, so an empty vehicle shows under All only; a tracer's is the shooter's), Walls Through walls /
Visible only (only what you can actually see), and Text − / + (label size, for the features with labels). A
setting that doesn't apply to a feature is greyed (items have no team; zones are always drawn only where you can see
them). Tick rows on the left and the bar above them sets On / Off, Show, Walls or Text for all of them at once (a
value lights up when every ticked row shares it). Presets: Off, Enemies through walls, Everything visible only,
My team, and Save as preset… keeps the current settings of every row under a name of your own (in that browser).
Every feature costs nothing while it is off; the depth copy for "visible only" is made only while a feature that is
on uses it. The settings last until the game closes (PELOAD_HB_TEAMS / PELOAD_HB_VISIBLE / PELOAD_HB_TEXT_PX
set every feature's start value).
Tools: Teleport with a list beside it -- the far side of the map, or one of the map's spots past its boundaries
(flat, roomy floors found in the map file for every map, re-tools/h3spots.py, each one checked in game -- you land on it
and stay alive there, shim/tools/spots-verify.sh; a map new to this Mac lists its spots as "(not checked yet)"; the
boundaries are switched off first).
In a game you host with PELOAD_ALLOW_TELEPORT=1, or as a player on our local server (the server moves you; so does the
map's click-to-teleport). Capture frame (saves the frame,
its depth buffer and the camera position to ~/reclaimer_cap_* for reporting a rendering glitch at a spot; the overlay
is not in it), (live / muted), and in games you host a menu: everyone's movement speed (the game
variant's own speed trait, 25% to 300%; in one click). With in a game you host,
or as a client of our local server (; the request goes to the server, which does it for
everyone): (kill zones, safe zones, soft ceilings) and (player clip:
the collision you can't see -- careful, many of those walls guard drops or the map's edge). Every switch costs nothing
while it is off.
votes a card of the lobby's map ballot between games for you (off / card 1-3 / none of the above; the
client's own vote, sent as the ballot opens -- a joined client never shows the ballot screen; at
launch) and answers kick / end-round / shuffle votes other players call with F5 (yes) or F6 (no)
().
The forensic archive link at the top opens the archive of voice, chat and game events this client received
(shim/docs/forensics.md). Add ?demo=1 to either page's address (http://127.0.0.1:8765/?demo=1,
/forensics?demo=1) for screenshots: every player becomes "Player 1", "Player 2", ... (the same alias everywhere on the
page, event texts and map labels included; players who share a name share its alias), ids are fake, players' chat lines
are placeholders, the archive's folder is shown as the default one; a feed or archived session without any spawn events
gets a few made-up ones (spawns, and pickups by the aliased players) so the pictures show them. Nothing else changes,
and without it the pages are as before. bash shim/tools/pageshot.sh remakes these pictures from a running game (shim/tools/pageshot.m: an
offscreen WKWebView that takes CSS-selected sections of a page as PNGs; TEXT=<dir> also keeps their text to check).
Machine-readable endpoints:
/stats.json -- the latest snapshot: players (name, slot, team, you, alive, health, shield, camo, weapon(s),
grenades, vehicle, seat, pos, speed, facing, deaths, life, longest_life, distance, top_speed, shots, hits, accuracy,
traits, effects, voice, muted, talking), session (the connected list), variant (options and trait sets),
overlay (the switches; overlay.features = each overlay feature's on, team, vis, px, null where a setting
doesn't apply), vehicles, projectiles in flight, map, in_match, match time/events -- the same JSON as a stream (Server-Sent Events): curl -N http://127.0.0.1:8765/events/feed.json?since=<seq> -- the event feed after event seq (the last 1,024 kept): kind (join, leave, kill, death,
vote, server, game, chat, weapon, vehicle, powerup), text, t (Unix time), a/b (players; chat: the sender), how
(weapon; chat: the channel -- team, nearby, server, notice, empty = everyone; spawns: the item), verb (kills; spawns:
spawned or picked up), ta/tb (teams), pos (spawns and power-up pickups: [x, y, z]), estimated, mine (a
chat line we sent); the snapshot's feed_seq is the newest event's number. PELOAD_FEED_LOG=1 also prints each event
to the log, PELOAD_SPAWN_LOG=1 what became of each spawn candidate (attached, left out, a power-up gone without a pickup)/heat.json -- the heatmap data (visited cells, deaths); /maptop/<map>.png|json -- the map picture and its world
bounds/recordings.json -- the clips in ~/Movies/Reclaimer (file, size, duration, time); /recordings/<file> -- one clip;
the snapshot's rec -- the recorder's state (POST /control?do=rec&secs=<n>&mode=av|audio starts one, do=recstop
ends it)POST /control?do=<hitboxes|tracers|info|items|vehicles|visible|zones|zonetype&t=|weaktype&t=|teams|textup|textdown| teleport|tpnext&slot=|tpto&x=&y=&z=|bounds&off=|invwalls&off=|capture|mic&on=|speed&v=|rec&secs=&mode=|recstop|mute|unmute| muteall|unmuteall>[&voice=<id>] with an
header (other web pages can't send it);
turns the controls off. The overlay panel's per-feature settings:
with any of ,
, , (each lands where the feature has that setting); the old
/ / / apply to every featureIt reads the game's own memory (object and player tables, the network session, the game variant) -- the information Halo 3 sends to every client -- and works with the overlay off. Health and shield are fractions (1.0 = full; shields can go above 1 when overcharged). On public servers this is information other players don't have; it is meant for your own and your dedicated server's matches.
bash shim/tools/speedstep-demo.sh shows, on your OWN local dedicated server only, that the server takes client-reported
player positions that jump up to ~2.5 units per update without a speed check (shim/docs/net-authority.md): it walks a
test player normally, then has the client step its own position forward, and records where the server puts the player.
Output in ~/speedstep-demo-<date>/: positions.csv, summary.txt (walking vs stepping speed) and speedstep-demo.mp4.
Two processes do the work:
shim/build/peload, x86-64, runs under Rosetta 2). It loads the Reclaimer client (a Rust Windows
exe) and MCC's halo3.dll, implements the Windows APIs they call, and translates their Direct3D 11 rendering into
OpenGL calls. The game's own code is x86-64 machine code, so this process can only run translated: it cannot be
made native ARM.shim/build/glhelper, native arm64, on by default since 2026-10-17). The loader encodes every
OpenGL call into a 256 MB shared-memory ring; the helper replays them on its own OpenGL context, so Apple's driver
runs at native speed and in parallel with the game. It renders each frame into one of three shared IOSurfaces,
which the loader's present thread shows in the window. PELOAD_GL_HELPER=0 = everything in one process (slower).Different Macs. shim/play.sh checks the Mac it runs on first (shim/tools/machine.sh) and prints one line, e.g.
[machine] Apple M1: 4 performance + 4 efficiency cores, 8 GPU cores (base), 16 GB -> particle workers 2, graphics preset Medium (first run on a base-tier GPU ...). It gives the particle workers the performance cores minus two (2 to 8), and on a
base-tier GPU (8-10 GPU cores: M1, M2, M3, M4 base chips) a profile that never chose a graphics preset starts at the client's
Medium preset instead of High (Settings > Advanced graphics changes it any time; your own choice is never touched;
RECLAIMER_KEEP_QUALITY=1 turns this off). Retina screens are handled by the loader itself: the picture is drawn at the
window's pixel size on any screen ([cocoa] window ... (scale 2) in the log).
What runs on your cores (an M4 Max has 10 performance + 4 efficiency cores):
| Thread / process | What it does |
|---|---|
| halo3's main thread (translated) | the game loop: simulation, AI/physics, render preparation, ~14 MB game-state copies a few times a frame. The limit in 80-100 player games (99% busy) |
| halo3's render thread (translated) | issues the Direct3D 11 calls the loader translates |
shim-gl-submit (loader) | takes the translated draw calls off the render thread; on automatically in games with 40+ players |
shim-compute × N (loader) | the game's GPU particle compute shaders, run on the CPU (Apple's OpenGL has no compute shaders): 64-lane SIMT interpreter; play.sh starts one per performance core minus two, 2..8 (8 on an M4 Max, 2 on an M1; PELOAD_CS_WORKERS) |
| GL helper process (native) | Apple's OpenGL driver: every draw call |
shim-present (loader) | shows each frame, draws the overlay, the client's layered windows and clip recording |
shim-copy × 3 (loader, opt-in) | splits big copies across cores (PELOAD_PAR_MEMCPY=3); off by default because copy-on-write (below) measured faster |
shim-stats-*, shim-forensic, shim-nav, recorder | live stats snapshot every 100 ms, the forensic archive writer, the test walker, clip encoding (hardware) |
| the client's own threads | network, voice (mcc-voice: since 2026-10-18 it sleeps 20 ms a loop as on Windows -- before, a missing NtDelayExecution made it spin and take a whole core), Rust thread pools |
What takes load off halo3's main thread (all default): the game-state copies are done copy-on-write (pages are
remapped instead of copied; +4% in a 70-player match), big copies go to the host's native memmove, the game and the
client are told the real CPU count (it used to be 1: the client's thread pools and halo3's locks now size and spin
accordingly; +1.7%), and hot Windows calls (thread ids, environment lookups) are cached; play.sh also turns on the import fast paths (a per-thread
heap block cache and cheaper thread-id / key-state calls: +1.3%; PELOAD_FASTCALL=0 = off). The client's voice thread used to spin
without ever sleeping (a missing NtDelayExecution) and fought the main thread for a lock: fixed on 2026-10-18, +11.8% in a
128-player game. Each was measured with an
in-match A/B (PELOAD_AB=...), because scenes vary too much between runs to compare runs.
Measured on an M4 Max, 1920x1080 internal resolution:
The client's own Settings → Graphics → Advanced graphics cuts draw calls and render preparation: Shadows (each
shadow caster draws again into the shadow maps), Level of detail, Draw distance, Effects and Decals. The window title shows
draws per frame next to the frame rate. A 60 Hz display caps what reaches the screen; the title shows the engine's own
rate beside it. Details and every measurement: shim/docs/PERF-PLAN.md.
The window opens at the largest 16:9 size that fits your screen (PELOAD_WIN_SIZE=1280x720 picks a size), can be
resized, and can go full screen (green button or Ctrl-Cmd-F). Developer and diagnostic switches are documented in
CLAUDE.md and in the source.
| Path | Contents |
|---|---|
shim/src/peload.c | PE loader, Win32/DXGI/D3D11 implementation, GL renderer, present path |
shim/src/dxbc2glsl.c | DXBC (Shader Model 5) → GLSL 4.10 translator |
shim/src/dxbc_cpu.c | CPU interpreter for the compute shaders (particles), run on a worker thread |
shim/src/xaudio.c | XAudio2 / X3DAudio on CoreAudio |
shim/src/wininput.c | keyboard/mouse → Raw Input and window messages |
shim/src/wasapi.c | WASAPI render endpoint (the client's menu music and voice chat) on AudioQueue |
shim/src/hitbox.c / shim/src/stats.c | overlay (hitboxes, tracers, labels, visible only, zones, frame capture) / live stats over HTTP |
shim/src/stats_page.html | the stats page (embedded at build time; served from the file in a source checkout) |
shim/src/rec.m / shim/tools/rec-test.sh | clip recording (mp4 / m4a from the stats page) / its offline test |
shim/src/fastcall.c / shim/tools/callcost.sh | opt-in fast paths for the game's hottest Win32 calls (PELOAD_FASTCALL) / their per-call costs under Rosetta and checks |
shim/play.sh / shim/build.sh | launcher / build script |
shim/target.sh / shim/dedicated.sh | windowless second player / local dedicated server |
shim/tools/servers.sh / shim/tools/maptop.sh | busiest public servers and their mods / top-down map pictures for the heatmap |
re-tools/ | reverse-engineering helpers (angr decompile, xrefs, .map reader h3map.py, map geometry h3bsp.py) |
shim/docs/ | research notes (index in CLAUDE.md); user guides: mods.md, game-options.md |
shim/docs/halo3-objects.mdPELOAD_HB_VISIBLE=1 | start every overlay feature as visible only: drawn only for what you can actually see, not behind walls (a small copy of the game's depth buffer, read back every frame while a feature that is on uses it); the panel sets it per feature (Walls) |
PELOAD_HB_TEXT_PX=7 | overlay label height in window pixels, every feature's start value (the panel's Text, per feature) |
PELOAD_HB_ZONES=1 | the map's boundaries in the game view: kill zones red, safe zones green (outside every safe zone you die), soft ceilings pink (soft kill), orange (push back) and yellow (slip surface), invisible walls light cyan (collision you can't see), read from the map being played (Workshop maps too), drawn only where you can see them; the panel's Kill/safe zones row, a chip per kind |
PELOAD_MIC=0 | don't use the Mac's microphone: the client gets a silent one, so its voice chat still works for listening (by default the Mac's microphone is the client's; it only sends voice while its talk key is held or toggled -- Communication settings). =none gives it no microphone at all (its voice chat then retries every 5 s). PELOAD_MIC_MUTED=1 starts it muted; the stats page's Mic button mutes it during play. macOS asks once whether Terminal may use the microphone |
PELOAD_D3DGL_INVARIANT=1 | experimental: vertex positions computed bit-identically in every pass; try it if you see seams/speckles along polygon edges (e.g. Valhalla's rock walls) |
PELOAD_ALLOW_TELEPORT=1 | teleport, in games you host (Host Game from the menu): the stats page's player details get Teleport next to them (you land beside that player, facing them, at a spot inside the map), the page's Teleport button jumps to the opposite side of the map. It uses the game's own teleport on the host, between frames, so every player sees the move. A client can't move itself (the host puts it straight back -- tested); in a private game where the host also runs this loader, set PELOAD_TELEPORT_HOST=<host ip>:<its stats port> on the client and its button asks the host to move you |
PELOAD_H3="name=value,..." | set Halo 3 engine settings, e.g. render_debug_depth_render=1, camera_fov_scale=1.2 — all 72 are listed in shim/docs/halo3-engine-globals.md |
PELOAD_ALLOW_TELEPORT=1bash shim/dedicated.shPELOAD_AUTOVOTE_MAPPELOAD_AUTOVOTERecord -- a clip of what the game window shows (overlay included) with the game's sound (and the client's menu
music and voice chat), as .mp4 (H.264 + AAC, the window's picture up to 1920x1080, up to 60 fps), or Audio only
as .m4a: set the length in seconds (1-600), press Record; it stops by itself (or Stop), a red REC 0:12 counts
while it runs. Clips go to ~/Movies/Reclaimer/<map>_<date-time>.mp4; the list under the row downloads or plays
them. Hardware encoding; nothing runs while you don't record. A bigger or full-screen window gives a sharper clip.
Details: shim/docs/recording.md.
Connected -- everyone in the game's network session, also in the lobby before a match starts, in alphabetical order. Players who are talking light up green. The speaker beside each name mutes or unmutes their voice (a red cross = muted); Mute all / Unmute all do everyone. This is the client's own voice mute (the same list as its scoreboard Mute buttons): it only affects what you hear and lasts until the game closes.
Event feed and Chat side by side, newest first. Chat is every line the client's text chat shows (players' messages with TEAM / NEARBY marks, the server's [SERVER] lines and welcome message, its own notices such as "sending messages too quickly"), read from the client as each line arrives. The feed shows joins and leaves (the client's own roster notices; when you join, everyone already there becomes one "N players in the game" line), kills, deaths, votes, server notices (team shuffles, kicks), the game's own feed lines ("X has become a zombie", flag events) and match start and end, each kind with its own checkbox. Kills come from the game itself -- killer, victim, weapon or vehicle, headshot, splatter, stuck, beat down, betrayals, suicides and falls -- wherever it tells this client: kills involving your team, so all of them in team games but only your own in free-for-all and Infection. The others are estimates, marked ≈: the last player whose projectile ended inside the body within 1.5 s before it died, with the weapon in their hands, the vehicle or turret they sit in, or the grenade (a death with no such shot shows as "died"). Spawns have three more checkboxes, weapons, vehicles and power-ups (remembered like the others): each weapon, equipment, vehicle and power-up as it appears on the map, with where -- "Wraith spawned at (-38.5, -25.2)", the height on hover -- and the match start's weapons as one "N weapons and equipment spawned: ..." line; who first picks up each spawned weapon, and who takes each power-up (the game's own pickup time for that colour on the player). Vehicles are listed one by one, respawns included; turrets mounted on a vehicle, deployed equipment (a thrown bubble shield, trip mine, auto turret), grenades and weapons dropped by players are left out.
Packets -- the type of everything the game sends and receives, never what is in it: each UDP datagram and TCP
connection with direction, peer (and the host name it was looked up by), size and what its first bytes show without
decrypting anything -- the game's QUIC traffic as handshake / initial / 1-RTT (connected) packets, plaintext tags, TCP
connects and closes. A table sums the last 10 s per type and peer (packets and bytes a second); the log beside it lists
them newest first, repeats folded into one line (connected 1-RTT traffic only with its checkbox: it is dozens a second).
halo3 message types (off by default) adds halo3's own network messages by name -- connect-request, join-request,
membership-update, player-add, time-synchronize ... -- as halo3 sends and receives them, with the session peer each one
goes to or came from (the player's name; "host" for a host without a player, such as a dedicated server), counted in a
second table (it wraps halo3's message table, and five small hooks on halo3's message senders / receivers note the peer;
switching it off restores the table). Cost: about 26 ns per datagram
(PELOAD_PKTLOG_BENCH=1 measures it); PELOAD_PKTLOG=0 turns it off. The forensic archive keeps per-second sums
(packets.jsonl, shim/docs/forensics.md).
Players -- living players first, then players waiting to respawn, then those who quit (still in the game's player list but gone from its network session); click a column title to sort (by team, or the whole list). Team, shield and health, weapon, kills, deaths, K/D, shots and accuracy, grenades, vehicle, speed and active traits (where they differ from the defaults, with powerup and respawn-trait countdowns). 🔊 marks a talker, 🔇 a muted player, "(you)" the local player (from Halo 3's own player mapping, so also right in third person and vehicles). Shots count every projectile (each shotgun pellet is one); a hit is a shot that ended inside another player's body bounds -- an estimate.
Death heatmap -- where players died (blue = few, red = many) over a top-down picture of the map and a grey
"where players walk" layer, with everyone's live position and the vehicles. Deaths for this match or all matches on
that map (always collected, kept in ~/Library/Application Support/ReclaimerMac/heatmaps/; the heatmap checkbox
only shows or hides them). The "on map" checkboxes names, x/y, health / shield, primary weapon and secondary weapon label every living
player's dot, one line each, stacked (name on top); each adds or removes its own line and is remembered by the browser. Your own label and the
selected player's always show the name. Labels of players standing close together are moved apart with a thin line back to
their dot; when there is no room left the rest are left out (the map's corner says how many; the full window has more
room). The spawn points checkboxes -- vehicles (orange outlined squares), weapons (gold diamonds) and items
(equipment: cyan triangles; power-ups: magenta rings) -- mark where things spawn on the map, collected from every spawn the
game shows and kept per map beside the deaths (heatmaps/<map>.spawns; this match or all matches like the deaths); hover
a marker for its name and how often it spawned there. A spawn point is bright while its item sits on it and dim once it
is taken or not back yet; an item away from its spawn point is drawn where it is now, live (a sniper rifle lying where
its carrier died, a vehicle being driven), with a dashed line back to its spawn point (not for vehicles), one being
carried beside its carrier's dot with a white outline (hover: who carries it); a player with a power-up active gets a
magenta ring with "Overshield 12 s · x, y". After joining a game in progress the first spawns seen are left
out (they are whatever lies around, not spawn points); what respawns later is recorded.
Click the map (or "full window") to fill the browser window; X or Esc closes it. The map pictures are made once from the game's own .map
files (bash shim/tools/maptop.sh; play.sh runs it in the background when they are missing; needs python3 with
numpy and Pillow).
Player details (click a player in the table) -- under the heatmap: a big Mute / Unmute button, Teleport next to
them (when you host the game and started it with PELOAD_ALLOW_TELEPORT=1), camo, all weapons,
grenades, vehicle and seat, deaths, current and longest life, distance, top speed, shots, hits, accuracy, active
traits, facing and position, and their address: their IP when you host the game (from the client's own host table,
the one its banip uses); a client of someone else's server is never told other players' addresses (only the server's).
Addresses are hidden when the page is reachable from other computers (PELOAD_STATS_ADDR not loopback) unless
PELOAD_STATS_IPS=1; PELOAD_STATS_IPS=0 always hides them. Their deaths are ringed on the heatmap.
X-Reclaimer-Control: 1PELOAD_STATS_READONLY=1POST /control?do=feat&f=<hitboxes|vehicles|tracers|info|items|zones, comma-separated, or all>&on=0|1&team=all|enemies|friends&vis=0|1&px=4..60teamsvisibletextuptextdown