mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-07-10 02:04:42 -06:00
MatchingP11 (#4683)
* MatchingP11 Added room messages, made multiple corrections to events, searches, attrs, and fixed a few titles. Added handling for shadnet matching. * clang fix
This commit is contained in:
parent
16d5f9c54c
commit
48f4bf889e
@ -30,6 +30,11 @@ UPnPClient::~UPnPClient() {
|
||||
}
|
||||
|
||||
void UPnPClient::Start() {
|
||||
if (!m_p2p_features_enabled.load()) {
|
||||
LOG_INFO(Lib_Net, "UPNP: skipped because Matching2 is disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
bool expected = false;
|
||||
if (!m_started.compare_exchange_strong(expected, true))
|
||||
return;
|
||||
@ -37,6 +42,10 @@ void UPnPClient::Start() {
|
||||
m_thread = std::thread(&UPnPClient::DiscoverThread, this);
|
||||
}
|
||||
|
||||
void UPnPClient::SetP2PFeaturesEnabled(bool enabled) {
|
||||
m_p2p_features_enabled.store(enabled);
|
||||
}
|
||||
|
||||
bool UPnPClient::WaitReady(int timeout_ms) {
|
||||
std::unique_lock lock(m_mutex);
|
||||
m_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), [this] { return m_done.load(); });
|
||||
@ -106,6 +115,9 @@ void UPnPClient::DiscoverThread() {
|
||||
}
|
||||
|
||||
void UPnPClient::AddMapping(u16 port) {
|
||||
if (!m_p2p_features_enabled.load())
|
||||
return;
|
||||
|
||||
if (!m_available.load())
|
||||
return;
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ public:
|
||||
|
||||
// Kick off async IGD discovery. Safe to call multiple times — only the first call has effect.
|
||||
void Start();
|
||||
void SetP2PFeaturesEnabled(bool enabled);
|
||||
|
||||
// Block until discovery completes or timeout_ms elapses. Returns true if IGD was found.
|
||||
bool WaitReady(int timeout_ms);
|
||||
@ -48,6 +49,7 @@ private:
|
||||
void DiscoverThread();
|
||||
|
||||
std::atomic<bool> m_started{false};
|
||||
std::atomic<bool> m_p2p_features_enabled{true};
|
||||
std::atomic<bool> m_done{false};
|
||||
std::atomic<bool> m_available{false};
|
||||
std::atomic<u32> m_external_ip_nbo{0};
|
||||
|
||||
@ -182,9 +182,6 @@ bool NpHandler::ConnectUser(s32 user_id, const std::string& host, u16 port, cons
|
||||
// Seed the current Appear-Offline preference so the login packet carries it (the send
|
||||
// is suppressed pre-auth; it just caches on the client).
|
||||
client->SetAppearOffline(m_appear_offline.load());
|
||||
if (EmulatorSettings.IsUPnPEnabled()) {
|
||||
Net::UPnPClient::Instance().Start();
|
||||
}
|
||||
client->Start(host, port, npid, password, token);
|
||||
|
||||
const ShadNet::ShadNetState conn_state = client->WaitForConnection();
|
||||
@ -206,6 +203,11 @@ 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());
|
||||
|
||||
Net::UPnPClient::Instance().SetP2PFeaturesEnabled(client->IsMatching2Enabled());
|
||||
if (client->IsMatching2Enabled() && EmulatorSettings.IsUPnPEnabled()) {
|
||||
Net::UPnPClient::Instance().Start();
|
||||
}
|
||||
|
||||
NpMatching2::SetMmShadNetClient(client, host, port);
|
||||
|
||||
// Build OrbisNpId
|
||||
@ -1883,7 +1885,7 @@ static u32 FillRankArrayFromProto(const shadnet::GetScoreResponse& resp,
|
||||
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 <= 26) {
|
||||
if (cmd_val >= 100 && cmd_val <= 200) {
|
||||
NpMatching2::OnMatchingReply(cmd, pkt_id, error, body);
|
||||
} else {
|
||||
OnScoreReply(user_id, cmd, pkt_id, error, body);
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
@ -317,11 +316,6 @@ int PS4_SYSV_ABI sceNpMatching2SetDefaultRequestOptParam(
|
||||
return ORBIS_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
|
||||
}
|
||||
|
||||
LOG_INFO(Lib_NpMatching2,
|
||||
"SetDefaultRequestOptParam: ctx={} opt={} callback={:#x} arg={} timeout={} appId={}",
|
||||
ctxId, fmt::ptr(requestOpt), reinterpret_cast<std::uintptr_t>(requestOpt->callback),
|
||||
fmt::ptr(requestOpt->arg), requestOpt->timeout, requestOpt->appId);
|
||||
|
||||
ctx->default_request_callback = requestOpt->callback;
|
||||
ctx->default_request_callback_arg = requestOpt->arg;
|
||||
|
||||
@ -479,9 +473,10 @@ int PS4_SYSV_ABI sceNpMatching2SendRoomMessage(OrbisNpMatching2ContextId ctxId,
|
||||
}
|
||||
|
||||
StoreRequestCallback(ctx, requestOpt);
|
||||
*requestId = AllocRequestId();
|
||||
LOG_WARNING(Lib_NpMatching2, "not implemented");
|
||||
return ORBIS_OK;
|
||||
const OrbisNpMatching2RequestId reqId = AllocRequestId();
|
||||
*requestId = reqId;
|
||||
return MmSendRoomMessage(ctxId, reqId,
|
||||
*static_cast<OrbisNpMatching2SendRoomMessageRequest*>(request));
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpMatching2SetRoomDataExternal(
|
||||
|
||||
@ -400,7 +400,6 @@ void* BuildSearchRoomPayload(ContextObject& ctx, const shadnet::SearchRoomReply&
|
||||
|
||||
if (!r.owner_npid().empty()) {
|
||||
auto& npid = p.ext_owner_npids.emplace_back();
|
||||
npid = Libraries::Np::OrbisNpId{};
|
||||
SetNpId(npid, r.owner_npid());
|
||||
dst.ownerNpId = &npid;
|
||||
}
|
||||
@ -610,7 +609,6 @@ void* BuildGetRoomDataExternalListPayload(ContextObject& ctx,
|
||||
|
||||
if (!r.owner_npid().empty()) {
|
||||
auto& npid = p.ext_owner_npids.emplace_back();
|
||||
npid = Libraries::Np::OrbisNpId{};
|
||||
SetNpId(npid, r.owner_npid());
|
||||
dst.ownerNpId = &npid;
|
||||
}
|
||||
@ -1043,6 +1041,68 @@ void* BuildGetRoomDataInternalPayload(ContextObject& ctx, OrbisNpMatching2RoomId
|
||||
return p.request_data;
|
||||
}
|
||||
|
||||
void* BuildRoomMessagePayload(CallbackPayload& p, bool a_variant, OrbisNpMatching2CastType castType,
|
||||
const std::vector<OrbisNpMatching2RoomMemberId>& dstMembers,
|
||||
const MemberCache* srcMember, const std::vector<u8>& msg) {
|
||||
p.Reset();
|
||||
|
||||
p.room_message_dst = std::make_unique<OrbisNpMatching2RoomMessageDestination>();
|
||||
*p.room_message_dst = {};
|
||||
if (castType == ORBIS_NP_MATCHING2_CASTTYPE_UNICAST && !dstMembers.empty()) {
|
||||
p.room_message_dst->unicastTarget = dstMembers.front();
|
||||
} else if (castType == ORBIS_NP_MATCHING2_CASTTYPE_MULTICAST && !dstMembers.empty()) {
|
||||
p.room_message_multicast_members = dstMembers;
|
||||
p.room_message_dst->multicastTarget.memberId = p.room_message_multicast_members.data();
|
||||
p.room_message_dst->multicastTarget.memberIdNum = p.room_message_multicast_members.size();
|
||||
}
|
||||
|
||||
p.room_message_data = msg;
|
||||
void* msg_ptr = p.room_message_data.empty() ? nullptr : p.room_message_data.data();
|
||||
|
||||
if (a_variant) {
|
||||
p.room_message_src_addr = std::make_unique<Libraries::Np::OrbisNpPeerAddressA>();
|
||||
p.room_message_src_online_id = std::make_unique<Libraries::Np::OrbisNpOnlineId>();
|
||||
*p.room_message_src_addr = {};
|
||||
*p.room_message_src_online_id = {};
|
||||
if (srcMember) {
|
||||
p.room_message_src_addr->accountId = srcMember->account_id;
|
||||
p.room_message_src_addr->platform = srcMember->platform;
|
||||
*p.room_message_src_online_id = srcMember->np_id.handle;
|
||||
}
|
||||
|
||||
p.room_message_info_a = std::make_unique<OrbisNpMatching2RoomMessageInfoA>();
|
||||
auto& info = *p.room_message_info_a;
|
||||
info = {};
|
||||
info.filtered = false;
|
||||
info.castType = castType;
|
||||
info.dst = p.room_message_dst.get();
|
||||
info.srcMember = p.room_message_src_addr.get();
|
||||
info.srcOnlineId = p.room_message_src_online_id.get();
|
||||
info.msg = msg_ptr;
|
||||
info.msgLen = static_cast<u32>(p.room_message_data.size());
|
||||
p.room_message_callback_data = p.room_message_info_a.get();
|
||||
return p.room_message_callback_data;
|
||||
}
|
||||
|
||||
p.room_message_src_npid = std::make_unique<Libraries::Np::OrbisNpId>();
|
||||
*p.room_message_src_npid = {};
|
||||
if (srcMember) {
|
||||
*p.room_message_src_npid = srcMember->np_id;
|
||||
}
|
||||
|
||||
p.room_message_info = std::make_unique<OrbisNpMatching2RoomMessageInfo>();
|
||||
auto& info = *p.room_message_info;
|
||||
info = {};
|
||||
info.filtered = false;
|
||||
info.castType = castType;
|
||||
info.dst = p.room_message_dst.get();
|
||||
info.srcMember = p.room_message_src_npid.get();
|
||||
info.msg = msg_ptr;
|
||||
info.msgLen = static_cast<u32>(p.room_message_data.size());
|
||||
p.room_message_callback_data = p.room_message_info.get();
|
||||
return p.room_message_callback_data;
|
||||
}
|
||||
|
||||
ContextManager& ContextManager::Instance() {
|
||||
static ContextManager instance;
|
||||
return instance;
|
||||
|
||||
@ -132,6 +132,16 @@ struct CallbackPayload {
|
||||
std::vector<Libraries::Np::OrbisNpId> ext_owner_npids;
|
||||
void* request_data = nullptr;
|
||||
|
||||
std::unique_ptr<OrbisNpMatching2RoomMessageInfo> room_message_info;
|
||||
std::unique_ptr<OrbisNpMatching2RoomMessageInfoA> room_message_info_a;
|
||||
std::unique_ptr<OrbisNpMatching2RoomMessageDestination> room_message_dst;
|
||||
std::unique_ptr<Libraries::Np::OrbisNpId> room_message_src_npid;
|
||||
std::unique_ptr<Libraries::Np::OrbisNpPeerAddressA> room_message_src_addr;
|
||||
std::unique_ptr<Libraries::Np::OrbisNpOnlineId> room_message_src_online_id;
|
||||
std::vector<OrbisNpMatching2RoomMemberId> room_message_multicast_members;
|
||||
std::vector<u8> room_message_data;
|
||||
void* room_message_callback_data = nullptr;
|
||||
|
||||
std::unique_ptr<OrbisNpMatching2RoomMemberUpdate> room_member_update;
|
||||
std::unique_ptr<OrbisNpMatching2RoomMemberUpdateA> room_member_update_a;
|
||||
std::unique_ptr<OrbisNpMatching2RoomUpdate> room_update;
|
||||
@ -186,6 +196,15 @@ struct CallbackPayload {
|
||||
ext_room_groups.clear();
|
||||
ext_owner_npids.clear();
|
||||
request_data = nullptr;
|
||||
room_message_info.reset();
|
||||
room_message_info_a.reset();
|
||||
room_message_dst.reset();
|
||||
room_message_src_npid.reset();
|
||||
room_message_src_addr.reset();
|
||||
room_message_src_online_id.reset();
|
||||
room_message_multicast_members.clear();
|
||||
room_message_data.clear();
|
||||
room_message_callback_data = nullptr;
|
||||
room_member_update.reset();
|
||||
room_member_update_a.reset();
|
||||
room_update.reset();
|
||||
@ -212,9 +231,6 @@ struct ContextObject {
|
||||
Libraries::Np::OrbisNpId owner_np_id{};
|
||||
Libraries::Np::OrbisNpOnlineId online_id{};
|
||||
|
||||
std::string signaling_addr;
|
||||
u16 signaling_port = 0;
|
||||
|
||||
u64 handler_registration_generation = 1;
|
||||
|
||||
OrbisNpMatching2WorldId world_id = 0;
|
||||
@ -260,8 +276,6 @@ struct ContextObject {
|
||||
service_label = 0;
|
||||
owner_np_id = {};
|
||||
online_id = {};
|
||||
signaling_addr.clear();
|
||||
signaling_port = 0;
|
||||
handler_registration_generation = 1;
|
||||
world_id = 0;
|
||||
lobby_id = 0;
|
||||
@ -414,6 +428,9 @@ void* BuildGetRoomMemberDataExternalListPayloadA(
|
||||
void* BuildGetUserInfoListPayload(ContextObject& ctx, const shadnet::GetUserInfoListReply& resp);
|
||||
void* BuildGetUserInfoListPayloadA(ContextObject& ctx, const shadnet::GetUserInfoListReply& resp);
|
||||
void* BuildGetRoomDataInternalPayload(ContextObject& ctx, OrbisNpMatching2RoomId room_id);
|
||||
void* BuildRoomMessagePayload(CallbackPayload& p, bool a_variant, OrbisNpMatching2CastType castType,
|
||||
const std::vector<OrbisNpMatching2RoomMemberId>& dstMembers,
|
||||
const MemberCache* srcMember, const std::vector<u8>& msg);
|
||||
|
||||
void InitEventDispatcher();
|
||||
void TermEventDispatcher();
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/network/net.h"
|
||||
#include "core/libraries/network/net_upnp.h"
|
||||
#include "core/libraries/network/sockets.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_matching2/np_matching2_internal.h"
|
||||
@ -40,6 +41,7 @@ struct MmClientState {
|
||||
std::mutex mutex;
|
||||
u32 server_addr = 0;
|
||||
u16 server_udp_port = 0;
|
||||
bool matching2_enabled = false;
|
||||
|
||||
std::mutex pending_mutex;
|
||||
std::map<u64, PendingRequest> pending;
|
||||
@ -59,6 +61,16 @@ u32 IpStringToAddr(std::string_view ip) {
|
||||
return a | (b << 8) | (c << 16) | (d << 24);
|
||||
}
|
||||
|
||||
bool IsMatching2BackendDisabled() {
|
||||
std::lock_guard lock(g_mm.mutex);
|
||||
return !g_mm.matching2_enabled;
|
||||
}
|
||||
|
||||
bool IsContextLifecycleEvent(OrbisNpMatching2Event event) {
|
||||
return event == ORBIS_NP_MATCHING2_CONTEXT_EVENT_STARTED ||
|
||||
event == ORBIS_NP_MATCHING2_CONTEXT_EVENT_STOPPED;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<u8> MakeProtoPayload(const T& msg) {
|
||||
std::string bytes;
|
||||
@ -339,8 +351,8 @@ void HandleRoomEvent(const ShadNet::NotifyRoomEvent& n) {
|
||||
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();
|
||||
auto payload_owner = std::make_shared<CallbackPayload>();
|
||||
CallbackPayload& p = *payload_owner;
|
||||
|
||||
switch (event) {
|
||||
case ORBIS_NP_MATCHING2_ROOM_EVENT_MEMBER_JOINED: {
|
||||
@ -553,6 +565,7 @@ void HandleRoomEvent(const ShadNet::NotifyRoomEvent& n) {
|
||||
ev.room_id = room_id;
|
||||
ev.room_event = fired_event;
|
||||
ev.room_event_data = p.room_event_data;
|
||||
ev.request_payload_owner = std::move(payload_owner);
|
||||
ScheduleEvent(std::move(ev));
|
||||
|
||||
if (event == ORBIS_NP_MATCHING2_ROOM_EVENT_MEMBER_JOINED) {
|
||||
@ -560,6 +573,57 @@ void HandleRoomEvent(const ShadNet::NotifyRoomEvent& n) {
|
||||
}
|
||||
}
|
||||
|
||||
void HandleRoomMessage(const ShadNet::NotifyRoomMessage& n) {
|
||||
ContextObject* ctx =
|
||||
ContextManager::Instance().Get(static_cast<OrbisNpMatching2ContextId>(n.ctx_id));
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto room_id = static_cast<OrbisNpMatching2RoomId>(n.room_id);
|
||||
const auto src_member_id = static_cast<OrbisNpMatching2RoomMemberId>(n.src_member_id);
|
||||
const MemberCache* src_member = nullptr;
|
||||
const auto room_it = ctx->room_cache.find(room_id);
|
||||
if (room_it != ctx->room_cache.end()) {
|
||||
const auto member_it = room_it->second.members.find(src_member_id);
|
||||
if (member_it != room_it->second.members.end()) {
|
||||
src_member = &member_it->second;
|
||||
}
|
||||
}
|
||||
|
||||
MemberCache fallback_src{};
|
||||
if (!src_member) {
|
||||
fallback_src.member_id = src_member_id;
|
||||
SetNpId(fallback_src.np_id, n.src_npid);
|
||||
fallback_src.account_id = static_cast<Libraries::Np::OrbisNpAccountId>(n.src_account_id);
|
||||
fallback_src.platform = static_cast<Libraries::Np::OrbisNpPlatformType>(n.src_platform);
|
||||
src_member = &fallback_src;
|
||||
}
|
||||
|
||||
std::vector<OrbisNpMatching2RoomMemberId> dst_members;
|
||||
dst_members.reserve(n.dst_member_ids.size());
|
||||
for (const u32 member_id : n.dst_member_ids) {
|
||||
dst_members.push_back(static_cast<OrbisNpMatching2RoomMemberId>(member_id));
|
||||
}
|
||||
|
||||
auto payload_owner = std::make_shared<CallbackPayload>();
|
||||
void* data = BuildRoomMessagePayload(*payload_owner, ctx->a_variant,
|
||||
static_cast<OrbisNpMatching2CastType>(n.cast_type),
|
||||
dst_members, src_member, n.msg);
|
||||
|
||||
PendingEvent ev{};
|
||||
ev.type = PendingEvent::ROOM_MESSAGE_CB;
|
||||
ev.ctx_id = ctx->ctx_id;
|
||||
ev.fire_at = std::chrono::steady_clock::now();
|
||||
ev.room_id = room_id;
|
||||
ev.src_member_id = src_member_id;
|
||||
ev.msg_event = ctx->a_variant ? ORBIS_NP_MATCHING2_ROOM_MSG_EVENT_MESSAGE_A
|
||||
: ORBIS_NP_MATCHING2_ROOM_MSG_EVENT_MESSAGE;
|
||||
ev.message_data = data;
|
||||
ev.request_payload_owner = std::move(payload_owner);
|
||||
ScheduleEvent(std::move(ev));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void OnMatchingReply(ShadNet::CommandType cmd, u64 pkt_id, ShadNet::ErrorType error,
|
||||
@ -592,21 +656,28 @@ void SetMmShadNetClient(std::shared_ptr<ShadNet::ShadNetClient> client,
|
||||
std::string_view server_host, u16 tcp_port) {
|
||||
u32 server_addr = 0;
|
||||
u16 server_udp_port = 0;
|
||||
bool matching2_enabled = false;
|
||||
{
|
||||
std::lock_guard lock(g_mm.mutex);
|
||||
g_mm.client = client;
|
||||
g_mm.matching2_enabled = client ? client->IsMatching2Enabled() : false;
|
||||
matching2_enabled = g_mm.matching2_enabled;
|
||||
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));
|
||||
g_mm.server_udp_port =
|
||||
matching2_enabled ? Libraries::Net::sceNetHtons(static_cast<u16>(tcp_port + 1)) : 0;
|
||||
server_addr = g_mm.server_addr;
|
||||
server_udp_port = g_mm.server_udp_port;
|
||||
}
|
||||
LOG_INFO(Lib_NpMatching2, "ShadNet features: matching2_enabled={}", matching2_enabled);
|
||||
Net::UPnPClient::Instance().SetP2PFeaturesEnabled(matching2_enabled);
|
||||
|
||||
if (!client) {
|
||||
NpSignaling::Stubs::SetTransportHooks({});
|
||||
NpSignaling::Stubs::SetPeerResolver(nullptr);
|
||||
NpSignaling::Stubs::SetMatching2Enabled(false);
|
||||
NpSignaling::Stubs::SetMmServerEndpoint(0, 0);
|
||||
StopMatching2HandshakeThread();
|
||||
return;
|
||||
@ -622,10 +693,16 @@ void SetMmShadNetClient(std::shared_ptr<ShadNet::ShadNetClient> client,
|
||||
.advertised_addr = Net::GetP2PAdvertisedAddr,
|
||||
.ensure_transport = Net::EnsureP2PTransport,
|
||||
});
|
||||
NpSignaling::Stubs::SetPeerResolver(RequestSignalingInfos);
|
||||
NpSignaling::Stubs::SetPeerResolver(matching2_enabled ? RequestSignalingInfos : nullptr);
|
||||
NpSignaling::Stubs::SetMatching2Enabled(matching2_enabled);
|
||||
NpSignaling::Stubs::SetMmServerEndpoint(server_addr, server_udp_port);
|
||||
StartMatching2HandshakeThread();
|
||||
if (matching2_enabled) {
|
||||
StartMatching2HandshakeThread();
|
||||
} else {
|
||||
StopMatching2HandshakeThread();
|
||||
}
|
||||
client->onRoomEvent = [](const ShadNet::NotifyRoomEvent& n) { HandleRoomEvent(n); };
|
||||
client->onRoomMessage = [](const ShadNet::NotifyRoomMessage& n) { HandleRoomMessage(n); };
|
||||
}
|
||||
|
||||
void ClearMmShadNetClient() {
|
||||
@ -635,12 +712,16 @@ void ClearMmShadNetClient() {
|
||||
old_client = std::move(g_mm.client);
|
||||
g_mm.server_addr = 0;
|
||||
g_mm.server_udp_port = 0;
|
||||
g_mm.matching2_enabled = false;
|
||||
}
|
||||
Net::UPnPClient::Instance().SetP2PFeaturesEnabled(false);
|
||||
if (old_client) {
|
||||
old_client->onRoomEvent = nullptr;
|
||||
old_client->onRoomMessage = nullptr;
|
||||
}
|
||||
NpSignaling::Stubs::SetTransportHooks({});
|
||||
NpSignaling::Stubs::SetPeerResolver(nullptr);
|
||||
NpSignaling::Stubs::SetMatching2Enabled(false);
|
||||
NpSignaling::Stubs::SetMmServerEndpoint(0, 0);
|
||||
StopMatching2HandshakeThread();
|
||||
{
|
||||
@ -692,6 +773,48 @@ s32 MmSubmitRequest(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId
|
||||
return ORBIS_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
|
||||
}
|
||||
const RequestCallbackInfo request_cb = ConsumeRequestCallback(ctx);
|
||||
if (IsMatching2BackendDisabled() && !IsContextLifecycleEvent(req_event)) {
|
||||
LOG_INFO(Lib_NpMatching2,
|
||||
"MmSubmitRequest: matching2 backend disabled; failing ctx={} reqId={} "
|
||||
"event={:#x} cmd={}",
|
||||
ctx_id, req_id, static_cast<u16>(req_event), static_cast<u16>(cmd));
|
||||
|
||||
s32 error_code = ORBIS_NP_MATCHING2_ERROR_SERVER_NOT_AVAILABLE;
|
||||
void* request_data = nullptr;
|
||||
std::shared_ptr<CallbackPayload> request_payload_owner;
|
||||
if (req_event == ORBIS_NP_MATCHING2_REQUEST_EVENT_GET_WORLD_INFO_LIST) {
|
||||
LOG_INFO(Lib_NpMatching2,
|
||||
"MmSubmitRequest: matching2 backend disabled; returning empty world");
|
||||
shadnet::GetWorldInfoListReply reply;
|
||||
auto* world = reply.add_worlds();
|
||||
world->set_world_id(1);
|
||||
world->set_lobbies_num(0);
|
||||
world->set_max_lobby_members(0);
|
||||
world->set_lobby_members_num(0);
|
||||
world->set_rooms_num(0);
|
||||
world->set_room_members_num(0);
|
||||
request_payload_owner = std::make_shared<CallbackPayload>();
|
||||
ctx->request_payload_override = request_payload_owner.get();
|
||||
request_data = BuildGetWorldInfoListPayload(*ctx, reply);
|
||||
ctx->request_payload_override = nullptr;
|
||||
error_code = ORBIS_OK;
|
||||
}
|
||||
|
||||
PendingEvent ev{};
|
||||
ev.type = PendingEvent::REQUEST_CB;
|
||||
ev.ctx_id = ctx_id;
|
||||
ev.fire_at = std::chrono::steady_clock::now();
|
||||
ev.req_id = req_id;
|
||||
ev.req_event = req_event;
|
||||
ev.error_code = error_code;
|
||||
ev.request_cb = request_cb.callback;
|
||||
ev.request_cb_arg = request_cb.arg;
|
||||
ev.request_data = request_data;
|
||||
ev.request_payload_owner = std::move(request_payload_owner);
|
||||
ScheduleEvent(std::move(ev));
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
LOG_INFO(Lib_NpMatching2,
|
||||
"MmSubmitRequest: ctx={} reqId={} event={:#x} cmd={} aVariant={} callback={:#x} "
|
||||
"arg={}",
|
||||
@ -978,6 +1101,30 @@ s32 MmSetUserInfo(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId re
|
||||
MmCommand::SetUserInfo, MakeProtoPayload(req));
|
||||
}
|
||||
|
||||
s32 MmSendRoomMessage(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
const OrbisNpMatching2SendRoomMessageRequest& request) {
|
||||
shadnet::SendRoomMessageRequest req;
|
||||
req.set_room_id(request.roomId);
|
||||
req.set_cast_type(request.castType);
|
||||
req.set_option(request.option);
|
||||
|
||||
if (request.castType == ORBIS_NP_MATCHING2_CASTTYPE_UNICAST) {
|
||||
req.add_dst_member_ids(request.dst.unicastTarget);
|
||||
} else if (request.castType == ORBIS_NP_MATCHING2_CASTTYPE_MULTICAST &&
|
||||
request.dst.multicastTarget.memberId) {
|
||||
for (u64 i = 0; i < request.dst.multicastTarget.memberIdNum; ++i) {
|
||||
req.add_dst_member_ids(request.dst.multicastTarget.memberId[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (request.msg && request.msgLen > 0) {
|
||||
req.set_msg(request.msg, request.msgLen);
|
||||
}
|
||||
|
||||
return MmSubmitRequest(ctx_id, req_id, ORBIS_NP_MATCHING2_REQUEST_EVENT_SEND_ROOM_MESSAGE,
|
||||
MmCommand::SendRoomMessage, MakeProtoPayload(req));
|
||||
}
|
||||
|
||||
s32 MmSetRoomDataInternal(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
const OrbisNpMatching2SetRoomDataInternalRequest& request) {
|
||||
shadnet::SetRoomDataInternalRequest req;
|
||||
@ -1039,6 +1186,9 @@ bool RequestSignalingInfos(std::string_view target_online_id, u32* out_addr, u16
|
||||
if (!out_addr || !out_port) {
|
||||
return false;
|
||||
}
|
||||
if (IsMatching2BackendDisabled()) {
|
||||
return false;
|
||||
}
|
||||
std::shared_ptr<ShadNet::ShadNetClient> client;
|
||||
{
|
||||
std::lock_guard lock(g_mm.mutex);
|
||||
|
||||
@ -19,21 +19,22 @@ enum class ErrorType : u8;
|
||||
namespace Libraries::Np::NpMatching2 {
|
||||
|
||||
enum class MmCommand : u16 {
|
||||
ContextStart = 12,
|
||||
CreateRoom = 13,
|
||||
JoinRoom = 14,
|
||||
LeaveRoom = 15,
|
||||
SearchRoom = 16,
|
||||
RequestSignalingInfos = 17,
|
||||
ContextStop = 18,
|
||||
SetUserInfo = 19,
|
||||
SetRoomDataInternal = 20,
|
||||
SetRoomDataExternal = 21,
|
||||
KickoutRoomMember = 22,
|
||||
GetWorldInfoList = 23,
|
||||
GetRoomDataExternalList = 24,
|
||||
GetUserInfoList = 25,
|
||||
GetRoomMemberDataExternalList = 26,
|
||||
ContextStart = 100,
|
||||
CreateRoom = 101,
|
||||
JoinRoom = 102,
|
||||
LeaveRoom = 103,
|
||||
SearchRoom = 104,
|
||||
RequestSignalingInfos = 105,
|
||||
ContextStop = 106,
|
||||
SetUserInfo = 107,
|
||||
SetRoomDataInternal = 108,
|
||||
SetRoomDataExternal = 109,
|
||||
KickoutRoomMember = 110,
|
||||
GetWorldInfoList = 111,
|
||||
GetRoomDataExternalList = 112,
|
||||
GetUserInfoList = 113,
|
||||
GetRoomMemberDataExternalList = 114,
|
||||
SendRoomMessage = 115,
|
||||
};
|
||||
|
||||
void SetMmShadNetClient(std::shared_ptr<ShadNet::ShadNetClient> client,
|
||||
@ -76,6 +77,8 @@ s32 MmGetUserInfoList(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestI
|
||||
bool a_variant = false);
|
||||
s32 MmSetUserInfo(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
const OrbisNpMatching2SetUserInfoRequest& request);
|
||||
s32 MmSendRoomMessage(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
const OrbisNpMatching2SendRoomMessageRequest& request);
|
||||
s32 MmSetRoomDataInternal(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
const OrbisNpMatching2SetRoomDataInternalRequest& request);
|
||||
s32 MmSetRoomDataExternal(OrbisNpMatching2ContextId ctx_id, OrbisNpMatching2RequestId req_id,
|
||||
|
||||
@ -311,7 +311,7 @@ void Matching2HandshakeThreadMain() {
|
||||
now - peer.last_send > kMatching2HandshakeTimeout) {
|
||||
peer.status = kMatching2ConnInactive;
|
||||
QueueMatching2SignalingEvent(*ctx, ctx->room_id, member_id,
|
||||
ORBIS_NP_MATCHING2_SIGNALING_EVENT_NETINFO_ERROR,
|
||||
ORBIS_NP_MATCHING2_SIGNALING_EVENT_DEAD,
|
||||
ORBIS_NP_MATCHING2_SIGNALING_ERROR_TIMEOUT);
|
||||
continue;
|
||||
}
|
||||
@ -335,6 +335,9 @@ void Matching2HandshakeThreadMain() {
|
||||
} // namespace
|
||||
|
||||
bool SendMatching2StunPing(const ContextObject& ctx) {
|
||||
if (!NpSignaling::Stubs::Matching2Enabled()) {
|
||||
return false;
|
||||
}
|
||||
if (!ctx.started || ctx.online_id.data[0] == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -390,19 +390,21 @@ static void PingThreadMain() {
|
||||
}
|
||||
}
|
||||
|
||||
const u32 server_addr = Stubs::MmServerAddr();
|
||||
const u16 server_port = Stubs::MmServerUdpPort();
|
||||
if (Stubs::Matching2Enabled()) {
|
||||
const u32 server_addr = Stubs::MmServerAddr();
|
||||
const u16 server_port = Stubs::MmServerUdpPort();
|
||||
|
||||
for (const auto& cs : contexts) {
|
||||
if (server_addr == 0 || server_port == 0) {
|
||||
break;
|
||||
for (const auto& cs : contexts) {
|
||||
if (server_addr == 0 || server_port == 0) {
|
||||
break;
|
||||
}
|
||||
StunPing ping{};
|
||||
ping.cmd = 0x01;
|
||||
std::memcpy(ping.online_id, cs.online_id.data, ORBIS_NP_ONLINEID_MAX_LENGTH);
|
||||
ping.local_ip = Stubs::AdvertisedAddr();
|
||||
|
||||
Stubs::SignalingSendTo(&ping, sizeof(ping), server_addr, server_port);
|
||||
}
|
||||
StunPing ping{};
|
||||
ping.cmd = 0x01;
|
||||
std::memcpy(ping.online_id, cs.online_id.data, ORBIS_NP_ONLINEID_MAX_LENGTH);
|
||||
ping.local_ip = Stubs::AdvertisedAddr();
|
||||
|
||||
Stubs::SignalingSendTo(&ping, sizeof(ping), server_addr, server_port);
|
||||
}
|
||||
|
||||
SendEchoPings();
|
||||
|
||||
@ -1304,6 +1304,10 @@ void HandleControlPacket(u32 from_addr, u16 from_port, const SignalingControl& p
|
||||
}
|
||||
|
||||
void SendStunPing(s32 ctx_id) {
|
||||
if (!Stubs::Matching2Enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
OrbisNpOnlineId online_id{};
|
||||
|
||||
{
|
||||
|
||||
@ -8,6 +8,7 @@ namespace Libraries::Np::NpSignaling::Stubs {
|
||||
namespace {
|
||||
TransportHooks g_transport{};
|
||||
PeerResolver g_peer_resolver = nullptr;
|
||||
bool g_matching2_enabled = false;
|
||||
u32 g_mm_server_addr = 0;
|
||||
u16 g_mm_server_udp_port = 0;
|
||||
} // namespace
|
||||
@ -55,6 +56,10 @@ void SetPeerResolver(PeerResolver fn) {
|
||||
g_peer_resolver = fn;
|
||||
}
|
||||
|
||||
void SetMatching2Enabled(bool enabled) {
|
||||
g_matching2_enabled = enabled;
|
||||
}
|
||||
|
||||
void SetMmServerEndpoint(u32 addr, u16 udp_port) {
|
||||
g_mm_server_addr = addr;
|
||||
g_mm_server_udp_port = udp_port;
|
||||
@ -64,6 +69,10 @@ bool ResolvePeer(std::string_view online_id, u32* out_addr, u16* out_port) {
|
||||
return g_peer_resolver ? g_peer_resolver(online_id, out_addr, out_port) : false;
|
||||
}
|
||||
|
||||
bool Matching2Enabled() {
|
||||
return g_matching2_enabled;
|
||||
}
|
||||
|
||||
u32 MmServerAddr() {
|
||||
return g_mm_server_addr;
|
||||
}
|
||||
|
||||
@ -31,9 +31,11 @@ bool EnsureTransport();
|
||||
|
||||
using PeerResolver = bool (*)(std::string_view online_id, u32* out_addr, u16* out_port);
|
||||
void SetPeerResolver(PeerResolver fn);
|
||||
void SetMatching2Enabled(bool enabled);
|
||||
void SetMmServerEndpoint(u32 addr, u16 udp_port);
|
||||
|
||||
bool ResolvePeer(std::string_view online_id, u32* out_addr, u16* out_port);
|
||||
bool Matching2Enabled();
|
||||
u32 MmServerAddr();
|
||||
u16 MmServerUdpPort();
|
||||
|
||||
|
||||
@ -42,6 +42,14 @@ using OrbisNpMatching2SignalingRequestId = u32;
|
||||
using OrbisNpMatching2TeamId = u8;
|
||||
using OrbisNpMatching2WorldId = u32;
|
||||
|
||||
using OrbisNpMatching2CastType = u8;
|
||||
|
||||
enum : OrbisNpMatching2CastType {
|
||||
ORBIS_NP_MATCHING2_CASTTYPE_BROADCAST = 1,
|
||||
ORBIS_NP_MATCHING2_CASTTYPE_UNICAST = 2,
|
||||
ORBIS_NP_MATCHING2_CASTTYPE_MULTICAST = 3,
|
||||
};
|
||||
|
||||
// Event of request/room/signaling/context functions
|
||||
enum OrbisNpMatching2Event : u16 {
|
||||
ORBIS_NP_MATCHING2_REQUEST_EVENT_GET_WORLD_INFO_LIST = 0x0002,
|
||||
@ -83,6 +91,7 @@ enum OrbisNpMatching2Event : u16 {
|
||||
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 = 0x2102,
|
||||
ORBIS_NP_MATCHING2_ROOM_MSG_EVENT_MESSAGE_A = 0x9102,
|
||||
};
|
||||
|
||||
@ -308,6 +317,48 @@ struct OrbisNpMatching2RoomGroupInfo {
|
||||
u32 groupMembers;
|
||||
};
|
||||
|
||||
union OrbisNpMatching2RoomMessageDestination {
|
||||
OrbisNpMatching2RoomMemberId unicastTarget;
|
||||
|
||||
struct {
|
||||
OrbisNpMatching2RoomMemberId* memberId;
|
||||
u64 memberIdNum;
|
||||
} multicastTarget;
|
||||
};
|
||||
static_assert(sizeof(OrbisNpMatching2RoomMessageDestination) == 0x10);
|
||||
|
||||
struct OrbisNpMatching2SendRoomMessageRequest {
|
||||
OrbisNpMatching2RoomId roomId;
|
||||
OrbisNpMatching2CastType castType;
|
||||
u8 padding[3];
|
||||
OrbisNpMatching2RoomMessageDestination dst;
|
||||
const void* msg;
|
||||
u32 msgLen;
|
||||
s32 option;
|
||||
};
|
||||
static_assert(sizeof(OrbisNpMatching2SendRoomMessageRequest) == 0x30);
|
||||
|
||||
struct OrbisNpMatching2RoomMessageInfo {
|
||||
bool filtered;
|
||||
OrbisNpMatching2CastType castType;
|
||||
u8 padding[2];
|
||||
OrbisNpMatching2RoomMessageDestination* dst;
|
||||
Libraries::Np::OrbisNpId* srcMember;
|
||||
void* msg;
|
||||
u32 msgLen;
|
||||
};
|
||||
|
||||
struct OrbisNpMatching2RoomMessageInfoA {
|
||||
bool filtered;
|
||||
OrbisNpMatching2CastType castType;
|
||||
u8 padding[2];
|
||||
OrbisNpMatching2RoomMessageDestination* dst;
|
||||
Libraries::Np::OrbisNpPeerAddressA* srcMember;
|
||||
Libraries::Np::OrbisNpOnlineId* srcOnlineId;
|
||||
void* msg;
|
||||
u32 msgLen;
|
||||
};
|
||||
|
||||
// Room-internal binary attribute
|
||||
struct OrbisNpMatching2RoomBinAttrInternal {
|
||||
Libraries::Rtc::OrbisRtcTick lastUpdate;
|
||||
@ -781,6 +832,7 @@ struct OrbisNpMatching2JoinedSessionInfo {
|
||||
};
|
||||
|
||||
// User information
|
||||
// from PS3 this was NpUserInfo2; setting addresses like our other A and nonA types TODO: confirm
|
||||
struct OrbisNpMatching2UserInfo {
|
||||
OrbisNpMatching2UserInfo* next;
|
||||
Libraries::Np::OrbisNpId npId;
|
||||
@ -791,6 +843,7 @@ struct OrbisNpMatching2UserInfo {
|
||||
};
|
||||
|
||||
// User information (account-id variant)
|
||||
// from PS3 this was NpUserInfo2; setting addresses like our other A and nonA types TODO: confirm
|
||||
struct OrbisNpMatching2UserInfoA {
|
||||
OrbisNpMatching2UserInfoA* next;
|
||||
Libraries::Np::OrbisNpPeerAddressA user;
|
||||
|
||||
@ -137,6 +137,9 @@ u32 ShadNetClient::GetAddrLocal() const {
|
||||
u32 ShadNetClient::GetAddrServer() const {
|
||||
return m_addr_server.load();
|
||||
}
|
||||
bool ShadNetClient::IsMatching2Enabled() const {
|
||||
return m_matching2_enabled.load();
|
||||
}
|
||||
|
||||
u32 ShadNetClient::GetNumFriends() const {
|
||||
std::lock_guard lock(m_mutex_friends);
|
||||
@ -518,6 +521,19 @@ u64 ShadNetClient::SetAppearOffline(bool enable) {
|
||||
return SubmitRequest(CommandType::SetAppearOffline, MakeProtoPayload(req));
|
||||
}
|
||||
|
||||
bool ShadNetClient::RequestServerFeatures() {
|
||||
const u64 pkt_id = m_pkt_counter.fetch_add(1);
|
||||
std::vector<u8> empty_payload;
|
||||
std::vector<u8> pkt = BuildPacket(CommandType::GetServerFeatures, pkt_id, empty_payload);
|
||||
{
|
||||
std::lock_guard lock(m_mutex_send_queue);
|
||||
m_send_queue.push_back(std::move(pkt));
|
||||
}
|
||||
m_cv_send_queue.notify_one();
|
||||
LOG_DEBUG(ShadNet, "GetServerFeatures request fired pkt_id={}", pkt_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Packet dispatch
|
||||
|
||||
void ShadNetClient::DispatchPacket(PacketType type, u16 cmd_raw, u64 pkt_id,
|
||||
@ -531,6 +547,9 @@ void ShadNetClient::DispatchPacket(PacketType type, u16 cmd_raw, u64 pkt_id,
|
||||
case CommandType::GetToken:
|
||||
HandleGetTokenReply(payload);
|
||||
break;
|
||||
case CommandType::GetServerFeatures:
|
||||
HandleServerFeaturesReply(payload);
|
||||
break;
|
||||
default:
|
||||
if (onAsyncReply) {
|
||||
// Every reply body starts with an ErrorType byte.
|
||||
@ -648,21 +667,21 @@ void ShadNetClient::HandleLoginReply(const std::vector<u8>& payload) {
|
||||
void ShadNetClient::HandleGetTokenReply(const std::vector<u8>& payload) {
|
||||
if (payload.empty()) {
|
||||
LOG_ERROR(ShadNet, "Empty GetToken reply");
|
||||
m_sem_authenticated.release();
|
||||
RequestServerFeatures();
|
||||
return;
|
||||
}
|
||||
const ErrorType err = static_cast<ErrorType>(payload[0]);
|
||||
if (err != ErrorType::NoError) {
|
||||
LOG_WARNING(ShadNet, "GetToken returned error={} — WebAPI calls will be unauthenticated",
|
||||
static_cast<int>(err));
|
||||
m_sem_authenticated.release();
|
||||
RequestServerFeatures();
|
||||
return;
|
||||
}
|
||||
shadnet::GetTokenReply pb;
|
||||
const std::string blob = ExtractBlob(payload, 1);
|
||||
if (blob.empty() || !pb.ParseFromString(blob)) {
|
||||
LOG_ERROR(ShadNet, "Failed to parse GetTokenReply proto");
|
||||
m_sem_authenticated.release();
|
||||
RequestServerFeatures();
|
||||
return;
|
||||
}
|
||||
{
|
||||
@ -671,6 +690,33 @@ void ShadNetClient::HandleGetTokenReply(const std::vector<u8>& payload) {
|
||||
}
|
||||
LOG_INFO(ShadNet, "Bearer token captured ({} chars) for accountID={} canonical npid='{}'",
|
||||
pb.token().size(), pb.user_id(), pb.npid());
|
||||
RequestServerFeatures();
|
||||
}
|
||||
|
||||
void ShadNetClient::HandleServerFeaturesReply(const std::vector<u8>& payload) {
|
||||
bool matching2_enabled = false;
|
||||
bool parsed = false;
|
||||
|
||||
if (!payload.empty()) {
|
||||
const ErrorType err = static_cast<ErrorType>(payload[0]);
|
||||
if (err == ErrorType::NoError) {
|
||||
shadnet::ServerFeaturesReply pb;
|
||||
const std::string blob = ExtractBlob(payload, 1);
|
||||
if (!blob.empty() && pb.ParseFromString(blob)) {
|
||||
matching2_enabled = pb.matching2_enabled();
|
||||
parsed = true;
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(ShadNet,
|
||||
"GetServerFeatures returned error={} - assuming Matching2 disabled",
|
||||
static_cast<int>(err));
|
||||
}
|
||||
}
|
||||
|
||||
m_matching2_enabled.store(matching2_enabled);
|
||||
m_server_features_received.store(parsed);
|
||||
LOG_INFO(ShadNet, "Server features: matching2_enabled={}{}",
|
||||
matching2_enabled ? "true" : "false", parsed ? "" : " (defaulted)");
|
||||
m_sem_authenticated.release();
|
||||
}
|
||||
|
||||
@ -795,6 +841,28 @@ void ShadNetClient::HandleNotification(u16 cmd_raw, const std::vector<u8>& paylo
|
||||
onRoomEvent(n);
|
||||
break;
|
||||
}
|
||||
case NotificationType::RoomMessage: {
|
||||
shadnet::NotifyRoomMessage pb;
|
||||
if (!pb.ParseFromString(blob)) {
|
||||
break;
|
||||
}
|
||||
NotifyRoomMessage n;
|
||||
n.ctx_id = pb.ctx_id();
|
||||
n.room_id = pb.room_id();
|
||||
n.src_member_id = pb.src_member_id();
|
||||
n.event = pb.event();
|
||||
n.cast_type = pb.cast_type();
|
||||
for (const u32 member_id : pb.dst_member_ids()) {
|
||||
n.dst_member_ids.push_back(member_id);
|
||||
}
|
||||
n.src_npid = pb.src_npid();
|
||||
n.src_account_id = pb.src_account_id();
|
||||
n.src_platform = pb.src_platform();
|
||||
n.msg.assign(pb.msg().begin(), pb.msg().end());
|
||||
if (onRoomMessage)
|
||||
onRoomMessage(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,22 +66,7 @@ enum class CommandType : u16 {
|
||||
RemoveFriend = 9,
|
||||
AddBlock = 10,
|
||||
RemoveBlock = 11,
|
||||
// Matchmaking
|
||||
ContextStart = 12,
|
||||
CreateRoom = 13,
|
||||
JoinRoom = 14,
|
||||
LeaveRoom = 15,
|
||||
SearchRoom = 16,
|
||||
RequestSignalingInfos = 17,
|
||||
ContextStop = 18,
|
||||
SetUserInfo = 19,
|
||||
SetRoomDataInternal = 20,
|
||||
SetRoomDataExternal = 21,
|
||||
KickoutRoomMember = 22,
|
||||
GetWorldInfoList = 23,
|
||||
GetRoomDataExternalList = 24,
|
||||
GetUserInfoList = 25,
|
||||
GetRoomMemberDataExternalList = 26,
|
||||
GetServerFeatures = 12,
|
||||
GetBoardInfos = 30,
|
||||
RecordScore = 31,
|
||||
RecordScoreData = 32,
|
||||
@ -93,6 +78,23 @@ enum class CommandType : u16 {
|
||||
GetScoreGameDataByAccId = 38,
|
||||
GetToken = 39,
|
||||
SetAppearOffline = 40,
|
||||
// Matchmaking
|
||||
ContextStart = 100,
|
||||
CreateRoom = 101,
|
||||
JoinRoom = 102,
|
||||
LeaveRoom = 103,
|
||||
SearchRoom = 104,
|
||||
RequestSignalingInfos = 105,
|
||||
ContextStop = 106,
|
||||
SetUserInfo = 107,
|
||||
SetRoomDataInternal = 108,
|
||||
SetRoomDataExternal = 109,
|
||||
KickoutRoomMember = 110,
|
||||
GetWorldInfoList = 111,
|
||||
GetRoomDataExternalList = 112,
|
||||
GetUserInfoList = 113,
|
||||
GetRoomMemberDataExternalList = 114,
|
||||
SendRoomMessage = 115,
|
||||
};
|
||||
|
||||
enum class NotificationType : u16 {
|
||||
@ -101,6 +103,7 @@ enum class NotificationType : u16 {
|
||||
FriendLost = 7,
|
||||
FriendStatus = 8,
|
||||
RoomEvent = 10,
|
||||
RoomMessage = 11,
|
||||
WebApiPushEvent = 17, // Generic NP WebApi push event
|
||||
};
|
||||
|
||||
@ -234,6 +237,19 @@ struct NotifyRoomEvent {
|
||||
std::vector<MatchingBinAttr> bin_attrs;
|
||||
};
|
||||
|
||||
struct NotifyRoomMessage {
|
||||
u32 ctx_id = 0;
|
||||
u64 room_id = 0;
|
||||
u32 src_member_id = 0;
|
||||
u32 event = 0;
|
||||
u32 cast_type = 0;
|
||||
std::vector<u32> dst_member_ids;
|
||||
std::string src_npid;
|
||||
u64 src_account_id = 0;
|
||||
u32 src_platform = 0;
|
||||
std::vector<u8> msg;
|
||||
};
|
||||
|
||||
// ShadNetClient
|
||||
|
||||
class ShadNetClient {
|
||||
@ -258,6 +274,7 @@ public:
|
||||
u64 GetUserId() const;
|
||||
u32 GetAddrLocal() const;
|
||||
u32 GetAddrServer() const;
|
||||
bool IsMatching2Enabled() const;
|
||||
u32 GetNumFriends() const;
|
||||
std::optional<std::string> GetFriendNpid(u32 index) const;
|
||||
|
||||
@ -270,6 +287,7 @@ public:
|
||||
std::function<void(const NotifyFriendLost&)> onFriendLost;
|
||||
std::function<void(const NotifyFriendStatus&)> onFriendStatus;
|
||||
std::function<void(const NotifyRoomEvent&)> onRoomEvent;
|
||||
std::function<void(const NotifyRoomMessage&)> onRoomMessage;
|
||||
std::function<void(const NotifyWebApiPushEvent&)> onWebApiPushEvent;
|
||||
// Async reply callback.
|
||||
// cmd —command this reply is for (matches the request's cmd)
|
||||
@ -304,7 +322,9 @@ private:
|
||||
void DispatchPacket(PacketType type, u16 cmd_raw, u64 pkt_id, const std::vector<u8>& payload);
|
||||
void HandleLoginReply(const std::vector<u8>& payload);
|
||||
void HandleGetTokenReply(const std::vector<u8>& payload);
|
||||
void HandleServerFeaturesReply(const std::vector<u8>& payload);
|
||||
void HandleNotification(u16 cmd_raw, const std::vector<u8>& payload);
|
||||
bool RequestServerFeatures();
|
||||
|
||||
// Helper: read a u32-LE-prefixed proto blob from a byte vector at pos.
|
||||
static std::string ExtractBlob(const std::vector<u8>& p, int pos);
|
||||
@ -349,6 +369,8 @@ private:
|
||||
std::string m_bearer_token;
|
||||
std::atomic<u32> m_addr_local{0};
|
||||
std::atomic<u32> m_addr_server{0};
|
||||
std::atomic<bool> m_matching2_enabled{false};
|
||||
std::atomic<bool> m_server_features_received{false};
|
||||
|
||||
mutable std::mutex m_mutex_friends;
|
||||
std::vector<FriendEntry> m_friends;
|
||||
|
||||
@ -29,6 +29,10 @@ message SetAppearOfflineRequest {
|
||||
bool appear_offline = 1;
|
||||
}
|
||||
|
||||
message ServerFeaturesReply {
|
||||
bool matching2_enabled = 1;
|
||||
}
|
||||
|
||||
message FriendEntry {
|
||||
string npid = 1;
|
||||
bool online = 2;
|
||||
@ -514,6 +518,16 @@ message SetRoomDataInternalReply { uint64 room_id = 1; }
|
||||
message SetRoomDataExternalReply { uint64 room_id = 1; }
|
||||
message KickoutRoomMemberReply { uint64 room_id = 1; }
|
||||
|
||||
message SendRoomMessageRequest {
|
||||
uint64 room_id = 1;
|
||||
uint32 cast_type = 2;
|
||||
repeated uint32 dst_member_ids = 3;
|
||||
bytes msg = 4;
|
||||
int32 option = 5;
|
||||
}
|
||||
|
||||
message SendRoomMessageReply { 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 ContextStartRequest).
|
||||
@ -534,3 +548,18 @@ message NotifyRoomEvent {
|
||||
bool has_passwd_mask = 9;
|
||||
uint64 passwd_slot_mask = 10;
|
||||
}
|
||||
|
||||
// NotificationType::RoomMessage (11) maps onto
|
||||
// OrbisNpMatching2RoomMessageCallback(ctxId, roomId, srcMemberId, event, data).
|
||||
message NotifyRoomMessage {
|
||||
uint32 ctx_id = 1;
|
||||
uint64 room_id = 2;
|
||||
uint32 src_member_id = 3;
|
||||
uint32 event = 4;
|
||||
uint32 cast_type = 5;
|
||||
repeated uint32 dst_member_ids = 6;
|
||||
string src_npid = 7;
|
||||
uint64 src_account_id = 8;
|
||||
uint32 src_platform = 9;
|
||||
bytes msg = 10;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user