Matching2 P6: Public API additions, Stub Additions, networking layer stubs, more type additions, more struct additions, matching2signaling work. (#4654)

This commit is contained in:
metr1k-91 2026-07-01 10:42:54 -05:00 committed by GitHub
parent b6026cc65d
commit 9756843860
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 1419 additions and 42 deletions

View File

@ -85,4 +85,44 @@ int P2PSocket::fstat(Libraries::Kernel::OrbisKernelStat* stat) {
return 0;
}
u16 GetP2PConfiguredPort() {
return 0;
}
u32 GetP2PAdvertisedAddr() {
return 0;
}
bool EnsureP2PTransport() {
return false;
}
bool P2PTransportIsReady() {
return false;
}
int P2PSignalingSendTo(const void* data, u32 len, u32 dest_addr, u16 dest_port) {
return -1;
}
int P2PSignalingRecvFrom(void* buf, u32 len, u32* from_addr, u16* from_port) {
return -1;
}
int P2PControlSendTo(const void* data, u32 len, u32 dest_addr, u16 dest_port) {
return -1;
}
int P2PControlRecvFrom(void* buf, u32 len, u32* from_addr, u16* from_port) {
return -1;
}
int P2PMatching2SendTo(const void* data, u32 len, u32 dest_addr, u16 dest_port) {
return -1;
}
int P2PMatching2RecvFrom(void* buf, u32 len, u32* from_addr, u16* from_port) {
return -1;
}
} // namespace Libraries::Net

View File

@ -179,4 +179,15 @@ struct UnixSocket : public Socket {
}
};
u16 GetP2PConfiguredPort();
u32 GetP2PAdvertisedAddr();
bool EnsureP2PTransport();
bool P2PTransportIsReady();
int P2PSignalingSendTo(const void* data, u32 len, u32 dest_addr, u16 dest_port);
int P2PSignalingRecvFrom(void* buf, u32 len, u32* from_addr, u16* from_port);
int P2PControlSendTo(const void* data, u32 len, u32 dest_addr, u16 dest_port);
int P2PControlRecvFrom(void* buf, u32 len, u32* from_addr, u16* from_port);
int P2PMatching2SendTo(const void* data, u32 len, u32 dest_addr, u16 dest_port);
int P2PMatching2RecvFrom(void* buf, u32 len, u32* from_addr, u16* from_port);
} // namespace Libraries::Net

View File

@ -8,6 +8,7 @@
#include "common/elf_info.h"
#include "common/logging/log.h"
#include "core/emulator_settings.h"
#include "core/libraries/network/net_upnp.h"
#include "core/libraries/np/np_error.h"
#include "core/libraries/np/np_manager.h"
#include "core/libraries/np/np_matching2/np_matching2_mm.h"
@ -177,6 +178,9 @@ bool NpHandler::ConnectUser(s32 user_id, const std::string& host, u16 port, cons
// Seed the current Appear-Offline preference so the login packet carries it (the send
// is suppressed pre-auth; it just caches on the client).
client->SetAppearOffline(m_appear_offline.load());
if (EmulatorSettings.IsUPnPEnabled()) {
Net::UPnPClient::Instance().Start();
}
client->Start(host, port, npid, password, token);
const ShadNet::ShadNetState conn_state = client->WaitForConnection();

View File

@ -2,11 +2,13 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include <cstring>
#include "common/logging/log.h"
#include "core/emulator_settings.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "core/libraries/network/net.h"
#include "core/libraries/np/np_handler.h"
#include "core/libraries/np/np_manager.h"
#include "core/libraries/np/np_matching2/np_matching2.h"
@ -57,13 +59,185 @@ int PS4_SYSV_ABI sceNpMatching2CreateContextA(const OrbisNpMatching2CreateContex
return ContextManager::Instance().CreateContext(&np_id, param->serviceLabel, ctxId);
}
static void StoreRequestCallback(ContextObject* ctx, OrbisNpMatching2RequestOptParam* requestOpt) {
static void StoreRequestCallback(ContextObject* ctx,
const OrbisNpMatching2RequestOptParam* requestOpt) {
if (requestOpt && requestOpt->callback) {
ctx->default_request_callback = requestOpt->callback;
ctx->default_request_callback_arg = requestOpt->arg;
}
}
static u32 GetRoomPingUs(const ContextObject& ctx, OrbisNpMatching2RoomId roomId) {
const auto room_it = ctx.room_cache.find(roomId);
if (room_it == ctx.room_cache.end()) {
return 0;
}
u64 total_ping = 0;
u32 ping_count = 0;
for (const auto& [member_id, member] : room_it->second.members) {
if (member_id == ctx.my_member_id) {
continue;
}
const auto peer_it = ctx.peers.find(member_id);
if (peer_it == ctx.peers.end() || peer_it->second.ping_us == 0) {
continue;
}
total_ping += peer_it->second.ping_us;
++ping_count;
}
return ping_count == 0 ? 0 : static_cast<u32>(total_ping / ping_count);
}
static void* BuildSignalingGetPingInfoPayload(ContextObject& ctx, OrbisNpMatching2RoomId roomId) {
CallbackPayload& p = ctx.request_payload;
p.Reset();
const auto room_it = ctx.room_cache.find(roomId);
p.ping_info_response = std::make_unique<OrbisNpMatching2SignalingGetPingInfoResponse>();
auto& out = *p.ping_info_response;
out = {};
if (room_it != ctx.room_cache.end()) {
out.serverId = room_it->second.server_id;
out.worldId = room_it->second.world_id;
out.roomId = room_it->second.room_id;
} else {
out.serverId = ctx.server_id;
out.worldId = ctx.world_id;
out.roomId = roomId;
}
out.rtt = GetRoomPingUs(ctx, roomId);
p.request_data = p.ping_info_response.get();
return p.request_data;
}
static Libraries::Np::OrbisNpId MakePeerNpId(const PeerInfo* peer, const MemberCache* member) {
Libraries::Np::OrbisNpId npid{};
if (member) {
npid = member->np_id;
} else if (peer) {
npid.handle = peer->online_id;
}
return npid;
}
static u64 StableAccountIdFromNpId(const Libraries::Np::OrbisNpId& npid) {
u64 hash = 1469598103934665603ULL;
for (const char c : npid.handle.data) {
if (c == '\0') {
break;
}
hash ^= static_cast<u8>(c);
hash *= 1099511628211ULL;
}
return hash == 0 ? 1 : hash;
}
static Libraries::Np::OrbisNpPeerAddressA MakePeerAddressA(const PeerInfo* peer,
const MemberCache* member) {
const Libraries::Np::OrbisNpId npid = MakePeerNpId(peer, member);
Libraries::Np::OrbisNpPeerAddressA addr{};
addr.accountId = StableAccountIdFromNpId(npid);
addr.platform = Libraries::Np::OrbisNpPlatformType::PS4;
return addr;
}
static s32 FillMatching2ConnectionInfo(const ContextObject& ctx, OrbisNpMatching2RoomId roomId,
OrbisNpMatching2RoomMemberId memberId, u32 infoType,
void* connInfo, bool a_variant) {
if (!connInfo) {
LOG_ERROR(Lib_NpMatching2, "connInfo null");
return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
const auto room_it = ctx.room_cache.find(roomId);
if (room_it == ctx.room_cache.end()) {
LOG_INFO(Lib_NpMatching2, "room={} not cached for connection info", roomId);
if (a_variant) {
*static_cast<OrbisNpMatching2SignalingConnectionInfoA*>(connInfo) = {};
} else {
*static_cast<OrbisNpMatching2SignalingConnectionInfo*>(connInfo) = {};
}
return ORBIS_OK;
}
const auto member_it = room_it->second.members.find(memberId);
const MemberCache* member =
member_it != room_it->second.members.end() ? &member_it->second : nullptr;
const auto peer_it = ctx.peers.find(memberId);
const PeerInfo* peer = peer_it != ctx.peers.end() ? &peer_it->second : nullptr;
if (a_variant) {
auto* out = static_cast<OrbisNpMatching2SignalingConnectionInfoA*>(connInfo);
*out = {};
switch (infoType) {
case ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_RTT:
out->rtt = peer ? peer->ping_us : 0;
break;
case ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_BANDWIDTH:
out->bandwidth = 0;
break;
case ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_PEER_ADDR:
case ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_MAPPED_ADDR:
if (peer) {
out->address.addr = peer->addr;
out->address.port = peer->port;
} else if (member) {
out->address.addr = member->addr;
out->address.port = member->port;
}
break;
case ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_PACKET_LOSS:
out->packetLoss = 0;
break;
case ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_PEER_ADDRESS_A:
out->peerAddrA = MakePeerAddressA(peer, member);
break;
default:
LOG_WARNING(Lib_NpMatching2, "unsupported connection info A type={}", infoType);
return ORBIS_NP_MATCHING2_SIGNALING_ERROR_INVALID_ARGUMENT;
}
} else {
auto* out = static_cast<OrbisNpMatching2SignalingConnectionInfo*>(connInfo);
*out = {};
switch (infoType) {
case ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_RTT:
out->rtt = peer ? peer->ping_us : 0;
break;
case ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_BANDWIDTH:
out->bandwidth = 0;
break;
case ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_PEER_NP_ID:
case ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_PEER_NPID:
out->npId = MakePeerNpId(peer, member);
break;
case ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_PEER_ADDR:
case ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_MAPPED_ADDR:
if (peer) {
out->address.addr = peer->addr;
out->address.port = peer->port;
} else if (member) {
out->address.addr = member->addr;
out->address.port = member->port;
}
break;
case ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_PACKET_LOSS:
out->packetLoss = 0;
break;
default:
LOG_WARNING(Lib_NpMatching2, "unsupported connection info type={}", infoType);
return ORBIS_NP_MATCHING2_SIGNALING_ERROR_INVALID_ARGUMENT;
}
}
LOG_INFO(Lib_NpMatching2, "connection info{}: ctx={} room={} member={} type={} rtt={}",
a_variant ? "A" : "", ctx.ctx_id, roomId, memberId, infoType,
peer ? peer->ping_us : 0);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2CreateJoinRoom(OrbisNpMatching2ContextId ctxId,
OrbisNpMatching2CreateJoinRoomRequest* request,
OrbisNpMatching2RequestOptParam* requestOpt,
@ -479,9 +653,9 @@ int PS4_SYSV_ABI sceNpMatching2SendRoomMessage(OrbisNpMatching2ContextId ctxId,
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2SetRoomDataExternal(OrbisNpMatching2ContextId ctxId, void* request,
OrbisNpMatching2RequestOptParam* requestOpt,
OrbisNpMatching2RequestId* requestId) {
int PS4_SYSV_ABI sceNpMatching2SetRoomDataExternal(
OrbisNpMatching2ContextId ctxId, OrbisNpMatching2SetRoomDataExternalRequest* request,
OrbisNpMatching2RequestOptParam* requestOpt, OrbisNpMatching2RequestId* requestId) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt));
if (!g_initialized) {
@ -500,14 +674,15 @@ int PS4_SYSV_ABI sceNpMatching2SetRoomDataExternal(OrbisNpMatching2ContextId ctx
}
StoreRequestCallback(ctx, requestOpt);
*requestId = AllocRequestId();
LOG_WARNING(Lib_NpMatching2, "not implemented");
const OrbisNpMatching2RequestId reqId = AllocRequestId();
*requestId = reqId;
MmSetRoomDataExternal(ctxId, reqId, *request);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2SetRoomDataInternal(OrbisNpMatching2ContextId ctxId, void* request,
OrbisNpMatching2RequestOptParam* requestOpt,
OrbisNpMatching2RequestId* requestId) {
int PS4_SYSV_ABI sceNpMatching2SetRoomDataInternal(
OrbisNpMatching2ContextId ctxId, OrbisNpMatching2SetRoomDataInternalRequest* request,
OrbisNpMatching2RequestOptParam* requestOpt, OrbisNpMatching2RequestId* requestId) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt));
if (!g_initialized) {
@ -526,8 +701,415 @@ int PS4_SYSV_ABI sceNpMatching2SetRoomDataInternal(OrbisNpMatching2ContextId ctx
}
StoreRequestCallback(ctx, requestOpt);
*requestId = AllocRequestId();
LOG_WARNING(Lib_NpMatching2, "not implemented");
const OrbisNpMatching2RequestId reqId = AllocRequestId();
*requestId = reqId;
MmSetRoomDataInternal(ctxId, reqId, *request);
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpMatching2SignalingGetConnectionStatus(OrbisNpMatching2ContextId ctxId,
OrbisNpMatching2RoomId roomId,
OrbisNpMatching2RoomMemberId memberId,
s32* connStatus, void* peerAddr,
u16* peerPort) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}, roomId = {}, memberId = {}", ctxId, roomId,
memberId);
if (!g_initialized) {
LOG_ERROR(Lib_NpMatching2, "not initialized");
return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
ContextObject* ctx = ContextManager::Instance().Get(ctxId);
if (!ctx) {
LOG_ERROR(Lib_NpMatching2, "invalid context id");
return ORBIS_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
}
constexpr s32 kInactive = 0;
constexpr s32 kPending = 1;
constexpr s32 kActive = 2;
const auto room_it = ctx->room_cache.find(roomId);
const bool in_room = room_it != ctx->room_cache.end() &&
room_it->second.members.find(memberId) != room_it->second.members.end();
if (!in_room) {
if (connStatus) {
*connStatus = kInactive;
}
LOG_INFO(Lib_NpMatching2, "member={} is not in room={}, status=inactive", memberId, roomId);
return ORBIS_OK;
}
auto peer_it = ctx->peers.find(memberId);
if (peer_it == ctx->peers.end()) {
PeerInfo pi{};
pi.member_id = memberId;
pi.status = kPending;
const auto member_it = room_it->second.members.find(memberId);
if (member_it != room_it->second.members.end()) {
pi.addr = member_it->second.addr;
pi.port = member_it->second.port;
std::strncpy(pi.online_id.data, member_it->second.np_id.handle.data,
sizeof(pi.online_id.data) - 1);
}
peer_it = ctx->peers.emplace(memberId, pi).first;
}
const s32 status = peer_it->second.status == kActive ? kActive : kPending;
if (connStatus) {
*connStatus = status;
}
if (peerAddr) {
*reinterpret_cast<u32*>(peerAddr) = peer_it->second.addr;
}
if (peerPort) {
*peerPort = peer_it->second.port;
}
LOG_INFO(Lib_NpMatching2, "member={} is in room={}, status={} addr={:#x}:{}", memberId, roomId,
status, peer_it->second.addr, Libraries::Net::sceNetNtohs(peer_it->second.port));
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2AbortContextStart(OrbisNpMatching2ContextId ctxId) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}", ctxId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2DestroyContext(OrbisNpMatching2ContextId ctxId) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}", ctxId);
if (!g_initialized) {
LOG_ERROR(Lib_NpMatching2, "not initialized");
return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
if (!ContextManager::Instance().Destroy(ctxId)) {
LOG_ERROR(Lib_NpMatching2, "invalid context id");
return ORBIS_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
}
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2RegisterLobbyMessageCallback(
OrbisNpMatching2ContextId ctxId, OrbisNpMatching2LobbyMessageCallback callback,
void* userdata) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}, userdata = {}", ctxId, userdata);
if (!g_initialized) {
LOG_ERROR(Lib_NpMatching2, "not initialized");
return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
ContextObject* ctx = ContextManager::Instance().Get(ctxId);
if (!ctx) {
LOG_ERROR(Lib_NpMatching2, "invalid context id");
return ORBIS_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
}
ctx->lobby_message_callback = callback;
ctx->lobby_message_callback_arg = userdata;
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2RegisterRoomMessageCallback(
OrbisNpMatching2ContextId ctxId, OrbisNpMatching2RoomMessageCallback callback, void* userdata) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}, userdata = {}", ctxId, userdata);
if (!g_initialized) {
LOG_ERROR(Lib_NpMatching2, "not initialized");
return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
ContextObject* ctx = ContextManager::Instance().Get(ctxId);
if (!ctx) {
LOG_ERROR(Lib_NpMatching2, "invalid context id");
return ORBIS_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
}
ctx->room_message_callback = callback;
ctx->room_message_callback_arg = userdata;
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2GetMemoryInfo() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2GetSslMemoryInfo() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2GetRoomMemberIdListLocal() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2GetRoomPasswordLocal() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2GetSignalingOptParamLocal() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2GetLobbyInfoList() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2GetLobbyMemberDataInternal() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2GetLobbyMemberDataInternalList() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2GetRoomDataExternalList() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2GetRoomDataInternal(
OrbisNpMatching2ContextId ctxId, OrbisNpMatching2GetRoomDataInternalRequest* request,
OrbisNpMatching2RequestOptParam* requestOpt, OrbisNpMatching2RequestId* requestId) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt));
if (!g_initialized) {
LOG_ERROR(Lib_NpMatching2, "not initialized");
return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
if (!request || !requestId) {
LOG_ERROR(Lib_NpMatching2, "request or requestId null");
return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
ContextObject* ctx = ContextManager::Instance().Get(ctxId);
if (!ctx) {
LOG_ERROR(Lib_NpMatching2, "invalid context id");
return ORBIS_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
}
StoreRequestCallback(ctx, requestOpt);
const OrbisNpMatching2RequestId reqId = AllocRequestId();
*requestId = reqId;
void* request_data = BuildGetRoomDataInternalPayload(*ctx, request->roomId);
PendingEvent ev{};
ev.type = PendingEvent::REQUEST_CB;
ev.ctx_id = ctxId;
ev.fire_at = std::chrono::steady_clock::now();
ev.req_id = reqId;
ev.req_event = ORBIS_NP_MATCHING2_REQUEST_EVENT_GET_ROOM_DATA_INTERNAL;
ev.error_code = 0;
ev.request_cb = ctx->default_request_callback;
ev.request_cb_arg = ctx->default_request_callback_arg;
ev.request_data = request_data;
ScheduleEvent(std::move(ev));
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2GetRoomMemberDataExternalList() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2GetRoomMemberDataInternal() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2GetUserInfoListA() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2GetUserInfoList() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2GrantRoomOwner(OrbisNpMatching2ContextId ctxId, void* reqParam,
void* optParam, s32* reqId) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}", ctxId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2JoinLobby(OrbisNpMatching2ContextId ctxId, void* reqParam,
void* optParam, s32* reqId) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}", ctxId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2JoinRoomA(OrbisNpMatching2ContextId ctxId, void* reqParam,
void* optParam, s32* reqId) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}", ctxId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2KickoutRoomMember(OrbisNpMatching2ContextId ctxId,
OrbisNpMatching2KickoutRoomMemberRequest* request,
OrbisNpMatching2RequestOptParam* requestOpt,
OrbisNpMatching2RequestId* requestId) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt));
if (!g_initialized) {
LOG_ERROR(Lib_NpMatching2, "not initialized");
return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
if (!request || !requestId) {
LOG_ERROR(Lib_NpMatching2, "request or requestId null");
return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
ContextObject* ctx = ContextManager::Instance().Get(ctxId);
if (!ctx) {
LOG_ERROR(Lib_NpMatching2, "invalid context id");
return ORBIS_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
}
StoreRequestCallback(ctx, requestOpt);
const OrbisNpMatching2RequestId reqId = AllocRequestId();
*requestId = reqId;
MmKickoutRoomMember(ctxId, reqId, *request);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2LeaveLobby() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2SendLobbyChatMessage() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2SendRoomChatMessage(OrbisNpMatching2ContextId ctxId, void* reqParam,
void* optParam, s32* reqId) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}", ctxId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2SetLobbyMemberDataInternal() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2SetRoomMemberDataInternal() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2SetSignalingOptParam() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2SignalingGetPeerNetInfo() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2SignalingGetPingInfo(OrbisNpMatching2ContextId ctxId,
const void* reqParam, const void* optParam,
s32* reqId) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}", ctxId);
if (!g_initialized) {
LOG_ERROR(Lib_NpMatching2, "not initialized");
return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
if (!reqParam || !reqId) {
LOG_ERROR(Lib_NpMatching2, "request or requestId null");
return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
ContextObject* ctx = ContextManager::Instance().Get(ctxId);
if (!ctx) {
LOG_ERROR(Lib_NpMatching2, "invalid context id");
return ORBIS_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
}
const auto* request = static_cast<const OrbisNpMatching2SignalingGetPingInfoRequest*>(reqParam);
StoreRequestCallback(ctx, static_cast<const OrbisNpMatching2RequestOptParam*>(optParam));
const OrbisNpMatching2RequestId request_id = AllocRequestId();
*reqId = request_id;
const bool room_found = ctx->room_cache.find(request->roomId) != ctx->room_cache.end();
void* request_data =
room_found ? BuildSignalingGetPingInfoPayload(*ctx, request->roomId) : nullptr;
PendingEvent ev{};
ev.type = PendingEvent::REQUEST_CB;
ev.ctx_id = ctxId;
ev.fire_at = std::chrono::steady_clock::now();
ev.req_id = request_id;
ev.req_event = ORBIS_NP_MATCHING2_REQUEST_EVENT_SIGNALING_GET_PING_INFO;
ev.error_code = room_found ? ORBIS_OK : ORBIS_NP_MATCHING2_ERROR_ROOM_NOT_FOUND;
ev.request_cb = ctx->default_request_callback;
ev.request_cb_arg = ctx->default_request_callback_arg;
ev.request_data = request_data;
ScheduleEvent(std::move(ev));
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2SignalingCancelPeerNetInfo() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2SignalingGetConnectionInfoA(OrbisNpMatching2ContextId ctxId,
OrbisNpMatching2RoomId roomId,
OrbisNpMatching2RoomMemberId memberId,
u32 infoType, void* connInfo) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}, roomId = {}, memberId = {}", ctxId, roomId,
memberId);
if (!g_initialized) {
LOG_ERROR(Lib_NpMatching2, "not initialized");
return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
ContextObject* ctx = ContextManager::Instance().Get(ctxId);
if (!ctx) {
LOG_ERROR(Lib_NpMatching2, "invalid context id");
return ORBIS_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
}
return FillMatching2ConnectionInfo(*ctx, roomId, memberId, infoType, connInfo, true);
}
int PS4_SYSV_ABI sceNpMatching2SignalingGetConnectionInfo(OrbisNpMatching2ContextId ctxId,
OrbisNpMatching2RoomId roomId,
OrbisNpMatching2RoomMemberId memberId,
u32 infoType, void* connInfo) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}, roomId = {}, memberId = {}", ctxId, roomId,
memberId);
if (!g_initialized) {
LOG_ERROR(Lib_NpMatching2, "not initialized");
return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
ContextObject* ctx = ContextManager::Instance().Get(ctxId);
if (!ctx) {
LOG_ERROR(Lib_NpMatching2, "invalid context id");
return ORBIS_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
}
return FillMatching2ConnectionInfo(*ctx, roomId, memberId, infoType, connInfo, false);
}
int PS4_SYSV_ABI sceNpMatching2SignalingGetLocalNetInfo(OrbisNpMatching2ContextId ctxId,
void* info) {
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}", ctxId);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpMatching2SignalingGetPeerNetInfoResult() {
LOG_INFO(Lib_NpMatching2, "called");
return ORBIS_OK;
}
@ -576,6 +1158,78 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) {
sceNpMatching2SetRoomDataExternal);
LIB_FUNCTION("S9D8JSYIrjE", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2SetRoomDataInternal);
LIB_FUNCTION("tHD5FPFXtu4", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2SignalingGetConnectionStatus);
LIB_FUNCTION("pFzhpCMlJXQ", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2AbortContextStart);
LIB_FUNCTION("Nz-ZE7ur32I", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2DestroyContext);
LIB_FUNCTION("DnPUsBAe8oI", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2RegisterLobbyMessageCallback);
LIB_FUNCTION("uBESzz4CQws", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2RegisterRoomMessageCallback);
LIB_FUNCTION("gpSAvdheZ0Q", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2GetMemoryInfo);
LIB_FUNCTION("8btynvj0KNA", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2GetSslMemoryInfo);
LIB_FUNCTION("KC+GnHzrK2o", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2GetRoomMemberIdListLocal);
LIB_FUNCTION("vbtWT3lZBOM", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2GetRoomPasswordLocal);
LIB_FUNCTION("cgQhq3E0eGo", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2GetSignalingOptParamLocal);
LIB_FUNCTION("wyvlEgZ-55w", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2GetLobbyInfoList);
LIB_FUNCTION("1JtbJ0kxm3E", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2GetLobbyMemberDataInternal);
LIB_FUNCTION("1Z4Xxumgm+Y", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2GetLobbyMemberDataInternalList);
LIB_FUNCTION("26vWrPAWJfM", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2GetRoomDataExternalList);
LIB_FUNCTION("Jraxifmoet4", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2GetRoomDataInternal);
LIB_FUNCTION("dMQ+xGvTdqM", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2GetRoomMemberDataExternalList);
LIB_FUNCTION("5lhvOqheFBA", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2GetRoomMemberDataInternal);
LIB_FUNCTION("GyI2f9yDUXM", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2GetUserInfoListA);
LIB_FUNCTION("qeF-q5KDtAc", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2GetUserInfoList);
LIB_FUNCTION("NCP3bLGPt+o", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2GrantRoomOwner);
LIB_FUNCTION("n5JmImxTiZU", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2JoinLobby);
LIB_FUNCTION("gQ6cUriNpgs", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2JoinRoomA);
LIB_FUNCTION("AUVfU6byg3c", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2KickoutRoomMember);
LIB_FUNCTION("BBbJ92uUdCg", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2LeaveLobby);
LIB_FUNCTION("K+KtxhPsMZ4", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2SendLobbyChatMessage);
LIB_FUNCTION("opDpl74pi2E", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2SendRoomChatMessage);
LIB_FUNCTION("ir2CzSs9K-g", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2SetLobbyMemberDataInternal);
LIB_FUNCTION("HoqTrkS9c5Q", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2SetRoomMemberDataInternal);
LIB_FUNCTION("ES3UMUWWj9U", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2SetSignalingOptParam);
LIB_FUNCTION("8CqniKDzjvg", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2SignalingGetPeerNetInfo);
LIB_FUNCTION("wUmwXZHaX1w", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2SignalingGetPingInfo);
LIB_FUNCTION("GNSN5849fjU", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2SignalingCancelPeerNetInfo);
LIB_FUNCTION("nNeC3F8-g+4", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2SignalingGetConnectionInfoA);
LIB_FUNCTION("twVupeaYYrk", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2SignalingGetConnectionInfo);
LIB_FUNCTION("380EWm2DrVg", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2SignalingGetLocalNetInfo);
LIB_FUNCTION("CTy4PBhpWDw", "libSceNpMatching2", 1, "libSceNpMatching2",
sceNpMatching2SignalingGetPeerNetInfoResult);
};
} // namespace Libraries::Np::NpMatching2
} // namespace Libraries::Np::NpMatching2

View File

@ -12,6 +12,7 @@ class SymbolsResolver;
namespace Libraries::Np::NpMatching2 {
using OrbisNpMatching2AttributeId = u16;
using OrbisNpMatching2BlockKickFlag = u8;
using OrbisNpMatching2ContextId = u16;
using OrbisNpMatching2Flags = u32;
using OrbisNpMatching2LobbyId = u64;
@ -85,6 +86,17 @@ enum OrbisNpMatching2SignalingType : u8 {
ORBIS_NP_MATCHING2_SIGNALING_TYPE_STAR = 2,
};
enum OrbisNpMatching2SignalingConnectionInfoType : s32 {
ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_RTT = 1,
ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_BANDWIDTH = 2,
ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_PEER_NP_ID = 3,
ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_PEER_ADDR = 4,
ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_MAPPED_ADDR = 5,
ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_PACKET_LOSS = 6,
ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_PEER_NPID = 7,
ORBIS_NP_MATCHING2_SIGNALING_CONN_INFO_PEER_ADDRESS_A = 7,
};
enum OrbisNpMatching2AttrId : OrbisNpMatching2AttributeId {
ORBIS_NP_MATCHING2_ROOM_SEARCHABLE_INT_ATTR_EXTERNAL_1_ID = 0x004C,
ORBIS_NP_MATCHING2_ROOM_SEARCHABLE_INT_ATTR_EXTERNAL_2_ID = 0x004D,
@ -141,4 +153,4 @@ int PS4_SYSV_ABI sceNpMatching2CreateContextA(const OrbisNpMatching2CreateContex
OrbisNpMatching2ContextId* ctxId);
void RegisterLib(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Np::NpMatching2
} // namespace Libraries::Np::NpMatching2

View File

@ -2,10 +2,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstdio>
#include <cstring>
#include "common/logging/log.h"
#include "common/thread.h"
#include "core/libraries/network/net.h"
#include "core/libraries/np/np_common.h"
#include "core/libraries/np/np_error.h"
#include "core/libraries/np/np_matching2/np_matching2_internal.h"
@ -15,6 +17,18 @@ namespace Libraries::Np::NpMatching2 {
NpMatching2State g_state;
namespace {
u32 IpStringToAddr(std::string_view ip) {
u32 a, b, c, d;
if (std::sscanf(std::string(ip).c_str(), "%u.%u.%u.%u", &a, &b, &c, &d) != 4) {
return 0;
}
return a | (b << 8) | (c << 16) | (d << 24);
}
} // namespace
void* BuildCreateJoinRoomPayload(ContextObject& ctx, const shadnet::CreateJoinRoomResponse& resp) {
CallbackPayload& p = ctx.request_payload;
p.Reset();
@ -99,11 +113,33 @@ void* BuildCreateJoinRoomPayload(ContextObject& ctx, const shadnet::CreateJoinRo
}
}
if (ctx.room_id != 0 && ctx.room_id != room.roomId) {
ctx.room_cache.erase(ctx.room_id);
}
ctx.peers.clear();
RoomCache& rc = ctx.room_cache[room.roomId];
rc = RoomCache{};
rc.num_slots = room.maxSlot;
rc.mask_password = room.passwdSlotMask;
rc.server_id = room.serverId;
rc.world_id = room.worldId;
rc.lobby_id = room.lobbyId;
rc.room_id = room.roomId;
rc.max_slot = room.maxSlot;
rc.public_slots = room.publicSlots;
rc.private_slots = room.privateSlots;
rc.open_public_slots = room.openPublicSlots;
rc.open_private_slots = room.openPrivateSlots;
rc.passwd_slot_mask = room.passwdSlotMask;
rc.joined_slot_mask = room.joinedSlotMask;
rc.flags = room.flags;
rc.owner = resp.me_member_id() == resp.owner_member_id();
rc.signaling_type = ORBIS_NP_MATCHING2_SIGNALING_TYPE_MESH;
rc.signaling_main_member = static_cast<OrbisNpMatching2RoomMemberId>(resp.owner_member_id());
ctx.room_id = room.roomId;
ctx.my_member_id = static_cast<OrbisNpMatching2RoomMemberId>(resp.me_member_id());
ctx.is_room_owner = rc.owner;
for (const auto& g : p.room_groups) {
rc.groups[g.id] = g;
}
@ -117,20 +153,47 @@ void* BuildCreateJoinRoomPayload(ContextObject& ctx, const shadnet::CreateJoinRo
mc.flag_attr = m.flag_attr();
mc.group_id = static_cast<OrbisNpMatching2RoomGroupId>(m.group_id());
mc.join_date = m.join_date();
mc.addr = 0;
mc.port = 0;
mc.addr = IpStringToAddr(m.addr());
mc.port = Libraries::Net::sceNetHtons(static_cast<u16>(m.port()));
std::strncpy(mc.np_id.handle.data, m.npid().c_str(), sizeof(mc.np_id.handle.data) - 1);
for (const auto& a : m.bin_attrs_internal()) {
MemberBinCache& b = mc.bins[a.attr_id()];
b.id = static_cast<OrbisNpMatching2AttributeId>(a.attr_id());
b.data.assign(a.data().begin(), a.data().end());
}
if (mc.member_id != ctx.my_member_id) {
PeerInfo peer{};
peer.member_id = mc.member_id;
peer.addr = mc.addr;
peer.port = mc.port;
std::strncpy(peer.online_id.data, m.npid().c_str(), sizeof(peer.online_id.data) - 1);
ctx.peers[mc.member_id] = peer;
}
}
p.request_data = p.create_join_response.get();
return p.request_data;
}
void* BuildLeaveRoomPayload(ContextObject& ctx, const shadnet::LeaveRoomReply& resp) {
CallbackPayload& p = ctx.request_payload;
p.Reset();
p.leave_room_response = std::make_unique<OrbisNpMatching2LeaveRoomResponse>();
p.leave_room_response->roomId = static_cast<OrbisNpMatching2RoomId>(resp.room_id());
if (ctx.room_id == p.leave_room_response->roomId) {
ctx.room_cache.erase(ctx.room_id);
ctx.peers.clear();
ctx.room_id = 0;
ctx.my_member_id = 0;
ctx.is_room_owner = false;
}
p.request_data = p.leave_room_response.get();
return p.request_data;
}
void* BuildGetWorldInfoListPayload(ContextObject& ctx, const shadnet::GetWorldInfoListReply& resp) {
CallbackPayload& p = ctx.request_payload;
p.Reset();
@ -263,6 +326,51 @@ void* BuildSearchRoomPayload(ContextObject& ctx, const shadnet::SearchRoomReply&
return p.request_data;
}
void* BuildGetRoomDataInternalPayload(ContextObject& ctx, OrbisNpMatching2RoomId room_id) {
const auto rc_it = ctx.room_cache.find(room_id);
if (rc_it == ctx.room_cache.end()) {
return nullptr;
}
const RoomCache& rc = rc_it->second;
CallbackPayload& p = ctx.request_payload;
p.Reset();
p.room_groups.clear();
p.room_groups.reserve(rc.groups.size());
for (const auto& [gid, g] : rc.groups) {
p.room_groups.push_back(g);
}
p.room_bin_attrs.resize(rc.bin_attrs_internal.size());
for (size_t i = 0; i < rc.bin_attrs_internal.size(); ++i) {
p.room_bin_attrs[i] = rc.bin_attrs_internal[i];
}
p.room_data = std::make_unique<OrbisNpMatching2RoomDataInternal>();
auto& room = *p.room_data;
room = {};
room.publicSlots = rc.public_slots;
room.privateSlots = rc.private_slots;
room.openPublicSlots = rc.open_public_slots;
room.openPrivateSlots = rc.open_private_slots;
room.maxSlot = rc.max_slot;
room.serverId = rc.server_id;
room.worldId = rc.world_id;
room.lobbyId = rc.lobby_id;
room.roomId = rc.room_id;
room.passwdSlotMask = rc.passwd_slot_mask;
room.joinedSlotMask = rc.joined_slot_mask;
room.flags = rc.flags;
room.roomGroup = p.room_groups.empty() ? nullptr : p.room_groups.data();
room.roomGroups = p.room_groups.size();
room.internalBinAttr = p.room_bin_attrs.empty() ? nullptr : p.room_bin_attrs.data();
room.internalBinAttrs = p.room_bin_attrs.size();
p.request_data = p.room_data.get();
return p.request_data;
}
ContextManager& ContextManager::Instance() {
static ContextManager instance;
return instance;

View File

@ -23,6 +23,7 @@
namespace shadnet {
class CreateJoinRoomResponse;
class GetWorldInfoListReply;
class LeaveRoomReply;
class SearchRoomReply;
} // namespace shadnet
@ -35,6 +36,13 @@ struct PeerInfo {
s32 conn_id = 0;
s32 status = 0;
Libraries::Np::OrbisNpOnlineId online_id{};
bool handshake_started = false;
bool sent_check = false;
bool sent_established = false;
u64 nonce = 0;
u32 ping_us = 0;
std::chrono::steady_clock::time_point last_send{};
std::chrono::steady_clock::time_point last_check_send{};
};
struct MemberBinCache {
@ -59,6 +67,22 @@ struct MemberCache {
struct RoomCache {
u32 num_slots = 0;
u64 mask_password = 0;
OrbisNpMatching2SignalingType signaling_type = ORBIS_NP_MATCHING2_SIGNALING_TYPE_MESH;
OrbisNpMatching2RoomMemberId signaling_main_member = 0;
OrbisNpMatching2ServerId server_id = 0;
OrbisNpMatching2WorldId world_id = 0;
OrbisNpMatching2LobbyId lobby_id = 0;
OrbisNpMatching2RoomId room_id = 0;
u16 max_slot = 0;
u16 public_slots = 0;
u16 private_slots = 0;
u16 open_public_slots = 0;
u16 open_private_slots = 0;
u64 passwd_slot_mask = 0;
u64 joined_slot_mask = 0;
OrbisNpMatching2Flags flags = 0;
std::vector<OrbisNpMatching2RoomBinAttrInternal> bin_attrs_internal;
std::vector<std::vector<u8>> bin_buffers;
std::map<OrbisNpMatching2RoomGroupId, OrbisNpMatching2RoomGroup> groups;
std::map<OrbisNpMatching2RoomMemberId, MemberCache> members;
bool owner = false;
@ -67,8 +91,10 @@ struct RoomCache {
struct CallbackPayload {
std::unique_ptr<OrbisNpMatching2RoomDataInternal> room_data;
std::unique_ptr<OrbisNpMatching2CreateJoinRoomResponse> create_join_response;
std::unique_ptr<OrbisNpMatching2LeaveRoomResponse> leave_room_response;
std::unique_ptr<OrbisNpMatching2SearchRoomResponse> search_room_response;
std::unique_ptr<OrbisNpMatching2GetWorldInfoListResponse> world_info_response;
std::unique_ptr<OrbisNpMatching2SignalingGetPingInfoResponse> ping_info_response;
std::vector<OrbisNpMatching2World> world_list;
std::vector<OrbisNpMatching2RoomMemberDataInternal> member_data;
std::vector<OrbisNpMatching2RoomGroup> room_groups;
@ -96,8 +122,10 @@ struct CallbackPayload {
void Reset() {
room_data.reset();
create_join_response.reset();
leave_room_response.reset();
search_room_response.reset();
world_info_response.reset();
ping_info_response.reset();
world_list.clear();
member_data.clear();
room_groups.clear();
@ -295,8 +323,10 @@ extern NpMatching2State g_state;
OrbisNpMatching2RequestId AllocRequestId();
void* BuildCreateJoinRoomPayload(ContextObject& ctx, const shadnet::CreateJoinRoomResponse& resp);
void* BuildLeaveRoomPayload(ContextObject& ctx, const shadnet::LeaveRoomReply& resp);
void* BuildGetWorldInfoListPayload(ContextObject& ctx, const shadnet::GetWorldInfoListReply& resp);
void* BuildSearchRoomPayload(ContextObject& ctx, const shadnet::SearchRoomReply& resp);
void* BuildGetRoomDataInternalPayload(ContextObject& ctx, OrbisNpMatching2RoomId room_id);
void InitEventDispatcher();
void TermEventDispatcher();

View File

@ -4,15 +4,20 @@
#include <chrono>
#include <condition_variable>
#include <cstring>
#include <limits>
#include <map>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>
#include "common/logging/log.h"
#include "core/libraries/network/net.h"
#include "core/libraries/network/sockets.h"
#include "core/libraries/np/np_error.h"
#include "core/libraries/np/np_matching2/np_matching2_internal.h"
#include "core/libraries/np/np_matching2/np_matching2_mm.h"
#include "core/libraries/np/np_signaling/np_signaling_stubs.h"
#include "shadnet.pb.h"
#include "shadnet/client.h"
@ -91,6 +96,391 @@ std::string ExtractProtoBytes(const std::vector<u8>& payload, size_t offset = 0)
return std::string(reinterpret_cast<const char*>(payload.data() + offset + 4), len);
}
constexpr s32 kMatching2ConnInactive = 0;
constexpr s32 kMatching2ConnPending = 1;
constexpr s32 kMatching2ConnActive = 2;
constexpr auto kMatching2HandshakeRetry = std::chrono::milliseconds(350);
constexpr auto kMatching2HandshakeTimeout = std::chrono::seconds(10);
std::atomic<bool> g_matching2_stop{false};
std::mutex g_matching2_thread_mutex;
std::thread g_matching2_thread;
enum class Matching2HandshakeKind : u8 {
Offer = 1,
Accept = 2,
Check = 3,
CheckAck = 4,
};
#pragma pack(push, 1)
struct Matching2HandshakePacket {
u8 magic[4] = {'S', 'H', 'A', 'D'};
u8 type = 0x21;
u8 kind = 0;
u64 room_id = 0;
u16 from_member_id = 0;
u16 to_member_id = 0;
u8 online_id_from[16]{};
u32 mapped_addr = 0;
u16 mapped_port = 0;
u16 reserved = 0;
u64 nonce = 0;
};
#pragma pack(pop)
static_assert(sizeof(Matching2HandshakePacket) == 0x32);
bool HasMatching2Magic(const Matching2HandshakePacket& pkt) {
return pkt.magic[0] == 'S' && pkt.magic[1] == 'H' && pkt.magic[2] == 'A' &&
pkt.magic[3] == 'D' && pkt.type == 0x21;
}
std::string OnlineIdToString(const Libraries::Np::OrbisNpOnlineId& online_id) {
char buf[ORBIS_NP_ONLINEID_MAX_LENGTH + 1]{};
std::memcpy(buf, online_id.data, ORBIS_NP_ONLINEID_MAX_LENGTH);
return std::string(buf);
}
bool ShouldConnectToPeer(const RoomCache& room, OrbisNpMatching2RoomMemberId self,
OrbisNpMatching2RoomMemberId peer) {
if (peer == 0 || peer == self) {
return false;
}
if (room.signaling_type == ORBIS_NP_MATCHING2_SIGNALING_TYPE_NONE) {
return false;
}
if (room.signaling_type == ORBIS_NP_MATCHING2_SIGNALING_TYPE_STAR &&
room.signaling_main_member != 0) {
return self == room.signaling_main_member || peer == room.signaling_main_member;
}
return true;
}
void QueueMatching2SignalingEvent(ContextObject& ctx, OrbisNpMatching2RoomId room_id,
OrbisNpMatching2RoomMemberId member_id,
OrbisNpMatching2Event event, s32 error_code) {
PendingEvent ev{};
ev.type = PendingEvent::SIGNALING_CB;
ev.ctx_id = ctx.ctx_id;
ev.fire_at = std::chrono::steady_clock::now();
ev.room_id = room_id;
ev.member_id = member_id;
ev.sig_event = event;
ev.error_code = error_code;
ScheduleEvent(std::move(ev));
}
void QueueMatching2DeadForRoomPeers(ContextObject& ctx, OrbisNpMatching2RoomId room_id,
s32 error_code) {
auto room_it = ctx.room_cache.find(room_id);
if (room_it == ctx.room_cache.end()) {
return;
}
for (const auto& [member_id, member] : room_it->second.members) {
if (member_id == 0 || member_id == ctx.my_member_id) {
continue;
}
auto peer_it = ctx.peers.find(member_id);
if (peer_it != ctx.peers.end()) {
peer_it->second.status = kMatching2ConnInactive;
peer_it->second.handshake_started = false;
peer_it->second.sent_check = false;
}
QueueMatching2SignalingEvent(ctx, room_id, member_id,
ORBIS_NP_MATCHING2_SIGNALING_EVENT_DEAD, error_code);
LOG_INFO(Lib_NpMatching2, "Matching2 signaling dead: ctx={} room={} member={} reason={:#x}",
ctx.ctx_id, room_id, member_id, static_cast<u32>(error_code));
}
}
bool ResolvePeerEndpoint(const MemberCache& member, PeerInfo& peer) {
if (peer.addr != 0 && peer.port != 0) {
return true;
}
if (member.addr != 0 && member.port != 0) {
peer.addr = member.addr;
peer.port = member.port;
if (peer.online_id.data[0] == 0) {
std::strncpy(peer.online_id.data, member.np_id.handle.data,
sizeof(peer.online_id.data) - 1);
}
return true;
}
const std::string online_id(member.np_id.handle.data);
u32 resolved_addr = 0;
u16 resolved_port = 0;
if (!online_id.empty() && RequestSignalingInfos(online_id, &resolved_addr, &resolved_port)) {
peer.addr = resolved_addr;
peer.port = resolved_port;
}
if (peer.online_id.data[0] == 0) {
std::strncpy(peer.online_id.data, member.np_id.handle.data,
sizeof(peer.online_id.data) - 1);
}
return peer.addr != 0 && peer.port != 0;
}
void MarkMatching2PeerActive(ContextObject& ctx, OrbisNpMatching2RoomId room_id,
OrbisNpMatching2RoomMemberId member_id, u32 addr, u16 port) {
if (member_id == 0 || member_id == ctx.my_member_id) {
return;
}
PeerInfo& peer = ctx.peers[member_id];
peer.member_id = member_id;
if (addr != 0) {
peer.addr = addr;
}
if (port != 0) {
peer.port = port;
}
const bool first_active = peer.status != kMatching2ConnActive;
peer.status = kMatching2ConnActive;
peer.handshake_started = true;
if (first_active && !peer.sent_established) {
peer.sent_established = true;
QueueMatching2SignalingEvent(ctx, room_id, member_id,
ORBIS_NP_MATCHING2_SIGNALING_EVENT_ESTABLISHED, ORBIS_OK);
LOG_INFO(Lib_NpMatching2,
"Matching2 signaling established: ctx={} room={} member={} addr={:#x}:{}",
ctx.ctx_id, room_id, member_id, peer.addr, Libraries::Net::sceNetNtohs(peer.port));
}
}
bool SendMatching2Handshake(ContextObject& ctx, OrbisNpMatching2RoomId room_id,
OrbisNpMatching2RoomMemberId member_id, Matching2HandshakeKind kind,
u64 nonce) {
auto room_it = ctx.room_cache.find(room_id);
if (room_it == ctx.room_cache.end()) {
return false;
}
auto member_it = room_it->second.members.find(member_id);
if (member_it == room_it->second.members.end()) {
return false;
}
PeerInfo& peer = ctx.peers[member_id];
peer.member_id = member_id;
if (!ResolvePeerEndpoint(member_it->second, peer)) {
LOG_WARNING(Lib_NpMatching2, "Matching2 signaling: unresolved endpoint room={} member={}",
room_id, member_id);
return false;
}
Matching2HandshakePacket pkt{};
pkt.kind = static_cast<u8>(kind);
pkt.room_id = room_id;
pkt.from_member_id = ctx.my_member_id;
pkt.to_member_id = member_id;
std::memcpy(pkt.online_id_from, ctx.online_id.data, ORBIS_NP_ONLINEID_MAX_LENGTH);
pkt.mapped_addr = Net::GetP2PAdvertisedAddr();
pkt.mapped_port = Net::GetP2PConfiguredPort() != 0
? Libraries::Net::sceNetHtons(Net::GetP2PConfiguredPort())
: 0;
pkt.nonce = nonce;
const int rc = Net::P2PMatching2SendTo(&pkt, sizeof(pkt), peer.addr, peer.port);
const auto now = std::chrono::steady_clock::now();
peer.last_send = now;
if (kind == Matching2HandshakeKind::Check) {
peer.last_check_send = now;
}
LOG_DEBUG(Lib_NpMatching2,
"Matching2 handshake send kind={} ctx={} room={} {}->{} dst={:#x}:{} rc={}",
static_cast<u8>(kind), ctx.ctx_id, room_id, ctx.my_member_id, member_id, peer.addr,
Libraries::Net::sceNetNtohs(peer.port), rc);
return rc >= 0;
}
void StartMatching2PeerHandshake(ContextObject& ctx, OrbisNpMatching2RoomId room_id,
OrbisNpMatching2RoomMemberId member_id) {
auto room_it = ctx.room_cache.find(room_id);
if (room_it == ctx.room_cache.end() ||
!ShouldConnectToPeer(room_it->second, ctx.my_member_id, member_id)) {
return;
}
auto member_it = room_it->second.members.find(member_id);
if (member_it == room_it->second.members.end()) {
return;
}
Net::EnsureP2PTransport();
PeerInfo& peer = ctx.peers[member_id];
peer.member_id = member_id;
if (peer.status == kMatching2ConnActive) {
return;
}
ResolvePeerEndpoint(member_it->second, peer);
peer.status = kMatching2ConnPending;
peer.handshake_started = true;
peer.sent_check = false;
if (peer.nonce == 0) {
peer.nonce = static_cast<u64>(std::chrono::steady_clock::now().time_since_epoch().count() ^
(static_cast<u64>(ctx.ctx_id) << 48) ^
(static_cast<u64>(member_id) << 16));
}
SendMatching2Handshake(ctx, room_id, member_id, Matching2HandshakeKind::Offer, 0);
}
void StartMatching2SignalingForRoomPeers(ContextObject& ctx, OrbisNpMatching2RoomId room_id) {
const auto room_it = ctx.room_cache.find(room_id);
if (room_it == ctx.room_cache.end()) {
return;
}
for (const auto& [member_id, member] : room_it->second.members) {
StartMatching2PeerHandshake(ctx, room_id, member_id);
}
}
ContextObject* FindContextForMatching2Packet(const Matching2HandshakePacket& pkt) {
for (u32 id = 1; id <= ContextManager::kMaxContexts; ++id) {
ContextObject* ctx =
ContextManager::Instance().Get(static_cast<OrbisNpMatching2ContextId>(id));
if (!ctx || ctx->room_id != pkt.room_id || ctx->my_member_id != pkt.to_member_id) {
continue;
}
if (ctx->room_cache.find(static_cast<OrbisNpMatching2RoomId>(pkt.room_id)) !=
ctx->room_cache.end()) {
return ctx;
}
}
return nullptr;
}
void HandleMatching2HandshakePacket(u32 from_addr, u16 from_port,
const Matching2HandshakePacket& pkt) {
if (!HasMatching2Magic(pkt)) {
return;
}
ContextObject* ctx = FindContextForMatching2Packet(pkt);
if (!ctx) {
LOG_DEBUG(Lib_NpMatching2, "Matching2 handshake: no ctx for room={} to_member={}",
pkt.room_id, pkt.to_member_id);
return;
}
const auto room_id = static_cast<OrbisNpMatching2RoomId>(pkt.room_id);
const auto member_id = static_cast<OrbisNpMatching2RoomMemberId>(pkt.from_member_id);
auto room_it = ctx->room_cache.find(room_id);
if (room_it == ctx->room_cache.end() ||
!ShouldConnectToPeer(room_it->second, ctx->my_member_id, member_id)) {
return;
}
PeerInfo& peer = ctx->peers[member_id];
peer.member_id = member_id;
peer.addr = pkt.mapped_addr != 0 ? pkt.mapped_addr : from_addr;
peer.port = pkt.mapped_port != 0 ? pkt.mapped_port : from_port;
peer.status =
peer.status == kMatching2ConnActive ? kMatching2ConnActive : kMatching2ConnPending;
peer.handshake_started = true;
std::memcpy(peer.online_id.data, pkt.online_id_from, ORBIS_NP_ONLINEID_MAX_LENGTH);
const auto kind = static_cast<Matching2HandshakeKind>(pkt.kind);
switch (kind) {
case Matching2HandshakeKind::Offer:
SendMatching2Handshake(*ctx, room_id, member_id, Matching2HandshakeKind::Accept, 0);
break;
case Matching2HandshakeKind::Accept:
peer.sent_check = true;
SendMatching2Handshake(*ctx, room_id, member_id, Matching2HandshakeKind::Check, peer.nonce);
break;
case Matching2HandshakeKind::Check:
SendMatching2Handshake(*ctx, room_id, member_id, Matching2HandshakeKind::CheckAck,
pkt.nonce);
MarkMatching2PeerActive(*ctx, room_id, member_id, peer.addr, peer.port);
break;
case Matching2HandshakeKind::CheckAck:
if (pkt.nonce == 0 || pkt.nonce == peer.nonce) {
if (peer.last_check_send.time_since_epoch().count() != 0) {
const auto rtt = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now() - peer.last_check_send);
peer.ping_us =
static_cast<u32>(std::min<u64>(static_cast<u64>(std::max<s64>(0, rtt.count())),
std::numeric_limits<u32>::max()));
peer.last_check_send = {};
}
MarkMatching2PeerActive(*ctx, room_id, member_id, peer.addr, peer.port);
}
break;
default:
break;
}
}
void Matching2HandshakeThreadMain() {
while (!g_matching2_stop.load(std::memory_order_relaxed)) {
Matching2HandshakePacket pkt{};
u32 from_addr = 0;
u16 from_port = 0;
const int rc = Net::P2PMatching2RecvFrom(&pkt, sizeof(pkt), &from_addr, &from_port);
if (rc == sizeof(pkt)) {
HandleMatching2HandshakePacket(from_addr, from_port, pkt);
}
const auto now = std::chrono::steady_clock::now();
for (u32 id = 1; id <= ContextManager::kMaxContexts; ++id) {
ContextObject* ctx =
ContextManager::Instance().Get(static_cast<OrbisNpMatching2ContextId>(id));
if (!ctx || ctx->room_id == 0) {
continue;
}
auto room_it = ctx->room_cache.find(ctx->room_id);
if (room_it == ctx->room_cache.end()) {
continue;
}
for (auto& [member_id, peer] : ctx->peers) {
if (peer.status != kMatching2ConnPending || !peer.handshake_started) {
continue;
}
if (peer.last_send.time_since_epoch().count() != 0 &&
now - peer.last_send > kMatching2HandshakeTimeout) {
peer.status = kMatching2ConnInactive;
QueueMatching2SignalingEvent(*ctx, ctx->room_id, member_id,
ORBIS_NP_MATCHING2_SIGNALING_EVENT_NETINFO_ERROR,
ORBIS_NP_MATCHING2_SIGNALING_ERROR_TIMEOUT);
continue;
}
if (peer.last_send.time_since_epoch().count() == 0 ||
now - peer.last_send >= kMatching2HandshakeRetry) {
SendMatching2Handshake(*ctx, ctx->room_id, member_id,
peer.sent_check ? Matching2HandshakeKind::Check
: Matching2HandshakeKind::Offer,
peer.sent_check ? peer.nonce : 0);
}
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
}
void StartMatching2HandshakeThread() {
std::lock_guard lock(g_matching2_thread_mutex);
if (g_matching2_thread.joinable()) {
return;
}
g_matching2_stop.store(false, std::memory_order_relaxed);
g_matching2_thread = std::thread(Matching2HandshakeThreadMain);
}
void StopMatching2HandshakeThread() {
{
std::lock_guard lock(g_matching2_thread_mutex);
g_matching2_stop.store(true, std::memory_order_relaxed);
}
if (g_matching2_thread.joinable()) {
g_matching2_thread.join();
}
}
void DispatchRequestComplete(const PendingRequest& pr, ShadNet::ErrorType error,
const std::vector<u8>& body) {
ContextObject* ctx = ContextManager::Instance().Get(pr.ctx_id);
@ -127,6 +517,14 @@ void DispatchRequestComplete(const PendingRequest& pr, ShadNet::ErrorType error,
if (reply.ParseFromString(proto) && reply.has_details()) {
request_data = BuildCreateJoinRoomPayload(*ctx, reply.details());
}
} else if (pr.req_event == ORBIS_NP_MATCHING2_REQUEST_EVENT_LEAVE_ROOM) {
shadnet::LeaveRoomReply reply;
if (reply.ParseFromString(proto)) {
QueueMatching2DeadForRoomPeers(
*ctx, static_cast<OrbisNpMatching2RoomId>(reply.room_id()),
ORBIS_NP_MATCHING2_SIGNALING_ERROR_TERMINATED_BY_MYSELF);
request_data = BuildLeaveRoomPayload(*ctx, reply);
}
} else if (pr.req_event == ORBIS_NP_MATCHING2_REQUEST_EVENT_GET_WORLD_INFO_LIST) {
shadnet::GetWorldInfoListReply reply;
if (reply.ParseFromString(proto)) {
@ -151,6 +549,11 @@ void DispatchRequestComplete(const PendingRequest& pr, ShadNet::ErrorType error,
ev.request_cb_arg = ctx->default_request_callback_arg;
ev.request_data = request_data;
ScheduleEvent(std::move(ev));
if (error_code == 0 && (pr.req_event == ORBIS_NP_MATCHING2_REQUEST_EVENT_CREATE_JOIN_ROOM ||
pr.req_event == ORBIS_NP_MATCHING2_REQUEST_EVENT_JOIN_ROOM)) {
StartMatching2SignalingForRoomPeers(*ctx, ctx->room_id);
}
}
void BuildMemberUpdate(CallbackPayload& p, const RoomCache& rc, const MemberCache& mc,
@ -254,6 +657,9 @@ void HandleRoomEvent(const ShadNet::NotifyRoomEvent& n) {
return;
}
BuildMemberUpdate(p, room_it->second, mem_it->second, cause);
QueueMatching2SignalingEvent(*ctx, room_id, member_id,
ORBIS_NP_MATCHING2_SIGNALING_EVENT_DEAD,
ORBIS_NP_MATCHING2_SIGNALING_ERROR_TERMINATED_BY_PEER);
room_it->second.members.erase(mem_it);
ctx->peers.erase(member_id);
break;
@ -283,6 +689,10 @@ void HandleRoomEvent(const ShadNet::NotifyRoomEvent& n) {
ev.room_event = event;
ev.room_event_data = p.room_event_data;
ScheduleEvent(std::move(ev));
if (event == ORBIS_NP_MATCHING2_ROOM_EVENT_MEMBER_JOINED) {
StartMatching2PeerHandshake(*ctx, room_id, member_id);
}
}
} // namespace
@ -315,6 +725,8 @@ void OnMatchingReply(ShadNet::CommandType cmd, u64 pkt_id, ShadNet::ErrorType er
void SetMmShadNetClient(std::shared_ptr<ShadNet::ShadNetClient> client,
std::string_view server_host, u16 tcp_port) {
u32 server_addr = 0;
u16 server_udp_port = 0;
{
std::lock_guard lock(g_mm.mutex);
g_mm.client = client;
@ -323,12 +735,31 @@ void SetMmShadNetClient(std::shared_ptr<ShadNet::ShadNetClient> client,
g_mm.server_addr = IpStringToAddr(server_host);
}
g_mm.server_udp_port = Libraries::Net::sceNetHtons(static_cast<u16>(tcp_port + 1));
server_addr = g_mm.server_addr;
server_udp_port = g_mm.server_udp_port;
}
if (!client) {
NpSignaling::Stubs::SetTransportHooks({});
NpSignaling::Stubs::SetPeerResolver(nullptr);
NpSignaling::Stubs::SetMmServerEndpoint(0, 0);
StopMatching2HandshakeThread();
return;
}
NpSignaling::Stubs::SetTransportHooks({
.signaling_send = Net::P2PSignalingSendTo,
.signaling_recv = Net::P2PSignalingRecvFrom,
.control_send = Net::P2PControlSendTo,
.control_recv = Net::P2PControlRecvFrom,
.transport_ready = Net::P2PTransportIsReady,
.configured_port = Net::GetP2PConfiguredPort,
.advertised_addr = Net::GetP2PAdvertisedAddr,
.ensure_transport = Net::EnsureP2PTransport,
});
NpSignaling::Stubs::SetPeerResolver(RequestSignalingInfos);
NpSignaling::Stubs::SetMmServerEndpoint(server_addr, server_udp_port);
StartMatching2HandshakeThread();
client->onRoomEvent = [](const ShadNet::NotifyRoomEvent& n) { HandleRoomEvent(n); };
}
@ -343,6 +774,10 @@ void ClearMmShadNetClient() {
if (old_client) {
old_client->onRoomEvent = nullptr;
}
NpSignaling::Stubs::SetTransportHooks({});
NpSignaling::Stubs::SetPeerResolver(nullptr);
NpSignaling::Stubs::SetMmServerEndpoint(0, 0);
StopMatching2HandshakeThread();
{
std::lock_guard lock(g_mm.pending_mutex);
g_mm.pending.clear();
@ -501,6 +936,53 @@ s32 MmSearchRoom(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req
MmCommand::SearchRoom, MakeProtoPayload(req));
}
s32 MmSetRoomDataInternal(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
const OrbisNpMatching2SetRoomDataInternalRequest& request) {
shadnet::SetRoomDataInternalRequest req;
req.set_req_id(req_id);
req.set_room_id(request.roomId);
req.set_flag_filter(request.flagFilter);
req.set_flag_attr(request.flagAttr);
for (u64 i = 0; i < request.roomBinAttrInternalNum; ++i) {
AppendBinAttr(req.add_bin_attrs(), request.roomBinAttrInternal[i]);
}
if (request.passwordSlotMask) {
req.set_has_passwd_mask(true);
req.set_passwd_slot_mask(*request.passwordSlotMask);
}
return MmSubmitRequest(ctx_id, req_id, ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_ROOM_DATA_INTERNAL,
MmCommand::SetRoomDataInternal, MakeProtoPayload(req));
}
s32 MmSetRoomDataExternal(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
const OrbisNpMatching2SetRoomDataExternalRequest& request) {
shadnet::SetRoomDataExternalRequest req;
req.set_req_id(req_id);
req.set_room_id(request.roomId);
for (u64 i = 0; i < request.roomSearchableIntAttrExternalNum; ++i) {
AppendIntAttr(req.add_search_int_attrs(), request.roomSearchableIntAttrExternal[i]);
}
for (u64 i = 0; i < request.roomSearchableBinAttrExternalNum; ++i) {
AppendBinAttr(req.add_search_bin_attrs(), request.roomSearchableBinAttrExternal[i]);
}
for (u64 i = 0; i < request.roomBinAttrExternalNum; ++i) {
AppendBinAttr(req.add_ext_bin_attrs(), request.roomBinAttrExternal[i]);
}
return MmSubmitRequest(ctx_id, req_id, ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_ROOM_DATA_EXTERNAL,
MmCommand::SetRoomDataExternal, MakeProtoPayload(req));
}
s32 MmKickoutRoomMember(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
const OrbisNpMatching2KickoutRoomMemberRequest& request) {
shadnet::KickoutRoomMemberRequest req;
req.set_req_id(req_id);
req.set_room_id(request.roomId);
req.set_target_member_id(request.memberId);
req.set_block_kick_flag(request.blockKickFlag);
return MmSubmitRequest(ctx_id, req_id, ORBIS_NP_MATCHING2_REQUEST_EVENT_KICKOUT_ROOM_MEMBER,
MmCommand::KickoutRoomMember, MakeProtoPayload(req));
}
u32 GetMmServerAddr() {
std::lock_guard lock(g_mm.mutex);
return g_mm.server_addr;

View File

@ -57,6 +57,12 @@ s32 MmGetWorldInfoList(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2Request
const OrbisNpMatching2GetWorldInfoListRequest& request);
s32 MmSearchRoom(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
const OrbisNpMatching2SearchRoomRequest& request);
s32 MmSetRoomDataInternal(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
const OrbisNpMatching2SetRoomDataInternalRequest& request);
s32 MmSetRoomDataExternal(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
const OrbisNpMatching2SetRoomDataExternalRequest& request);
s32 MmKickoutRoomMember(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
const OrbisNpMatching2KickoutRoomMemberRequest& request);
u32 GetMmServerAddr();
u16 GetMmServerUdpPort();

View File

@ -221,6 +221,12 @@ struct OrbisNpMatching2GetWorldInfoListRequest {
OrbisNpMatching2ServerId serverId;
};
struct OrbisNpMatching2GetRoomDataInternalRequest {
OrbisNpMatching2RoomId roomId;
const OrbisNpMatching2AttributeId* attrId;
u64 attrIdNum;
};
struct OrbisNpMatching2World {
OrbisNpMatching2World* next;
OrbisNpMatching2WorldId worldId;
@ -297,14 +303,9 @@ static_assert(sizeof(OrbisNpMatching2JoinRoomRequest) == 0x58);
struct OrbisNpMatching2KickoutRoomMemberRequest {
OrbisNpMatching2RoomId roomId;
OrbisNpMatching2RoomMemberId memberId;
u8 pad0[6];
u32 blockKickFlag;
u8 pad1[12];
u64 reserved;
int Validate() {
return 0;
}
OrbisNpMatching2BlockKickFlag blockKickFlag;
u8 padding[5];
OrbisNpMatching2PresenceOptionData optData;
};
static_assert(sizeof(OrbisNpMatching2KickoutRoomMemberRequest) == 0x28);
@ -397,6 +398,12 @@ struct OrbisNpMatching2RoomGroupConfig {
u8 pad[2];
};
struct OrbisNpMatching2RoomGroupPasswordConfig {
OrbisNpMatching2RoomGroupId groupId;
bool hasPassword;
u8 pad[1];
};
struct OrbisNpMatching2RoomGroupInfo {
OrbisNpMatching2RoomGroupId id;
bool hasPasswd;
@ -505,29 +512,25 @@ struct OrbisNpMatching2SearchRoomResponseA {
struct OrbisNpMatching2SetRoomDataExternalRequest {
OrbisNpMatching2RoomId roomId;
OrbisNpMatching2IntAttr* roomSearchableIntAttrExternal;
u32 roomSearchableIntAttrExternalNum;
u8 pad1[4];
OrbisNpMatching2BinAttr* roomSearchableBinAttrExternal;
u32 roomSearchableBinAttrExternalNum;
u8 pad2[4];
OrbisNpMatching2BinAttr* roomBinAttrExternal;
u32 roomBinAttrExternalNum;
u8 pad3[4];
const OrbisNpMatching2IntAttr* roomSearchableIntAttrExternal;
u64 roomSearchableIntAttrExternalNum;
const OrbisNpMatching2BinAttr* roomSearchableBinAttrExternal;
u64 roomSearchableBinAttrExternalNum;
const OrbisNpMatching2BinAttr* roomBinAttrExternal;
u64 roomBinAttrExternalNum;
};
struct OrbisNpMatching2SetRoomDataInternalRequest {
OrbisNpMatching2RoomId roomId;
OrbisNpMatching2Flags flagFilter;
OrbisNpMatching2Flags flagAttr;
OrbisNpMatching2BinAttr* roomBinAttrInternal;
u32 roomBinAttrInternalNum;
OrbisNpMatching2RoomGroupConfig* passwordConfig;
u32 passwordConfigNum;
u64* passwordSlotMask;
OrbisNpMatching2RoomMemberId* ownerPrivilegeRank;
u32 ownerPrivilegeRankNum;
u8 padding[4];
const OrbisNpMatching2BinAttr* roomBinAttrInternal;
u64 roomBinAttrInternalNum;
const OrbisNpMatching2RoomGroupPasswordConfig* passwordConfig;
u64 passwordConfigNum;
const OrbisNpMatching2RoomPasswordSlotMask* passwordSlotMask;
const OrbisNpMatching2RoomMemberId* ownerPrivilegeRank;
u64 ownerPrivilegeRankNum;
};
struct OrbisNpMatching2SetUserInfoRequest {
@ -543,9 +546,23 @@ struct OrbisNpMatching2SignalingConnectionInfoAddr {
u8 pad[2];
};
struct OrbisNpMatching2SignalingConnectionInfo {
union OrbisNpMatching2SignalingConnectionInfo {
u32 rtt;
u32 bandwidth;
Libraries::Np::OrbisNpId npId;
OrbisNpMatching2SignalingConnectionInfoAddr address;
u32 packetLoss;
};
static_assert(sizeof(OrbisNpMatching2SignalingConnectionInfo) == 0x24);
union OrbisNpMatching2SignalingConnectionInfoA {
u32 rtt;
u32 bandwidth;
Libraries::Np::OrbisNpPeerAddressA peerAddrA;
OrbisNpMatching2SignalingConnectionInfoAddr address;
u32 packetLoss;
};
static_assert(sizeof(OrbisNpMatching2SignalingConnectionInfoA) == 0x10);
// internal - to be removed.
struct OrbisNpMatching2SignalingEvent {
@ -570,9 +587,10 @@ struct OrbisNpMatching2SignalingGetPingInfoResponse {
u8 pad[2];
OrbisNpMatching2WorldId worldId;
OrbisNpMatching2RoomId roomId;
u32 pingUs;
u32 rtt;
u8 reserved[20];
};
static_assert(sizeof(OrbisNpMatching2SignalingGetPingInfoResponse) == 0x28);
struct OrbisNpMatching2SignalingParam {
int type;

View File

@ -43,6 +43,14 @@ u16 ConfiguredPort() {
return g_transport.configured_port ? g_transport.configured_port() : 0;
}
u32 AdvertisedAddr() {
return g_transport.advertised_addr ? g_transport.advertised_addr() : 0;
}
bool EnsureTransport() {
return g_transport.ensure_transport ? g_transport.ensure_transport() : TransportIsReady();
}
void SetPeerResolver(PeerResolver fn) {
g_peer_resolver = fn;
}

View File

@ -15,6 +15,8 @@ struct TransportHooks {
int (*control_recv)(void* buf, u32 len, u32* from_addr, u16* from_port) = nullptr;
bool (*transport_ready)() = nullptr;
u16 (*configured_port)() = nullptr;
u32 (*advertised_addr)() = nullptr;
bool (*ensure_transport)() = nullptr;
};
void SetTransportHooks(const TransportHooks& hooks);
@ -24,6 +26,8 @@ int ControlSendTo(const void* data, u32 len, u32 dest_addr, u16 dest_port);
int ControlRecvFrom(void* buf, u32 len, u32* from_addr, u16* from_port);
bool TransportIsReady();
u16 ConfiguredPort();
u32 AdvertisedAddr();
bool EnsureTransport();
using PeerResolver = bool (*)(std::string_view online_id, u32* out_addr, u16* out_port);
void SetPeerResolver(PeerResolver fn);