np-legacy-state-callback (#4410)

* Core: Add user state callback management and improve user login/logout handling

* Core: Implement handle key lookup and special handle checks in pad library

---------

Co-authored-by: w1naenator <valdis.bogdans@hotmail.com>
This commit is contained in:
Valdis Bogdāns 2026-05-14 22:22:43 +03:00 committed by GitHub
parent 9559468c2e
commit b39d529324
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 287 additions and 41 deletions

View File

@ -9,11 +9,14 @@
constexpr int ORBIS_NP_ERROR_INVALID_ARGUMENT = 0x80550003; constexpr int ORBIS_NP_ERROR_INVALID_ARGUMENT = 0x80550003;
constexpr int ORBIS_NP_ERROR_SIGNED_OUT = 0x80550006; constexpr int ORBIS_NP_ERROR_SIGNED_OUT = 0x80550006;
constexpr int ORBIS_NP_ERROR_USER_NOT_FOUND = 0x80550007; constexpr int ORBIS_NP_ERROR_USER_NOT_FOUND = 0x80550007;
constexpr int ORBIS_NP_ERROR_CALLBACK_ALREADY_REGISTERED = 0x80550008;
constexpr int ORBIS_NP_ERROR_CALLBACK_NOT_REGISTERED = 0x80550009;
constexpr int ORBIS_NP_ERROR_INVALID_SIZE = 0x80550011; constexpr int ORBIS_NP_ERROR_INVALID_SIZE = 0x80550011;
constexpr int ORBIS_NP_ERROR_ABORTED = 0x80550012; constexpr int ORBIS_NP_ERROR_ABORTED = 0x80550012;
constexpr int ORBIS_NP_ERROR_REQUEST_MAX = 0x80550013; constexpr int ORBIS_NP_ERROR_REQUEST_MAX = 0x80550013;
constexpr int ORBIS_NP_ERROR_REQUEST_NOT_FOUND = 0x80550014; constexpr int ORBIS_NP_ERROR_REQUEST_NOT_FOUND = 0x80550014;
constexpr int ORBIS_NP_ERROR_INVALID_ID = 0x80550015; constexpr int ORBIS_NP_ERROR_INVALID_ID = 0x80550015;
constexpr int ORBIS_NP_ERROR_CALLBACK_MAX = 0x8055001D;
constexpr int ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT = 0x80550704; constexpr int ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT = 0x80550704;
constexpr int ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS = 0x80550706; constexpr int ORBIS_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS = 0x80550706;

View File

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <deque>
#include <map> #include <map>
#include <mutex> #include <mutex>
#include <variant> #include <variant>
@ -22,6 +24,10 @@ static std::mutex g_request_mutex;
static std::map<std::string, std::function<void()>> g_np_callbacks; static std::map<std::string, std::function<void()>> g_np_callbacks;
static std::mutex g_np_callbacks_mutex; static std::mutex g_np_callbacks_mutex;
static std::mutex g_np_state_events_mutex;
static std::mutex g_np_state_callbacks_mutex;
constexpr s32 ORBIS_NP_STATE_CALLBACK_MAX = 8;
// Internal types for storing request-related information // Internal types for storing request-related information
enum class NpRequestState { enum class NpRequestState {
@ -614,6 +620,10 @@ s32 PS4_SYSV_ABI sceNpGetAccountIdA(Libraries::UserService::OrbisUserServiceUser
if (account_id == nullptr) { if (account_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT; return ORBIS_NP_ERROR_INVALID_ARGUMENT;
} }
if (UserManagement.GetUserByID(user_id) == nullptr) {
*account_id = 0;
return ORBIS_NP_ERROR_USER_NOT_FOUND;
}
if (!g_shadnet_enabled) { if (!g_shadnet_enabled) {
*account_id = 0; *account_id = 0;
return ORBIS_NP_ERROR_SIGNED_OUT; return ORBIS_NP_ERROR_SIGNED_OUT;
@ -628,12 +638,15 @@ s32 PS4_SYSV_ABI sceNpGetNpId(Libraries::UserService::OrbisUserServiceUserId use
if (np_id == nullptr) { if (np_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT; return ORBIS_NP_ERROR_INVALID_ARGUMENT;
} }
const auto* user = UserManagement.GetUserByID(user_id);
if (user == nullptr) {
return ORBIS_NP_ERROR_USER_NOT_FOUND;
}
if (!g_shadnet_enabled) { if (!g_shadnet_enabled) {
return ORBIS_NP_ERROR_SIGNED_OUT; return ORBIS_NP_ERROR_SIGNED_OUT;
} }
memset(np_id, 0, sizeof(OrbisNpId)); memset(np_id, 0, sizeof(OrbisNpId));
strncpy(np_id->handle.data, UserManagement.GetDefaultUser().user_name.c_str(), strncpy(np_id->handle.data, user->user_name.c_str(), sizeof(np_id->handle.data) - 1);
sizeof(np_id->handle.data));
return ORBIS_OK; return ORBIS_OK;
} }
@ -643,12 +656,15 @@ s32 PS4_SYSV_ABI sceNpGetOnlineId(Libraries::UserService::OrbisUserServiceUserId
if (online_id == nullptr) { if (online_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT; return ORBIS_NP_ERROR_INVALID_ARGUMENT;
} }
const auto* user = UserManagement.GetUserByID(user_id);
if (user == nullptr) {
return ORBIS_NP_ERROR_USER_NOT_FOUND;
}
if (!g_shadnet_enabled) { if (!g_shadnet_enabled) {
return ORBIS_NP_ERROR_SIGNED_OUT; return ORBIS_NP_ERROR_SIGNED_OUT;
} }
memset(online_id, 0, sizeof(OrbisNpOnlineId)); memset(online_id, 0, sizeof(OrbisNpOnlineId));
strncpy(online_id->data, UserManagement.GetDefaultUser().user_name.c_str(), strncpy(online_id->data, user->user_name.c_str(), sizeof(online_id->data) - 1);
sizeof(online_id->data));
return ORBIS_OK; return ORBIS_OK;
} }
@ -657,6 +673,9 @@ s32 PS4_SYSV_ABI sceNpGetNpReachabilityState(Libraries::UserService::OrbisUserSe
if (state == nullptr) { if (state == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT; return ORBIS_NP_ERROR_INVALID_ARGUMENT;
} }
if (UserManagement.GetUserByID(user_id) == nullptr) {
return ORBIS_NP_ERROR_USER_NOT_FOUND;
}
*state = g_shadnet_enabled ? OrbisNpReachabilityState::Reachable *state = g_shadnet_enabled ? OrbisNpReachabilityState::Reachable
: OrbisNpReachabilityState::Unavailable; : OrbisNpReachabilityState::Unavailable;
@ -668,6 +687,9 @@ s32 PS4_SYSV_ABI sceNpGetState(Libraries::UserService::OrbisUserServiceUserId us
if (state == nullptr) { if (state == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT; return ORBIS_NP_ERROR_INVALID_ARGUMENT;
} }
if (UserManagement.GetUserByID(user_id) == nullptr) {
return ORBIS_NP_ERROR_USER_NOT_FOUND;
}
*state = g_shadnet_enabled ? OrbisNpState::SignedIn : OrbisNpState::SignedOut; *state = g_shadnet_enabled ? OrbisNpState::SignedIn : OrbisNpState::SignedOut;
LOG_DEBUG(Lib_NpManager, "Signed {}", g_shadnet_enabled ? "in" : "out"); LOG_DEBUG(Lib_NpManager, "Signed {}", g_shadnet_enabled ? "in" : "out");
return ORBIS_OK; return ORBIS_OK;
@ -692,6 +714,9 @@ s32 PS4_SYSV_ABI sceNpHasSignedUp(Libraries::UserService::OrbisUserServiceUserId
if (has_signed_up == nullptr) { if (has_signed_up == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT; return ORBIS_NP_ERROR_INVALID_ARGUMENT;
} }
if (UserManagement.GetUserByID(user_id) == nullptr) {
return ORBIS_NP_ERROR_USER_NOT_FOUND;
}
*has_signed_up = g_shadnet_enabled ? true : false; *has_signed_up = g_shadnet_enabled ? true : false;
return ORBIS_OK; return ORBIS_OK;
} }
@ -730,15 +755,151 @@ struct NpStateCallbackForNpToolkit {
NpStateCallbackForNpToolkit NpStateCbForNp; NpStateCallbackForNpToolkit NpStateCbForNp;
struct NpStateCallback { struct LegacyNpStateCallback {
std::variant<OrbisNpStateCallback, OrbisNpStateCallbackA> func; OrbisNpStateCallback func;
void* userdata; void* userdata;
}; };
NpStateCallback NpStateCb; LegacyNpStateCallback LegacyNpStateCb;
struct NpStateCallbackAEntry {
OrbisNpStateCallbackA func;
void* userdata;
bool in_use;
};
static std::array<NpStateCallbackAEntry, ORBIS_NP_STATE_CALLBACK_MAX> g_np_state_callbacks{};
struct PendingNpStateEvent {
Libraries::UserService::OrbisUserServiceUserId user_id;
OrbisNpState state;
OrbisNpId np_id;
bool has_np_id;
};
static std::deque<PendingNpStateEvent> g_np_state_events;
static void QueueNpStateEvent(Libraries::UserService::OrbisUserServiceUserId user_id,
OrbisNpState state) {
const auto* user = UserManagement.GetUserByID(user_id);
if (user == nullptr) {
return;
}
PendingNpStateEvent event{};
event.user_id = user_id;
event.state = state;
event.has_np_id = state == OrbisNpState::SignedIn;
if (event.has_np_id) {
std::strncpy(event.np_id.handle.data, user->user_name.c_str(),
sizeof(event.np_id.handle.data) - 1);
}
std::scoped_lock lk{g_np_state_events_mutex};
g_np_state_events.emplace_back(event);
}
void NotifyNpStateFromUserServiceEvent(Libraries::UserService::OrbisUserServiceEventType event_type,
Libraries::UserService::OrbisUserServiceUserId user_id) {
switch (event_type) {
case Libraries::UserService::OrbisUserServiceEventType::Login:
QueueNpStateEvent(user_id,
g_shadnet_enabled ? OrbisNpState::SignedIn : OrbisNpState::SignedOut);
break;
case Libraries::UserService::OrbisUserServiceEventType::Logout:
QueueNpStateEvent(user_id, OrbisNpState::SignedOut);
break;
default:
break;
}
}
static s32 RegisterStateCallbackA(OrbisNpStateCallbackA callback, void* userdata) {
if (callback == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
std::scoped_lock lk{g_np_state_callbacks_mutex};
for (const auto& entry : g_np_state_callbacks) {
if (!entry.in_use) {
continue;
}
if (entry.func == callback) {
return ORBIS_NP_ERROR_CALLBACK_ALREADY_REGISTERED;
}
}
for (size_t i = 0; i < g_np_state_callbacks.size(); ++i) {
auto& entry = g_np_state_callbacks[i];
if (entry.in_use) {
continue;
}
entry.func = callback;
entry.userdata = userdata;
entry.in_use = true;
return static_cast<s32>(i + 1);
}
return ORBIS_NP_ERROR_CALLBACK_MAX;
}
static s32 UnregisterStateCallbackAById(s32 callback_id) {
if (callback_id <= 0 || callback_id > static_cast<s32>(g_np_state_callbacks.size())) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
std::scoped_lock lk{g_np_state_callbacks_mutex};
auto& entry = g_np_state_callbacks[callback_id - 1];
if (!entry.in_use) {
return ORBIS_NP_ERROR_CALLBACK_NOT_REGISTERED;
}
entry = {};
return ORBIS_OK;
}
static void DispatchPendingNpStateCallbacks() {
std::deque<PendingNpStateEvent> pending_events;
LegacyNpStateCallback legacy_callback{};
std::array<NpStateCallbackAEntry, ORBIS_NP_STATE_CALLBACK_MAX> callbacks;
{
std::scoped_lock lk{g_np_state_events_mutex, g_np_state_callbacks_mutex};
if (g_np_state_events.empty()) {
return;
}
pending_events.swap(g_np_state_events);
legacy_callback = LegacyNpStateCb;
callbacks = g_np_state_callbacks;
}
for (auto& event : pending_events) {
if (legacy_callback.func != nullptr) {
legacy_callback.func(event.user_id, event.state,
event.has_np_id ? &event.np_id : nullptr,
legacy_callback.userdata);
}
for (const auto& entry : callbacks) {
if (!entry.in_use) {
continue;
}
if (entry.func != nullptr) {
entry.func(event.user_id, event.state, entry.userdata);
}
}
if (NpStateCbForNp.func != nullptr) {
NpStateCbForNp.func(event.user_id, event.state, NpStateCbForNp.userdata);
}
}
}
s32 PS4_SYSV_ABI sceNpCheckCallback() { s32 PS4_SYSV_ABI sceNpCheckCallback() {
LOG_DEBUG(Lib_NpManager, "(STUBBED) called"); LOG_DEBUG(Lib_NpManager, "(STUBBED) called");
DispatchPendingNpStateCallbacks();
std::scoped_lock lk{g_np_callbacks_mutex}; std::scoped_lock lk{g_np_callbacks_mutex};
@ -751,25 +912,49 @@ s32 PS4_SYSV_ABI sceNpCheckCallback() {
s32 PS4_SYSV_ABI sceNpCheckCallbackForLib() { s32 PS4_SYSV_ABI sceNpCheckCallbackForLib() {
LOG_DEBUG(Lib_NpManager, "(STUBBED) called"); LOG_DEBUG(Lib_NpManager, "(STUBBED) called");
DispatchPendingNpStateCallbacks();
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceNpRegisterStateCallback(OrbisNpStateCallback callback, void* userdata) { s32 PS4_SYSV_ABI sceNpRegisterStateCallback(OrbisNpStateCallback callback, void* userdata) {
static s32 id = 0; if (callback == nullptr) {
LOG_ERROR(Lib_NpManager, "(STUBBED) called, userdata = {}", userdata); return ORBIS_NP_ERROR_INVALID_ARGUMENT;
NpStateCb.func = callback; }
NpStateCb.userdata = userdata;
return id; std::scoped_lock lk{g_np_state_callbacks_mutex};
if (LegacyNpStateCb.func != nullptr) {
return ORBIS_NP_ERROR_CALLBACK_ALREADY_REGISTERED;
}
LOG_INFO(Lib_NpManager, "called, userdata = {}", userdata);
LegacyNpStateCb.func = callback;
LegacyNpStateCb.userdata = userdata;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpUnregisterStateCallback(s32 callback_id) {
LOG_INFO(Lib_NpManager, "called, callback_id = {}", callback_id);
if (callback_id != 0) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
std::scoped_lock lk{g_np_state_callbacks_mutex};
if (LegacyNpStateCb.func == nullptr) {
return ORBIS_NP_ERROR_CALLBACK_NOT_REGISTERED;
}
LegacyNpStateCb = {};
return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceNpRegisterStateCallbackA(OrbisNpStateCallbackA callback, void* userdata) { s32 PS4_SYSV_ABI sceNpRegisterStateCallbackA(OrbisNpStateCallbackA callback, void* userdata) {
static s32 id = 0; LOG_INFO(Lib_NpManager, "called, userdata = {}", userdata);
LOG_ERROR(Lib_NpManager, "(STUBBED) called, userdata = {}", userdata); return RegisterStateCallbackA(callback, userdata);
NpStateCb.func = callback; }
NpStateCb.userdata = userdata;
return id; s32 PS4_SYSV_ABI sceNpUnregisterStateCallbackA(s32 callback_id) {
LOG_INFO(Lib_NpManager, "called, callback_id = {}", callback_id);
return UnregisterStateCallbackAById(callback_id);
} }
struct NpReachabilityStateCallback { struct NpReachabilityStateCallback {
@ -781,11 +966,26 @@ NpReachabilityStateCallback NpReachabilityCb;
s32 PS4_SYSV_ABI sceNpRegisterNpReachabilityStateCallback(OrbisNpReachabilityStateCallback callback, s32 PS4_SYSV_ABI sceNpRegisterNpReachabilityStateCallback(OrbisNpReachabilityStateCallback callback,
void* userdata) { void* userdata) {
static s32 id = 0; if (callback == nullptr) {
LOG_ERROR(Lib_NpManager, "(STUBBED) called"); return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
if (NpReachabilityCb.func != nullptr) {
return ORBIS_NP_ERROR_CALLBACK_ALREADY_REGISTERED;
}
LOG_INFO(Lib_NpManager, "called");
NpReachabilityCb.func = callback; NpReachabilityCb.func = callback;
NpReachabilityCb.userdata = userdata; NpReachabilityCb.userdata = userdata;
return id; return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpUnregisterNpReachabilityStateCallback() {
if (NpReachabilityCb.func == nullptr) {
return ORBIS_NP_ERROR_CALLBACK_NOT_REGISTERED;
}
NpReachabilityCb = {};
return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceNpRegisterStateCallbackForToolkit(OrbisNpStateCallbackForNpToolkit callback, s32 PS4_SYSV_ABI sceNpRegisterStateCallbackForToolkit(OrbisNpStateCallbackForNpToolkit callback,
@ -861,8 +1061,16 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("JELHf4xPufo", "libSceNpManager", 1, "libSceNpManager", sceNpCheckCallbackForLib); LIB_FUNCTION("JELHf4xPufo", "libSceNpManager", 1, "libSceNpManager", sceNpCheckCallbackForLib);
LIB_FUNCTION("VfRSmPmj8Q8", "libSceNpManager", 1, "libSceNpManager", LIB_FUNCTION("VfRSmPmj8Q8", "libSceNpManager", 1, "libSceNpManager",
sceNpRegisterStateCallback); sceNpRegisterStateCallback);
LIB_FUNCTION("mjjTXh+NHWY", "libSceNpManager", 1, "libSceNpManager",
sceNpUnregisterStateCallback);
LIB_FUNCTION("qQJfO8HAiaY", "libSceNpManager", 1, "libSceNpManager",
sceNpRegisterStateCallbackA);
LIB_FUNCTION("M3wFXbYQtAA", "libSceNpManager", 1, "libSceNpManager",
sceNpUnregisterStateCallbackA);
LIB_FUNCTION("hw5KNqAAels", "libSceNpManager", 1, "libSceNpManager", LIB_FUNCTION("hw5KNqAAels", "libSceNpManager", 1, "libSceNpManager",
sceNpRegisterNpReachabilityStateCallback); sceNpRegisterNpReachabilityStateCallback);
LIB_FUNCTION("cRILAEvn+9M", "libSceNpManager", 1, "libSceNpManager",
sceNpUnregisterNpReachabilityStateCallback);
LIB_FUNCTION("JELHf4xPufo", "libSceNpManagerForToolkit", 1, "libSceNpManager", LIB_FUNCTION("JELHf4xPufo", "libSceNpManagerForToolkit", 1, "libSceNpManager",
sceNpCheckCallbackForLib); sceNpCheckCallbackForLib);
LIB_FUNCTION("0c7HbXRKUt4", "libSceNpManagerForToolkit", 1, "libSceNpManager", LIB_FUNCTION("0c7HbXRKUt4", "libSceNpManagerForToolkit", 1, "libSceNpManager",

View File

@ -106,6 +106,8 @@ struct OrbisNpCreateAsyncRequestParameter {
void RegisterNpCallback(std::string key, std::function<void()> cb); void RegisterNpCallback(std::string key, std::function<void()> cb);
void DeregisterNpCallback(std::string key); void DeregisterNpCallback(std::string key);
void NotifyNpStateFromUserServiceEvent(Libraries::UserService::OrbisUserServiceEventType event_type,
Libraries::UserService::OrbisUserServiceUserId user_id);
s32 PS4_SYSV_ABI sceNpGetNpId(Libraries::UserService::OrbisUserServiceUserId user_id, s32 PS4_SYSV_ABI sceNpGetNpId(Libraries::UserService::OrbisUserServiceUserId user_id,
OrbisNpId* np_id); OrbisNpId* np_id);

View File

@ -12,6 +12,9 @@
#include "input/controller.h" #include "input/controller.h"
#include "pad.h" #include "pad.h"
#include <algorithm>
#include <optional>
namespace Libraries::Pad { namespace Libraries::Pad {
using Input::GameController; using Input::GameController;
@ -39,6 +42,20 @@ static u64 pad_handle_counter = 1;
static std::unordered_map<HandleKey, s32, HandleKeyHash> pad_handle_map{}; static std::unordered_map<HandleKey, s32, HandleKeyHash> pad_handle_map{};
static std::unordered_map<s32, GameController*> handle_to_controller_map{}; static std::unordered_map<s32, GameController*> handle_to_controller_map{};
static std::optional<HandleKey> FindHandleKeyByHandle(s32 handle) {
for (const auto& [key, value] : pad_handle_map) {
if (value == handle) {
return key;
}
}
return std::nullopt;
}
static bool IsUnavailableSpecialHandle(const std::optional<HandleKey>& handle_key) {
return handle_key.has_value() && handle_key->device_class == ORBIS_PAD_PORT_TYPE_SPECIAL &&
!EmulatorSettings.IsUsingSpecialPad();
}
int PS4_SYSV_ABI scePadClose(s32 handle) { int PS4_SYSV_ABI scePadClose(s32 handle) {
LOG_WARNING(Lib_Pad, "called, handle: {}", handle); LOG_WARNING(Lib_Pad, "called, handle: {}", handle);
if (handle_to_controller_map.erase(handle) == 0) { if (handle_to_controller_map.erase(handle) == 0) {
@ -134,17 +151,28 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn
if (it == handle_to_controller_map.end()) { if (it == handle_to_controller_map.end()) {
return ORBIS_PAD_ERROR_INVALID_HANDLE; return ORBIS_PAD_ERROR_INVALID_HANDLE;
} }
const auto handle_key = FindHandleKeyByHandle(handle);
bool connected = false;
int connected_count = 0;
Input::State state{};
if (!IsUnavailableSpecialHandle(handle_key)) {
it->second->ReadState(&state, &connected, &connected_count);
}
std::memset(pInfo, 0, sizeof(OrbisPadControllerInformation));
pInfo->touchPadInfo.pixelDensity = 1; pInfo->touchPadInfo.pixelDensity = 1;
pInfo->touchPadInfo.resolution.x = 1920; pInfo->touchPadInfo.resolution.x = 1920;
pInfo->touchPadInfo.resolution.y = 950; pInfo->touchPadInfo.resolution.y = 950;
pInfo->stickInfo.deadZoneLeft = 1; pInfo->stickInfo.deadZoneLeft = 1;
pInfo->stickInfo.deadZoneRight = 1; pInfo->stickInfo.deadZoneRight = 1;
pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD; pInfo->connectionType = ORBIS_PAD_CONNECTION_TYPE_LOCAL;
pInfo->connectedCount = 1; pInfo->connectedCount = static_cast<u8>(std::clamp(connected_count, 0, 0xff));
pInfo->deviceClass = OrbisPadDeviceClass::Standard; pInfo->deviceClass = OrbisPadDeviceClass::Invalid;
pInfo->connected = true; pInfo->connected = connected;
if (EmulatorSettings.IsUsingSpecialPad()) { if (handle_key.has_value() && handle_key->device_class == ORBIS_PAD_PORT_TYPE_STANDARD) {
pInfo->connectionType = ORBIS_PAD_PORT_TYPE_SPECIAL; pInfo->deviceClass = OrbisPadDeviceClass::Standard;
} else if (handle_key.has_value() && handle_key->device_class == ORBIS_PAD_PORT_TYPE_SPECIAL &&
EmulatorSettings.IsUsingSpecialPad()) {
pInfo->deviceClass = (OrbisPadDeviceClass)EmulatorSettings.GetSpecialPadClass(); pInfo->deviceClass = (OrbisPadDeviceClass)EmulatorSettings.GetSpecialPadClass();
} }
return ORBIS_OK; return ORBIS_OK;
@ -168,16 +196,8 @@ int PS4_SYSV_ABI scePadGetDeviceInfo() {
int PS4_SYSV_ABI scePadGetExtControllerInformation(s32 handle, int PS4_SYSV_ABI scePadGetExtControllerInformation(s32 handle,
OrbisPadExtendedControllerInformation* pInfo) { OrbisPadExtendedControllerInformation* pInfo) {
LOG_INFO(Lib_Pad, "called handle = {}", handle); LOG_INFO(Lib_Pad, "called handle = {}", handle);
std::memset(pInfo, 0, sizeof(OrbisPadExtendedControllerInformation));
pInfo->padType1 = 0; return scePadGetControllerInformation(handle, &pInfo->base);
pInfo->padType2 = 0;
pInfo->capability = 0;
auto res = scePadGetControllerInformation(handle, &pInfo->base);
if (!EmulatorSettings.IsUsingSpecialPad()) {
pInfo->base.connected = false;
}
return res;
} }
int PS4_SYSV_ABI scePadGetExtensionUnitInfo() { int PS4_SYSV_ABI scePadGetExtensionUnitInfo() {

View File

@ -20,6 +20,8 @@ constexpr int ORBIS_PAD_PORT_TYPE_STANDARD = 0;
constexpr int ORBIS_PAD_PORT_TYPE_SPECIAL = 2; constexpr int ORBIS_PAD_PORT_TYPE_SPECIAL = 2;
constexpr int ORBIS_PAD_PORT_TYPE_REMOTE_CONTROL = 16; constexpr int ORBIS_PAD_PORT_TYPE_REMOTE_CONTROL = 16;
constexpr int ORBIS_PAD_CONNECTION_TYPE_LOCAL = 0;
enum class OrbisPadDeviceClass { enum class OrbisPadDeviceClass {
Invalid = -1, Invalid = -1,
Standard = 0, Standard = 0,

View File

@ -10,6 +10,7 @@
#include "common/singleton.h" #include "common/singleton.h"
#include "core/emulator_settings.h" #include "core/emulator_settings.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include "core/libraries/np/np_manager.h"
#include "core/libraries/system/userservice.h" #include "core/libraries/system/userservice.h"
#include "core/libraries/system/userservice_error.h" #include "core/libraries/system/userservice_error.h"
#include "core/tls.h" #include "core/tls.h"
@ -127,6 +128,7 @@ s32 PS4_SYSV_ABI sceUserServiceGetEvent(OrbisUserServiceEvent* event) {
event->event = temp.event; event->event = temp.event;
event->userId = temp.userId; event->userId = temp.userId;
user_service_event_queue.pop(); user_service_event_queue.pop();
Libraries::Np::NpManager::NotifyNpStateFromUserServiceEvent(temp.event, temp.userId);
LOG_INFO(Lib_UserService, "Event processed by the game: {} {}", (u8)temp.event, LOG_INFO(Lib_UserService, "Event processed by the game: {} {}", (u8)temp.event,
temp.userId); temp.userId);
return ORBIS_OK; return ORBIS_OK;

View File

@ -195,11 +195,18 @@ LoggedInUsers UserManager::GetLoggedInUsers() const {
using namespace Libraries::UserService; using namespace Libraries::UserService;
void UserManager::LoginUser(User* u, s32 player_index) { void UserManager::LoginUser(User* u, s32 player_index) {
if (!u) { if (!u || player_index < 1 || player_index > static_cast<s32>(logged_in_users.size())) {
return; return;
} }
for (auto& logged_in_user : logged_in_users) {
if (logged_in_user == u) {
logged_in_user = nullptr;
}
}
u->logged_in = true; u->logged_in = true;
// u->player_index = player_index; u->player_index = player_index;
AddUserServiceEvent({OrbisUserServiceEventType::Login, u->user_id}); AddUserServiceEvent({OrbisUserServiceEventType::Login, u->user_id});
logged_in_users[player_index - 1] = u; logged_in_users[player_index - 1] = u;
} }
@ -210,7 +217,9 @@ void UserManager::LogoutUser(User* u) {
} }
u->logged_in = false; u->logged_in = false;
AddUserServiceEvent({OrbisUserServiceEventType::Logout, u->user_id}); AddUserServiceEvent({OrbisUserServiceEventType::Logout, u->user_id});
logged_in_users[u->player_index - 1] = {}; if (u->player_index >= 1 && u->player_index <= static_cast<s32>(logged_in_users.size())) {
logged_in_users[u->player_index - 1] = {};
}
} }
bool UserManager::Save() const { bool UserManager::Save() const {