Pylux is a cross-platform PS4/PS5 Remote Play + Internet/Cloud Play client (fork of
chiaki-ng): shared C library (lib/), Qt desktop
(gui/ — macOS/Windows/Linux/Steam Deck), Android (android/), iOS (ios/).
- DO put protocol, streaming, catalog, and feature logic in the shared C (
lib/) so every platform gets it and stays consistent. Platforms only map inputs/outputs (controllers, audio, video, UI). - DON'T patch shared behavior in one platform. If a fix would need repeating per platform,
it belongs in
lib/. - DO debug cross-platform discrepancies empirically: capture runtime logs on a working and
a broken platform and diff them, and diff suspect files against
upstream/main(remote is configured). - DON'T make sweeping edits to upstream-owned Qt files; prefer new files so chiaki-ng fixes stay cherry-pickable.
- Feature branches off
master. PRs targetrelease/beta, which auto-builds and deploys BETA to all stores on merge.masterdeploys to production. DON'T push to release branches casually.
- DON'T infer prefix/regex patterns from the ID samples you happen to see. PSN IDs are region- and account-dependent; a pattern that fits one library corrupts matching for others.
- DO match structurally: split on
-/_and drop the region-varying tail, viacc_stable_key()inlib/src/cloudcatalog_merge.c(see alsonormalize_apollo_game/normalize_titlethere). Extend those helpers; don't add ad-hoc string checks elsewhere.
- DO use the build scripts and read their headers first — they are self-documenting:
- macOS (Qt):
./scripts/build-macos.sh arm64 --iterate --skip-deps --ad-hoc --no-credentials-file --skip-notary-keychain --no-steamworks; runbuild-output/Pylux.apponly; logs intmp/pylux-macos.log(logssubcommand). - iOS:
./ios/build.sh dev|iterate|launch|logs|stop-logs(+PYLUX_FULL_BUILD=1when the C lib changed). Device logs stream over Wi-Fi intoios/logs/pylux.log; app relaunch = capture relaunch. - Android:
android/build.ps1/android/build-local.sh.
- macOS (Qt):
- DON'T improvise around the scripts (custom launch paths, alternate log tools, manual
bundle surgery,
idevicesyslog, requiring USB for iOS logs, TERM-ing the iOS log capture —stop-logshandles it). - DO verify changes by running on the real platform and reading its logs; compiling is not verification.
ChiakiSession's memory layout depends on the lib's CMake config; Xcode compiles ObjC/Swift
without that config, so struct offsets differ between the two sides.
- DON'T call
static inlinechiaki functions that touch struct fields fromios/Pylux/**(e.g. thechiaki_session_set_*sink/callback setters) — the write lands at the wrong offset and silently no-ops.ios/build.shfails the build on violations (check_forbidden_inline_setters). - DO use the
CHIAKI_EXPORT_exwrappers inlib/src/ios_bridge_helpers.c, adding a wrapper there whenever the lib gains a setter iOS needs, and allocateChiakiSessionviachiaki_session_get_sizeof(), neversizeof.
- PS5 Remote Play delivers rumble ONLY as DualSense haptic audio: the client must set
enable_dualsense=trueand register a haptics sink (on iOS viachiaki_session_set_haptics_sink_ex). Cloud rumble uses classic rumble events — cloud working says nothing about Remote Play. - Qt keyboard/controller navigation relies on
Qt::TabFocusAllControls(gui main.cpp) and dialogs overridingseedFocus(); don't add competingforceActiveFocuscalls. - iOS binds the Create/PS controller buttons to system gestures by default;
StreamInput.attachControllerclaims them withpreferredSystemGestureState = .disabled.