From 97568438607ad345e76e77cd482b8332791664fe Mon Sep 17 00:00:00 2001 From: metr1k-91 Date: Wed, 1 Jul 2026 10:42:54 -0500 Subject: [PATCH] Matching2 P6: Public API additions, Stub Additions, networking layer stubs, more type additions, more struct additions, matching2signaling work. (#4654) --- src/core/libraries/network/p2p_sockets.cpp | 40 ++ src/core/libraries/network/sockets.h | 11 + src/core/libraries/np/np_handler.cpp | 4 + .../np/np_matching2/np_matching2.cpp | 678 +++++++++++++++++- .../libraries/np/np_matching2/np_matching2.h | 14 +- .../np/np_matching2/np_matching2_internal.cpp | 112 ++- .../np/np_matching2/np_matching2_internal.h | 30 + .../np/np_matching2/np_matching2_mm.cpp | 482 +++++++++++++ .../np/np_matching2/np_matching2_mm.h | 6 + .../np/np_matching2/np_matching2_types.h | 72 +- .../np/np_signaling/np_signaling_stubs.cpp | 8 + .../np/np_signaling/np_signaling_stubs.h | 4 + 12 files changed, 1419 insertions(+), 42 deletions(-) diff --git a/src/core/libraries/network/p2p_sockets.cpp b/src/core/libraries/network/p2p_sockets.cpp index cfdd1bda7..0eb3a4623 100644 --- a/src/core/libraries/network/p2p_sockets.cpp +++ b/src/core/libraries/network/p2p_sockets.cpp @@ -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 \ No newline at end of file diff --git a/src/core/libraries/network/sockets.h b/src/core/libraries/network/sockets.h index 86661c71c..5c8b083d9 100644 --- a/src/core/libraries/network/sockets.h +++ b/src/core/libraries/network/sockets.h @@ -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 \ No newline at end of file diff --git a/src/core/libraries/np/np_handler.cpp b/src/core/libraries/np/np_handler.cpp index 0b23842a3..c27e1f51a 100644 --- a/src/core/libraries/np/np_handler.cpp +++ b/src/core/libraries/np/np_handler.cpp @@ -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(); diff --git a/src/core/libraries/np/np_matching2/np_matching2.cpp b/src/core/libraries/np/np_matching2/np_matching2.cpp index c07cc4870..5708a36bc 100644 --- a/src/core/libraries/np/np_matching2/np_matching2.cpp +++ b/src/core/libraries/np/np_matching2/np_matching2.cpp @@ -2,11 +2,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #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(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(); + 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(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(connInfo) = {}; + } else { + *static_cast(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(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(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(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(reqParam); + StoreRequestCallback(ctx, static_cast(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 \ No newline at end of file +} // namespace Libraries::Np::NpMatching2 diff --git a/src/core/libraries/np/np_matching2/np_matching2.h b/src/core/libraries/np/np_matching2/np_matching2.h index 9bf6a8189..3606eb6bd 100644 --- a/src/core/libraries/np/np_matching2/np_matching2.h +++ b/src/core/libraries/np/np_matching2/np_matching2.h @@ -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 \ No newline at end of file +} // namespace Libraries::Np::NpMatching2 diff --git a/src/core/libraries/np/np_matching2/np_matching2_internal.cpp b/src/core/libraries/np/np_matching2/np_matching2_internal.cpp index 82a44e28d..476548e24 100644 --- a/src/core/libraries/np/np_matching2/np_matching2_internal.cpp +++ b/src/core/libraries/np/np_matching2/np_matching2_internal.cpp @@ -2,10 +2,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #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(resp.owner_member_id()); + ctx.room_id = room.roomId; + ctx.my_member_id = static_cast(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(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(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(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(); + p.leave_room_response->roomId = static_cast(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(); + 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; diff --git a/src/core/libraries/np/np_matching2/np_matching2_internal.h b/src/core/libraries/np/np_matching2/np_matching2_internal.h index 2f0ceeb10..62a01d9e7 100644 --- a/src/core/libraries/np/np_matching2/np_matching2_internal.h +++ b/src/core/libraries/np/np_matching2/np_matching2_internal.h @@ -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 bin_attrs_internal; + std::vector> bin_buffers; std::map groups; std::map members; bool owner = false; @@ -67,8 +91,10 @@ struct RoomCache { struct CallbackPayload { std::unique_ptr room_data; std::unique_ptr create_join_response; + std::unique_ptr leave_room_response; std::unique_ptr search_room_response; std::unique_ptr world_info_response; + std::unique_ptr ping_info_response; std::vector world_list; std::vector member_data; std::vector 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(); diff --git a/src/core/libraries/np/np_matching2/np_matching2_mm.cpp b/src/core/libraries/np/np_matching2/np_matching2_mm.cpp index 5102a2865..afa8ae461 100644 --- a/src/core/libraries/np/np_matching2/np_matching2_mm.cpp +++ b/src/core/libraries/np/np_matching2/np_matching2_mm.cpp @@ -4,15 +4,20 @@ #include #include #include +#include #include #include #include +#include +#include #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& payload, size_t offset = 0) return std::string(reinterpret_cast(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 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(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(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(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(std::chrono::steady_clock::now().time_since_epoch().count() ^ + (static_cast(ctx.ctx_id) << 48) ^ + (static_cast(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(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(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(pkt.room_id); + const auto member_id = static_cast(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(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::steady_clock::now() - peer.last_check_send); + peer.ping_us = + static_cast(std::min(static_cast(std::max(0, rtt.count())), + std::numeric_limits::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(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& 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(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 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 client, g_mm.server_addr = IpStringToAddr(server_host); } g_mm.server_udp_port = Libraries::Net::sceNetHtons(static_cast(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; diff --git a/src/core/libraries/np/np_matching2/np_matching2_mm.h b/src/core/libraries/np/np_matching2/np_matching2_mm.h index a0ec301dd..35abfd78b 100644 --- a/src/core/libraries/np/np_matching2/np_matching2_mm.h +++ b/src/core/libraries/np/np_matching2/np_matching2_mm.h @@ -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(); diff --git a/src/core/libraries/np/np_matching2/np_matching2_types.h b/src/core/libraries/np/np_matching2/np_matching2_types.h index dd6e2a787..ef43fa919 100644 --- a/src/core/libraries/np/np_matching2/np_matching2_types.h +++ b/src/core/libraries/np/np_matching2/np_matching2_types.h @@ -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; diff --git a/src/core/libraries/np/np_signaling/np_signaling_stubs.cpp b/src/core/libraries/np/np_signaling/np_signaling_stubs.cpp index d1ec9509d..981b97ec6 100644 --- a/src/core/libraries/np/np_signaling/np_signaling_stubs.cpp +++ b/src/core/libraries/np/np_signaling/np_signaling_stubs.cpp @@ -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; } diff --git a/src/core/libraries/np/np_signaling/np_signaling_stubs.h b/src/core/libraries/np/np_signaling/np_signaling_stubs.h index 7645cb98a..63067abc6 100644 --- a/src/core/libraries/np/np_signaling/np_signaling_stubs.h +++ b/src/core/libraries/np/np_signaling/np_signaling_stubs.h @@ -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);