Added pTo and pFrom to OrbisNpPeerAddressA support (#5108)

* Added pTo and pFrom to OrbisNpPeerAddressA support

* all mighty clang

* improved OrbisNpSessionInvitationEventParam

* changed diagnostic to debug

* all mighty clang again
This commit is contained in:
georgemoralis
2026-09-26 07:56:55 +03:00
committed by GitHub
parent 7e0c81118a
commit da9fc3a3aa
9 changed files with 120 additions and 22 deletions
@@ -117,12 +117,18 @@ struct OrbisNpSessionInvitationEventParam {
OrbisNpInvitationId invitationId;
OrbisNpSessionInvitationEventFlag flag;
char padding[4];
Libraries::Np::OrbisNpOnlineId onlineId;
Libraries::Np::OrbisNpOnlineId onlineId; // accepting (local) user
Libraries::UserService::OrbisUserServiceUserId userId; // accepting (local) user
Libraries::Np::OrbisNpOnlineId referralOnlineId; // inviter
Libraries::Np::OrbisNpAccountId referralAccountId; // inviter
};
static_assert(sizeof(OrbisNpSessionInvitationEventParam) == 0x8c,
"SESSION_INVITATION param must be 140 bytes");
static_assert(offsetof(OrbisNpSessionInvitationEventParam, flag) == 112);
static_assert(offsetof(OrbisNpSessionInvitationEventParam, onlineId) == 120);
static_assert(offsetof(OrbisNpSessionInvitationEventParam, userId) == 140);
static_assert(offsetof(OrbisNpSessionInvitationEventParam, referralOnlineId) == 144);
static_assert(offsetof(OrbisNpSessionInvitationEventParam, referralAccountId) == 168);
static_assert(sizeof(OrbisNpSessionInvitationEventParam) == 176,
"SESSION_INVITATION param must be 176 bytes");
union OrbisInvitationDialogDataParam {
struct {
+41 -8
View File
@@ -611,9 +611,11 @@ bool NpHandler::SendSessionInvitation(s32 user_id, const std::string& session_id
return true;
}
void NpHandler::PostSessionInvitationEvent(const std::string& session_id,
void NpHandler::PostSessionInvitationEvent(s32 user_id, const std::string& session_id,
const std::string& invitation_id,
const std::string& accepter_online_id) {
const std::string& accepter_online_id,
const std::string& inviter_online_id,
OrbisNpAccountId inviter_account_id) {
using Libraries::InvitationDialog::ORBIS_NP_SESSION_INVITATION_EVENT_FLAG_INVITATION;
using Libraries::InvitationDialog::OrbisNpSessionInvitationEventParam;
@@ -630,12 +632,19 @@ void NpHandler::PostSessionInvitationEvent(const std::string& session_id,
} else {
param->flag = 0; // join from session info (no invitation id in the push)
}
std::strncpy(param->onlineId.data, accepter_online_id.c_str(),
sizeof(param->onlineId.data) - 1);
// data[16] is followed by its own 'term' byte, so a full 16-char online id must not be cut.
std::strncpy(param->onlineId.data, accepter_online_id.c_str(), sizeof(param->onlineId.data));
param->userId = user_id;
std::strncpy(param->referralOnlineId.data, inviter_online_id.c_str(),
sizeof(param->referralOnlineId.data));
param->referralAccountId = inviter_account_id;
Libraries::SystemService::PushSystemServiceEvent(event);
LOG_INFO(NpHandler, "Posted SESSION_INVITATION session='{}' flag={} onlineId='{}'", session_id,
param->flag, accepter_online_id);
LOG_INFO(NpHandler,
"Posted SESSION_INVITATION user_id={} session='{}' invitation='{}' flag={} "
"onlineId='{}' referral='{}'({})",
user_id, session_id, invitation_id, param->flag, accepter_online_id, inviter_online_id,
inviter_account_id);
}
std::vector<NpHandler::PendingInvitation> NpHandler::GetPendingInvitations(s32 user_id) const {
@@ -685,7 +694,8 @@ bool NpHandler::AcceptSessionInvitation(s32 user_id, const std::string& invitati
}
// Raise the join event now that the user has explicitly accepted (via the RECV dialog or the
// emulator's system-UI equivalent).
PostSessionInvitationEvent(inv.session_id, invitation_id, inv.to_npid);
PostSessionInvitationEvent(user_id, inv.session_id, invitation_id, inv.to_npid, inv.from_npid,
inv.from_account_id);
// Consume it server-side (PUT usedFlag=true).
const std::string base_url = EmulatorSettings.GetShadNetWebApiServer();
const std::string token = GetBearerToken(user_id);
@@ -845,6 +855,27 @@ void NpHandler::OnWebApiPushEvent(s32 user_id, const ShadNet::NotifyWebApiPushEv
SetNpOnlineId(ev.toOnlineId, n.toNpid);
}
ev.extdData = n.extdData; // extended-data (key,value) pairs -> dispatched as pExtdData
// Account ids for pFrom/pTo.
ev.toAccountId = n.toAccountId != 0 ? n.toAccountId : GetAccountId(user_id);
ev.fromAccountId = n.fromAccountId;
if (ev.fromAccountId == 0) {
for (const auto& kv : n.extdData) {
if (kv.first == "fromAccountId") {
ev.fromAccountId = std::strtoull(kv.second.c_str(), nullptr, 10);
break;
}
}
}
if (ev.toAccountId != 0) {
ev.hasTo = true;
if (n.toNpid.empty()) {
// e.g. friendlist events carry no toNpid; the recipient is still us.
std::lock_guard lock(m_mutex_clients);
if (const auto it = m_np_ids.find(user_id); it != m_np_ids.end()) {
std::memcpy(ev.toOnlineId.data, it->second.handle.data, sizeof(ev.toOnlineId.data));
}
}
}
NpWebApi::EnqueuePushEvent(ev);
// Also surface a SESSION_INVITATION system-service event for titles that watch it instead of
@@ -870,7 +901,9 @@ void NpHandler::OnWebApiPushEvent(s32 user_id, const ShadNet::NotifyWebApiPushEv
return p.invitation_id == invitation_id;
}),
v.end());
v.push_back({session_id, invitation_id, n.fromNpid, n.toNpid, valid_until});
PendingInvitation inv{session_id, invitation_id, n.fromNpid, n.toNpid, valid_until};
inv.from_account_id = ev.fromAccountId;
v.push_back(std::move(inv));
}
// ORBIS_SYSTEM_SERVICE_EVENT_SESSION_INVITATION is a *join* event: it
// fires only after the user explicitly accepts, via the game-opened invitation dialog
+9 -5
View File
@@ -82,16 +82,20 @@ public:
// Raises ORBIS_SYSTEM_SERVICE_EVENT_SESSION_INVITATION (0x10000002) so titles that watch the
// system-service event learn about the invite.
void PostSessionInvitationEvent(const std::string& session_id, const std::string& invitation_id,
const std::string& accepter_online_id);
void PostSessionInvitationEvent(s32 user_id, const std::string& session_id,
const std::string& invitation_id,
const std::string& accepter_online_id,
const std::string& inviter_online_id,
OrbisNpAccountId inviter_account_id);
// A session invitation surfaced from a shadNet push, stashed until the user acts on it.
struct PendingInvitation {
std::string session_id;
std::string invitation_id;
std::string from_npid; // sender (for display)
std::string to_npid; // local recipient / accepter
int64_t valid_until = 0; // ms since epoch; 0 = never expires
std::string from_npid; // sender (for display)
std::string to_npid; // local recipient / accepter
int64_t valid_until = 0; // ms since epoch; 0 = never expires
OrbisNpAccountId from_account_id = 0; // sender account id (0 = unknown)
};
// Pending invitations stashed for a local user (newest last), populated on arrival.
std::vector<PendingInvitation> GetPendingInvitations(s32 user_id) const;
@@ -142,6 +142,9 @@ struct PushEventInput {
bool hasFrom = false;
OrbisNpOnlineId toOnlineId{};
bool hasTo = false;
// Account ids for the OrbisNpPeerAddressA pFrom/pTo of extended push callbacks.
OrbisNpAccountId fromAccountId = 0;
OrbisNpAccountId toAccountId = 0;
std::vector<std::pair<std::string, std::string>> extdData; // (key,value) for extd push
};
void EnqueuePushEvent(const PushEventInput& ev);
@@ -1592,13 +1592,19 @@ s32 createExtendedPushEventFilterInternal(
sizeof(OrbisNpWebApiExtdPushEventFilterParameter));
LOG_INFO(Lib_NpWebApi, " filterParam[{}] dataType='{}' extdKeys={}", param_idx,
copy.dataType.val, copy.extdDataKeyNum); // debug
std::vector<std::string> keys;
if (copy.pExtdDataKey != nullptr) {
for (u64 k = 0; k < copy.extdDataKeyNum; k++) {
LOG_INFO(Lib_NpWebApi, " extdDataKey[{}]='{}'", k,
copy.pExtdDataKey[k].val); // debug
keys.emplace_back(
copy.pExtdDataKey[k].val,
strnlen(copy.pExtdDataKey[k].val, sizeof(copy.pExtdDataKey[k].val)));
}
}
copy.pExtdDataKey = nullptr; // guest memory, see extdDataKeys
filter->filterParams.emplace_back(copy);
filter->extdDataKeys.emplace_back(std::move(keys));
// TODO: Every parameter is registered with an extended data filter through
// sceNpPushRegisterExtendedDataFilter
}
@@ -2128,6 +2134,14 @@ void DrainPushEvents() {
std::snprintf(dt.val, sizeof(dt.val), "%s", ev.dataType.c_str());
const OrbisNpOnlineId* from_p = ev.hasFrom ? &ev.fromOnlineId : nullptr;
const OrbisNpOnlineId* to_p = ev.hasTo ? &ev.toOnlineId : nullptr;
OrbisNpPeerAddressA to_addr{};
OrbisNpPeerAddressA from_addr{};
to_addr.accountId = ev.toAccountId;
to_addr.platform = OrbisNpPlatformType::PS4;
from_addr.accountId = ev.fromAccountId;
from_addr.platform = OrbisNpPlatformType::PS4;
const OrbisNpPeerAddressA* to_addr_p = ev.toAccountId != 0 ? &to_addr : nullptr;
const OrbisNpPeerAddressA* from_addr_p = ev.fromAccountId != 0 ? &from_addr : nullptr;
std::scoped_lock gl{g_global_mutex};
for (auto& [libId, context] : g_contexts) {
@@ -2162,9 +2176,29 @@ void DrainPushEvents() {
if (!filterMatches(flt, ev.npServiceName, ev_has_service, ev.dataType)) {
continue;
}
// Only hand the title the extended-data keys its filter asked for for this
// dataType (SDK behaviour). This also keeps server-internal pairs such as
// fromAccountId/toAccountId/fromPlatform out of pExtdData.
const std::vector<std::string>* wanted_keys = nullptr;
for (size_t i = 0; i < flt->filterParams.size(); ++i) {
if (ev.dataType == flt->filterParams[i].dataType.val) {
if (i < flt->extdDataKeys.size()) {
wanted_keys = &flt->extdDataKeys[i];
}
break;
}
}
auto key_requested = [wanted_keys](const std::string& k) {
return wanted_keys != nullptr &&
std::find(wanted_keys->begin(), wanted_keys->end(), k) !=
wanted_keys->end();
};
std::vector<OrbisNpWebApiExtdPushEventExtdData> exarr;
exarr.reserve(ev.extdData.size());
for (auto& [k, v] : ev.extdData) {
if (!key_requested(k)) {
continue;
}
OrbisNpWebApiExtdPushEventExtdData e{};
std::snprintf(e.extdDataKey.val, sizeof(e.extdDataKey.val), "%s",
k.c_str());
@@ -2186,8 +2220,9 @@ void DrainPushEvents() {
title_user_ctx_id, cbId,
ev.dataType); // debug confirm the listener callback fires. to be removed
reinterpret_cast<ExtdCbA>(raw)(
title_user_ctx_id, cbId, svc, flt->npServiceLabel, nullptr, to_p, nullptr,
from_p, &dt, ext_data, ev.data.size(), ext_arr, exarr.size(), cb->pUserArg);
title_user_ctx_id, cbId, svc, flt->npServiceLabel, to_addr_p, to_p,
from_addr_p, from_p, &dt, ext_data, ev.data.size(), ext_arr, exarr.size(),
cb->pUserArg);
}
// Service push
@@ -129,6 +129,7 @@ struct OrbisNpWebApiExtendedPushEventFilter {
s32 filterId;
bool internal;
std::vector<OrbisNpWebApiExtdPushEventFilterParameter> filterParams;
std::vector<std::vector<std::string>> extdDataKeys;
std::string npServiceName;
OrbisNpServiceLabel npServiceLabel;
OrbisNpWebApiContext* parentContext;
@@ -2004,6 +2004,8 @@ s32 PS4_SYSV_ABI sceSystemServiceReceiveEvent(OrbisSystemServiceEvent* event) {
*event = g_event_queue.front();
g_event_queue.pop();
LOG_DEBUG(Lib_SystemService, "delivered event type={:#x} ({} still queued)",
static_cast<u32>(event->event_type), g_event_queue.size());
return ORBIS_OK;
}
+15 -4
View File
@@ -978,20 +978,31 @@ void ShadNetClient::HandleNotification(u16 cmd_raw, const std::vector<u8>& paylo
if (off + 4 <= static_cast<int>(payload.size())) {
const u32 count = GetLE32(payload.data() + off);
off += 4;
bool extd_complete = count <= 256;
for (u32 i = 0; i < count && i < 256; ++i) {
if (off + 4 > static_cast<int>(payload.size()))
if (off + 4 > static_cast<int>(payload.size())) {
extd_complete = false;
break;
}
std::string key = ExtractBlob(payload, off);
off += 4 + static_cast<int>(key.size());
if (off + 4 > static_cast<int>(payload.size()))
if (off + 4 > static_cast<int>(payload.size())) {
extd_complete = false;
break;
}
std::string val = ExtractBlob(payload, off);
off += 4 + static_cast<int>(val.size());
n.extdData.emplace_back(std::move(key), std::move(val));
}
if (extd_complete && off + 16 <= static_cast<int>(payload.size())) {
n.fromAccountId = GetLE64(payload.data() + off);
n.toAccountId = GetLE64(payload.data() + off + 8);
}
}
LOG_INFO(ShadNet, "WebApiPushEvent svc='{}' type='{}' from='{}' bytes={} extd={}",
n.npServiceName, n.dataType, n.fromNpid, n.data.size(), n.extdData.size());
LOG_INFO(ShadNet,
"WebApiPushEvent svc='{}' type='{}' from='{}'({}) to='{}'({}) bytes={} extd={}",
n.npServiceName, n.dataType, n.fromNpid, n.fromAccountId, n.toNpid, n.toAccountId,
n.data.size(), n.extdData.size());
if (onWebApiPushEvent)
onWebApiPushEvent(n);
break;
+3
View File
@@ -222,6 +222,9 @@ struct NotifyWebApiPushEvent {
// Optional extended-data (key,value) pairs (e.g. friendlist trigger/additionalTrigger,
// presence gameStatus/gameData). Empty when the server sends none / is older.
std::vector<std::pair<std::string, std::string>> extdData;
// Optional trailing account ids (server >= push-account-ids). 0 = unknown / not sent.
u64 fromAccountId = 0;
u64 toAccountId = 0;
};
struct MatchingBinAttr {