Repository navigation
feat(libSceNpManager): register NP state callbacks and report signed-out from sceNpCheckCallback - #6121
Conversation
…out from sceNpCheckCallback sceNpRegisterStateCallback and sceNpRegisterStateCallbackA stored nothing, the unregister calls always succeeded and sceNpCheckCallback did nothing, so a title that waits for its NP state callback never learned the user's state. boykopovar#1277 reports that Minecraft Preview (PPSA19639) needs the callback (stop 2). sceNpRegisterStateCallbackA now keeps up to 8 callbacks and returns the slot plus 1. A null callback returns INVALID_ARGUMENT, a callback that is already registered CALLBACK_ALREADY_REGISTERED and a ninth one CALLBACK_MAX (0x8055001D). sceNpUnregisterStateCallbackA returns INVALID_ARGUMENT for an id outside 1..8 and CALLBACK_NOT_REGISTERED for an empty slot. The legacy sceNpRegisterStateCallback keeps one callback with the same errors. The rules follow shadPS4 src/core/libraries/np/np_manager.cpp @15abf68c9b52 (RegisterStateCallbackA and UnregisterStateCallbackAById L857-L901, the legacy pair L981-L1005, ORBIS_NP_STATE_CALLBACK_MAX L36) and np_error.h L38. Each registration is marked pending. sceNpCheckCallback takes the pending registrations under the lock and calls them after releasing it, on the calling thread, with SIGNED_OUT for the initial user 0x10000000. For the A form this follows KytyPS5 src/libs/network.cpp @9e41d8320a27 (NpRegisterStateCallbackA L4021-L4034 marks the registration pending, NpCheckCallback L3959-L3974 delivers it once). KytyPS5 never calls the legacy callback (NpRegisterStateCallback and NpUnregisterStateCallback, L4009-L4019, do nothing). The legacy callback is called as (user, SIGNED_OUT, nullptr, userdata), as in shadPS4, which passes an NpId only for SIGNED_IN (L823, L938-L940). shadPS4 queues SIGNED_OUT from the user service login event instead (DispatchPendingNpStateCallbacks L903-L962), and the same event calls the reachability callback once with UNAVAILABLE (L922-L931, L959-L961). The reachability callback is still not called here. These differences and the unmeasured console behaviour are recorded in docs/dev/debt/libSceNpManager.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.
|
|
Checked on Linux: Ubuntu 26.04 (WSL2), GCC 15.2.0, Release, this head (
Not checked: macOS, the titles. AI-assisted: yes (Claude Code). |
|
Preview check for head I built the complete The parent control also entered gameplay. I built the parent revision's complete NP module ( The PR's |
|
Checked the state callback registration (8 slots, legacy single slot, error codes vs shadPS4), delivery from |
|
I have a small offline-profile follow-up for issue #1794 that adds |
|
I adjusted the include placement; the latest |
What
#1277 reports that Minecraft Preview (PPSA19639) needs this for stop 2: the title registers an NP state callback and expects
sceNpCheckCallbackto deliver SIGNED_OUT once. This PR is the NpManager part of that stop. Onmain,sceNpRegisterStateCallbackandsceNpRegisterStateCallbackAstore nothing (the A form always returns 1), both unregister calls always return 0, andsceNpCheckCallbackdoes nothing.Registration follows shadPS4 np_manager.cpp @15abf68c (
RegisterStateCallbackAandUnregisterStateCallbackAByIdL857-L901, the legacy pair L981-L1005,ORBIS_NP_STATE_CALLBACK_MAX = 8at L36):sceNpRegisterStateCallbackA(cb, ud)keeps up to 8 callbacks and returns the slot plus 1. A null callback returnsINVALID_ARGUMENT(0x80550003), a callback that is already registeredCALLBACK_ALREADY_REGISTERED(0x80550008), and a ninth oneCALLBACK_MAX(0x8055001D, np_error.h L38).sceNpUnregisterStateCallbackA(id)returnsINVALID_ARGUMENTfor an id outside 1..8 andCALLBACK_NOT_REGISTERED(0x80550009) for an empty slot.sceNpRegisterStateCallback(cb, ud)is the legacy formvoid(int userId, uint32_t state, NpId* npId, void* ud)with one slot: a null callback isINVALID_ARGUMENT, a second registrationCALLBACK_ALREADY_REGISTERED.sceNpUnregisterStateCallbackwith nothing registered returnsCALLBACK_NOT_REGISTERED.APS5_VABI.Delivery:
sceNpCheckCallbackcopies the registrations and clears the pending flags under the mutex. After releasing it, on the calling thread, it calls the A form ascb(0x10000000, SIGNED_OUT = 1, ud)and the legacy form ascb(0x10000000, 1, nullptr, ud). 0x10000000 isUSER_SERVICE_INITIAL_USER_IDfromprx/libSceUserService/UserService.hpp.NpRegisterStateCallbackA(L4021-L4034) marks the registration pending, andNpCheckCallback(L3959-L3974) delivers it once. KytyPS5 never calls the legacy callback: itsNpRegisterStateCallbackandNpUnregisterStateCallback(L4009-L4019) do nothing.has_np_idonly forSignedIn(L823) and passesnullptrotherwise (L938-L940).DispatchPendingNpStateCallbacksalso calls it once with UNAVAILABLE for the same event (L922-L931, L959-L961), it gets no initial call.shadPS4 queues SIGNED_OUT from the user service login event and delivers it from
sceNpCheckCallback(DispatchPendingNpStateCallbacksL903-L962). That difference, the reachability difference, the unregister race and the fact that the console's timing and errors are not measured are recorded indocs/dev/debt/libSceNpManager.md.Test:
GuestNpManager.cpp(guest_np_manager) now also checks both forms withAPS5_VABIcallbacks that record userId, state, npId and userdata:sceNpUnregisterStateCallbackAon its own id gets 0, and its id is then empty.npId, and a second check makes none. Unregistering returns 0, and a second unregister returns 0x80550009.guest_np_reachabilitystill passes.Need on current
main: the only evidence for stop 2 is the #1277 reporter's local patch onmaind4c196c2, and a 2026-10-14 comment there notes that stop 2 is not covered by #5133. Minecraft (PPSA17221), which shares the engine, is reported to run onmainwithout this change. The change affects every title that registers a state callback: it now gets one SIGNED_OUT call that it never got before. I have not run either title, so this PR does not confirm the stop on currentmain. @ddrsilva, or anyone who runs PPSA17221: could you run this branch with the Preview and with PPSA17221 before merge? I will add the result to Tested.Tested
Windows Server 2025 (10.0.26100), WinLibs MinGW-w64 GCC 15.2.0, Ninja, Release,
BUILD_TESTING=ON, onmain922abe6.main'sExport.cpp:guest_np_manageraborts (0xc0000409) withNpManager: duplicate state callback, in 2 of 2 runs on 922abe6.unregistering one id removed another callback. If the reachability callback is called with the state event, it aborts withthe reachability callback was called. If the callbacks are called with the mutex held, the self-unregistering callback deadlocks, and the test times out.guest_np_managerandguest_np_reachabilitypass in 3 of 3 runs, each in under 0.75 s on a host running parallel builds.ctest -R guest_np_(9 tests, includingguest_np_auth,guest_np_partner,guest_np_web_api2andguest_np_session_signaling): 9 of 9 passed in 3 runs on 922abe6.tools/check_conventions.py --base upstream/main(the tools frommain): passed.git merge-treeof this branch (on 922abe6) with each of the four open PRs that change the same files adds no conflict.sceNpSetContentRestriction,sceNpRegisterGamePresenceCallback) and feat(npmanager): implement sceNpGetUserIdByAccountId #2852 (head 3351d1f,sceNpGetUserIdByAccountId) change other hunks ofExport.cppandGuestNpManager.cpp. feat(libSceNpManager): retain the game presence callback and validate content restrictions offline #6023 also adds entries next to ours indocs/dev/debt/libSceNpManager.md, which merge cleanly.Export.cpp,GuestNpManager.cpp,libSceNpManager/CMakeLists.txtand the same debt file.GuestNpManager.cpptocore/libs/tests/services/andGuestNpReachability.cpptocore/libs/tests/networking/, and editslibSceNpManager/CMakeLists.txt. git follows the move, and the merged tree has this PR's test changes at the new path. test: organize library tests and label CTest suites #6044's conflicts are inlibSceAgcDriver/CMakeLists.txtandlibc/CMakeLists.txt, and it has them withmainalone.guest_np_managerandguest_np_reachabilitywere built and passed on this branch merged with feat(libSceNpManager): retain the game presence callback and validate content restrictions offline #6023 and feat(libSceNpManager): export two unknown NIDs so PPSA34015 can load #6056, and on this branch merged with feat(libSceNpManager): retain the game presence callback and validate content restrictions offline #6023 and feat(npmanager): implement sceNpGetUserIdByAccountId #2852. feat(npmanager): implement sceNpGetUserIdByAccountId #2852 conflicts withmainonly indocs/dev/TechnicalDebt.md, and with feat(libSceNpManager): export two unknown NIDs so PPSA34015 can load #6056 inGuestNpManager.cpp; this PR changes neither conflict.Not tested: Linux, macOS, titles.
Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code