mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-07-10 10:14:44 -06:00
Matching2 P3 + P4 (#4643)
* matching2 part 3: context states, initial payload holder, additional constants, enums. Matchmaking block, comid additon, ctx_id additon to shadnet.proto. * Matching2 P4: this time its matching: starting rewrites on external APIs. Shadnet changes to mirror on emu side. * Matching P5: SearchRoom
This commit is contained in:
parent
8ead142315
commit
c581ce89a9
@ -634,6 +634,8 @@ set(NP_LIBS src/core/libraries/np/np_error.h
|
||||
src/core/libraries/np/np_matching2/np_matching2.h
|
||||
src/core/libraries/np/np_matching2/np_matching2_internal.cpp
|
||||
src/core/libraries/np/np_matching2/np_matching2_internal.h
|
||||
src/core/libraries/np/np_matching2/np_matching2_mm.cpp
|
||||
src/core/libraries/np/np_matching2/np_matching2_mm.h
|
||||
src/core/libraries/np/np_matching2/np_matching2_types.h
|
||||
src/core/libraries/np/np_trophy.cpp
|
||||
src/core/libraries/np/np_trophy.h
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include "core/emulator_settings.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"
|
||||
#include "core/libraries/np/np_score/np_score.h"
|
||||
#include "core/libraries/np/np_web_api/np_web_api.h"
|
||||
#include "core/user_settings.h"
|
||||
@ -167,7 +168,7 @@ bool NpHandler::ConnectUser(s32 user_id, const std::string& host, u16 port, cons
|
||||
};
|
||||
client->onAsyncReply = [this, user_id](ShadNet::CommandType cmd, u64 pkt_id,
|
||||
ShadNet::ErrorType err, const std::vector<u8>& body) {
|
||||
OnScoreReply(user_id, cmd, pkt_id, err, body);
|
||||
OnAsyncReply(user_id, cmd, pkt_id, err, body);
|
||||
};
|
||||
client->onLoginResult = [this, user_id](const ShadNet::LoginResult& res) {
|
||||
OnLoginResult(user_id, res);
|
||||
@ -194,6 +195,8 @@ bool NpHandler::ConnectUser(s32 user_id, const std::string& host, u16 port, cons
|
||||
LOG_INFO(NpHandler, "user_id={} signed in npid='{}' accountId={}", user_id, npid,
|
||||
client->GetUserId());
|
||||
|
||||
NpMatching2::SetMmShadNetClient(client, host, port);
|
||||
|
||||
// Build OrbisNpId
|
||||
{
|
||||
OrbisNpId np_id{};
|
||||
@ -1603,6 +1606,16 @@ static u32 FillRankArrayFromProto(const shadnet::GetScoreResponse& resp,
|
||||
return found;
|
||||
}
|
||||
|
||||
void NpHandler::OnAsyncReply(s32 user_id, ShadNet::CommandType cmd, u64 pkt_id,
|
||||
ShadNet::ErrorType error, const std::vector<u8>& body) {
|
||||
const auto cmd_val = static_cast<u16>(cmd);
|
||||
if (cmd_val >= 12 && cmd_val <= 23) {
|
||||
NpMatching2::OnMatchingReply(cmd, pkt_id, error, body);
|
||||
} else {
|
||||
OnScoreReply(user_id, cmd, pkt_id, error, body);
|
||||
}
|
||||
}
|
||||
|
||||
void NpHandler::OnScoreReply(s32 user_id, ShadNet::CommandType cmd, u64 pkt_id,
|
||||
ShadNet::ErrorType error, const std::vector<u8>& body) {
|
||||
PendingScoreRequest pending;
|
||||
|
||||
@ -215,8 +215,11 @@ private:
|
||||
void OnWebApiPushEvent(s32 user_id, const ShadNet::NotifyWebApiPushEvent& n);
|
||||
void OnLoginResult(s32 user_id, const ShadNet::LoginResult& res);
|
||||
|
||||
// Async reply dispatch for score commands. Called from the per-user
|
||||
// ShadNetClient on the reader thread.
|
||||
// General async reply dispatch. Routes a reply to the owning subsystem by
|
||||
// command. Called from the per-user ShadNetClient on the reader thread.
|
||||
void OnAsyncReply(s32 user_id, ShadNet::CommandType cmd, u64 pkt_id, ShadNet::ErrorType error,
|
||||
const std::vector<u8>& body);
|
||||
|
||||
void OnScoreReply(s32 user_id, ShadNet::CommandType cmd, u64 pkt_id, ShadNet::ErrorType error,
|
||||
const std::vector<u8>& body);
|
||||
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <chrono>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/emulator_settings.h"
|
||||
@ -12,6 +11,7 @@
|
||||
#include "core/libraries/np/np_manager.h"
|
||||
#include "core/libraries/np/np_matching2/np_matching2.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_matching2/np_matching2_types.h"
|
||||
#include "core/libraries/np/np_types.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
@ -19,24 +19,18 @@
|
||||
namespace Libraries::Np::NpMatching2 {
|
||||
|
||||
static bool g_initialized = false;
|
||||
static OrbisNpMatching2ContextId contextId = 1;
|
||||
|
||||
static std::mutex g_events_mutex;
|
||||
static std::deque<NpMatching2ContextEvent> g_ctx_events;
|
||||
static std::deque<NpMatching2LobbyEvent> g_lobby_events;
|
||||
static std::deque<NpMatching2RoomEvent> g_room_events;
|
||||
static std::mutex g_responses_mutex;
|
||||
static std::deque<std::function<void()>> g_responses;
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2CreateContext(const OrbisNpMatching2CreateContextParameter* param,
|
||||
OrbisNpMatching2ContextId* ctxId) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, npId = {}, serviceLabel = {}, size = {}",
|
||||
param->npId->handle.data, param->serviceLabel, param->size);
|
||||
LOG_INFO(Lib_NpMatching2, "called, npId = {}, serviceLabel = {}, size = {}",
|
||||
param->npId->handle.data, param->serviceLabel, param->size);
|
||||
|
||||
if (!g_initialized) {
|
||||
LOG_ERROR(Lib_NpMatching2, "not initialized");
|
||||
return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (!param || param->size != 0x28 || !ctxId) {
|
||||
LOG_ERROR(Lib_NpMatching2, "param null, invalid size, or ctxId null");
|
||||
return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
@ -45,13 +39,15 @@ int PS4_SYSV_ABI sceNpMatching2CreateContext(const OrbisNpMatching2CreateContext
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2CreateContextA(const OrbisNpMatching2CreateContextParameterA* param,
|
||||
OrbisNpMatching2ContextId* ctxId) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, userId = {}, serviceLabel = {}, size = {}", param->userId,
|
||||
param->serviceLabel, param->size);
|
||||
LOG_INFO(Lib_NpMatching2, "called, userId = {}, serviceLabel = {}, size = {}", param->userId,
|
||||
param->serviceLabel, param->size);
|
||||
|
||||
if (!g_initialized) {
|
||||
LOG_ERROR(Lib_NpMatching2, "not initialized");
|
||||
return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (!param || param->size != 0x10 || !ctxId) {
|
||||
LOG_ERROR(Lib_NpMatching2, "param null, invalid size, or ctxId null");
|
||||
return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
@ -61,219 +57,215 @@ int PS4_SYSV_ABI sceNpMatching2CreateContextA(const OrbisNpMatching2CreateContex
|
||||
return ContextManager::Instance().CreateContext(&np_id, param->serviceLabel, ctxId);
|
||||
}
|
||||
|
||||
static std::optional<OrbisNpMatching2RequestOptParam> defaultRequestOptParam = std::nullopt;
|
||||
static void StoreRequestCallback(ContextObject* ctx, OrbisNpMatching2RequestOptParam* requestOpt) {
|
||||
if (requestOpt && requestOpt->callback) {
|
||||
ctx->default_request_callback = requestOpt->callback;
|
||||
ctx->default_request_callback_arg = requestOpt->arg;
|
||||
}
|
||||
}
|
||||
|
||||
auto GetOptParam(OrbisNpMatching2RequestOptParam* requestOpt) {
|
||||
return requestOpt ? *requestOpt
|
||||
: (defaultRequestOptParam ? defaultRequestOptParam
|
||||
: std::optional<OrbisNpMatching2RequestOptParam>{});
|
||||
int PS4_SYSV_ABI sceNpMatching2CreateJoinRoom(OrbisNpMatching2ContextId ctxId,
|
||||
OrbisNpMatching2CreateJoinRoomRequest* request,
|
||||
OrbisNpMatching2RequestOptParam* requestOpt,
|
||||
OrbisNpMatching2RequestId* requestId) {
|
||||
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}", ctxId);
|
||||
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;
|
||||
MmCreateJoinRoom(ctxId, reqId, *request);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2JoinRoom(OrbisNpMatching2ContextId ctxId,
|
||||
OrbisNpMatching2JoinRoomRequest* request,
|
||||
OrbisNpMatching2RequestOptParam* requestOpt,
|
||||
OrbisNpMatching2RequestId* requestId) {
|
||||
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}", ctxId);
|
||||
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;
|
||||
MmJoinRoom(ctxId, reqId, *request);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2CreateJoinRoomA(OrbisNpMatching2ContextId ctxId,
|
||||
OrbisNpMatching2CreateJoinRoomRequestA* request,
|
||||
OrbisNpMatching2RequestOptParam* requestOpt,
|
||||
OrbisNpMatching2RequestId* requestId) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt));
|
||||
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;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Lib_NpMatching2,
|
||||
"maxSlot = {}, teamId = {}, worldId = {}, lobbyId = {}, groupConfig = {}, "
|
||||
"joinGroupLabel = {}",
|
||||
request->maxSlot, request->teamId, request->worldId, request->lobbyId,
|
||||
request->groupConfig, request->joinGroupLabel);
|
||||
|
||||
static OrbisNpMatching2RequestId id = 10;
|
||||
*requestId = id++;
|
||||
|
||||
if (auto optParam = GetOptParam(requestOpt); optParam) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "optParam.timeout = {}, optParam.appId = {}", optParam->timeout,
|
||||
optParam->appId);
|
||||
std::scoped_lock lk{g_responses_mutex};
|
||||
auto reqIdCopy = *requestId;
|
||||
auto requestCopy = *request;
|
||||
g_responses.emplace_back([=]() {
|
||||
Libraries::Np::OrbisNpOnlineId onlineId{};
|
||||
if (NpManager::sceNpGetOnlineId(1, &onlineId) != ORBIS_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
OrbisNpMatching2RoomMemberDataInternalA me{
|
||||
nullptr,
|
||||
0,
|
||||
{0xace104e, Libraries::Np::OrbisNpPlatformType::PS4},
|
||||
onlineId,
|
||||
{0, 0, 0, 0},
|
||||
1,
|
||||
requestCopy.teamId,
|
||||
1,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0};
|
||||
OrbisNpMatching2RoomDataInternal room{requestCopy.maxSlot,
|
||||
0,
|
||||
static_cast<u16>(requestCopy.maxSlot - 1u),
|
||||
0,
|
||||
15,
|
||||
0xac,
|
||||
requestCopy.worldId,
|
||||
requestCopy.lobbyId,
|
||||
0x10,
|
||||
0,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
0,
|
||||
{0, 0, 0, 0},
|
||||
nullptr,
|
||||
0};
|
||||
OrbisNpMatching2CreateJoinRoomResponseA resp{&room, {&me, 1, &me, &me}};
|
||||
optParam->callback(ctxId, reqIdCopy,
|
||||
ORBIS_NP_MATCHING2_REQUEST_EVENT_CREATE_JOIN_ROOM_A, 0, &resp,
|
||||
optParam->arg);
|
||||
});
|
||||
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;
|
||||
LOG_WARNING(Lib_NpMatching2, "not implemented");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
std::function<void(const NpMatching2ContextEvent*)> npMatching2ContextCallback = nullptr;
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2RegisterContextCallback(OrbisNpMatching2ContextCallback callback,
|
||||
void* userdata) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, userdata = {}", userdata);
|
||||
LOG_INFO(Lib_NpMatching2, "called, userdata = {}", userdata);
|
||||
|
||||
if (!g_initialized) {
|
||||
LOG_ERROR(Lib_NpMatching2, "not initialized");
|
||||
return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
npMatching2ContextCallback = [callback, userdata](auto arg) {
|
||||
callback(arg->contextId, arg->event, arg->cause, arg->errorCode, userdata);
|
||||
};
|
||||
|
||||
ContextManager::Instance().ApplyContextCallback(callback, userdata);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
std::function<void(const NpMatching2LobbyEvent*)> npMatching2LobbyCallback = nullptr;
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2RegisterLobbyEventCallback(
|
||||
OrbisNpMatching2ContextId ctxId, OrbisNpMatching2LobbyEventCallback callback, void* userdata) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, userdata = {}", ctxId, 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;
|
||||
}
|
||||
|
||||
npMatching2LobbyCallback = [callback, userdata](auto arg) {
|
||||
callback(arg->contextId, arg->lobbyId, arg->event, arg->data, userdata);
|
||||
};
|
||||
|
||||
ctx->lobby_event_callback = callback;
|
||||
ctx->lobby_event_callback_arg = userdata;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
std::function<void(const NpMatching2RoomEvent*)> npMatching2RoomCallback = nullptr;
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2RegisterRoomEventCallback(OrbisNpMatching2ContextId ctxId,
|
||||
OrbisNpMatching2RoomEventCallback callback,
|
||||
void* userdata) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, userdata = {}", ctxId, 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;
|
||||
}
|
||||
|
||||
npMatching2RoomCallback = [callback, userdata](auto arg) {
|
||||
callback(arg->contextId, arg->roomId, arg->event, arg->data, userdata);
|
||||
};
|
||||
|
||||
ctx->room_event_callback = callback;
|
||||
ctx->room_event_callback_arg = userdata;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
std::function<void(const OrbisNpMatching2SignalingEvent*)> npMatching2SignalingCallback = nullptr;
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2RegisterSignalingCallback(OrbisNpMatching2ContextId ctxId,
|
||||
OrbisNpMatching2SignalingCallback callback,
|
||||
void* userdata) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, userdata = {}", ctxId, 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;
|
||||
}
|
||||
|
||||
npMatching2SignalingCallback = [callback, userdata](auto arg) {
|
||||
callback(arg->contextId, arg->roomId, arg->roomMemberId, arg->event, arg->errorCode,
|
||||
userdata);
|
||||
};
|
||||
|
||||
ctx->signaling_callback = callback;
|
||||
ctx->signaling_callback_arg = userdata;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2ContextStart(OrbisNpMatching2ContextId ctxId, u64 timeout) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, timeout = {}", ctxId, timeout);
|
||||
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}, timeout = {}", ctxId, timeout);
|
||||
|
||||
if (!g_initialized) {
|
||||
LOG_ERROR(Lib_NpMatching2, "not initialized");
|
||||
return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{g_events_mutex};
|
||||
if (EmulatorSettings.IsConnectedToNetwork() && EmulatorSettings.IsShadNetEnabled()) {
|
||||
g_ctx_events.emplace_back(ctxId, ORBIS_NP_MATCHING2_CONTEXT_EVENT_STARTED,
|
||||
ORBIS_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ACTION, 0);
|
||||
} else {
|
||||
if (!EmulatorSettings.IsConnectedToNetwork() || !EmulatorSettings.IsShadNetEnabled()) {
|
||||
// error confirmed with a real console disconnected from the internet
|
||||
constexpr int ORBIS_NET_ERROR_RESOLVER_ETIMEDOUT = 0x804101e2;
|
||||
g_ctx_events.emplace_back(ctxId, ORBIS_NP_MATCHING2_CONTEXT_EVENT_START_OVER,
|
||||
ORBIS_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ERROR,
|
||||
ORBIS_NET_ERROR_RESOLVER_ETIMEDOUT);
|
||||
LOG_ERROR(Lib_NpMatching2, "not connected to network");
|
||||
return ORBIS_NET_ERROR_RESOLVER_ETIMEDOUT;
|
||||
}
|
||||
|
||||
const s32 rc = ContextManager::Instance().Start(ctxId);
|
||||
if (rc != ORBIS_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
MmContextStart(ctxId);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void ProcessEvents() {
|
||||
{
|
||||
std::scoped_lock lk{g_events_mutex};
|
||||
int PS4_SYSV_ABI sceNpMatching2ContextStop(OrbisNpMatching2ContextId ctxId) {
|
||||
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}", ctxId);
|
||||
|
||||
if (npMatching2ContextCallback) {
|
||||
while (!g_ctx_events.empty()) {
|
||||
npMatching2ContextCallback(&g_ctx_events.front());
|
||||
g_ctx_events.pop_front();
|
||||
}
|
||||
}
|
||||
if (npMatching2LobbyCallback) {
|
||||
while (!g_lobby_events.empty()) {
|
||||
npMatching2LobbyCallback(&g_lobby_events.front());
|
||||
g_lobby_events.pop_front();
|
||||
}
|
||||
}
|
||||
if (npMatching2RoomCallback) {
|
||||
while (!g_room_events.empty()) {
|
||||
npMatching2RoomCallback(&g_room_events.front());
|
||||
g_room_events.pop_front();
|
||||
}
|
||||
}
|
||||
if (!g_initialized) {
|
||||
LOG_ERROR(Lib_NpMatching2, "not initialized");
|
||||
return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{g_responses_mutex};
|
||||
while (!g_responses.empty()) {
|
||||
(g_responses.front())();
|
||||
g_responses.pop_front();
|
||||
const s32 rc = ContextManager::Instance().Stop(ctxId);
|
||||
if (rc != ORBIS_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
MmContextStop(ctxId);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2Initialize(OrbisNpMatching2InitializeParameter* param) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called");
|
||||
LOG_INFO(Lib_NpMatching2, "called");
|
||||
|
||||
if (g_initialized) {
|
||||
LOG_ERROR(Lib_NpMatching2, "already initialized");
|
||||
return ORBIS_NP_MATCHING2_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!param || (param->size != 0x30 && param->size != 0x28)) {
|
||||
LOG_ERROR(Lib_NpMatching2, "param null or invalid size");
|
||||
return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
@ -287,21 +279,20 @@ int PS4_SYSV_ABI sceNpMatching2Initialize(OrbisNpMatching2InitializeParameter* p
|
||||
|
||||
g_initialized = true;
|
||||
g_state.initialized.store(true);
|
||||
Libraries::Np::NpManager::RegisterNpCallback("NpMatching2", ProcessEvents);
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2Terminate() {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called");
|
||||
LOG_INFO(Lib_NpMatching2, "called");
|
||||
|
||||
if (!g_initialized) {
|
||||
LOG_ERROR(Lib_NpMatching2, "not initialized");
|
||||
return ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
g_initialized = false;
|
||||
g_state.initialized.store(false);
|
||||
Libraries::Np::NpManager::DeregisterNpCallback("NpMatching2");
|
||||
|
||||
TermEventDispatcher();
|
||||
ContextManager::Instance().Reset();
|
||||
@ -311,32 +302,42 @@ int PS4_SYSV_ABI sceNpMatching2Terminate() {
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2SetDefaultRequestOptParam(
|
||||
OrbisNpMatching2ContextId ctxId, OrbisNpMatching2RequestOptParam* requestOpt) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}", 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 (!requestOpt) {
|
||||
LOG_ERROR(Lib_NpMatching2, "requestOpt 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;
|
||||
}
|
||||
|
||||
defaultRequestOptParam = *requestOpt;
|
||||
ctx->default_request_callback = requestOpt->callback;
|
||||
ctx->default_request_callback_arg = requestOpt->arg;
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2GetServerId(OrbisNpMatching2ContextId ctxId,
|
||||
OrbisNpMatching2ServerId* serverId) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}", 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 (!serverId) {
|
||||
LOG_ERROR(Lib_NpMatching2, "serverId null");
|
||||
return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
*serverId = 0xac;
|
||||
*serverId = 1;
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
@ -345,33 +346,28 @@ int PS4_SYSV_ABI sceNpMatching2GetWorldInfoList(OrbisNpMatching2ContextId ctxId,
|
||||
OrbisNpMatching2GetWorldInfoListRequest* request,
|
||||
OrbisNpMatching2RequestOptParam* requestOpt,
|
||||
OrbisNpMatching2RequestId* requestId) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, request.serverId = {}, requestOpt = {}", ctxId,
|
||||
request ? request->serverId : 0xFFFF, fmt::ptr(requestOpt));
|
||||
LOG_INFO(Lib_NpMatching2, "called, ctxId = {}, request.serverId = {}, requestOpt = {}", ctxId,
|
||||
request ? request->serverId : 0xFFFF, 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;
|
||||
}
|
||||
|
||||
static OrbisNpMatching2RequestId id = 1;
|
||||
*requestId = id++;
|
||||
|
||||
if (auto optParam = GetOptParam(requestOpt); optParam) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "optParam.timeout = {}, optParam.appId = {}", optParam->timeout,
|
||||
optParam->appId);
|
||||
auto reqIdCopy = *requestId;
|
||||
std::scoped_lock lk{g_responses_mutex};
|
||||
g_responses.emplace_back([=]() {
|
||||
OrbisNpMatching2World w{nullptr, 1, 10, 0, 10, 0, {}};
|
||||
OrbisNpMatching2GetWorldInfoListResponse resp{&w, 1};
|
||||
optParam->callback(ctxId, reqIdCopy,
|
||||
ORBIS_NP_MATCHING2_REQUEST_EVENT_GET_WORLD_INFO_LIST, 0, &resp,
|
||||
optParam->arg);
|
||||
});
|
||||
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;
|
||||
MmGetWorldInfoList(ctxId, reqId, *request);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -379,29 +375,26 @@ int PS4_SYSV_ABI sceNpMatching2LeaveRoom(OrbisNpMatching2ContextId ctxId,
|
||||
OrbisNpMatching2LeaveRoomRequest* request,
|
||||
OrbisNpMatching2RequestOptParam* requestOpt,
|
||||
OrbisNpMatching2RequestId* requestId) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt));
|
||||
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;
|
||||
}
|
||||
|
||||
static OrbisNpMatching2RequestId id = 500;
|
||||
*requestId = id++;
|
||||
|
||||
if (auto optParam = GetOptParam(requestOpt); optParam) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "optParam.timeout = {}, optParam.appId = {}", optParam->timeout,
|
||||
optParam->appId);
|
||||
std::scoped_lock lk{g_responses_mutex};
|
||||
auto reqIdCopy = *requestId;
|
||||
g_responses.emplace_back([=]() {
|
||||
optParam->callback(ctxId, reqIdCopy, ORBIS_NP_MATCHING2_REQUEST_EVENT_LEAVE_ROOM, 0,
|
||||
nullptr, optParam->arg);
|
||||
});
|
||||
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;
|
||||
MmLeaveRoom(ctxId, reqId, *request);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -409,31 +402,27 @@ int PS4_SYSV_ABI sceNpMatching2SearchRoom(OrbisNpMatching2ContextId ctxId,
|
||||
OrbisNpMatching2SearchRoomRequest* request,
|
||||
OrbisNpMatching2RequestOptParam* requestOpt,
|
||||
OrbisNpMatching2RequestId* requestId) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt));
|
||||
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;
|
||||
}
|
||||
|
||||
static OrbisNpMatching2RequestId id = 1;
|
||||
*requestId = id++;
|
||||
|
||||
if (auto optParam = GetOptParam(requestOpt); optParam) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "optParam.timeout = {}, optParam.appId = {}", optParam->timeout,
|
||||
optParam->appId);
|
||||
std::scoped_lock lk{g_responses_mutex};
|
||||
auto reqIdCopy = *requestId;
|
||||
auto requestCopy = *request;
|
||||
g_responses.emplace_back([=]() {
|
||||
OrbisNpMatching2SearchRoomResponseA resp{{0, 0, 0, {}}, nullptr};
|
||||
optParam->callback(ctxId, reqIdCopy, ORBIS_NP_MATCHING2_REQUEST_EVENT_SEARCH_ROOM_A, 0,
|
||||
&resp, optParam->arg);
|
||||
});
|
||||
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;
|
||||
MmSearchRoom(ctxId, reqId, *request);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -441,118 +430,104 @@ int PS4_SYSV_ABI sceNpMatching2SetUserInfo(OrbisNpMatching2ContextId ctxId,
|
||||
OrbisNpMatching2SetUserInfoRequest* request,
|
||||
OrbisNpMatching2RequestOptParam* requestOpt,
|
||||
OrbisNpMatching2RequestId* requestId) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt));
|
||||
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;
|
||||
}
|
||||
|
||||
static OrbisNpMatching2RequestId id = 100;
|
||||
*requestId = id++;
|
||||
|
||||
if (auto optParam = GetOptParam(requestOpt); optParam) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "optParam.timeout = {}, optParam.appId = {}", optParam->timeout,
|
||||
optParam->appId);
|
||||
std::scoped_lock lk{g_responses_mutex};
|
||||
auto reqIdCopy = *requestId;
|
||||
g_responses.emplace_back([=]() {
|
||||
optParam->callback(ctxId, reqIdCopy, ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_USER_INFO, 0,
|
||||
nullptr, optParam->arg);
|
||||
});
|
||||
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);
|
||||
*requestId = AllocRequestId();
|
||||
LOG_WARNING(Lib_NpMatching2, "not implemented");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2SendRoomMessage(OrbisNpMatching2ContextId ctxId, void* request,
|
||||
OrbisNpMatching2RequestOptParam* requestOpt,
|
||||
OrbisNpMatching2RequestId* requestId) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt));
|
||||
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;
|
||||
}
|
||||
|
||||
static OrbisNpMatching2RequestId id = 1000;
|
||||
*requestId = id++;
|
||||
|
||||
if (auto optParam = GetOptParam(requestOpt); optParam) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "optParam.timeout = {}, optParam.appId = {}", optParam->timeout,
|
||||
optParam->appId);
|
||||
std::scoped_lock lk{g_responses_mutex};
|
||||
auto reqIdCopy = *requestId;
|
||||
g_responses.emplace_back([=]() {
|
||||
optParam->callback(ctxId, reqIdCopy, ORBIS_NP_MATCHING2_REQUEST_EVENT_SEND_ROOM_MESSAGE,
|
||||
0, nullptr, optParam->arg);
|
||||
});
|
||||
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);
|
||||
*requestId = AllocRequestId();
|
||||
LOG_WARNING(Lib_NpMatching2, "not implemented");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2SetRoomDataExternal(OrbisNpMatching2ContextId ctxId, void* request,
|
||||
OrbisNpMatching2RequestOptParam* requestOpt,
|
||||
OrbisNpMatching2RequestId* requestId) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt));
|
||||
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;
|
||||
}
|
||||
|
||||
static OrbisNpMatching2RequestId id = 800;
|
||||
*requestId = id++;
|
||||
|
||||
if (auto optParam = GetOptParam(requestOpt); optParam) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "optParam.timeout = {}, optParam.appId = {}", optParam->timeout,
|
||||
optParam->appId);
|
||||
std::scoped_lock lk{g_responses_mutex};
|
||||
auto reqIdCopy = *requestId;
|
||||
g_responses.emplace_back([=]() {
|
||||
optParam->callback(ctxId, reqIdCopy,
|
||||
ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_ROOM_DATA_EXTERNAL, 0, nullptr,
|
||||
optParam->arg);
|
||||
});
|
||||
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);
|
||||
*requestId = AllocRequestId();
|
||||
LOG_WARNING(Lib_NpMatching2, "not implemented");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2SetRoomDataInternal(OrbisNpMatching2ContextId ctxId, void* request,
|
||||
OrbisNpMatching2RequestOptParam* requestOpt,
|
||||
OrbisNpMatching2RequestId* requestId) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "called, ctxId = {}, requestOpt = {}", ctxId, fmt::ptr(requestOpt));
|
||||
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;
|
||||
}
|
||||
|
||||
static OrbisNpMatching2RequestId id = 200;
|
||||
*requestId = id++;
|
||||
|
||||
if (auto optParam = GetOptParam(requestOpt); optParam) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "optParam.timeout = {}, optParam.appId = {}", optParam->timeout,
|
||||
optParam->appId);
|
||||
std::scoped_lock lk{g_responses_mutex};
|
||||
auto reqIdCopy = *requestId;
|
||||
g_responses.emplace_back([=]() {
|
||||
optParam->callback(ctxId, reqIdCopy,
|
||||
ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_ROOM_DATA_INTERNAL, 0, nullptr,
|
||||
optParam->arg);
|
||||
});
|
||||
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);
|
||||
*requestId = AllocRequestId();
|
||||
LOG_WARNING(Lib_NpMatching2, "not implemented");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -567,6 +542,10 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
sceNpMatching2CreateContextA);
|
||||
LIB_FUNCTION("V6KSpKv9XJE", "libSceNpMatching2", 1, "libSceNpMatching2",
|
||||
sceNpMatching2CreateJoinRoomA);
|
||||
LIB_FUNCTION("zCWZmXXN600", "libSceNpMatching2", 1, "libSceNpMatching2",
|
||||
sceNpMatching2CreateJoinRoom);
|
||||
LIB_FUNCTION("CSIMDsVjs-g", "libSceNpMatching2", 1, "libSceNpMatching2",
|
||||
sceNpMatching2JoinRoom);
|
||||
LIB_FUNCTION("fQQfP87I7hs", "libSceNpMatching2", 1, "libSceNpMatching2",
|
||||
sceNpMatching2RegisterContextCallback);
|
||||
LIB_FUNCTION("4Nj7u5B5yCA", "libSceNpMatching2", 1, "libSceNpMatching2",
|
||||
@ -577,6 +556,8 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
sceNpMatching2RegisterSignalingCallback);
|
||||
LIB_FUNCTION("7vjNQ6Z1op0", "libSceNpMatching2", 1, "libSceNpMatching2",
|
||||
sceNpMatching2ContextStart);
|
||||
LIB_FUNCTION("-f6M4caNe8k", "libSceNpMatching2", 1, "libSceNpMatching2",
|
||||
sceNpMatching2ContextStop);
|
||||
LIB_FUNCTION("LhCPctIICxQ", "libSceNpMatching2", 1, "libSceNpMatching2",
|
||||
sceNpMatching2GetServerId);
|
||||
LIB_FUNCTION("rJNPJqDCpiI", "libSceNpMatching2", 1, "libSceNpMatching2",
|
||||
|
||||
@ -85,6 +85,44 @@ enum OrbisNpMatching2SignalingType : u8 {
|
||||
ORBIS_NP_MATCHING2_SIGNALING_TYPE_STAR = 2,
|
||||
};
|
||||
|
||||
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,
|
||||
ORBIS_NP_MATCHING2_ROOM_SEARCHABLE_INT_ATTR_EXTERNAL_3_ID = 0x004E,
|
||||
ORBIS_NP_MATCHING2_ROOM_SEARCHABLE_INT_ATTR_EXTERNAL_4_ID = 0x004F,
|
||||
ORBIS_NP_MATCHING2_ROOM_SEARCHABLE_INT_ATTR_EXTERNAL_5_ID = 0x0050,
|
||||
ORBIS_NP_MATCHING2_ROOM_SEARCHABLE_INT_ATTR_EXTERNAL_6_ID = 0x0051,
|
||||
ORBIS_NP_MATCHING2_ROOM_SEARCHABLE_INT_ATTR_EXTERNAL_7_ID = 0x0052,
|
||||
ORBIS_NP_MATCHING2_ROOM_SEARCHABLE_INT_ATTR_EXTERNAL_8_ID = 0x0053,
|
||||
ORBIS_NP_MATCHING2_ROOM_SEARCHABLE_BIN_ATTR_EXTERNAL_1_ID = 0x0054,
|
||||
ORBIS_NP_MATCHING2_ROOM_BIN_ATTR_EXTERNAL_1_ID = 0x0055,
|
||||
ORBIS_NP_MATCHING2_ROOM_BIN_ATTR_EXTERNAL_2_ID = 0x0056,
|
||||
ORBIS_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_1_ID = 0x0057,
|
||||
ORBIS_NP_MATCHING2_ROOM_BIN_ATTR_INTERNAL_2_ID = 0x0058,
|
||||
ORBIS_NP_MATCHING2_ROOMMEMBER_BIN_ATTR_INTERNAL_1_ID = 0x0059,
|
||||
};
|
||||
|
||||
enum OrbisNpMatching2RoomFlagAttr : OrbisNpMatching2Flags {
|
||||
ORBIS_NP_MATCHING2_ROOM_FLAG_ATTR_OWNER_AUTO_GRANT = 0x80000000,
|
||||
ORBIS_NP_MATCHING2_ROOM_FLAG_ATTR_CLOSED = 0x40000000,
|
||||
ORBIS_NP_MATCHING2_ROOM_FLAG_ATTR_FULL = 0x20000000,
|
||||
ORBIS_NP_MATCHING2_ROOM_FLAG_ATTR_HIDDEN = 0x10000000,
|
||||
ORBIS_NP_MATCHING2_ROOM_FLAG_ATTR_NAT_TYPE_RESTRICTION = 0x04000000,
|
||||
ORBIS_NP_MATCHING2_ROOM_FLAG_ATTR_PROHIBITIVE_MODE = 0x02000000,
|
||||
};
|
||||
|
||||
enum OrbisNpMatching2RoomMemberFlagAttr : OrbisNpMatching2Flags {
|
||||
ORBIS_NP_MATCHING2_ROOMMEMBER_FLAG_ATTR_OWNER = 0x80000000,
|
||||
};
|
||||
|
||||
enum OrbisNpMatching2Role : u8 {
|
||||
ORBIS_NP_MATCHING2_ROLE_MEMBER = 1,
|
||||
ORBIS_NP_MATCHING2_ROLE_OWNER = 2,
|
||||
};
|
||||
|
||||
constexpr u32 ORBIS_NP_MATCHING2_SESSION_PASSWORD_SIZE = 8;
|
||||
constexpr u32 ORBIS_NP_MATCHING2_GROUP_LABEL_SIZE = 8;
|
||||
|
||||
enum OrbisNpMatching2InitDefault : u64 {
|
||||
ORBIS_NP_MATCHING2_THREAD_STACK_SIZE_DEFAULT = 32768,
|
||||
ORBIS_NP_MATCHING2_POOLSIZE_DEFAULT = 131072,
|
||||
|
||||
@ -1,17 +1,268 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/thread.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"
|
||||
#include "shadnet.pb.h"
|
||||
|
||||
namespace Libraries::Np::NpMatching2 {
|
||||
|
||||
NpMatching2State g_state;
|
||||
|
||||
void* BuildCreateJoinRoomPayload(ContextObject& ctx, const shadnet::CreateJoinRoomResponse& resp) {
|
||||
CallbackPayload& p = ctx.request_payload;
|
||||
p.Reset();
|
||||
|
||||
const auto& rd = resp.room_data();
|
||||
|
||||
p.room_groups.resize(rd.groups_size());
|
||||
for (int i = 0; i < rd.groups_size(); ++i) {
|
||||
const auto& g = rd.groups(i);
|
||||
auto& dst = p.room_groups[i];
|
||||
dst.id = static_cast<OrbisNpMatching2RoomGroupId>(g.group_id());
|
||||
dst.hasPasswd = g.has_passwd();
|
||||
dst.hasLabel = g.has_label();
|
||||
std::memset(dst.label.data, 0, sizeof(dst.label.data));
|
||||
std::memcpy(dst.label.data, g.label().data(),
|
||||
std::min(g.label().size(), sizeof(dst.label.data)));
|
||||
dst.slots = g.slot_count();
|
||||
dst.groupMembers = g.num_members();
|
||||
}
|
||||
|
||||
p.room_bin_attrs.resize(rd.bin_attrs_internal_size());
|
||||
for (int i = 0; i < rd.bin_attrs_internal_size(); ++i) {
|
||||
const auto& a = rd.bin_attrs_internal(i);
|
||||
p.bin_buffers.emplace_back(a.data().begin(), a.data().end());
|
||||
auto& buf = p.bin_buffers.back();
|
||||
auto& dst = p.room_bin_attrs[i];
|
||||
dst = {};
|
||||
dst.binAttr.id = static_cast<OrbisNpMatching2AttributeId>(a.attr_id());
|
||||
dst.binAttr.data = buf.empty() ? nullptr : buf.data();
|
||||
dst.binAttr.dataSize = buf.size();
|
||||
}
|
||||
|
||||
p.room_data = std::make_unique<OrbisNpMatching2RoomDataInternal>();
|
||||
auto& room = *p.room_data;
|
||||
room = {};
|
||||
room.publicSlots = static_cast<u16>(rd.public_slots());
|
||||
room.privateSlots = static_cast<u16>(rd.private_slots());
|
||||
room.openPublicSlots = static_cast<u16>(rd.open_public_slots());
|
||||
room.openPrivateSlots = static_cast<u16>(rd.open_private_slots());
|
||||
room.maxSlot = static_cast<u16>(rd.max_slot());
|
||||
room.serverId = static_cast<OrbisNpMatching2ServerId>(rd.server_id());
|
||||
room.worldId = rd.world_id();
|
||||
room.lobbyId = rd.lobby_id();
|
||||
room.roomId = rd.room_id();
|
||||
room.passwdSlotMask = rd.passwd_slot_mask();
|
||||
room.joinedSlotMask = rd.joined_slot_mask();
|
||||
room.flags = rd.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();
|
||||
|
||||
const int member_count = resp.members_size();
|
||||
p.member_data.resize(member_count);
|
||||
p.member_bin_attrs.clear();
|
||||
for (int i = 0; i < member_count; ++i) {
|
||||
const auto& m = resp.members(i);
|
||||
auto& dst = p.member_data[i];
|
||||
dst = {};
|
||||
dst.next = (i + 1 < member_count) ? &p.member_data[i + 1] : nullptr;
|
||||
std::strncpy(dst.npId.handle.data, m.npid().c_str(), sizeof(dst.npId.handle.data) - 1);
|
||||
dst.memberId = static_cast<OrbisNpMatching2RoomMemberId>(m.member_id());
|
||||
dst.teamId = static_cast<OrbisNpMatching2TeamId>(m.team_id());
|
||||
if (m.is_owner()) {
|
||||
dst.flagAttr |= ORBIS_NP_MATCHING2_ROOMMEMBER_FLAG_ATTR_OWNER;
|
||||
}
|
||||
}
|
||||
|
||||
p.create_join_response = std::make_unique<OrbisNpMatching2CreateJoinRoomResponse>();
|
||||
auto& out = *p.create_join_response;
|
||||
out.roomData = p.room_data.get();
|
||||
out.members.members = p.member_data.empty() ? nullptr : p.member_data.data();
|
||||
out.members.membersNum = p.member_data.size();
|
||||
out.members.me = nullptr;
|
||||
out.members.owner = nullptr;
|
||||
for (auto& md : p.member_data) {
|
||||
if (md.memberId == static_cast<OrbisNpMatching2RoomMemberId>(resp.me_member_id())) {
|
||||
out.members.me = &md;
|
||||
}
|
||||
if (md.memberId == static_cast<OrbisNpMatching2RoomMemberId>(resp.owner_member_id())) {
|
||||
out.members.owner = &md;
|
||||
}
|
||||
}
|
||||
|
||||
RoomCache& rc = ctx.room_cache[room.roomId];
|
||||
rc = RoomCache{};
|
||||
rc.num_slots = room.maxSlot;
|
||||
rc.mask_password = room.passwdSlotMask;
|
||||
rc.owner = resp.me_member_id() == resp.owner_member_id();
|
||||
for (const auto& g : p.room_groups) {
|
||||
rc.groups[g.id] = g;
|
||||
}
|
||||
for (int i = 0; i < member_count; ++i) {
|
||||
const auto& m = resp.members(i);
|
||||
MemberCache& mc = rc.members[static_cast<OrbisNpMatching2RoomMemberId>(m.member_id())];
|
||||
mc = MemberCache{};
|
||||
mc.member_id = static_cast<OrbisNpMatching2RoomMemberId>(m.member_id());
|
||||
mc.team_id = static_cast<OrbisNpMatching2TeamId>(m.team_id());
|
||||
mc.nat_type = static_cast<OrbisNpMatching2NatType>(m.nat_type());
|
||||
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;
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
p.request_data = p.create_join_response.get();
|
||||
return p.request_data;
|
||||
}
|
||||
|
||||
void* BuildGetWorldInfoListPayload(ContextObject& ctx, const shadnet::GetWorldInfoListReply& resp) {
|
||||
CallbackPayload& p = ctx.request_payload;
|
||||
p.Reset();
|
||||
|
||||
p.world_list.resize(resp.worlds_size());
|
||||
for (int i = 0; i < resp.worlds_size(); ++i) {
|
||||
const auto& w = resp.worlds(i);
|
||||
auto& dst = p.world_list[i];
|
||||
dst = OrbisNpMatching2World{};
|
||||
dst.worldId = static_cast<OrbisNpMatching2WorldId>(w.world_id());
|
||||
dst.lobbiesNum = w.lobbies_num();
|
||||
dst.maxLobbyMembersNum = w.max_lobby_members();
|
||||
dst.lobbyMembersNum = w.lobby_members_num();
|
||||
dst.roomsNum = w.rooms_num();
|
||||
dst.roomMembersNum = w.room_members_num();
|
||||
}
|
||||
for (size_t i = 0; i + 1 < p.world_list.size(); ++i) {
|
||||
p.world_list[i].next = &p.world_list[i + 1];
|
||||
}
|
||||
|
||||
p.world_info_response = std::make_unique<OrbisNpMatching2GetWorldInfoListResponse>();
|
||||
p.world_info_response->world = p.world_list.empty() ? nullptr : p.world_list.data();
|
||||
p.world_info_response->worldNum = p.world_list.size();
|
||||
|
||||
p.request_data = p.world_info_response.get();
|
||||
return p.request_data;
|
||||
}
|
||||
|
||||
void* BuildSearchRoomPayload(ContextObject& ctx, const shadnet::SearchRoomReply& resp) {
|
||||
CallbackPayload& p = ctx.request_payload;
|
||||
p.Reset();
|
||||
|
||||
const int room_count = resp.rooms_size();
|
||||
p.room_data_external.resize(room_count);
|
||||
for (int i = 0; i < room_count; ++i) {
|
||||
const auto& r = resp.rooms(i);
|
||||
auto& dst = p.room_data_external[i];
|
||||
dst = OrbisNpMatching2RoomDataExternal{};
|
||||
dst.next = (i + 1 < room_count) ? &p.room_data_external[i + 1] : nullptr;
|
||||
dst.maxSlot = static_cast<u16>(r.max_slot());
|
||||
dst.curMembers = static_cast<u16>(r.cur_members());
|
||||
dst.flags = r.flags();
|
||||
dst.serverId = static_cast<OrbisNpMatching2ServerId>(r.server_id());
|
||||
dst.worldId = static_cast<OrbisNpMatching2WorldId>(r.world_id());
|
||||
dst.lobbyId = r.lobby_id();
|
||||
dst.roomId = r.room_id();
|
||||
dst.passwdSlotMask = r.passwd_slot_mask();
|
||||
dst.joinedSlotMask = r.joined_slot_mask();
|
||||
dst.publicSlots = static_cast<u16>(r.public_slots());
|
||||
dst.privateSlots = static_cast<u16>(r.private_slots());
|
||||
dst.openPublicSlots = static_cast<u16>(r.open_public_slots());
|
||||
dst.openPrivateSlots = static_cast<u16>(r.open_private_slots());
|
||||
|
||||
if (!r.owner_npid().empty()) {
|
||||
auto& npid = p.ext_owner_npids.emplace_back();
|
||||
npid = Libraries::Np::OrbisNpId{};
|
||||
std::strncpy(npid.handle.data, r.owner_npid().c_str(), sizeof(npid.handle.data) - 1);
|
||||
dst.ownerNpId = &npid;
|
||||
}
|
||||
|
||||
if (r.groups_size() > 0) {
|
||||
OrbisNpMatching2RoomGroupInfo* first = nullptr;
|
||||
for (int g = 0; g < r.groups_size(); ++g) {
|
||||
const auto& src = r.groups(g);
|
||||
auto& gi = p.ext_room_groups.emplace_back();
|
||||
gi = OrbisNpMatching2RoomGroupInfo{};
|
||||
gi.id = static_cast<OrbisNpMatching2RoomGroupId>(src.group_id());
|
||||
gi.hasPasswd = src.has_passwd();
|
||||
gi.slots = src.slot_count();
|
||||
gi.groupMembers = src.num_members();
|
||||
if (!first) {
|
||||
first = &gi;
|
||||
}
|
||||
}
|
||||
dst.roomGroup = first;
|
||||
dst.roomGroups = static_cast<u64>(r.groups_size());
|
||||
}
|
||||
|
||||
if (r.external_search_int_attrs_size() > 0) {
|
||||
OrbisNpMatching2IntAttr* first = nullptr;
|
||||
for (int a = 0; a < r.external_search_int_attrs_size(); ++a) {
|
||||
const auto& src = r.external_search_int_attrs(a);
|
||||
auto& ia = p.ext_int_attrs.emplace_back();
|
||||
ia = OrbisNpMatching2IntAttr{};
|
||||
ia.id = static_cast<OrbisNpMatching2AttributeId>(src.attr_id());
|
||||
ia.num = src.attr_value();
|
||||
if (!first) {
|
||||
first = &ia;
|
||||
}
|
||||
}
|
||||
dst.externalSearchIntAttr = first;
|
||||
dst.externalSearchIntAttrs = static_cast<u64>(r.external_search_int_attrs_size());
|
||||
}
|
||||
|
||||
auto build_bin = [&](const auto& src_attrs, OrbisNpMatching2BinAttr*& out_ptr,
|
||||
u64& out_num) {
|
||||
if (src_attrs.empty()) {
|
||||
return;
|
||||
}
|
||||
OrbisNpMatching2BinAttr* first = nullptr;
|
||||
for (const auto& src : src_attrs) {
|
||||
p.bin_buffers.emplace_back(src.data().begin(), src.data().end());
|
||||
auto& buf = p.bin_buffers.back();
|
||||
auto& ba = p.ext_bin_attrs.emplace_back();
|
||||
ba = OrbisNpMatching2BinAttr{};
|
||||
ba.id = static_cast<OrbisNpMatching2AttributeId>(src.attr_id());
|
||||
ba.data = buf.empty() ? nullptr : buf.data();
|
||||
ba.dataSize = buf.size();
|
||||
if (!first) {
|
||||
first = &ba;
|
||||
}
|
||||
}
|
||||
out_ptr = first;
|
||||
out_num = static_cast<u64>(src_attrs.size());
|
||||
};
|
||||
build_bin(r.external_search_bin_attrs(), dst.externalSearchBinAttr,
|
||||
dst.externalSearchBinAttrs);
|
||||
build_bin(r.external_bin_attrs(), dst.externalBinAttr, dst.externalBinAttrs);
|
||||
}
|
||||
|
||||
p.search_room_response = std::make_unique<OrbisNpMatching2SearchRoomResponse>();
|
||||
auto& out = *p.search_room_response;
|
||||
out = OrbisNpMatching2SearchRoomResponse{};
|
||||
out.range.start = resp.range_start();
|
||||
out.range.total = resp.range_total();
|
||||
out.range.results = resp.range_result();
|
||||
out.roomDataExt = p.room_data_external.empty() ? nullptr : p.room_data_external.data();
|
||||
|
||||
p.request_data = p.search_room_response.get();
|
||||
return p.request_data;
|
||||
}
|
||||
|
||||
ContextManager& ContextManager::Instance() {
|
||||
static ContextManager instance;
|
||||
return instance;
|
||||
@ -52,18 +303,15 @@ s32 ContextManager::CreateContext(const OrbisNpId* owner_np_id, OrbisNpServiceLa
|
||||
ctx.service_label = service_label;
|
||||
if (owner_np_id) {
|
||||
ctx.owner_np_id = *owner_np_id;
|
||||
ctx.online_id =
|
||||
std::string(owner_np_id->handle.data,
|
||||
strnlen(owner_np_id->handle.data, sizeof(owner_np_id->handle.data)));
|
||||
}
|
||||
if (ctx.online_id.empty()) {
|
||||
ctx.online_id = "shadPS4_player";
|
||||
ctx.online_id = owner_np_id->handle;
|
||||
}
|
||||
ctx.context_callback = m_pending_context_callback;
|
||||
ctx.context_callback_arg = m_pending_context_callback_arg;
|
||||
m_used[id] = true;
|
||||
|
||||
*out_ctx_id = id;
|
||||
LOG_INFO(Lib_NpMatching2, "context created: id={} online_id={} serviceLabel={:#x}", id,
|
||||
ctx.online_id, service_label);
|
||||
LOG_DEBUG(Lib_NpMatching2, "context created: id={} online_id={} serviceLabel={:#x}", id,
|
||||
ctx.online_id.data, service_label);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -84,7 +332,7 @@ bool ContextManager::Destroy(OrbisNpMatching2ContextId ctx_id) {
|
||||
}
|
||||
m_contexts[ctx_id] = ContextObject{};
|
||||
m_used[ctx_id] = false;
|
||||
LOG_INFO(Lib_NpMatching2, "context destroyed: id={}", ctx_id);
|
||||
LOG_DEBUG(Lib_NpMatching2, "context destroyed: id={}", ctx_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -98,7 +346,7 @@ s32 ContextManager::Start(OrbisNpMatching2ContextId ctx_id) {
|
||||
return ORBIS_NP_MATCHING2_ERROR_CONTEXT_ALREADY_STARTED;
|
||||
}
|
||||
ctx->started = true;
|
||||
LOG_INFO(Lib_NpMatching2, "context started: id={}", ctx_id);
|
||||
LOG_DEBUG(Lib_NpMatching2, "context started: id={}", ctx_id);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -112,15 +360,29 @@ s32 ContextManager::Stop(OrbisNpMatching2ContextId ctx_id) {
|
||||
return ORBIS_NP_MATCHING2_ERROR_CONTEXT_NOT_STARTED;
|
||||
}
|
||||
ctx->started = false;
|
||||
LOG_INFO(Lib_NpMatching2, "context stopped: id={}", ctx_id);
|
||||
LOG_DEBUG(Lib_NpMatching2, "context stopped: id={}", ctx_id);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void ContextManager::ApplyContextCallback(OrbisNpMatching2ContextCallback callback, void* arg) {
|
||||
std::lock_guard lock(m_mutex);
|
||||
m_pending_context_callback = callback;
|
||||
m_pending_context_callback_arg = arg;
|
||||
for (u32 id = 1; id <= kMaxContexts; ++id) {
|
||||
if (m_used[id]) {
|
||||
m_contexts[id].context_callback = callback;
|
||||
m_contexts[id].context_callback_arg = arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContextManager::Reset() {
|
||||
std::lock_guard lock(m_mutex);
|
||||
m_contexts = {};
|
||||
m_used = {};
|
||||
m_next_id = 1;
|
||||
m_pending_context_callback = nullptr;
|
||||
m_pending_context_callback_arg = nullptr;
|
||||
}
|
||||
|
||||
OrbisNpMatching2RequestId AllocRequestId() {
|
||||
@ -137,49 +399,99 @@ namespace {
|
||||
void FireEvent(const PendingEvent& ev) {
|
||||
ContextObject* ctx = ContextManager::Instance().Get(ev.ctx_id);
|
||||
if (!ctx) {
|
||||
LOG_WARNING(Lib_NpMatching2, "FireEvent type={} dropped: ctx={} not found",
|
||||
static_cast<int>(ev.type), ev.ctx_id);
|
||||
return;
|
||||
}
|
||||
switch (ev.type) {
|
||||
case PendingEvent::CONTEXT_CB:
|
||||
if (ctx->context_callback) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "callback CONTEXT ctx={} event={:#x} cause={} err={:#x}",
|
||||
ev.ctx_id, static_cast<u16>(ev.ctx_event),
|
||||
static_cast<u8>(ev.ctx_event_cause), ev.error_code);
|
||||
ctx->context_callback(ev.ctx_id, ev.ctx_event, ev.ctx_event_cause, ev.error_code,
|
||||
ctx->context_callback_arg);
|
||||
} else {
|
||||
LOG_WARNING(Lib_NpMatching2, "callback CONTEXT ctx={} event={:#x} SKIPPED: no callback",
|
||||
ev.ctx_id, static_cast<u16>(ev.ctx_event));
|
||||
}
|
||||
break;
|
||||
case PendingEvent::REQUEST_CB:
|
||||
if (ev.request_cb) {
|
||||
LOG_DEBUG(Lib_NpMatching2,
|
||||
"callback REQUEST ctx={} reqId={} event={:#x} err={:#x} data={}", ev.ctx_id,
|
||||
ev.req_id, static_cast<u16>(ev.req_event), ev.error_code,
|
||||
fmt::ptr(ev.request_data));
|
||||
ev.request_cb(ev.ctx_id, ev.req_id, ev.req_event, ev.error_code, ev.request_data,
|
||||
ev.request_cb_arg);
|
||||
} else {
|
||||
LOG_WARNING(Lib_NpMatching2,
|
||||
"callback REQUEST ctx={} reqId={} event={:#x} SKIPPED: no callback",
|
||||
ev.ctx_id, ev.req_id, static_cast<u16>(ev.req_event));
|
||||
}
|
||||
break;
|
||||
case PendingEvent::SIGNALING_CB:
|
||||
if (ctx->signaling_callback) {
|
||||
LOG_DEBUG(Lib_NpMatching2,
|
||||
"callback SIGNALING ctx={} room={} member={} event={:#x} err={:#x}",
|
||||
ev.ctx_id, ev.room_id, ev.member_id, static_cast<u16>(ev.sig_event),
|
||||
ev.error_code);
|
||||
ctx->signaling_callback(ev.ctx_id, ev.room_id, ev.member_id, ev.sig_event,
|
||||
ev.error_code, ctx->signaling_callback_arg);
|
||||
} else {
|
||||
LOG_WARNING(Lib_NpMatching2,
|
||||
"callback SIGNALING ctx={} room={} event={:#x} SKIPPED: no callback",
|
||||
ev.ctx_id, ev.room_id, static_cast<u16>(ev.sig_event));
|
||||
}
|
||||
break;
|
||||
case PendingEvent::ROOM_EVENT_CB:
|
||||
if (ctx->room_event_callback) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "callback ROOM_EVENT ctx={} room={} event={:#x} data={}",
|
||||
ev.ctx_id, ev.room_id, static_cast<u16>(ev.room_event),
|
||||
fmt::ptr(ev.room_event_data));
|
||||
ctx->room_event_callback(ev.ctx_id, ev.room_id, ev.room_event, ev.room_event_data,
|
||||
ctx->room_event_callback_arg);
|
||||
} else {
|
||||
LOG_WARNING(Lib_NpMatching2,
|
||||
"callback ROOM_EVENT ctx={} room={} event={:#x} SKIPPED: no callback",
|
||||
ev.ctx_id, ev.room_id, static_cast<u16>(ev.room_event));
|
||||
}
|
||||
break;
|
||||
case PendingEvent::LOBBY_EVENT_CB:
|
||||
if (ctx->lobby_event_callback) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "callback LOBBY_EVENT ctx={} lobby={} event={:#x} data={}",
|
||||
ev.ctx_id, ev.lobby_id, static_cast<u16>(ev.lobby_event),
|
||||
fmt::ptr(ev.lobby_event_data));
|
||||
ctx->lobby_event_callback(ev.ctx_id, ev.lobby_id, ev.lobby_event, ev.lobby_event_data,
|
||||
ctx->lobby_event_callback_arg);
|
||||
} else {
|
||||
LOG_WARNING(Lib_NpMatching2,
|
||||
"callback LOBBY_EVENT ctx={} lobby={} event={:#x} SKIPPED: no callback",
|
||||
ev.ctx_id, ev.lobby_id, static_cast<u16>(ev.lobby_event));
|
||||
}
|
||||
break;
|
||||
case PendingEvent::LOBBY_MESSAGE_CB:
|
||||
if (ctx->lobby_message_callback) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "callback LOBBY_MESSAGE ctx={} lobby={} src={} event={:#x}",
|
||||
ev.ctx_id, ev.lobby_id, ev.src_member_id, static_cast<u16>(ev.msg_event));
|
||||
ctx->lobby_message_callback(ev.ctx_id, ev.lobby_id, ev.src_member_id, ev.msg_event,
|
||||
ev.message_data, ctx->lobby_message_callback_arg);
|
||||
} else {
|
||||
LOG_WARNING(Lib_NpMatching2,
|
||||
"callback LOBBY_MESSAGE ctx={} lobby={} SKIPPED: no callback", ev.ctx_id,
|
||||
ev.lobby_id);
|
||||
}
|
||||
break;
|
||||
case PendingEvent::ROOM_MESSAGE_CB:
|
||||
if (ctx->room_message_callback) {
|
||||
LOG_DEBUG(Lib_NpMatching2, "callback ROOM_MESSAGE ctx={} room={} src={} event={:#x}",
|
||||
ev.ctx_id, ev.room_id, ev.src_member_id, static_cast<u16>(ev.msg_event));
|
||||
ctx->room_message_callback(ev.ctx_id, ev.room_id, ev.src_member_id, ev.msg_event,
|
||||
ev.message_data, ctx->room_message_callback_arg);
|
||||
} else {
|
||||
LOG_WARNING(Lib_NpMatching2,
|
||||
"callback ROOM_MESSAGE ctx={} room={} SKIPPED: no callback", ev.ctx_id,
|
||||
ev.room_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -187,7 +499,8 @@ void FireEvent(const PendingEvent& ev) {
|
||||
|
||||
} // namespace
|
||||
|
||||
PS4_SYSV_ABI void* EventDispatcherThreadMain(void* /*arg*/) {
|
||||
PS4_SYSV_ABI void* EventDispatcherThreadMain(void*) {
|
||||
Common::SetCurrentThreadName("Matching2:Dispatch");
|
||||
std::unique_lock lock(g_state.queue_mutex);
|
||||
while (g_state.dispatch_running.load()) {
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
@ -236,7 +549,7 @@ void InitEventDispatcher() {
|
||||
g_state.dispatch_thread = nullptr;
|
||||
return;
|
||||
}
|
||||
LOG_INFO(Lib_NpMatching2, "event dispatcher thread created");
|
||||
LOG_DEBUG(Lib_NpMatching2, "event dispatcher thread created");
|
||||
}
|
||||
|
||||
void TermEventDispatcher() {
|
||||
|
||||
@ -7,7 +7,9 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -18,6 +20,12 @@
|
||||
#include "core/libraries/np/np_matching2/np_matching2_types.h"
|
||||
#include "core/libraries/np/np_types.h"
|
||||
|
||||
namespace shadnet {
|
||||
class CreateJoinRoomResponse;
|
||||
class GetWorldInfoListReply;
|
||||
class SearchRoomReply;
|
||||
} // namespace shadnet
|
||||
|
||||
namespace Libraries::Np::NpMatching2 {
|
||||
|
||||
struct PeerInfo {
|
||||
@ -26,7 +34,62 @@ struct PeerInfo {
|
||||
OrbisNpMatching2RoomMemberId member_id = 0;
|
||||
s32 conn_id = 0;
|
||||
s32 status = 0;
|
||||
std::string online_id;
|
||||
Libraries::Np::OrbisNpOnlineId online_id{};
|
||||
};
|
||||
|
||||
struct MemberBinCache {
|
||||
OrbisNpMatching2AttributeId id = 0;
|
||||
u64 update_date = 0;
|
||||
std::vector<u8> data;
|
||||
};
|
||||
|
||||
struct MemberCache {
|
||||
Libraries::Np::OrbisNpId np_id{};
|
||||
u64 join_date = 0;
|
||||
OrbisNpMatching2RoomMemberId member_id = 0;
|
||||
OrbisNpMatching2TeamId team_id = 0;
|
||||
OrbisNpMatching2RoomGroupId group_id = 0;
|
||||
OrbisNpMatching2NatType nat_type = 0;
|
||||
OrbisNpMatching2Flags flag_attr = 0;
|
||||
u32 addr = 0;
|
||||
u16 port = 0;
|
||||
std::map<OrbisNpMatching2AttributeId, MemberBinCache> bins;
|
||||
};
|
||||
|
||||
struct RoomCache {
|
||||
u32 num_slots = 0;
|
||||
u64 mask_password = 0;
|
||||
std::map<OrbisNpMatching2RoomGroupId, OrbisNpMatching2RoomGroup> groups;
|
||||
std::map<OrbisNpMatching2RoomMemberId, MemberCache> members;
|
||||
bool owner = false;
|
||||
};
|
||||
|
||||
struct CallbackPayload {
|
||||
std::unique_ptr<OrbisNpMatching2RoomDataInternal> room_data;
|
||||
std::unique_ptr<OrbisNpMatching2CreateJoinRoomResponse> create_join_response;
|
||||
std::unique_ptr<OrbisNpMatching2SearchRoomResponse> search_room_response;
|
||||
std::unique_ptr<OrbisNpMatching2GetWorldInfoListResponse> world_info_response;
|
||||
std::vector<OrbisNpMatching2World> world_list;
|
||||
std::vector<OrbisNpMatching2RoomMemberDataInternal> member_data;
|
||||
std::vector<OrbisNpMatching2RoomGroup> room_groups;
|
||||
std::vector<OrbisNpMatching2RoomBinAttrInternal> room_bin_attrs;
|
||||
std::vector<OrbisNpMatching2RoomMemberBinAttrInternal> member_bin_attrs;
|
||||
std::vector<OrbisNpMatching2RoomDataExternal> room_data_external;
|
||||
std::vector<std::vector<u8>> bin_buffers;
|
||||
std::deque<OrbisNpMatching2IntAttr> ext_int_attrs;
|
||||
std::deque<OrbisNpMatching2BinAttr> ext_bin_attrs;
|
||||
std::deque<OrbisNpMatching2RoomGroupInfo> ext_room_groups;
|
||||
std::deque<Libraries::Np::OrbisNpId> ext_owner_npids;
|
||||
void* request_data = nullptr;
|
||||
|
||||
std::unique_ptr<OrbisNpMatching2RoomMemberUpdate> room_member_update;
|
||||
std::unique_ptr<OrbisNpMatching2RoomUpdate> room_update;
|
||||
std::unique_ptr<OrbisNpMatching2RoomMemberDataInternal> event_member;
|
||||
void* room_event_data = nullptr;
|
||||
|
||||
void Reset() {
|
||||
*this = CallbackPayload{};
|
||||
}
|
||||
};
|
||||
|
||||
struct ContextObject {
|
||||
@ -36,7 +99,7 @@ struct ContextObject {
|
||||
OrbisNpMatching2ServerId server_id = 1;
|
||||
OrbisNpServiceLabel service_label = 0;
|
||||
Libraries::Np::OrbisNpId owner_np_id{};
|
||||
std::string online_id;
|
||||
Libraries::Np::OrbisNpOnlineId online_id{};
|
||||
|
||||
std::string signaling_addr;
|
||||
u16 signaling_port = 0;
|
||||
@ -53,6 +116,11 @@ struct ContextObject {
|
||||
|
||||
std::map<OrbisNpMatching2RoomMemberId, PeerInfo> peers;
|
||||
|
||||
std::map<OrbisNpMatching2RoomId, RoomCache> room_cache;
|
||||
|
||||
CallbackPayload request_payload;
|
||||
CallbackPayload room_event_payload;
|
||||
|
||||
OrbisNpMatching2ContextCallback context_callback = nullptr;
|
||||
void* context_callback_arg = nullptr;
|
||||
OrbisNpMatching2RequestCallback default_request_callback = nullptr;
|
||||
@ -87,6 +155,8 @@ public:
|
||||
s32 Start(OrbisNpMatching2ContextId ctx_id);
|
||||
s32 Stop(OrbisNpMatching2ContextId ctx_id);
|
||||
|
||||
void ApplyContextCallback(OrbisNpMatching2ContextCallback callback, void* arg);
|
||||
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
@ -97,6 +167,9 @@ private:
|
||||
std::array<ContextObject, kMaxContexts + 1> m_contexts{};
|
||||
std::array<bool, kMaxContexts + 1> m_used{};
|
||||
OrbisNpMatching2ContextId m_next_id = 1;
|
||||
|
||||
OrbisNpMatching2ContextCallback m_pending_context_callback = nullptr;
|
||||
void* m_pending_context_callback_arg = nullptr;
|
||||
};
|
||||
|
||||
struct PendingEvent {
|
||||
@ -157,6 +230,10 @@ extern NpMatching2State g_state;
|
||||
|
||||
OrbisNpMatching2RequestId AllocRequestId();
|
||||
|
||||
void* BuildCreateJoinRoomPayload(ContextObject& ctx, const shadnet::CreateJoinRoomResponse& resp);
|
||||
void* BuildGetWorldInfoListPayload(ContextObject& ctx, const shadnet::GetWorldInfoListReply& resp);
|
||||
void* BuildSearchRoomPayload(ContextObject& ctx, const shadnet::SearchRoomReply& resp);
|
||||
|
||||
void InitEventDispatcher();
|
||||
void TermEventDispatcher();
|
||||
|
||||
|
||||
564
src/core/libraries/np/np_matching2/np_matching2_mm.cpp
Normal file
564
src/core/libraries/np/np_matching2/np_matching2_mm.cpp
Normal file
@ -0,0 +1,564 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/network/net.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 "shadnet.pb.h"
|
||||
#include "shadnet/client.h"
|
||||
|
||||
namespace Libraries::Np::NpMatching2 {
|
||||
|
||||
namespace {
|
||||
|
||||
struct PendingRequest {
|
||||
OrbisNpMatching2ContextId ctx_id = 0;
|
||||
OrbisNpMatching2RequestId req_id = 0;
|
||||
OrbisNpMatching2Event req_event{};
|
||||
};
|
||||
|
||||
struct MmClientState {
|
||||
std::shared_ptr<ShadNet::ShadNetClient> client;
|
||||
std::mutex mutex;
|
||||
u32 server_addr = 0;
|
||||
u16 server_udp_port = 0;
|
||||
|
||||
std::mutex pending_mutex;
|
||||
std::map<u64, PendingRequest> pending;
|
||||
|
||||
std::mutex sig_mutex;
|
||||
std::condition_variable sig_cv;
|
||||
std::map<u64, std::pair<ShadNet::ErrorType, std::vector<u8>>> sig_replies;
|
||||
};
|
||||
|
||||
MmClientState g_mm;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<u8> MakeProtoPayload(const T& msg) {
|
||||
std::string bytes;
|
||||
(void)msg.SerializeToString(&bytes);
|
||||
const u32 len = static_cast<u32>(bytes.size());
|
||||
std::vector<u8> out;
|
||||
out.reserve(4 + bytes.size());
|
||||
out.push_back(static_cast<u8>(len));
|
||||
out.push_back(static_cast<u8>(len >> 8));
|
||||
out.push_back(static_cast<u8>(len >> 16));
|
||||
out.push_back(static_cast<u8>(len >> 24));
|
||||
out.insert(out.end(), bytes.begin(), bytes.end());
|
||||
return out;
|
||||
}
|
||||
|
||||
void AppendBinAttr(shadnet::MatchingBinAttr* dst, const OrbisNpMatching2BinAttr& src) {
|
||||
dst->set_attr_id(src.id);
|
||||
if (src.data && src.dataSize > 0) {
|
||||
dst->set_data(src.data, src.dataSize);
|
||||
}
|
||||
}
|
||||
|
||||
void AppendIntAttr(shadnet::MatchingIntAttr* dst, const OrbisNpMatching2IntAttr& src) {
|
||||
dst->set_attr_id(src.id);
|
||||
dst->set_attr_value(src.num);
|
||||
}
|
||||
|
||||
std::string ExtractProtoBytes(const std::vector<u8>& payload, size_t offset = 0) {
|
||||
if (payload.size() < offset + 4) {
|
||||
return {};
|
||||
}
|
||||
const u32 len = static_cast<u32>(payload[offset]) |
|
||||
(static_cast<u32>(payload[offset + 1]) << 8) |
|
||||
(static_cast<u32>(payload[offset + 2]) << 16) |
|
||||
(static_cast<u32>(payload[offset + 3]) << 24);
|
||||
if (payload.size() < offset + 4 + len) {
|
||||
return {};
|
||||
}
|
||||
return std::string(reinterpret_cast<const char*>(payload.data() + offset + 4), len);
|
||||
}
|
||||
|
||||
void DispatchRequestComplete(const PendingRequest& pr, ShadNet::ErrorType error,
|
||||
const std::vector<u8>& body) {
|
||||
ContextObject* ctx = ContextManager::Instance().Get(pr.ctx_id);
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
const s32 error_code = (error == ShadNet::ErrorType::NoError) ? 0 : static_cast<s32>(error);
|
||||
|
||||
if (pr.req_event == ORBIS_NP_MATCHING2_CONTEXT_EVENT_STARTED ||
|
||||
pr.req_event == ORBIS_NP_MATCHING2_CONTEXT_EVENT_STOPPED) {
|
||||
PendingEvent ev{};
|
||||
ev.type = PendingEvent::CONTEXT_CB;
|
||||
ev.ctx_id = pr.ctx_id;
|
||||
ev.fire_at = std::chrono::steady_clock::now();
|
||||
ev.ctx_event = pr.req_event;
|
||||
ev.ctx_event_cause = ORBIS_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ACTION;
|
||||
ev.error_code = error_code;
|
||||
ScheduleEvent(std::move(ev));
|
||||
return;
|
||||
}
|
||||
|
||||
void* request_data = nullptr;
|
||||
|
||||
if (error_code == 0) {
|
||||
const std::string proto = ExtractProtoBytes(body);
|
||||
if (pr.req_event == ORBIS_NP_MATCHING2_REQUEST_EVENT_CREATE_JOIN_ROOM ||
|
||||
pr.req_event == ORBIS_NP_MATCHING2_REQUEST_EVENT_JOIN_ROOM) {
|
||||
shadnet::CreateRoomReply reply;
|
||||
if (reply.ParseFromString(proto) && reply.has_details()) {
|
||||
request_data = BuildCreateJoinRoomPayload(*ctx, reply.details());
|
||||
}
|
||||
} else if (pr.req_event == ORBIS_NP_MATCHING2_REQUEST_EVENT_GET_WORLD_INFO_LIST) {
|
||||
shadnet::GetWorldInfoListReply reply;
|
||||
if (reply.ParseFromString(proto)) {
|
||||
request_data = BuildGetWorldInfoListPayload(*ctx, reply);
|
||||
}
|
||||
} else if (pr.req_event == ORBIS_NP_MATCHING2_REQUEST_EVENT_SEARCH_ROOM) {
|
||||
shadnet::SearchRoomReply reply;
|
||||
if (reply.ParseFromString(proto)) {
|
||||
request_data = BuildSearchRoomPayload(*ctx, reply);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PendingEvent ev{};
|
||||
ev.type = PendingEvent::REQUEST_CB;
|
||||
ev.ctx_id = pr.ctx_id;
|
||||
ev.fire_at = std::chrono::steady_clock::now();
|
||||
ev.req_id = pr.req_id;
|
||||
ev.req_event = pr.req_event;
|
||||
ev.error_code = error_code;
|
||||
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));
|
||||
}
|
||||
|
||||
void BuildMemberUpdate(CallbackPayload& p, const RoomCache& rc, const MemberCache& mc,
|
||||
OrbisNpMatching2EventCause cause) {
|
||||
p.event_member = std::make_unique<OrbisNpMatching2RoomMemberDataInternal>();
|
||||
OrbisNpMatching2RoomMemberDataInternal& m = *p.event_member;
|
||||
m = OrbisNpMatching2RoomMemberDataInternal{};
|
||||
m.memberId = mc.member_id;
|
||||
m.teamId = mc.team_id;
|
||||
m.natType = mc.nat_type;
|
||||
m.flagAttr = mc.flag_attr;
|
||||
m.joinDate = mc.join_date;
|
||||
m.npId = mc.np_id;
|
||||
|
||||
if (mc.group_id != 0) {
|
||||
auto grp_it = rc.groups.find(mc.group_id);
|
||||
if (grp_it != rc.groups.end()) {
|
||||
p.room_groups.resize(1);
|
||||
p.room_groups[0] = grp_it->second;
|
||||
m.roomGroup = p.room_groups.data();
|
||||
}
|
||||
}
|
||||
|
||||
p.member_bin_attrs.resize(mc.bins.size());
|
||||
p.bin_buffers.reserve(mc.bins.size());
|
||||
size_t i = 0;
|
||||
for (const auto& [attr_id, bin] : mc.bins) {
|
||||
p.bin_buffers.emplace_back(bin.data.begin(), bin.data.end());
|
||||
auto& buf = p.bin_buffers.back();
|
||||
auto& dst = p.member_bin_attrs[i++];
|
||||
dst = OrbisNpMatching2RoomMemberBinAttrInternal{};
|
||||
dst.binAttr.id = bin.id;
|
||||
dst.binAttr.data = buf.empty() ? nullptr : buf.data();
|
||||
dst.binAttr.dataSize = buf.size();
|
||||
}
|
||||
m.roomMemberBinAttrInternal = p.member_bin_attrs.empty() ? nullptr : p.member_bin_attrs.data();
|
||||
m.roomMemberBinAttrInternalNum = p.member_bin_attrs.size();
|
||||
|
||||
p.room_member_update = std::make_unique<OrbisNpMatching2RoomMemberUpdate>();
|
||||
OrbisNpMatching2RoomMemberUpdate& u = *p.room_member_update;
|
||||
u = OrbisNpMatching2RoomMemberUpdate{};
|
||||
u.roomMemberDataInternal = p.event_member.get();
|
||||
u.eventCause = cause;
|
||||
p.room_event_data = p.room_member_update.get();
|
||||
}
|
||||
|
||||
void HandleRoomEvent(const ShadNet::NotifyRoomEvent& n) {
|
||||
ContextObject* ctx =
|
||||
ContextManager::Instance().Get(static_cast<OrbisNpMatching2ContextId>(n.ctx_id));
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
const auto event = static_cast<OrbisNpMatching2Event>(n.event);
|
||||
const auto cause = static_cast<OrbisNpMatching2EventCause>(n.event_cause);
|
||||
const auto room_id = static_cast<OrbisNpMatching2RoomId>(n.room_id);
|
||||
const auto member_id = static_cast<OrbisNpMatching2RoomMemberId>(n.member_id);
|
||||
|
||||
CallbackPayload& p = ctx->room_event_payload;
|
||||
p.Reset();
|
||||
|
||||
switch (event) {
|
||||
case ORBIS_NP_MATCHING2_ROOM_EVENT_MEMBER_JOINED: {
|
||||
auto room_it = ctx->room_cache.find(room_id);
|
||||
if (room_it == ctx->room_cache.end()) {
|
||||
return;
|
||||
}
|
||||
MemberCache& mc = room_it->second.members[member_id];
|
||||
mc = MemberCache{};
|
||||
mc.member_id = member_id;
|
||||
mc.team_id = static_cast<OrbisNpMatching2TeamId>(n.member_team_id);
|
||||
mc.nat_type = static_cast<OrbisNpMatching2NatType>(n.member_nat_type);
|
||||
mc.flag_attr = n.member_flag_attr;
|
||||
mc.group_id = static_cast<OrbisNpMatching2RoomGroupId>(n.member_group_id);
|
||||
mc.join_date = n.member_join_date;
|
||||
mc.addr = IpStringToAddr(n.member_addr);
|
||||
mc.port = Libraries::Net::sceNetHtons(static_cast<u16>(n.member_port));
|
||||
std::strncpy(mc.np_id.handle.data, n.member_npid.c_str(), sizeof(mc.np_id.handle.data) - 1);
|
||||
for (const auto& a : n.member_bin_attrs) {
|
||||
MemberBinCache& b = mc.bins[a.attr_id];
|
||||
b.id = static_cast<OrbisNpMatching2AttributeId>(a.attr_id);
|
||||
b.data = a.data;
|
||||
}
|
||||
|
||||
PeerInfo pi{};
|
||||
pi.member_id = member_id;
|
||||
pi.addr = mc.addr;
|
||||
pi.port = mc.port;
|
||||
std::strncpy(pi.online_id.data, n.member_npid.c_str(), sizeof(pi.online_id.data) - 1);
|
||||
ctx->peers[member_id] = pi;
|
||||
|
||||
BuildMemberUpdate(p, room_it->second, mc, cause);
|
||||
break;
|
||||
}
|
||||
case ORBIS_NP_MATCHING2_ROOM_EVENT_MEMBER_LEFT: {
|
||||
auto room_it = ctx->room_cache.find(room_id);
|
||||
if (room_it == ctx->room_cache.end()) {
|
||||
return;
|
||||
}
|
||||
auto mem_it = room_it->second.members.find(member_id);
|
||||
if (mem_it == room_it->second.members.end()) {
|
||||
return;
|
||||
}
|
||||
BuildMemberUpdate(p, room_it->second, mem_it->second, cause);
|
||||
room_it->second.members.erase(mem_it);
|
||||
ctx->peers.erase(member_id);
|
||||
break;
|
||||
}
|
||||
case ORBIS_NP_MATCHING2_ROOM_EVENT_KICKEDOUT: {
|
||||
p.room_update = std::make_unique<OrbisNpMatching2RoomUpdate>();
|
||||
OrbisNpMatching2RoomUpdate& u = *p.room_update;
|
||||
u = OrbisNpMatching2RoomUpdate{};
|
||||
u.eventCause = cause;
|
||||
u.errorCode = n.error_code;
|
||||
p.room_event_data = p.room_update.get();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_WARNING(Lib_NpMatching2, "unhandled room event {:#x}", n.event);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Lib_NpMatching2, "RoomEvent ctx={} room={} event={:#x} cause={}", n.ctx_id, n.room_id,
|
||||
n.event, n.event_cause);
|
||||
|
||||
PendingEvent ev{};
|
||||
ev.type = PendingEvent::ROOM_EVENT_CB;
|
||||
ev.ctx_id = ctx->ctx_id;
|
||||
ev.fire_at = std::chrono::steady_clock::now();
|
||||
ev.room_id = room_id;
|
||||
ev.room_event = event;
|
||||
ev.room_event_data = p.room_event_data;
|
||||
ScheduleEvent(std::move(ev));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void OnMatchingReply(ShadNet::CommandType cmd, u64 pkt_id, ShadNet::ErrorType error,
|
||||
const std::vector<u8>& body) {
|
||||
if (cmd == ShadNet::CommandType::RequestSignalingInfos) {
|
||||
std::lock_guard lock(g_mm.sig_mutex);
|
||||
g_mm.sig_replies[pkt_id] = {error, body};
|
||||
g_mm.sig_cv.notify_all();
|
||||
return;
|
||||
}
|
||||
|
||||
PendingRequest pr;
|
||||
{
|
||||
std::lock_guard lock(g_mm.pending_mutex);
|
||||
auto it = g_mm.pending.find(pkt_id);
|
||||
if (it == g_mm.pending.end()) {
|
||||
LOG_WARNING(Lib_NpMatching2, "reply pkt_id={} cmd={} has no pending entry (have {})",
|
||||
pkt_id, static_cast<u16>(cmd), g_mm.pending.size());
|
||||
return;
|
||||
}
|
||||
pr = it->second;
|
||||
g_mm.pending.erase(it);
|
||||
}
|
||||
LOG_DEBUG(Lib_NpMatching2, "reply pkt_id={} cmd={} -> ctx={} reqId={} event={:#x}", pkt_id,
|
||||
static_cast<u16>(cmd), pr.ctx_id, pr.req_id, static_cast<u16>(pr.req_event));
|
||||
DispatchRequestComplete(pr, error, body);
|
||||
}
|
||||
|
||||
void SetMmShadNetClient(std::shared_ptr<ShadNet::ShadNetClient> client,
|
||||
std::string_view server_host, u16 tcp_port) {
|
||||
{
|
||||
std::lock_guard lock(g_mm.mutex);
|
||||
g_mm.client = client;
|
||||
g_mm.server_addr = client ? client->GetAddrServer() : 0;
|
||||
if (g_mm.server_addr == 0) {
|
||||
g_mm.server_addr = IpStringToAddr(server_host);
|
||||
}
|
||||
g_mm.server_udp_port = Libraries::Net::sceNetHtons(static_cast<u16>(tcp_port + 1));
|
||||
}
|
||||
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
|
||||
client->onRoomEvent = [](const ShadNet::NotifyRoomEvent& n) { HandleRoomEvent(n); };
|
||||
}
|
||||
|
||||
void ClearMmShadNetClient() {
|
||||
std::shared_ptr<ShadNet::ShadNetClient> old_client;
|
||||
{
|
||||
std::lock_guard lock(g_mm.mutex);
|
||||
old_client = std::move(g_mm.client);
|
||||
g_mm.server_addr = 0;
|
||||
g_mm.server_udp_port = 0;
|
||||
}
|
||||
if (old_client) {
|
||||
old_client->onRoomEvent = nullptr;
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(g_mm.pending_mutex);
|
||||
g_mm.pending.clear();
|
||||
}
|
||||
g_mm.sig_cv.notify_all();
|
||||
}
|
||||
|
||||
bool IsMmClientRunning() {
|
||||
std::lock_guard lock(g_mm.mutex);
|
||||
return g_mm.client && g_mm.client->IsAuthenticated();
|
||||
}
|
||||
|
||||
void MmContextStart(OrbisNpMatching2ContextId ctx_id) {
|
||||
shadnet::ContextStartRequest req;
|
||||
req.set_ctx_id(ctx_id);
|
||||
MmSubmitRequest(ctx_id, 0, ORBIS_NP_MATCHING2_CONTEXT_EVENT_STARTED, MmCommand::ContextStart,
|
||||
MakeProtoPayload(req));
|
||||
}
|
||||
|
||||
void MmContextStop(OrbisNpMatching2ContextId ctx_id) {
|
||||
shadnet::ContextStopRequest req;
|
||||
req.set_ctx_id(ctx_id);
|
||||
MmSubmitRequest(ctx_id, 0, ORBIS_NP_MATCHING2_CONTEXT_EVENT_STOPPED, MmCommand::ContextStop,
|
||||
MakeProtoPayload(req));
|
||||
}
|
||||
|
||||
s32 MmSubmitRequest(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
OrbisNpMatching2Event req_event, MmCommand cmd,
|
||||
const std::vector<u8>& payload) {
|
||||
std::shared_ptr<ShadNet::ShadNetClient> client;
|
||||
{
|
||||
std::lock_guard lock(g_mm.mutex);
|
||||
client = g_mm.client;
|
||||
}
|
||||
if (!client || !client->IsAuthenticated()) {
|
||||
LOG_ERROR(Lib_NpMatching2, "MmSubmitRequest({}): not connected", static_cast<u16>(cmd));
|
||||
return ORBIS_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
|
||||
}
|
||||
|
||||
const u64 pkt_id = client->SubmitRequest(static_cast<ShadNet::CommandType>(cmd), payload);
|
||||
{
|
||||
std::lock_guard lock(g_mm.pending_mutex);
|
||||
g_mm.pending[pkt_id] = {ctx_id, req_id, req_event};
|
||||
}
|
||||
LOG_DEBUG(Lib_NpMatching2, "submit cmd={} pkt_id={} ctx={} reqId={}", static_cast<u16>(cmd),
|
||||
pkt_id, ctx_id, req_id);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 MmCreateJoinRoom(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
const OrbisNpMatching2CreateJoinRoomRequest& request) {
|
||||
shadnet::CreateRoomRequest req;
|
||||
req.set_req_id(req_id);
|
||||
req.set_max_slots(request.maxSlot);
|
||||
req.set_team_id(request.teamId);
|
||||
req.set_world_id(request.worldId);
|
||||
req.set_lobby_id(static_cast<u32>(request.lobbyId));
|
||||
req.set_flags(request.flags);
|
||||
req.set_group_config_count(static_cast<u32>(request.groupConfigs));
|
||||
req.set_allowed_user_count(static_cast<u32>(request.allowedUsers));
|
||||
req.set_blocked_user_count(static_cast<u32>(request.blockedUsers));
|
||||
req.set_internal_bin_attr_count(static_cast<u32>(request.internalBinAttrs));
|
||||
for (u64 i = 0; i < request.externalSearchIntAttrs; ++i) {
|
||||
AppendIntAttr(req.add_external_search_int_attrs(), request.externalSearchIntAttr[i]);
|
||||
}
|
||||
for (u64 i = 0; i < request.externalSearchBinAttrs; ++i) {
|
||||
AppendBinAttr(req.add_external_search_bin_attrs(), request.externalSearchBinAttr[i]);
|
||||
}
|
||||
for (u64 i = 0; i < request.externalBinAttrs; ++i) {
|
||||
AppendBinAttr(req.add_external_bin_attrs(), request.externalBinAttr[i]);
|
||||
}
|
||||
for (u64 i = 0; i < request.memberInternalBinAttrs; ++i) {
|
||||
AppendBinAttr(req.add_member_bin_attrs(), request.memberInternalBinAttr[i]);
|
||||
}
|
||||
req.set_join_group_label_present(request.joinGroupLabel != nullptr);
|
||||
req.set_room_password_present(request.roomPasswd != nullptr);
|
||||
if (request.signalingParam) {
|
||||
req.set_sig_type(request.signalingParam->type);
|
||||
req.set_sig_flag(request.signalingParam->flag);
|
||||
req.set_sig_main_member(request.signalingParam->memberId);
|
||||
}
|
||||
return MmSubmitRequest(ctx_id, req_id, ORBIS_NP_MATCHING2_REQUEST_EVENT_CREATE_JOIN_ROOM,
|
||||
MmCommand::CreateRoom, MakeProtoPayload(req));
|
||||
}
|
||||
|
||||
s32 MmJoinRoom(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
const OrbisNpMatching2JoinRoomRequest& request) {
|
||||
shadnet::JoinRoomRequest req;
|
||||
req.set_room_id(request.roomId);
|
||||
req.set_req_id(req_id);
|
||||
req.set_team_id(request.teamId);
|
||||
req.set_join_flags(request.flags);
|
||||
req.set_blocked_user_count(static_cast<u32>(request.blockedUsers));
|
||||
for (u64 i = 0; i < request.roomMemberBinInternalAttrNum; ++i) {
|
||||
AppendBinAttr(req.add_member_bin_attrs(), request.roomMemberBinInternalAttr[i]);
|
||||
}
|
||||
req.set_room_password_present(request.roomPasswd != nullptr);
|
||||
req.set_join_group_label_present(request.joinGroupLabel != nullptr);
|
||||
return MmSubmitRequest(ctx_id, req_id, ORBIS_NP_MATCHING2_REQUEST_EVENT_JOIN_ROOM,
|
||||
MmCommand::JoinRoom, MakeProtoPayload(req));
|
||||
}
|
||||
|
||||
s32 MmLeaveRoom(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
const OrbisNpMatching2LeaveRoomRequest& request) {
|
||||
shadnet::LeaveRoomRequest req;
|
||||
req.set_room_id(request.roomId);
|
||||
req.set_req_id(req_id);
|
||||
return MmSubmitRequest(ctx_id, req_id, ORBIS_NP_MATCHING2_REQUEST_EVENT_LEAVE_ROOM,
|
||||
MmCommand::LeaveRoom, MakeProtoPayload(req));
|
||||
}
|
||||
|
||||
s32 MmGetWorldInfoList(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
const OrbisNpMatching2GetWorldInfoListRequest& request) {
|
||||
shadnet::GetWorldInfoListRequest req;
|
||||
req.set_server_id(request.serverId);
|
||||
return MmSubmitRequest(ctx_id, req_id, ORBIS_NP_MATCHING2_REQUEST_EVENT_GET_WORLD_INFO_LIST,
|
||||
MmCommand::GetWorldInfoList, MakeProtoPayload(req));
|
||||
}
|
||||
|
||||
s32 MmSearchRoom(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
const OrbisNpMatching2SearchRoomRequest& request) {
|
||||
shadnet::SearchRoomRequest req;
|
||||
req.set_world_id(request.worldId);
|
||||
req.set_lobby_id(request.lobbyId);
|
||||
req.set_range_filter_start(request.rangeFilter.start);
|
||||
req.set_range_filter_max(request.rangeFilter.max);
|
||||
req.set_flag_filter(request.flags1);
|
||||
req.set_flag_attr(request.flags2);
|
||||
LOG_DEBUG(Lib_NpMatching2,
|
||||
"search world={} lobby={} range={}+{} flags={:#x}/{:#x} intN={} binN={} attrN={}",
|
||||
request.worldId, request.lobbyId, request.rangeFilter.start, request.rangeFilter.max,
|
||||
request.flags1, request.flags2, request.intFilters, request.binFilters,
|
||||
request.attrs);
|
||||
for (u64 i = 0; i < request.intFilters; ++i) {
|
||||
const auto& f = request.intFilter[i];
|
||||
LOG_DEBUG(Lib_NpMatching2, " intFilter[{}] op={} id={:#x} num={}", i, f.searchOperator,
|
||||
f.attr.id, f.attr.num);
|
||||
auto* dst = req.add_int_filters();
|
||||
dst->set_op(f.searchOperator);
|
||||
dst->set_attr_id(f.attr.id);
|
||||
dst->set_attr_value(f.attr.num);
|
||||
}
|
||||
for (u64 i = 0; i < request.binFilters; ++i) {
|
||||
const auto& f = request.binFilter[i];
|
||||
auto* dst = req.add_bin_filters();
|
||||
dst->set_op(f.searchOperator);
|
||||
dst->set_attr_id(f.attr.id);
|
||||
if (f.attr.data && f.attr.dataSize > 0) {
|
||||
dst->set_data(f.attr.data, f.attr.dataSize);
|
||||
}
|
||||
}
|
||||
for (u64 i = 0; i < request.attrs; ++i) {
|
||||
req.add_attr_ids(request.attr[i]);
|
||||
}
|
||||
return MmSubmitRequest(ctx_id, req_id, ORBIS_NP_MATCHING2_REQUEST_EVENT_SEARCH_ROOM,
|
||||
MmCommand::SearchRoom, MakeProtoPayload(req));
|
||||
}
|
||||
|
||||
u32 GetMmServerAddr() {
|
||||
std::lock_guard lock(g_mm.mutex);
|
||||
return g_mm.server_addr;
|
||||
}
|
||||
|
||||
u16 GetMmServerUdpPort() {
|
||||
std::lock_guard lock(g_mm.mutex);
|
||||
return g_mm.server_udp_port;
|
||||
}
|
||||
|
||||
bool RequestSignalingInfos(std::string_view target_online_id, u32* out_addr, u16* out_port) {
|
||||
if (!out_addr || !out_port) {
|
||||
return false;
|
||||
}
|
||||
std::shared_ptr<ShadNet::ShadNetClient> client;
|
||||
{
|
||||
std::lock_guard lock(g_mm.mutex);
|
||||
client = g_mm.client;
|
||||
}
|
||||
if (!client || !client->IsAuthenticated()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
shadnet::RequestSignalingInfosRequest req;
|
||||
req.set_target_npid(std::string(target_online_id));
|
||||
const u64 pkt_id =
|
||||
client->SubmitRequest(ShadNet::CommandType::RequestSignalingInfos, MakeProtoPayload(req));
|
||||
|
||||
std::pair<ShadNet::ErrorType, std::vector<u8>> reply;
|
||||
{
|
||||
std::unique_lock lock(g_mm.sig_mutex);
|
||||
if (!g_mm.sig_cv.wait_for(lock, std::chrono::seconds(5),
|
||||
[&] { return g_mm.sig_replies.count(pkt_id) > 0; })) {
|
||||
LOG_WARNING(Lib_NpMatching2, "timed out for '{}'", target_online_id);
|
||||
return false;
|
||||
}
|
||||
reply = std::move(g_mm.sig_replies[pkt_id]);
|
||||
g_mm.sig_replies.erase(pkt_id);
|
||||
}
|
||||
if (reply.first != ShadNet::ErrorType::NoError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
shadnet::RequestSignalingInfosReply rep;
|
||||
const std::string proto = ExtractProtoBytes(reply.second);
|
||||
if (proto.empty() || !rep.ParseFromString(proto)) {
|
||||
return false;
|
||||
}
|
||||
const std::string addr_str = rep.target_ip();
|
||||
const u16 port_host = static_cast<u16>(rep.target_port());
|
||||
if (addr_str.empty() || port_host == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 addr_nbo = 0;
|
||||
if (Libraries::Net::sceNetInetPton(Net::ORBIS_NET_AF_INET, addr_str.c_str(), &addr_nbo) <= 0) {
|
||||
return false;
|
||||
}
|
||||
*out_addr = addr_nbo;
|
||||
*out_port = Libraries::Net::sceNetHtons(port_host);
|
||||
LOG_DEBUG(Lib_NpMatching2, "'{}' -> {}:{}", target_online_id, addr_str, port_host);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Libraries::Np::NpMatching2
|
||||
66
src/core/libraries/np/np_matching2/np_matching2_mm.h
Normal file
66
src/core/libraries/np/np_matching2/np_matching2_mm.h
Normal file
@ -0,0 +1,66 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/np/np_matching2/np_matching2.h"
|
||||
#include "core/libraries/np/np_matching2/np_matching2_types.h"
|
||||
|
||||
namespace ShadNet {
|
||||
class ShadNetClient;
|
||||
enum class CommandType : u16;
|
||||
enum class ErrorType : u8;
|
||||
} // namespace ShadNet
|
||||
|
||||
namespace Libraries::Np::NpMatching2 {
|
||||
|
||||
enum class MmCommand : u16 {
|
||||
ContextStart = 12,
|
||||
CreateRoom = 13,
|
||||
JoinRoom = 14,
|
||||
LeaveRoom = 15,
|
||||
SearchRoom = 16,
|
||||
RequestSignalingInfos = 17,
|
||||
ContextStop = 18,
|
||||
SetRoomDataInternal = 20,
|
||||
SetRoomDataExternal = 21,
|
||||
KickoutRoomMember = 22,
|
||||
GetWorldInfoList = 23,
|
||||
};
|
||||
|
||||
void SetMmShadNetClient(std::shared_ptr<ShadNet::ShadNetClient> client,
|
||||
std::string_view server_host, u16 tcp_port);
|
||||
void ClearMmShadNetClient();
|
||||
bool IsMmClientRunning();
|
||||
|
||||
void OnMatchingReply(ShadNet::CommandType cmd, u64 pkt_id, ShadNet::ErrorType error,
|
||||
const std::vector<u8>& body);
|
||||
|
||||
void MmContextStart(OrbisNpMatching2ContextId ctx_id);
|
||||
void MmContextStop(OrbisNpMatching2ContextId ctx_id);
|
||||
|
||||
s32 MmSubmitRequest(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
OrbisNpMatching2Event req_event, MmCommand cmd, const std::vector<u8>& payload);
|
||||
|
||||
s32 MmCreateJoinRoom(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
const OrbisNpMatching2CreateJoinRoomRequest& request);
|
||||
s32 MmJoinRoom(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
const OrbisNpMatching2JoinRoomRequest& request);
|
||||
s32 MmLeaveRoom(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
const OrbisNpMatching2LeaveRoomRequest& request);
|
||||
s32 MmGetWorldInfoList(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
const OrbisNpMatching2GetWorldInfoListRequest& request);
|
||||
s32 MmSearchRoom(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
const OrbisNpMatching2SearchRoomRequest& request);
|
||||
|
||||
u32 GetMmServerAddr();
|
||||
u16 GetMmServerUdpPort();
|
||||
|
||||
bool RequestSignalingInfos(std::string_view target_online_id, u32* out_addr, u16* out_port);
|
||||
|
||||
} // namespace Libraries::Np::NpMatching2
|
||||
@ -261,6 +261,20 @@ struct OrbisNpMatching2PresenceOptionData {
|
||||
u64 len;
|
||||
};
|
||||
|
||||
struct OrbisNpMatching2RoomMemberUpdateA {
|
||||
OrbisNpMatching2RoomMemberDataInternalA* roomMemberDataInternal;
|
||||
OrbisNpMatching2EventCause eventCause;
|
||||
u8 padding[7];
|
||||
OrbisNpMatching2PresenceOptionData optData;
|
||||
};
|
||||
|
||||
struct OrbisNpMatching2RoomUpdate {
|
||||
OrbisNpMatching2EventCause eventCause;
|
||||
u8 padding[3];
|
||||
s32 errorCode;
|
||||
OrbisNpMatching2PresenceOptionData optData;
|
||||
};
|
||||
|
||||
struct OrbisNpMatching2JoinRoomRequest {
|
||||
OrbisNpMatching2RoomId roomId;
|
||||
OrbisNpMatching2SessionPassword* roomPasswd;
|
||||
@ -315,6 +329,20 @@ struct OrbisNpMatching2RangeFilter {
|
||||
u32 max;
|
||||
};
|
||||
|
||||
using OrbisNpMatching2Operator = u8;
|
||||
|
||||
struct OrbisNpMatching2IntSearchFilter {
|
||||
OrbisNpMatching2Operator searchOperator;
|
||||
u8 padding[7];
|
||||
OrbisNpMatching2IntAttr attr;
|
||||
};
|
||||
|
||||
struct OrbisNpMatching2BinSearchFilter {
|
||||
OrbisNpMatching2Operator searchOperator;
|
||||
u8 padding[7];
|
||||
OrbisNpMatching2BinAttr attr;
|
||||
};
|
||||
|
||||
struct OrbisNpMatching2RequestOptParam {
|
||||
OrbisNpMatching2RequestCallback callback;
|
||||
void* arg;
|
||||
@ -404,6 +432,13 @@ struct OrbisNpMatching2RoomMemberDataInternal {
|
||||
};
|
||||
static_assert(sizeof(OrbisNpMatching2RoomMemberDataInternal) == 0x58);
|
||||
|
||||
struct OrbisNpMatching2RoomMemberUpdate {
|
||||
OrbisNpMatching2RoomMemberDataInternal* roomMemberDataInternal;
|
||||
OrbisNpMatching2EventCause eventCause;
|
||||
u8 padding[7];
|
||||
OrbisNpMatching2PresenceOptionData optData;
|
||||
};
|
||||
|
||||
struct OrbisNpMatching2RoomMemberDataInternalList {
|
||||
OrbisNpMatching2RoomMemberDataInternal* members;
|
||||
u64 membersNum;
|
||||
@ -450,9 +485,9 @@ struct OrbisNpMatching2SearchRoomRequest {
|
||||
OrbisNpMatching2RangeFilter rangeFilter;
|
||||
OrbisNpMatching2Flags flags1;
|
||||
OrbisNpMatching2Flags flags2;
|
||||
void* intFilter;
|
||||
OrbisNpMatching2IntSearchFilter* intFilter;
|
||||
u64 intFilters;
|
||||
void* binFilter;
|
||||
OrbisNpMatching2BinSearchFilter* binFilter;
|
||||
u64 binFilters;
|
||||
OrbisNpMatching2AttributeId* attr;
|
||||
u64 attrs;
|
||||
|
||||
@ -7,7 +7,9 @@
|
||||
#include <thread>
|
||||
|
||||
#include "client.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/thread.h"
|
||||
#include "shadnet.pb.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -132,6 +134,9 @@ u64 ShadNetClient::GetUserId() const {
|
||||
u32 ShadNetClient::GetAddrLocal() const {
|
||||
return m_addr_local.load();
|
||||
}
|
||||
u32 ShadNetClient::GetAddrServer() const {
|
||||
return m_addr_server.load();
|
||||
}
|
||||
|
||||
u32 ShadNetClient::GetNumFriends() const {
|
||||
std::lock_guard lock(m_mutex_friends);
|
||||
@ -153,6 +158,7 @@ std::string ShadNetClient::GetBearerToken() const {
|
||||
// Threading
|
||||
|
||||
void ShadNetClient::ConnectThread() {
|
||||
Common::SetCurrentThreadName("ShadNet:Connect");
|
||||
bool connected = false;
|
||||
u32 backoff_ms = SHAD_CONNECT_RETRY_BACKOFF_MS;
|
||||
for (u32 attempt = 1; attempt <= SHAD_CONNECT_MAX_ATTEMPTS && !m_terminate; ++attempt) {
|
||||
@ -187,6 +193,7 @@ void ShadNetClient::ConnectThread() {
|
||||
req.set_password(m_password);
|
||||
if (!m_token.empty())
|
||||
req.set_token(m_token);
|
||||
req.set_title_id(std::string(Common::ElfInfo::Instance().GameSerial()));
|
||||
|
||||
const u64 id = m_pkt_counter.fetch_add(1);
|
||||
if (!SendAll(BuildPacket(CommandType::Login, id, MakeProtoPayload(req)))) {
|
||||
@ -198,6 +205,7 @@ void ShadNetClient::ConnectThread() {
|
||||
}
|
||||
|
||||
void ShadNetClient::ReaderThread() {
|
||||
Common::SetCurrentThreadName("ShadNet:Reader");
|
||||
while (!m_terminate) {
|
||||
u8 hdr[SHAD_HEADER_SIZE];
|
||||
if (!RecvN(hdr, SHAD_HEADER_SIZE)) {
|
||||
@ -239,6 +247,7 @@ void ShadNetClient::ReaderThread() {
|
||||
}
|
||||
|
||||
void ShadNetClient::WriterThread() {
|
||||
Common::SetCurrentThreadName("ShadNet:Writer");
|
||||
while (!m_terminate) {
|
||||
std::unique_lock lock(m_mutex_send_queue);
|
||||
m_cv_send_queue.wait(lock, [&] { return m_terminate.load() || !m_send_queue.empty(); });
|
||||
@ -342,6 +351,11 @@ bool ShadNetClient::DoConnect() {
|
||||
if (::getsockname(m_sock, reinterpret_cast<struct sockaddr*>(&local), &alen) == 0)
|
||||
m_addr_local.store(local.sin_addr.s_addr);
|
||||
|
||||
struct sockaddr_in peer{};
|
||||
socklen_t plen = sizeof(peer);
|
||||
if (::getpeername(m_sock, reinterpret_cast<struct sockaddr*>(&peer), &plen) == 0)
|
||||
m_addr_server.store(peer.sin_addr.s_addr);
|
||||
|
||||
LOG_INFO(ShadNet, "TCP connected to {}:{}", m_host, m_port);
|
||||
|
||||
// Apply receive timeout for the ServerInfo handshake.
|
||||
@ -717,6 +731,50 @@ void ShadNetClient::HandleNotification(u16 cmd_raw, const std::vector<u8>& paylo
|
||||
onFriendStatus(n);
|
||||
break;
|
||||
}
|
||||
case NotificationType::RoomEvent: {
|
||||
shadnet::NotifyRoomEvent pb;
|
||||
if (!pb.ParseFromString(blob)) {
|
||||
LOG_WARNING(ShadNet, "RoomEvent parse error");
|
||||
break;
|
||||
}
|
||||
NotifyRoomEvent n;
|
||||
n.ctx_id = pb.ctx_id();
|
||||
n.room_id = pb.room_id();
|
||||
n.event = pb.event();
|
||||
n.event_cause = pb.event_cause();
|
||||
n.error_code = pb.error_code();
|
||||
n.flags = pb.flags();
|
||||
if (pb.has_member()) {
|
||||
const auto& m = pb.member();
|
||||
n.member_npid = m.npid();
|
||||
n.member_id = m.member_id();
|
||||
n.member_team_id = m.team_id();
|
||||
n.member_is_owner = m.is_owner();
|
||||
n.member_join_date = m.join_date();
|
||||
n.member_nat_type = m.nat_type();
|
||||
n.member_flag_attr = m.flag_attr();
|
||||
n.member_group_id = m.group_id();
|
||||
n.member_addr = m.addr();
|
||||
n.member_port = m.port();
|
||||
for (const auto& a : m.bin_attrs_internal()) {
|
||||
MatchingBinAttr ba;
|
||||
ba.attr_id = a.attr_id();
|
||||
ba.data.assign(a.data().begin(), a.data().end());
|
||||
n.member_bin_attrs.push_back(std::move(ba));
|
||||
}
|
||||
}
|
||||
for (const auto& a : pb.bin_attrs()) {
|
||||
MatchingBinAttr ba;
|
||||
ba.attr_id = a.attr_id();
|
||||
ba.data.assign(a.data().begin(), a.data().end());
|
||||
n.bin_attrs.push_back(std::move(ba));
|
||||
}
|
||||
LOG_DEBUG(ShadNet, "RoomEvent room_id={} event={:#x} cause={}", n.room_id, n.event,
|
||||
n.event_cause);
|
||||
if (onRoomEvent)
|
||||
onRoomEvent(n);
|
||||
break;
|
||||
}
|
||||
case NotificationType::WebApiPushEvent: {
|
||||
// Length-prefixed fields: npServiceName, npServiceLabel(u32 LE), dataType,
|
||||
// data, fromNpid, toNpid. ExtractBlob returns {} past the end, so a short
|
||||
|
||||
@ -66,16 +66,18 @@ enum class CommandType : u16 {
|
||||
AddBlock = 10,
|
||||
RemoveBlock = 11,
|
||||
// Matchmaking
|
||||
RegisterHandlers = 12,
|
||||
ContextStart = 12,
|
||||
CreateRoom = 13,
|
||||
JoinRoom = 14,
|
||||
LeaveRoom = 15,
|
||||
GetRoomList = 16,
|
||||
SearchRoom = 16,
|
||||
RequestSignalingInfos = 17,
|
||||
ContextStop = 18,
|
||||
SetRoomDataInternal = 20,
|
||||
SetRoomDataExternal = 21,
|
||||
KickoutRoomMember = 22,
|
||||
// 23-29 reserved for future matchmaking commands
|
||||
GetWorldInfoList = 23,
|
||||
// 24-29 reserved for future matchmaking commands
|
||||
GetBoardInfos = 30,
|
||||
RecordScore = 31,
|
||||
RecordScoreData = 32,
|
||||
@ -93,6 +95,7 @@ enum class NotificationType : u16 {
|
||||
FriendNew = 6,
|
||||
FriendLost = 7,
|
||||
FriendStatus = 8,
|
||||
RoomEvent = 10,
|
||||
WebApiPushEvent = 17, // Generic NP WebApi push event
|
||||
};
|
||||
|
||||
@ -189,6 +192,34 @@ struct NotifyWebApiPushEvent {
|
||||
std::string toNpid; // may be empty
|
||||
};
|
||||
|
||||
struct MatchingBinAttr {
|
||||
u32 attr_id = 0;
|
||||
std::vector<u8> data;
|
||||
};
|
||||
|
||||
struct NotifyRoomEvent {
|
||||
u32 ctx_id = 0;
|
||||
u64 room_id = 0;
|
||||
u32 event = 0;
|
||||
u32 event_cause = 0;
|
||||
s32 error_code = 0;
|
||||
u32 flags = 0;
|
||||
|
||||
std::string member_npid;
|
||||
u32 member_id = 0;
|
||||
u32 member_team_id = 0;
|
||||
bool member_is_owner = false;
|
||||
u64 member_join_date = 0;
|
||||
u32 member_nat_type = 0;
|
||||
u32 member_flag_attr = 0;
|
||||
u32 member_group_id = 0;
|
||||
std::string member_addr;
|
||||
u32 member_port = 0;
|
||||
std::vector<MatchingBinAttr> member_bin_attrs;
|
||||
|
||||
std::vector<MatchingBinAttr> bin_attrs;
|
||||
};
|
||||
|
||||
// ShadNetClient
|
||||
|
||||
class ShadNetClient {
|
||||
@ -212,6 +243,7 @@ public:
|
||||
const std::string& GetAvatarUrl() const;
|
||||
u64 GetUserId() const;
|
||||
u32 GetAddrLocal() const;
|
||||
u32 GetAddrServer() const;
|
||||
u32 GetNumFriends() const;
|
||||
std::optional<std::string> GetFriendNpid(u32 index) const;
|
||||
|
||||
@ -223,6 +255,7 @@ public:
|
||||
std::function<void(const NotifyFriendNew&)> onFriendNew;
|
||||
std::function<void(const NotifyFriendLost&)> onFriendLost;
|
||||
std::function<void(const NotifyFriendStatus&)> onFriendStatus;
|
||||
std::function<void(const NotifyRoomEvent&)> onRoomEvent;
|
||||
std::function<void(const NotifyWebApiPushEvent&)> onWebApiPushEvent;
|
||||
// Async reply callback.
|
||||
// cmd —command this reply is for (matches the request's cmd)
|
||||
@ -298,6 +331,7 @@ private:
|
||||
mutable std::mutex m_mutex_bearer;
|
||||
std::string m_bearer_token;
|
||||
std::atomic<u32> m_addr_local{0};
|
||||
std::atomic<u32> m_addr_server{0};
|
||||
|
||||
mutable std::mutex m_mutex_friends;
|
||||
std::vector<FriendEntry> m_friends;
|
||||
|
||||
@ -13,7 +13,8 @@ option optimize_for = LITE_RUNTIME;
|
||||
message LoginRequest {
|
||||
string npid = 1;
|
||||
string password = 2;
|
||||
string token = 3;
|
||||
string token = 3;
|
||||
string title_id = 4;
|
||||
}
|
||||
|
||||
message FriendEntry {
|
||||
@ -202,12 +203,6 @@ message MatchingRoomGroup {
|
||||
uint32 num_members = 6;
|
||||
}
|
||||
|
||||
message MatchingCallbackEntry {
|
||||
bool enabled = 1;
|
||||
uint64 callback_addr = 2;
|
||||
uint64 callback_arg = 3;
|
||||
}
|
||||
|
||||
// Room data structures
|
||||
|
||||
message MatchingRoomDataInternal {
|
||||
@ -234,6 +229,12 @@ message MatchingRoomMemberData {
|
||||
bool is_owner = 4;
|
||||
MatchingRoomGroup group = 5;
|
||||
repeated MatchingBinAttr bin_attrs_internal = 6;
|
||||
uint64 join_date = 7;
|
||||
uint32 nat_type = 8;
|
||||
uint32 flag_attr = 9;
|
||||
string addr = 10;
|
||||
uint32 port = 11;
|
||||
uint32 group_id = 12;
|
||||
}
|
||||
|
||||
message MatchingRoomDataExternal {
|
||||
@ -266,12 +267,12 @@ message CreateJoinRoomResponse {
|
||||
|
||||
// Command requests
|
||||
|
||||
message RegisterHandlersRequest {
|
||||
string addr = 1;
|
||||
uint32 port = 2;
|
||||
uint32 ctx_id = 3;
|
||||
uint32 service_label = 4;
|
||||
repeated MatchingCallbackEntry callbacks = 5;
|
||||
message ContextStartRequest {
|
||||
uint32 ctx_id = 1;
|
||||
}
|
||||
|
||||
message ContextStopRequest {
|
||||
uint32 ctx_id = 1;
|
||||
}
|
||||
|
||||
message CreateRoomRequest {
|
||||
@ -364,8 +365,52 @@ message JoinRoomReply {
|
||||
CreateJoinRoomResponse details = 6;
|
||||
}
|
||||
|
||||
message GetRoomListReply {
|
||||
message MatchingIntSearchFilter {
|
||||
uint32 op = 1; // 1=Eq 2=Ne 3=Lt 4=Le 5=Gt 6=Ge
|
||||
uint32 attr_id = 2;
|
||||
uint32 attr_value = 3;
|
||||
}
|
||||
|
||||
message MatchingBinSearchFilter {
|
||||
uint32 op = 1; // 1=Eq 2=Ne
|
||||
uint32 attr_id = 2;
|
||||
bytes data = 3;
|
||||
}
|
||||
|
||||
message SearchRoomRequest {
|
||||
uint32 world_id = 1;
|
||||
uint64 lobby_id = 2;
|
||||
uint32 range_filter_start = 3;
|
||||
uint32 range_filter_max = 4;
|
||||
uint32 flag_filter = 5;
|
||||
uint32 flag_attr = 6;
|
||||
repeated MatchingIntSearchFilter int_filters = 7;
|
||||
repeated MatchingBinSearchFilter bin_filters = 8;
|
||||
repeated uint32 attr_ids = 9;
|
||||
}
|
||||
|
||||
message SearchRoomReply {
|
||||
repeated MatchingRoomDataExternal rooms = 1;
|
||||
uint32 range_start = 2;
|
||||
uint32 range_total = 3;
|
||||
uint32 range_result = 4;
|
||||
}
|
||||
|
||||
message MatchingWorld {
|
||||
uint32 world_id = 1;
|
||||
uint32 lobbies_num = 2;
|
||||
uint32 max_lobby_members = 3;
|
||||
uint32 lobby_members_num = 4;
|
||||
uint32 rooms_num = 5;
|
||||
uint32 room_members_num = 6;
|
||||
}
|
||||
|
||||
message GetWorldInfoListRequest {
|
||||
uint32 server_id = 1;
|
||||
}
|
||||
|
||||
message GetWorldInfoListReply {
|
||||
repeated MatchingWorld worlds = 1;
|
||||
}
|
||||
|
||||
message RequestSignalingInfosReply {
|
||||
@ -382,74 +427,19 @@ message KickoutRoomMemberReply { uint64 room_id = 1; }
|
||||
|
||||
// Notification payloads
|
||||
// Every notification carries ctx_id so the client can route it to the owning
|
||||
// context (the server tracks ctx_id per connection from RegisterHandlersRequest).
|
||||
// context (the server tracks ctx_id per connection from ContextStartRequest).
|
||||
|
||||
// NotificationType::RequestEvent (9)
|
||||
message NotifyRequestEvent {
|
||||
uint32 ctx_id = 1;
|
||||
uint32 server_id = 2;
|
||||
uint32 world_id = 3;
|
||||
uint32 lobby_id = 4;
|
||||
uint32 req_event = 5;
|
||||
uint32 req_id = 6;
|
||||
uint32 error_code = 7;
|
||||
uint64 room_id = 8;
|
||||
uint32 member_id = 9;
|
||||
uint32 max_slots = 10;
|
||||
uint32 flags = 11;
|
||||
bool is_owner = 12;
|
||||
bytes response_blob = 13;
|
||||
}
|
||||
|
||||
// NotificationType::MemberJoined (10)
|
||||
message NotifyMemberJoined {
|
||||
uint64 room_id = 1;
|
||||
uint32 member_id = 2;
|
||||
string npid = 3;
|
||||
string addr = 4;
|
||||
uint32 port = 5;
|
||||
repeated MatchingBinAttr bin_attrs = 6;
|
||||
uint32 ctx_id = 7;
|
||||
}
|
||||
|
||||
// NotificationType::MemberLeft (11)
|
||||
message NotifyMemberLeft {
|
||||
uint64 room_id = 1;
|
||||
uint32 member_id = 2;
|
||||
string npid = 3;
|
||||
uint32 ctx_id = 4;
|
||||
}
|
||||
|
||||
// NotificationType::SignalingHelper (12)
|
||||
message NotifySignalingHelper {
|
||||
string npid = 1;
|
||||
uint32 member_id = 2;
|
||||
string addr = 3;
|
||||
uint32 port = 4;
|
||||
uint32 ctx_id = 5;
|
||||
}
|
||||
|
||||
// NotificationType::SignalingEvent (13)
|
||||
message NotifySignalingEvent {
|
||||
uint32 event_type = 1;
|
||||
uint64 room_id = 2;
|
||||
uint32 member_id = 3;
|
||||
uint32 conn_id = 4;
|
||||
uint32 ctx_id = 5;
|
||||
}
|
||||
|
||||
// NotificationType::RoomDataInternalUpdated (15)
|
||||
message NotifyRoomDataInternalUpdated {
|
||||
uint64 room_id = 1;
|
||||
uint32 flags = 2;
|
||||
repeated MatchingBinAttr bin_attrs = 3;
|
||||
uint32 ctx_id = 4;
|
||||
}
|
||||
|
||||
// NotificationType::KickedOut (16)
|
||||
message NotifyKickedOut {
|
||||
uint64 room_id = 1;
|
||||
int32 status_code = 2;
|
||||
uint32 guard_value = 3;
|
||||
uint32 ctx_id = 4;
|
||||
// NotificationType::RoomEvent (10) — every room event maps onto
|
||||
// OrbisNpMatching2RoomEventCallback(ctxId, roomId, event, data). The Orbis
|
||||
// event id (0x11xx) is carried in `event`; the data shape depends on it:
|
||||
// member events fill `member`; room-data-updated fills bin_attrs + flags.
|
||||
message NotifyRoomEvent {
|
||||
uint32 ctx_id = 1;
|
||||
uint64 room_id = 2;
|
||||
uint32 event = 3;
|
||||
uint32 event_cause = 4;
|
||||
int32 error_code = 5;
|
||||
MatchingRoomMemberData member = 6;
|
||||
repeated MatchingBinAttr bin_attrs = 7;
|
||||
uint32 flags = 8;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user