Files
DroidDeck/tools/xbox/README.md
The412Banner c5d7cdc058 Xbox Cloud Gaming: stream from the store and Games tab, native player with DroidDeck frame generation (#1531)
Xbox Cloud Gaming: stream from the store and Games tab, native player with DroidDeck frame generation

Stream Xbox games with Xbox Cloud Gaming (Game Pass) from DroidDeck, outside
the Linux session.

- Backend: `xodus-cli cloud-token` (tools/xbox/droiddeck-cloud-token.patch)
  gets the streaming tokens; they stay in memory.
- Web player (default): the Greenlight player page (tools/xcloud-player, MIT)
  in a WebView, plus Microsoft's own xbox.com/play player. In-stream menu with
  screen effects (sharpen, colour, deband, FXAA, CRT, NTSC, Toon, fps cap),
  stream quality, performance overlay (level, layout, top right / top left)
  and rumble.
- Native player (opt-in, Android 10+, Player engine: Native): Google's WebRTC
  (io.github.webrtc-sdk 125.6422.07, BSD) with the Greenlight protocol ported
  to Kotlin, in its own process (:xcloudnative) with its own instance of
  DroidDeck's compositor. Decoded frames reach the compositor as
  AHardwareBuffers (new stream mode, VK_ANDROID_external_memory_android_
  hardware_buffer import), so DDFG 2x and LSFG 2x/3x/4x run on them. Live
  frame generation, frame-rate target (presets and a 10-165 fps slider, even
  pacing), zero-copy picture, small network buffer, fast decoder, instant
  controller input, overlay with engine and on-screen fps, background end,
  restart and web fallback. It takes the main process's tokens through
  XboxCloudTokenService; they are fetched ahead while a cloud game is shown.
- The compositor requests the dma-buf import extensions only where the driver
  has them (the system driver has none).
- Catalog: cloud availability, Game Pass plan CFQ7TTC0K6L8 counted, library
  filter All / Owned / Game Pass / Cloud, Stream and Add to Games on the store
  page, stream-only Games-tab entries (Stream + Download / Play + Stream).
- Xbox games get VC++ 2015-2022 and the ARM64 runtime registry entries
  (vcredist2022_arm64, built in until the catalog has it) before each launch.
- Controller focus reaches pickers opened from a game page (install location,
  uninstall); Add / Remove from Games keep the focus.

Credits: Greenlight by UnknownSKL (Jim Kroon), MIT, player and protocol;
Better xCloud for the resolution / OS mapping; MGamer404's Bannerlator #627
for the ARM64 runtime approach. Ported from the412banner's Bannerlator.
2026-10-24 23:17:57 -04:00

120 lines
7.5 KiB
Markdown

# Xbox backend for DroidDeck
DroidDeck uses the Nexus backend protocol with a native Linux AArch64 CLI and
service. It does not install the Nexus desktop launcher or x86 Linux Wine.
`backend.lock.json` pins Xodus, the Nexus commit, Rust, and the ordered patch
checksums. `runtime.lock.json` pins the Nexus Windows runtime release and each
extracted PE DLL. `prepare.py` verifies both source revisions and all Nexus patch
checksums before applying the local transport, launch and library patches.
`droiddeck-cloud-token.patch` adds `xodus-cli cloud-token`: the stored account's
Xbox Cloud Gaming sign-in (XSTS for `http://gssv.xboxlive.com/`, then the streaming
service's `/v2/login/user`) and the login proof token a session's `/connect` step
takes. It prints one JSON object and never opens a login or provisions a device.
Ported from Bannerlator.
`droiddeck-library-subscriptions.patch` queries fresh subscription membership
alongside product entitlements using the same Store session, account and market.
Android can validate a Game Pass child's parent plan when that plan is absent
from the product response. A failed membership query fails the refresh and
preserves the previous library. Empty membership results add no plan evidence;
valid plans already present in the product response retain their existing checks.
Build on AArch64 Ubuntu 24.04 with Rust from the lock, a C/C++ compiler, OpenSSL
development headers, pkg-config, protoc, CMake, Git, Python 3 and xz:
```sh
bash tools/xbox/build.sh
```
On macOS, `bash tools/xbox/build-local.sh` uses an AArch64 Ubuntu container.
`tools/build_local.sh` includes this step and caches it by recipe inputs. CI
builds the same recipe on `ubuntu-24.04-arm` and transfers the result to the APK
job. Cache hits still download that build's Xbox artifact before APK assembly.
The build runs account-free CLI, credential and streaming tests. It retains the
patched backend source archive and the checksum-verified runtime source archive
in the build cache and publishes both in CI's `xbox-corresponding-source`
artifact; distribute corresponding sources with published binaries.
`droiddeck-credential-fallback.patch` names the reason when the backend cannot reach the
app's credential socket (`NEXUS_CREDENTIAL_ERROR_V1 socket-refused`, `-denied`, ...) and then
uses a keyed loopback port the app also listens on; some devices refuse the abstract socket
from inside PRoot. The key is passed in the backend's environment only.
`droiddeck-credential-diagnostics.patch` names the step behind a `credential-io` failure: the
session lock (`lock-dir-owner`, `lock-file-mode`, `lock-flock-<reason>`, ...), a socket or loopback
exchange that broke after connecting (`socket-exchange-<reason>`), a malformed host reply, or a
missing socket name, port or key. Errno values without a name are spelled with letters, a = 0 ... j = 9.
`droiddeck-credentials.patch` replaces Linux Secret Service with a bounded,
Android abstract socket restricted to the backend process started by the host. The Android host encrypts every persistent
write using its existing Keystore cipher, verifies backend read-backs, and
rejects writes when Keystore is unavailable. No token values enter process
arguments, external storage, application logs or a plaintext credential file.
Account-generation invalidation prevents a signed-out child's later writes from
restoring credentials. Each backend gets its own socket. The host checks the peer UID, the PRoot tracer
owned by its live child, and an executable mapping of the backend file with a matching inode and
SHA-256 against the APK asset. PRoot's loader can occupy `/proc/<pid>/exe`, so
authentication checks the mapped backend image instead. A socket name alone grants no credential operations, including to another
copy of Xodus started by the guest.
Licensed launch-view preparation runs in an Android-owned Xodus child, which
never starts Wine or a game. Steam and DroidDeck launch paths can request
preparation for an installed Xbox title during a live session with an Xbox
account. The reply contains a random launch lease, a materialized executable
path, a game-service socket and the existing PlayFab game token; it contains no
account credential endpoint.
Leases bind product, account and session, expire without heartbeats, and release
idempotently. Cancellation, session stop and account changes revoke leases;
closing the child's stdin lets it remove its private view. Suspension preserves
live leases.
The Proton initialization and game processes do not inherit the raw credential
channel or command-file environment variables. The authenticated GDK bridge
remains available to the game. Like the other stores, games share DroidDeck's
Linux environment and Android UID. The process checks close the direct
credential-socket route; they do not isolate writable runtime files, prevent
same-UID process tampering, or provide a sandbox against malicious games.
Microsoft sign-in uses Nexus's private stdio protocol in an isolated Android
WebView process. Its page bridge is restricted to the allowed Microsoft/Xbox
HTTPS origins and the main frame; it has no credential-store or launcher API.
Cookies and page storage are cleared for each attempt. This protocol requires
an embedded host for CloudExperienceHost messages; opening its URL in an ordinary
Android browser does not complete it. Passkey behavior needs device testing.
`droiddeck-launch-view.patch` materializes all licensed encrypted images,
including DLLs, in a private temporary directory. Original package files remain
encrypted, and the temporary view is removed after the Wine child exits. Wine
receives normal files with no Xodus FD-map requirement. This costs temporary
storage proportional to the encrypted images. The app keeps the account service
alive for the session; an authenticated loopback bridge carries GDK requests.
Wine's builtin PE marker makes `WINEDLLOVERRIDES=n` reject the Nexus modules.
The extractor verifies their original hashes, then changes only that 16-byte DOS
stub marker to `DroidDeck native`; code, imports and exports stay unchanged. This
lets ARM64EC Wine load the AMD64 modules through its normal native DLL path.
Only each Xbox game's prefix receives these Windows modules. Existing
Steam games and other store prefixes retain their selected runtimes.
Cloud saves and in-game identity use the reused XGameRuntime/service interfaces;
those are game-dependent. Entitlements distinguish purchases from Game Pass
access, and the backend performs a fresh license check for downloads and launch.
The store lists PC MSIXVC games only. It does not emulate Xbox consoles or add
support for UWP/APPX packages. Update / verify refreshes an installed package through the normal licensed
streaming path. Manage content resolves manifest-related licensed PC DLC into
separate content roots and checks its parent identity after download. These
flows still require game/account validation; runtime API presence does not
establish that they work on Thor.
Upstream contribution restrictions are respected: these are launcher, storage,
build and file-lifecycle adapters in DroidDeck, not submitted reverse-engineering
contributions to Xodus.
Sources and licenses:
- Xodus: https://github.com/xodus-gaming/xodus (GPL-3.0, `LICENSE.xodus`).
- Nexus patches: https://github.com/domsvoboda/xodus-nexus (MIT, `LICENSE.nexus`).
- XGameRuntime/Wine modules: the exact source archive is identified in
`runtime.lock.json` (LGPL-2.1, `LICENSE.LGPL-2.1`).
- `libnghttp2.dll`: nghttp2, MIT; its license is retained in the runtime source
archive and is packaged as `LICENSE.nghttp2`.