Matching2 part 2: more types, structs, enums, context backend, init, term, createcontext / createcontextA. added ctxid to all shadnet notifications for matching2 and added matching2 events. (#4631)

This commit is contained in:
metr1k-91 2026-06-27 00:48:55 -05:00 committed by GitHub
parent 38444639a5
commit 59d7c92e45
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 1053 additions and 60 deletions

View File

@ -632,6 +632,8 @@ set(NP_LIBS src/core/libraries/np/np_error.h
src/core/libraries/np/np_manager.h
src/core/libraries/np/np_matching2/np_matching2.cpp
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_types.h
src/core/libraries/np/np_trophy.cpp
src/core/libraries/np/np_trophy.h

View File

@ -8,8 +8,10 @@
#include "core/emulator_settings.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "core/libraries/np/np_handler.h"
#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_types.h"
#include "core/libraries/np/np_types.h"
#include "core/libraries/system/userservice.h"
@ -38,9 +40,7 @@ int PS4_SYSV_ABI sceNpMatching2CreateContext(const OrbisNpMatching2CreateContext
return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
*ctxId = contextId++;
return ORBIS_OK;
return ContextManager::Instance().CreateContext(param->npId, param->serviceLabel, ctxId);
}
int PS4_SYSV_ABI sceNpMatching2CreateContextA(const OrbisNpMatching2CreateContextParameterA* param,
@ -55,9 +55,10 @@ int PS4_SYSV_ABI sceNpMatching2CreateContextA(const OrbisNpMatching2CreateContex
return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
*ctxId = contextId++;
const Libraries::Np::OrbisNpId np_id =
Libraries::Np::NpHandler::GetInstance().GetNpId(param->userId);
return ORBIS_OK;
return ContextManager::Instance().CreateContext(&np_id, param->serviceLabel, ctxId);
}
static std::optional<OrbisNpMatching2RequestOptParam> defaultRequestOptParam = std::nullopt;
@ -272,7 +273,20 @@ int PS4_SYSV_ABI sceNpMatching2Initialize(OrbisNpMatching2InitializeParameter* p
return ORBIS_NP_MATCHING2_ERROR_ALREADY_INITIALIZED;
}
if (!param || (param->size != 0x30 && param->size != 0x28)) {
return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
LOG_INFO(Lib_NpMatching2, "poolSize={:#x} stackSize={:#x} priority={} size={:#x}",
param->poolSize, param->stackSize, param->priority, param->size);
if (param->size == 0x30) {
LOG_INFO(Lib_NpMatching2, "sslPoolSize={:#x}", param->sslPoolSize);
}
InitEventDispatcher();
g_initialized = true;
g_state.initialized.store(true);
Libraries::Np::NpManager::RegisterNpCallback("NpMatching2", ProcessEvents);
return ORBIS_OK;
@ -286,8 +300,12 @@ int PS4_SYSV_ABI sceNpMatching2Terminate() {
}
g_initialized = false;
g_state.initialized.store(false);
Libraries::Np::NpManager::DeregisterNpCallback("NpMatching2");
TermEventDispatcher();
ContextManager::Instance().Reset();
return ORBIS_OK;
}

View File

@ -13,70 +13,94 @@ namespace Libraries::Np::NpMatching2 {
using OrbisNpMatching2AttributeId = u16;
using OrbisNpMatching2ContextId = u16;
using OrbisNpMatching2Event = u16;
using OrbisNpMatching2EventCause = u8;
using OrbisNpMatching2Flags = u32;
using OrbisNpMatching2LobbyId = u64;
using OrbisNpMatching2LobbyMemberId = u16;
using OrbisNpMatching2NatType = u8;
using OrbisNpMatching2RequestId = u32;
using OrbisNpMatching2RoomGroupId = u8;
using OrbisNpMatching2RoomId = u64;
using OrbisNpMatching2RoomMemberId = u16;
using OrbisNpMatching2RoomPasswordSlotMask = u64;
using OrbisNpMatching2ServerId = u16;
using OrbisNpMatching2SignalingFlag = u8;
using OrbisNpMatching2SignalingRequestId = u32;
using OrbisNpMatching2SignalingType = u8;
using OrbisNpMatching2TeamId = u8;
using OrbisNpMatching2WorldId = u32;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_GET_WORLD_INFO_LIST = 0x0002;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_ROOM_DATA_EXTERNAL = 0x0004;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_USER_INFO = 0x0007;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_CREATE_JOIN_ROOM = 0x0101;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_JOIN_ROOM = 0x0102;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_LEAVE_ROOM = 0x0103;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_KICKOUT_ROOM_MEMBER = 0x0104;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_GRANT_ROOM_OWNER = 0x0105;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_SEARCH_ROOM = 0x0106;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_SEND_ROOM_MESSAGE = 0x0108;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_ROOM_DATA_INTERNAL = 0x0109;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_GET_ROOM_DATA_INTERNAL = 0x010A;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_JOIN_LOBBY = 0x0201;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_SIGNALING_GET_PING_INFO = 0x0E01;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_ROOM_EVENT_MEMBER_JOINED = 0x1101;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_ROOM_EVENT_MEMBER_LEFT = 0x1102;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_ROOM_EVENT_KICKEDOUT = 0x1103;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_ROOM_EVENT_ROOM_DESTROYED = 0x1104;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_ROOM_EVENT_ROOM_OWNER_CHANGED = 0x1105;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_ROOM_EVENT_UPDATED_ROOM_DATA_INTERNAL = 0x1106;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_ROOM_EVENT_UPDATED_ROOM_MEMBER_DATA_INTERNAL =
0x1107;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_ROOM_EVENT_UPDATED_SIGNALING_OPT_PARAM = 0x1108;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_SIGNALING_EVENT_DEAD = 0x5101;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_SIGNALING_EVENT_ESTABLISHED = 0x5102;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_SIGNALING_EVENT_NETINFO_ERROR = 0x5103;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_CONTEXT_EVENT_START_OVER = 0x6F01;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_CONTEXT_EVENT_STARTED = 0x6F02;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_CONTEXT_EVENT_STOPPED = 0x6F03;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_CREATE_JOIN_ROOM_A = 0x7101;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_REQUEST_EVENT_SEARCH_ROOM_A = 0x7106;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_ROOM_EVENT_MEMBER_JOINED_A = 0x8101;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_ROOM_EVENT_MEMBER_LEFT_A = 0x8102;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_ROOM_EVENT_UPDATED_ROOM_MEMBER_DATA_INTERNAL_A =
0x8107;
constexpr OrbisNpMatching2Event ORBIS_NP_MATCHING2_ROOM_MSG_EVENT_MESSAGE_A = 0x9102;
enum OrbisNpMatching2Event : u16 {
ORBIS_NP_MATCHING2_REQUEST_EVENT_GET_WORLD_INFO_LIST = 0x0002,
ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_ROOM_DATA_EXTERNAL = 0x0004,
ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_USER_INFO = 0x0007,
ORBIS_NP_MATCHING2_REQUEST_EVENT_CREATE_JOIN_ROOM = 0x0101,
ORBIS_NP_MATCHING2_REQUEST_EVENT_JOIN_ROOM = 0x0102,
ORBIS_NP_MATCHING2_REQUEST_EVENT_LEAVE_ROOM = 0x0103,
ORBIS_NP_MATCHING2_REQUEST_EVENT_KICKOUT_ROOM_MEMBER = 0x0104,
ORBIS_NP_MATCHING2_REQUEST_EVENT_GRANT_ROOM_OWNER = 0x0105,
ORBIS_NP_MATCHING2_REQUEST_EVENT_SEARCH_ROOM = 0x0106,
ORBIS_NP_MATCHING2_REQUEST_EVENT_SEND_ROOM_MESSAGE = 0x0108,
ORBIS_NP_MATCHING2_REQUEST_EVENT_SET_ROOM_DATA_INTERNAL = 0x0109,
ORBIS_NP_MATCHING2_REQUEST_EVENT_GET_ROOM_DATA_INTERNAL = 0x010A,
ORBIS_NP_MATCHING2_REQUEST_EVENT_JOIN_LOBBY = 0x0201,
ORBIS_NP_MATCHING2_REQUEST_EVENT_SIGNALING_GET_PING_INFO = 0x0E01,
ORBIS_NP_MATCHING2_ROOM_EVENT_MEMBER_JOINED = 0x1101,
ORBIS_NP_MATCHING2_ROOM_EVENT_MEMBER_LEFT = 0x1102,
ORBIS_NP_MATCHING2_ROOM_EVENT_KICKEDOUT = 0x1103,
ORBIS_NP_MATCHING2_ROOM_EVENT_ROOM_DESTROYED = 0x1104,
ORBIS_NP_MATCHING2_ROOM_EVENT_ROOM_OWNER_CHANGED = 0x1105,
ORBIS_NP_MATCHING2_ROOM_EVENT_UPDATED_ROOM_DATA_INTERNAL = 0x1106,
ORBIS_NP_MATCHING2_ROOM_EVENT_UPDATED_ROOM_MEMBER_DATA_INTERNAL = 0x1107,
ORBIS_NP_MATCHING2_ROOM_EVENT_UPDATED_SIGNALING_OPT_PARAM = 0x1108,
ORBIS_NP_MATCHING2_SIGNALING_EVENT_DEAD = 0x5101,
ORBIS_NP_MATCHING2_SIGNALING_EVENT_ESTABLISHED = 0x5102,
ORBIS_NP_MATCHING2_SIGNALING_EVENT_NETINFO_ERROR = 0x5103,
ORBIS_NP_MATCHING2_CONTEXT_EVENT_START_OVER = 0x6F01,
ORBIS_NP_MATCHING2_CONTEXT_EVENT_STARTED = 0x6F02,
ORBIS_NP_MATCHING2_CONTEXT_EVENT_STOPPED = 0x6F03,
ORBIS_NP_MATCHING2_REQUEST_EVENT_CREATE_JOIN_ROOM_A = 0x7101,
ORBIS_NP_MATCHING2_REQUEST_EVENT_SEARCH_ROOM_A = 0x7106,
ORBIS_NP_MATCHING2_ROOM_EVENT_MEMBER_JOINED_A = 0x8101,
ORBIS_NP_MATCHING2_ROOM_EVENT_MEMBER_LEFT_A = 0x8102,
ORBIS_NP_MATCHING2_ROOM_EVENT_UPDATED_ROOM_MEMBER_DATA_INTERNAL_A = 0x8107,
ORBIS_NP_MATCHING2_ROOM_MSG_EVENT_MESSAGE_A = 0x9102,
};
constexpr OrbisNpMatching2EventCause ORBIS_NP_MATCHING2_EVENT_CAUSE_LEAVE_ACTION = 1;
constexpr OrbisNpMatching2EventCause ORBIS_NP_MATCHING2_EVENT_CAUSE_KICKOUT_ACTION = 2;
constexpr OrbisNpMatching2EventCause ORBIS_NP_MATCHING2_EVENT_CAUSE_GRANT_OWNER_ACTION = 3;
constexpr OrbisNpMatching2EventCause ORBIS_NP_MATCHING2_EVENT_CAUSE_MEMBER_DISAPPEARED = 5;
constexpr OrbisNpMatching2EventCause ORBIS_NP_MATCHING2_EVENT_CAUSE_NP_SIGNED_OUT = 8;
constexpr OrbisNpMatching2EventCause ORBIS_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ERROR = 10;
constexpr OrbisNpMatching2EventCause ORBIS_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ACTION = 11;
enum OrbisNpMatching2EventCause : u8 {
ORBIS_NP_MATCHING2_EVENT_CAUSE_LEAVE_ACTION = 1,
ORBIS_NP_MATCHING2_EVENT_CAUSE_KICKOUT_ACTION = 2,
ORBIS_NP_MATCHING2_EVENT_CAUSE_GRANT_OWNER_ACTION = 3,
ORBIS_NP_MATCHING2_EVENT_CAUSE_SERVER_OPERATION = 4,
ORBIS_NP_MATCHING2_EVENT_CAUSE_MEMBER_DISAPPEARED = 5,
ORBIS_NP_MATCHING2_EVENT_CAUSE_SERVER_INTERNAL = 6,
ORBIS_NP_MATCHING2_EVENT_CAUSE_CONNECTION_ERROR = 7,
ORBIS_NP_MATCHING2_EVENT_CAUSE_NP_SIGNED_OUT = 8,
ORBIS_NP_MATCHING2_EVENT_CAUSE_SYSTEM_ERROR = 9,
ORBIS_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ERROR = 10,
ORBIS_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ACTION = 11,
};
constexpr OrbisNpMatching2SignalingType ORBIS_NP_MATCHING2_SIGNALING_TYPE_NONE = 0;
constexpr OrbisNpMatching2SignalingType ORBIS_NP_MATCHING2_SIGNALING_TYPE_MESH = 1;
constexpr OrbisNpMatching2SignalingType ORBIS_NP_MATCHING2_SIGNALING_TYPE_STAR = 2;
enum OrbisNpMatching2SignalingType : u8 {
ORBIS_NP_MATCHING2_SIGNALING_TYPE_NONE = 0,
ORBIS_NP_MATCHING2_SIGNALING_TYPE_MESH = 1,
ORBIS_NP_MATCHING2_SIGNALING_TYPE_STAR = 2,
};
enum OrbisNpMatching2InitDefault : u64 {
ORBIS_NP_MATCHING2_THREAD_STACK_SIZE_DEFAULT = 32768,
ORBIS_NP_MATCHING2_POOLSIZE_DEFAULT = 131072,
ORBIS_NP_MATCHING2_SSL_POOLSIZE_DEFAULT = 196608,
};
struct OrbisNpMatching2InitializeParameter;
struct OrbisNpMatching2CreateContextParameter;
struct OrbisNpMatching2CreateContextParameterA;
int PS4_SYSV_ABI sceNpMatching2Initialize(OrbisNpMatching2InitializeParameter* param);
int PS4_SYSV_ABI sceNpMatching2Terminate();
int PS4_SYSV_ABI sceNpMatching2CreateContext(const OrbisNpMatching2CreateContextParameter* param,
OrbisNpMatching2ContextId* ctxId);
int PS4_SYSV_ABI sceNpMatching2CreateContextA(const OrbisNpMatching2CreateContextParameterA* param,
OrbisNpMatching2ContextId* ctxId);
void RegisterLib(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Np::NpMatching2

View File

@ -0,0 +1,263 @@
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/logging/log.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"
namespace Libraries::Np::NpMatching2 {
NpMatching2State g_state;
ContextManager& ContextManager::Instance() {
static ContextManager instance;
return instance;
}
ContextObject* ContextManager::GetLocked(OrbisNpMatching2ContextId ctx_id) {
if (ctx_id == 0 || ctx_id > kMaxContexts || !m_used[ctx_id]) {
return nullptr;
}
return &m_contexts[ctx_id];
}
s32 ContextManager::CreateContext(const OrbisNpId* owner_np_id, OrbisNpServiceLabel service_label,
OrbisNpMatching2ContextId* out_ctx_id) {
if (!out_ctx_id) {
return ORBIS_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
std::lock_guard lock(m_mutex);
OrbisNpMatching2ContextId id = 0;
for (u32 i = 0; i < kMaxContexts; ++i) {
const OrbisNpMatching2ContextId candidate =
static_cast<OrbisNpMatching2ContextId>(((m_next_id - 1 + i) % kMaxContexts) + 1);
if (!m_used[candidate]) {
id = candidate;
break;
}
}
if (id == 0) {
return ORBIS_NP_MATCHING2_ERROR_CONTEXT_MAX;
}
m_next_id = static_cast<OrbisNpMatching2ContextId>((id % kMaxContexts) + 1);
ContextObject& ctx = m_contexts[id];
ctx = ContextObject{};
ctx.ctx_id = id;
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";
}
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);
return ORBIS_OK;
}
bool ContextManager::Check(OrbisNpMatching2ContextId ctx_id) {
std::lock_guard lock(m_mutex);
return GetLocked(ctx_id) != nullptr;
}
ContextObject* ContextManager::Get(OrbisNpMatching2ContextId ctx_id) {
std::lock_guard lock(m_mutex);
return GetLocked(ctx_id);
}
bool ContextManager::Destroy(OrbisNpMatching2ContextId ctx_id) {
std::lock_guard lock(m_mutex);
if (!GetLocked(ctx_id)) {
return false;
}
m_contexts[ctx_id] = ContextObject{};
m_used[ctx_id] = false;
LOG_INFO(Lib_NpMatching2, "context destroyed: id={}", ctx_id);
return true;
}
s32 ContextManager::Start(OrbisNpMatching2ContextId ctx_id) {
std::lock_guard lock(m_mutex);
ContextObject* ctx = GetLocked(ctx_id);
if (!ctx) {
return ORBIS_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
}
if (ctx->started) {
return ORBIS_NP_MATCHING2_ERROR_CONTEXT_ALREADY_STARTED;
}
ctx->started = true;
LOG_INFO(Lib_NpMatching2, "context started: id={}", ctx_id);
return ORBIS_OK;
}
s32 ContextManager::Stop(OrbisNpMatching2ContextId ctx_id) {
std::lock_guard lock(m_mutex);
ContextObject* ctx = GetLocked(ctx_id);
if (!ctx) {
return ORBIS_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
}
if (!ctx->started) {
return ORBIS_NP_MATCHING2_ERROR_CONTEXT_NOT_STARTED;
}
ctx->started = false;
LOG_INFO(Lib_NpMatching2, "context stopped: id={}", ctx_id);
return ORBIS_OK;
}
void ContextManager::Reset() {
std::lock_guard lock(m_mutex);
m_contexts = {};
m_used = {};
m_next_id = 1;
}
OrbisNpMatching2RequestId AllocRequestId() {
std::lock_guard lock(g_state.mutex);
const OrbisNpMatching2RequestId id = g_state.next_request_id++;
if (g_state.next_request_id == 0) {
g_state.next_request_id = 1;
}
return id;
}
namespace {
void FireEvent(const PendingEvent& ev) {
ContextObject* ctx = ContextManager::Instance().Get(ev.ctx_id);
if (!ctx) {
return;
}
switch (ev.type) {
case PendingEvent::CONTEXT_CB:
if (ctx->context_callback) {
ctx->context_callback(ev.ctx_id, ev.ctx_event, ev.ctx_event_cause, ev.error_code,
ctx->context_callback_arg);
}
break;
case PendingEvent::REQUEST_CB:
if (ev.request_cb) {
ev.request_cb(ev.ctx_id, ev.req_id, ev.req_event, ev.error_code, ev.request_data,
ev.request_cb_arg);
}
break;
case PendingEvent::SIGNALING_CB:
if (ctx->signaling_callback) {
ctx->signaling_callback(ev.ctx_id, ev.room_id, ev.member_id, ev.sig_event,
ev.error_code, ctx->signaling_callback_arg);
}
break;
case PendingEvent::ROOM_EVENT_CB:
if (ctx->room_event_callback) {
ctx->room_event_callback(ev.ctx_id, ev.room_id, ev.room_event, ev.room_event_data,
ctx->room_event_callback_arg);
}
break;
case PendingEvent::LOBBY_EVENT_CB:
if (ctx->lobby_event_callback) {
ctx->lobby_event_callback(ev.ctx_id, ev.lobby_id, ev.lobby_event, ev.lobby_event_data,
ctx->lobby_event_callback_arg);
}
break;
case PendingEvent::LOBBY_MESSAGE_CB:
if (ctx->lobby_message_callback) {
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);
}
break;
case PendingEvent::ROOM_MESSAGE_CB:
if (ctx->room_message_callback) {
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);
}
break;
}
}
} // namespace
PS4_SYSV_ABI void* EventDispatcherThreadMain(void* /*arg*/) {
std::unique_lock lock(g_state.queue_mutex);
while (g_state.dispatch_running.load()) {
const auto now = std::chrono::steady_clock::now();
std::vector<PendingEvent> ready;
auto nearest = std::chrono::steady_clock::time_point::max();
for (auto it = g_state.pending_events.begin(); it != g_state.pending_events.end();) {
if (it->fire_at <= now) {
ready.push_back(std::move(*it));
it = g_state.pending_events.erase(it);
} else {
nearest = std::min(nearest, it->fire_at);
++it;
}
}
if (!ready.empty()) {
lock.unlock();
for (const PendingEvent& ev : ready) {
FireEvent(ev);
}
lock.lock();
continue;
}
if (nearest == std::chrono::steady_clock::time_point::max()) {
g_state.queue_cv.wait(lock);
} else {
g_state.queue_cv.wait_until(lock, nearest);
}
}
return nullptr;
}
void InitEventDispatcher() {
if (g_state.dispatch_running.exchange(true)) {
return;
}
const s32 rc = NpCommon::sceNpCreateThread(
&g_state.dispatch_thread, EventDispatcherThreadMain, nullptr,
Libraries::Kernel::ORBIS_KERNEL_PRIO_FIFO_DEFAULT,
ORBIS_NP_MATCHING2_THREAD_STACK_SIZE_DEFAULT, 0, "SceNpMatching2Ex");
if (rc < 0) {
LOG_ERROR(Lib_NpMatching2, "failed to create event dispatcher thread: {:#x}", rc);
g_state.dispatch_running.store(false);
g_state.dispatch_thread = nullptr;
return;
}
LOG_INFO(Lib_NpMatching2, "event dispatcher thread created");
}
void TermEventDispatcher() {
if (!g_state.dispatch_running.exchange(false)) {
return;
}
g_state.queue_cv.notify_all();
if (g_state.dispatch_thread) {
NpCommon::sceNpJoinThread(g_state.dispatch_thread, nullptr);
g_state.dispatch_thread = nullptr;
}
std::lock_guard lock(g_state.queue_mutex);
g_state.pending_events.clear();
}
void ScheduleEvent(PendingEvent ev) {
{
std::lock_guard lock(g_state.queue_mutex);
g_state.pending_events.push_back(std::move(ev));
}
g_state.queue_cv.notify_all();
}
} // namespace Libraries::Np::NpMatching2

View File

@ -0,0 +1,167 @@
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <map>
#include <mutex>
#include <string>
#include <vector>
#include "common/types.h"
#include "core/libraries/kernel/threads.h"
#include "core/libraries/np/np_matching2/np_matching2.h"
#include "core/libraries/np/np_matching2/np_matching2_types.h"
#include "core/libraries/np/np_types.h"
namespace Libraries::Np::NpMatching2 {
struct PeerInfo {
u32 addr = 0;
u16 port = 0;
OrbisNpMatching2RoomMemberId member_id = 0;
s32 conn_id = 0;
s32 status = 0;
std::string online_id;
};
struct ContextObject {
OrbisNpMatching2ContextId ctx_id = 0;
bool started = false;
OrbisNpMatching2ServerId server_id = 1;
OrbisNpServiceLabel service_label = 0;
Libraries::Np::OrbisNpId owner_np_id{};
std::string online_id;
std::string signaling_addr;
u16 signaling_port = 0;
u64 handler_registration_generation = 1;
OrbisNpMatching2WorldId world_id = 0;
OrbisNpMatching2LobbyId lobby_id = 0;
OrbisNpMatching2RoomId room_id = 0;
OrbisNpMatching2RoomMemberId my_member_id = 0;
bool is_room_owner = false;
u32 max_slot = 5;
OrbisNpMatching2Flags flag_attr = 0;
std::map<OrbisNpMatching2RoomMemberId, PeerInfo> peers;
OrbisNpMatching2ContextCallback context_callback = nullptr;
void* context_callback_arg = nullptr;
OrbisNpMatching2RequestCallback default_request_callback = nullptr;
void* default_request_callback_arg = nullptr;
OrbisNpMatching2RequestCallback per_request_callback = nullptr;
void* per_request_callback_arg = nullptr;
OrbisNpMatching2RoomEventCallback room_event_callback = nullptr;
void* room_event_callback_arg = nullptr;
OrbisNpMatching2RoomMessageCallback room_message_callback = nullptr;
void* room_message_callback_arg = nullptr;
OrbisNpMatching2LobbyEventCallback lobby_event_callback = nullptr;
void* lobby_event_callback_arg = nullptr;
OrbisNpMatching2LobbyMessageCallback lobby_message_callback = nullptr;
void* lobby_message_callback_arg = nullptr;
OrbisNpMatching2SignalingCallback signaling_callback = nullptr;
void* signaling_callback_arg = nullptr;
};
class ContextManager {
public:
static constexpr u32 kMaxContexts = 255;
static ContextManager& Instance();
s32 CreateContext(const OrbisNpId* owner_np_id, OrbisNpServiceLabel service_label,
OrbisNpMatching2ContextId* out_ctx_id);
bool Check(OrbisNpMatching2ContextId ctx_id);
ContextObject* Get(OrbisNpMatching2ContextId ctx_id);
bool Destroy(OrbisNpMatching2ContextId ctx_id);
s32 Start(OrbisNpMatching2ContextId ctx_id);
s32 Stop(OrbisNpMatching2ContextId ctx_id);
void Reset();
private:
ContextManager() = default;
ContextObject* GetLocked(OrbisNpMatching2ContextId ctx_id);
std::mutex m_mutex;
std::array<ContextObject, kMaxContexts + 1> m_contexts{};
std::array<bool, kMaxContexts + 1> m_used{};
OrbisNpMatching2ContextId m_next_id = 1;
};
struct PendingEvent {
enum Type {
CONTEXT_CB,
REQUEST_CB,
SIGNALING_CB,
ROOM_EVENT_CB,
LOBBY_EVENT_CB,
LOBBY_MESSAGE_CB,
ROOM_MESSAGE_CB,
};
Type type = CONTEXT_CB;
OrbisNpMatching2ContextId ctx_id = 0;
std::chrono::steady_clock::time_point fire_at;
OrbisNpMatching2Event ctx_event{};
OrbisNpMatching2EventCause ctx_event_cause{};
s32 error_code = 0;
OrbisNpMatching2RequestId req_id = 0;
OrbisNpMatching2Event req_event{};
OrbisNpMatching2RequestCallback request_cb = nullptr;
void* request_cb_arg = nullptr;
void* request_data = nullptr;
OrbisNpMatching2RoomId room_id = 0;
OrbisNpMatching2RoomMemberId member_id = 0;
OrbisNpMatching2Event sig_event{};
u32 conn_id = 0;
OrbisNpMatching2Event room_event{};
void* room_event_data = nullptr;
OrbisNpMatching2LobbyId lobby_id = 0;
OrbisNpMatching2Event lobby_event{};
void* lobby_event_data = nullptr;
OrbisNpMatching2RoomMemberId src_member_id = 0;
OrbisNpMatching2Event msg_event{};
void* message_data = nullptr;
};
struct NpMatching2State {
std::atomic<bool> initialized = false;
OrbisNpMatching2RequestId next_request_id = 1;
std::mutex mutex;
Kernel::PthreadT dispatch_thread = nullptr;
std::mutex queue_mutex;
std::condition_variable queue_cv;
std::vector<PendingEvent> pending_events;
std::atomic<bool> dispatch_running{false};
};
extern NpMatching2State g_state;
OrbisNpMatching2RequestId AllocRequestId();
void InitEventDispatcher();
void TermEventDispatcher();
void ScheduleEvent(PendingEvent ev);
PS4_SYSV_ABI void* EventDispatcherThreadMain(void* arg);
} // namespace Libraries::Np::NpMatching2

View File

@ -11,6 +11,11 @@
namespace Libraries::Np::NpMatching2 {
struct OrbisNpMatching2RoomGroup;
struct OrbisNpMatching2RoomGroupInfo;
struct OrbisNpMatching2RoomBinAttrInternal;
struct OrbisNpMatching2RoomMemberBinAttrInternal;
using OrbisNpMatching2ContextCallback = PS4_SYSV_ABI void (*)(OrbisNpMatching2ContextId contextId,
OrbisNpMatching2Event event,
OrbisNpMatching2EventCause cause,
@ -18,7 +23,12 @@ using OrbisNpMatching2ContextCallback = PS4_SYSV_ABI void (*)(OrbisNpMatching2Co
using OrbisNpMatching2LobbyEventCallback =
PS4_SYSV_ABI void (*)(OrbisNpMatching2ContextId contextId, OrbisNpMatching2LobbyId lobbyId,
OrbisNpMatching2Event event, void* data, void* userdata);
OrbisNpMatching2Event event, const void* data, void* userdata);
using OrbisNpMatching2LobbyMessageCallback =
PS4_SYSV_ABI void (*)(OrbisNpMatching2ContextId contextId, OrbisNpMatching2LobbyId lobbyId,
OrbisNpMatching2LobbyMemberId srcMemberId, OrbisNpMatching2Event event,
const void* data, void* userdata);
using OrbisNpMatching2RequestCallback = PS4_SYSV_ABI void (*)(OrbisNpMatching2ContextId,
OrbisNpMatching2RequestId,
@ -100,6 +110,36 @@ struct OrbisNpMatching2SignalingOptParam {
u8 padding[4];
};
struct OrbisNpMatching2CreateJoinRoomRequest {
u16 maxSlot;
OrbisNpMatching2TeamId teamId;
u8 pad[5];
OrbisNpMatching2Flags flags;
OrbisNpMatching2WorldId worldId;
OrbisNpMatching2LobbyId lobbyId;
void* roomPasswd;
OrbisNpMatching2RoomPasswordSlotMask* passwdSlotMask;
void* groupConfig;
u64 groupConfigs;
void* joinGroupLabel;
Libraries::Np::OrbisNpOnlineId* allowedUser;
u64 allowedUsers;
Libraries::Np::OrbisNpOnlineId* blockedUser;
u64 blockedUsers;
OrbisNpMatching2BinAttr* internalBinAttr;
u64 internalBinAttrs;
OrbisNpMatching2IntAttr* externalSearchIntAttr;
u64 externalSearchIntAttrs;
OrbisNpMatching2BinAttr* externalSearchBinAttr;
u64 externalSearchBinAttrs;
OrbisNpMatching2BinAttr* externalBinAttr;
u64 externalBinAttrs;
OrbisNpMatching2BinAttr* memberInternalBinAttr;
u64 memberInternalBinAttrs;
OrbisNpMatching2SignalingOptParam* signalingParam;
};
static_assert(sizeof(OrbisNpMatching2CreateJoinRoomRequest) == 184);
struct OrbisNpMatching2CreateJoinRoomRequestA {
u16 maxSlot;
OrbisNpMatching2TeamId teamId;
@ -142,11 +182,11 @@ struct OrbisNpMatching2RoomDataInternal {
OrbisNpMatching2RoomId roomId;
OrbisNpMatching2RoomPasswordSlotMask passwdSlotMask;
u64 joinedSlotMask;
void* roomGroup;
OrbisNpMatching2RoomGroup* roomGroup;
u64 roomGroups;
OrbisNpMatching2Flags flags;
u8 pad[4];
void* internalBinAttr;
OrbisNpMatching2RoomBinAttrInternal* internalBinAttr;
u64 internalBinAttrs;
};
@ -160,8 +200,8 @@ struct OrbisNpMatching2RoomMemberDataInternalA {
OrbisNpMatching2TeamId teamId;
OrbisNpMatching2NatType natType;
OrbisNpMatching2Flags flags;
void* roomGroup;
void* roomMemberInternalBinAttr;
OrbisNpMatching2RoomGroup* roomGroup;
OrbisNpMatching2RoomMemberBinAttrInternal* roomMemberInternalBinAttr;
u64 roomMemberInternalBinAttrs;
};
@ -197,6 +237,14 @@ struct OrbisNpMatching2GetWorldInfoListResponse {
u64 worldNum;
};
struct OrbisNpMatching2GroupLabel {
u8 data[8];
};
struct OrbisNpMatching2SessionPassword {
u8 data[8];
};
struct OrbisNpMatching2InitializeParameter {
u64 poolSize;
u64 unknown;
@ -213,11 +261,48 @@ struct OrbisNpMatching2PresenceOptionData {
u64 len;
};
struct OrbisNpMatching2JoinRoomRequest {
OrbisNpMatching2RoomId roomId;
OrbisNpMatching2SessionPassword* roomPasswd;
OrbisNpMatching2GroupLabel* joinGroupLabel;
OrbisNpMatching2BinAttr* roomMemberBinInternalAttr;
u64 roomMemberBinInternalAttrNum;
OrbisNpMatching2PresenceOptionData optData;
OrbisNpMatching2TeamId teamId;
u8 pad[3];
OrbisNpMatching2Flags flags;
Libraries::Np::OrbisNpOnlineId* blockedUser;
u64 blockedUsers;
int Validate() {
return 0;
}
};
static_assert(sizeof(OrbisNpMatching2JoinRoomRequest) == 0x58);
struct OrbisNpMatching2KickoutRoomMemberRequest {
OrbisNpMatching2RoomId roomId;
OrbisNpMatching2RoomMemberId memberId;
u8 pad0[6];
u32 blockKickFlag;
u8 pad1[12];
u64 reserved;
int Validate() {
return 0;
}
};
static_assert(sizeof(OrbisNpMatching2KickoutRoomMemberRequest) == 0x28);
struct OrbisNpMatching2LeaveRoomRequest {
OrbisNpMatching2RoomId roomId;
OrbisNpMatching2PresenceOptionData optData;
};
struct OrbisNpMatching2LeaveRoomResponse {
OrbisNpMatching2RoomId roomId;
};
struct OrbisNpMatching2Range {
u32 start;
u32 total;
@ -256,7 +341,99 @@ struct OrbisNpMatching2RoomDataExternalA {
u16 openPrivateSlots;
Libraries::Np::OrbisNpPeerAddressA owner;
Libraries::Np::OrbisNpOnlineId ownerOnlineId;
void* roomGroup;
OrbisNpMatching2RoomGroupInfo* roomGroup;
u64 roomGroups;
OrbisNpMatching2IntAttr* externalSearchIntAttr;
u64 externalSearchIntAttrs;
OrbisNpMatching2BinAttr* externalSearchBinAttr;
u64 externalSearchBinAttrs;
OrbisNpMatching2BinAttr* externalBinAttr;
u64 externalBinAttrs;
};
struct OrbisNpMatching2RoomGroup {
OrbisNpMatching2RoomGroupId id;
bool hasPasswd;
bool hasLabel;
u8 pad;
OrbisNpMatching2GroupLabel label;
u32 slots;
u32 groupMembers;
};
struct OrbisNpMatching2RoomGroupConfig {
u32 slots;
bool hasLabel;
OrbisNpMatching2GroupLabel label;
bool hasPassword;
u8 pad[2];
};
struct OrbisNpMatching2RoomGroupInfo {
OrbisNpMatching2RoomGroupId id;
bool hasPasswd;
u8 pad[2];
u32 slots;
u32 groupMembers;
};
struct OrbisNpMatching2RoomBinAttrInternal {
Libraries::Rtc::OrbisRtcTick lastUpdate;
OrbisNpMatching2RoomMemberId memberId;
u8 pad[6];
OrbisNpMatching2BinAttr binAttr;
};
struct OrbisNpMatching2RoomMemberBinAttrInternal {
Libraries::Rtc::OrbisRtcTick lastUpdate;
OrbisNpMatching2BinAttr binAttr;
};
struct OrbisNpMatching2RoomMemberDataInternal {
OrbisNpMatching2RoomMemberDataInternal* next;
u64 joinDate;
Libraries::Np::OrbisNpId npId;
u8 pad[4];
OrbisNpMatching2RoomMemberId memberId;
OrbisNpMatching2TeamId teamId;
OrbisNpMatching2NatType natType;
OrbisNpMatching2Flags flagAttr;
OrbisNpMatching2RoomGroup* roomGroup;
OrbisNpMatching2RoomMemberBinAttrInternal* roomMemberBinAttrInternal;
u64 roomMemberBinAttrInternalNum;
};
static_assert(sizeof(OrbisNpMatching2RoomMemberDataInternal) == 0x58);
struct OrbisNpMatching2RoomMemberDataInternalList {
OrbisNpMatching2RoomMemberDataInternal* members;
u64 membersNum;
OrbisNpMatching2RoomMemberDataInternal* me;
OrbisNpMatching2RoomMemberDataInternal* owner;
};
struct OrbisNpMatching2CreateJoinRoomResponse {
const OrbisNpMatching2RoomDataInternal* roomData;
OrbisNpMatching2RoomMemberDataInternalList members;
};
struct OrbisNpMatching2RoomDataExternal {
OrbisNpMatching2RoomDataExternal* next;
u16 maxSlot;
u16 curMembers;
OrbisNpMatching2Flags flags;
OrbisNpMatching2ServerId serverId;
u8 pad[2];
OrbisNpMatching2WorldId worldId;
OrbisNpMatching2LobbyId lobbyId;
OrbisNpMatching2RoomId roomId;
u64 passwdSlotMask;
u64 joinedSlotMask;
u16 publicSlots;
u16 privateSlots;
u16 openPublicSlots;
u16 openPrivateSlots;
Libraries::Np::OrbisNpId* ownerNpId;
OrbisNpMatching2RoomGroupInfo* roomGroup;
u64 roomGroups;
OrbisNpMatching2IntAttr* externalSearchIntAttr;
u64 externalSearchIntAttrs;
@ -281,11 +458,43 @@ struct OrbisNpMatching2SearchRoomRequest {
u64 attrs;
};
struct OrbisNpMatching2SearchRoomResponse {
OrbisNpMatching2Range range;
OrbisNpMatching2RoomDataExternal* roomDataExt;
};
struct OrbisNpMatching2SearchRoomResponseA {
OrbisNpMatching2Range range;
OrbisNpMatching2RoomDataExternalA* roomDataExt;
};
struct OrbisNpMatching2SetRoomDataExternalRequest {
OrbisNpMatching2RoomId roomId;
OrbisNpMatching2IntAttr* roomSearchableIntAttrExternal;
u32 roomSearchableIntAttrExternalNum;
u8 pad1[4];
OrbisNpMatching2BinAttr* roomSearchableBinAttrExternal;
u32 roomSearchableBinAttrExternalNum;
u8 pad2[4];
OrbisNpMatching2BinAttr* roomBinAttrExternal;
u32 roomBinAttrExternalNum;
u8 pad3[4];
};
struct OrbisNpMatching2SetRoomDataInternalRequest {
OrbisNpMatching2RoomId roomId;
OrbisNpMatching2Flags flagFilter;
OrbisNpMatching2Flags flagAttr;
OrbisNpMatching2BinAttr* roomBinAttrInternal;
u32 roomBinAttrInternalNum;
OrbisNpMatching2RoomGroupConfig* passwordConfig;
u32 passwordConfigNum;
u64* passwordSlotMask;
OrbisNpMatching2RoomMemberId* ownerPrivilegeRank;
u32 ownerPrivilegeRankNum;
u8 padding[4];
};
struct OrbisNpMatching2SetUserInfoRequest {
OrbisNpMatching2ServerId serverId;
u8 padding[6];
@ -293,6 +502,16 @@ struct OrbisNpMatching2SetUserInfoRequest {
u64 userBinAttrs;
};
struct OrbisNpMatching2SignalingConnectionInfoAddr {
u32 addr;
u16 port;
u8 pad[2];
};
struct OrbisNpMatching2SignalingConnectionInfo {
OrbisNpMatching2SignalingConnectionInfoAddr address;
};
// internal - to be removed.
struct OrbisNpMatching2SignalingEvent {
OrbisNpMatching2ContextId contextId;
@ -302,4 +521,29 @@ struct OrbisNpMatching2SignalingEvent {
int errorCode;
};
struct OrbisNpMatching2SignalingGetPingInfoRequest {
OrbisNpMatching2RoomId roomId;
u8 pad[16];
int Validate() {
return 0;
}
};
struct OrbisNpMatching2SignalingGetPingInfoResponse {
OrbisNpMatching2ServerId serverId;
u8 pad[2];
OrbisNpMatching2WorldId worldId;
OrbisNpMatching2RoomId roomId;
u32 pingUs;
u8 reserved[20];
};
struct OrbisNpMatching2SignalingParam {
int type;
int flag;
OrbisNpMatching2RoomMemberId mainMember;
u8 pad[4];
};
} // namespace Libraries::Np::NpMatching2

View File

@ -178,3 +178,278 @@ message GetScoreResponse {
uint64 lastSortDate = 4;
uint32 totalRecord = 5;
}
// Matching
// Shared attribute types
message MatchingBinAttr {
uint32 attr_id = 1;
bytes data = 2;
}
message MatchingIntAttr {
uint32 attr_id = 1;
uint32 attr_value = 2;
}
message MatchingRoomGroup {
uint32 group_id = 1;
bool has_passwd = 2;
bool has_label = 3;
bytes label = 4;
uint32 slot_count = 5;
uint32 num_members = 6;
}
message MatchingCallbackEntry {
bool enabled = 1;
uint64 callback_addr = 2;
uint64 callback_arg = 3;
}
// Room data structures
message MatchingRoomDataInternal {
uint32 public_slots = 1;
uint32 private_slots = 2;
uint32 open_public_slots = 3;
uint32 open_private_slots = 4;
uint32 max_slot = 5;
uint32 server_id = 6;
uint32 world_id = 7;
uint32 lobby_id = 8;
uint64 room_id = 9;
uint64 passwd_slot_mask = 10;
uint32 joined_slot_mask = 11;
repeated MatchingRoomGroup groups = 12;
uint32 flags = 13;
repeated MatchingBinAttr bin_attrs_internal = 14;
}
message MatchingRoomMemberData {
string npid = 1;
uint32 member_id = 2;
uint32 team_id = 3;
bool is_owner = 4;
MatchingRoomGroup group = 5;
repeated MatchingBinAttr bin_attrs_internal = 6;
}
message MatchingRoomDataExternal {
uint32 max_slot = 1;
uint32 cur_members = 2;
uint32 flags = 3;
uint32 server_id = 4;
uint32 world_id = 5;
uint64 lobby_id = 6;
uint64 room_id = 7;
uint64 passwd_slot_mask = 8;
uint64 joined_slot_mask = 9;
uint32 public_slots = 10;
uint32 private_slots = 11;
uint32 open_public_slots = 12;
uint32 open_private_slots = 13;
repeated MatchingRoomGroup groups = 14;
repeated MatchingIntAttr external_search_int_attrs = 15;
repeated MatchingBinAttr external_search_bin_attrs = 16;
repeated MatchingBinAttr external_bin_attrs = 17;
string owner_npid = 18;
}
message CreateJoinRoomResponse {
MatchingRoomDataInternal room_data = 1;
repeated MatchingRoomMemberData members = 2;
uint32 me_member_id = 3;
uint32 owner_member_id = 4;
}
// Command requests
message RegisterHandlersRequest {
string addr = 1;
uint32 port = 2;
uint32 ctx_id = 3;
uint32 service_label = 4;
repeated MatchingCallbackEntry callbacks = 5;
}
message CreateRoomRequest {
uint32 req_id = 1;
uint32 max_slots = 2;
uint32 team_id = 3;
uint32 world_id = 4;
uint32 lobby_id = 5;
uint32 flags = 6;
uint32 group_config_count = 7;
uint32 allowed_user_count = 8;
uint32 blocked_user_count = 9;
uint32 internal_bin_attr_count = 10;
repeated MatchingIntAttr external_search_int_attrs = 11;
repeated MatchingBinAttr external_search_bin_attrs = 12;
repeated MatchingBinAttr external_bin_attrs = 13;
repeated MatchingBinAttr member_bin_attrs = 14;
bool join_group_label_present = 15;
bool room_password_present = 16;
uint32 sig_type = 17;
uint32 sig_flag = 18;
uint32 sig_main_member = 19;
}
message JoinRoomRequest {
uint64 room_id = 1;
uint32 req_id = 2;
uint32 team_id = 3;
uint32 join_flags = 4;
uint32 blocked_user_count = 5;
repeated MatchingBinAttr member_bin_attrs = 6;
bool room_password_present = 7;
bool join_group_label_present = 8;
}
message LeaveRoomRequest {
uint64 room_id = 1;
uint32 req_id = 2;
}
message RequestSignalingInfosRequest {
string target_npid = 1;
}
message SetRoomDataInternalRequest {
uint32 req_id = 1;
uint64 room_id = 2;
uint32 flag_filter = 3;
uint32 flag_attr = 4;
repeated MatchingBinAttr bin_attrs = 5;
bool has_passwd_mask = 6;
uint64 passwd_slot_mask = 7;
}
message SetRoomDataExternalRequest {
uint32 req_id = 1;
uint64 room_id = 2;
repeated MatchingIntAttr search_int_attrs = 3;
repeated MatchingBinAttr search_bin_attrs = 4;
repeated MatchingBinAttr ext_bin_attrs = 5;
}
message KickoutRoomMemberRequest {
uint64 room_id = 1;
uint32 req_id = 2;
uint32 target_member_id = 3;
uint32 block_kick_flag = 4;
}
// Command replies
message CreateRoomReply {
uint64 room_id = 1;
uint32 server_id = 2;
uint32 world_id = 3;
uint32 lobby_id = 4;
uint32 member_id = 5;
uint32 max_slots = 6;
uint32 flags = 7;
uint32 cur_members = 8;
CreateJoinRoomResponse details = 9;
}
message JoinRoomReply {
uint64 room_id = 1;
uint32 member_id = 2;
uint32 max_slots = 3;
uint32 flags = 4;
uint32 cur_members = 5;
CreateJoinRoomResponse details = 6;
}
message GetRoomListReply {
repeated MatchingRoomDataExternal rooms = 1;
}
message RequestSignalingInfosReply {
string target_npid = 1;
string target_ip = 2;
uint32 target_port = 3;
uint32 target_member_id = 4;
}
message LeaveRoomReply { uint64 room_id = 1; }
message SetRoomDataInternalReply { uint64 room_id = 1; }
message SetRoomDataExternalReply { uint64 room_id = 1; }
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).
// 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;
}