Files
CoopAllTheThings/README.md
BlackMark cf058aecfa Phase 0: donor-launch spike foundation
Scaffold CoopAllTheThings: Remote Play Together for any XInput game via a
mirror app under a donor appid (real game keeps its own appid, so DRM,
achievements, and playtime stay intact).

- Build: CMake skeleton, ImGui + SafetyHook submodules (no vcpkg)
- common/: host<->hook IPC contract (seqlock pad state, shared-memory RAII)
- host/: borderless D3D11 window + ImGui overlay listing visible XInput pads,
  behind an InputSource interface (Steam Input slots in later)
- README documents the Phase 0 donor-launch validation procedure, anti-cheat
  limitation, and XInput/bitness constraints

Phase 0 validates the riskiest assumption (Steam RPT streams an arbitrary
window under a donor appid and routes guest input to it) before capture and
injection are built.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 22:48:31 +02:00

4.9 KiB

CoopAllTheThings

Steam Remote Play Together (RPT) for any XInput game — without breaking DRM, achievements, or playtime.

Existing "donor game" tools (e.g. RemotePlayWhatever) copy a target game's files into a donor game's folder and rename the executable so Steam streams the target under the donor's appid. That breaks DRM-protected games, breaks achievements, and credits playtime to the donor.

CoopAllTheThings takes a different approach: the real game runs normally under its own appid (so DRM, achievements, and playtime all work), while a lightweight mirror app runs under the donor appid. The mirror presents a borderless window that is a live copy of the game's video + audio, and forwards the guests' input back into the real game. Steam's RPT captures the mirror window — so any XInput game becomes Remote-Play-Together-able.

See docs and the in-repo plan for the full design.

Architecture (target)

Concern Mechanism Where
Receive guest input Steam Input / XInput (RPT delivers guests to the focused window) coop_host.exe
Forward input to game DLL injection + XInput hook (SafetyHook) — game sees only our pad coop_hook.dll
Mirror video Windows Graphics Capture first; IDXGISwapChain::Present hook as the low-latency upgrade host (+ hook)
Mirror audio WASAPI process-loopback capture of the game, re-rendered coop_host.exe
Host ↔ hook IPC Named shared memory (seqlock for input, shared D3D11 texture for video) common/

Limitations

  • Anti-cheat: the input path injects coop_hook.dll into the target game. Games protected by kernel-level anti-cheat (Easy Anti-Cheat, BattlEye, Vanguard, etc.) will detect the injected module and may kick the player or issue a ban. Such games are explicitly out of scope and unsupported — do not use CoopAllTheThings with them. The tool targets single-player and co-op/local-multiplayer titles without active anti-cheat.
  • XInput only: the game must read controllers via XInput (the common case). DirectInput-only / RawInput-only games are not handled.
  • Architecture match: the host and hook DLL must match the game's bitness. x64 is supported first; x86 support is a later phase (see the plan).

Status

Phase 0 — donor spike (current). coop_host.exe is a borderless D3D11 window with an ImGui overlay that lists every controller it can see. It exists to validate the riskiest assumption before anything else is built: can Steam RPT stream an arbitrary window launched under a donor appid, and route a guest's gamepad into it?

Later phases (capture, injection, audio) are scoped in the plan and gated on Phase 0 passing.

Building

Requirements: Windows 10/11, Visual Studio 2022 (MSVC + C++ workload), CMake ≥ 3.21.

git clone --recurse-submodules <repo-url>
# or, if already cloned:
git submodule update --init --recursive

cmake -S . -B build -G "Visual Studio 17 2022" -A x64
cmake --build build --config Debug
# output: bin/Debug/coop_host.exe

Third-party dependencies (Dear ImGui, SafetyHook) are git submodules under third_party/; the Steamworks SDK is vendored manually there when wired. No vcpkg / package manager is used.

Phase 0: validating the donor-launch assumption

This is a manual test — it needs Steam, a second person (or second account), and a donor game that supports Remote Play Together.

  1. Pick a donor game you own that supports Remote Play Together (check the "Remote Play Together" tag on its store page). The donor only needs RPT support; it is never actually played.

  2. Launch the host under the donor's appid. Find the donor's appid (the number in its store URL), then run:

    "C:\Program Files (x86)\Steam\steam.exe" -applaunch <donorAppId> "D:\dev\CoopAllTheThings\bin\Debug\coop_host.exe"
    

    The borderless overlay window should appear and Steam should consider the donor "running" (green status / "Stop" button in the library).

    If the donor ignores the trailing path, set the host as the donor's Launch Options instead ("D:\...\coop_host.exe" %command% variants), or use a launcher such as RemotePlayDetached. Recording which method makes Steam attribute our window to the donor is the main deliverable of Phase 0.

  3. Start Remote Play Together from the Steam friends list / overlay and invite a friend (or a second machine/account).

  4. Verify on the guest side:

    • The guest sees the borderless overlay window streamed (not a black screen).
    • The guest presses buttons on their controller and the corresponding slot in the overlay lights up. This proves RPT routes guest input into our window as XInput — the foundation the whole tool relies on.
  5. Press Esc in the host window to quit.

If steps 2 and 4 both work, the core premise holds and we proceed to Phase 1 (window capture + input injection). If not, we revisit the donor-attribution approach before building further.