diff --git a/CMakeLists.txt b/CMakeLists.txt index e9f6eafa8..323915186 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/src/core/libraries/np/np_matching2/np_matching2.cpp b/src/core/libraries/np/np_matching2/np_matching2.cpp index 916a432bd..2b417fe64 100644 --- a/src/core/libraries/np/np_matching2/np_matching2.cpp +++ b/src/core/libraries/np/np_matching2/np_matching2.cpp @@ -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 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; } diff --git a/src/core/libraries/np/np_matching2/np_matching2.h b/src/core/libraries/np/np_matching2/np_matching2.h index 903e1227d..4c1b4cb68 100644 --- a/src/core/libraries/np/np_matching2/np_matching2.h +++ b/src/core/libraries/np/np_matching2/np_matching2.h @@ -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 \ No newline at end of file diff --git a/src/core/libraries/np/np_matching2/np_matching2_internal.cpp b/src/core/libraries/np/np_matching2/np_matching2_internal.cpp new file mode 100644 index 000000000..ca90b977f --- /dev/null +++ b/src/core/libraries/np/np_matching2/np_matching2_internal.cpp @@ -0,0 +1,263 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#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(((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((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 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 diff --git a/src/core/libraries/np/np_matching2/np_matching2_internal.h b/src/core/libraries/np/np_matching2/np_matching2_internal.h new file mode 100644 index 000000000..7e73127ab --- /dev/null +++ b/src/core/libraries/np/np_matching2/np_matching2_internal.h @@ -0,0 +1,167 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 m_contexts{}; + std::array 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 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 pending_events; + std::atomic 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 diff --git a/src/core/libraries/np/np_matching2/np_matching2_types.h b/src/core/libraries/np/np_matching2/np_matching2_types.h index 5cb32cee6..2d1fa7c73 100644 --- a/src/core/libraries/np/np_matching2/np_matching2_types.h +++ b/src/core/libraries/np/np_matching2/np_matching2_types.h @@ -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 diff --git a/src/shadnet/shadnet.proto b/src/shadnet/shadnet.proto index 0888eb342..321ab2189 100644 --- a/src/shadnet/shadnet.proto +++ b/src/shadnet/shadnet.proto @@ -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; +}