mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-10-11 21:49:59 +00:00
spilt np_handler to multiple files based on it purpose. (#5333)
* spilt np_handler to multiple files based on it purpose . No functionality added * qos
This commit is contained in:
+8
-2
@@ -647,8 +647,14 @@ set(NP_LIBS src/core/libraries/np/np_error.h
|
||||
src/core/libraries/np/np_partner.cpp
|
||||
src/core/libraries/np/np_partner.h
|
||||
src/core/libraries/np/object_manager.h
|
||||
src/core/libraries/np/np_handler.cpp
|
||||
src/core/libraries/np/np_handler.h
|
||||
src/core/libraries/np/np_handler/np_handler.cpp
|
||||
src/core/libraries/np/np_handler/np_handler.h
|
||||
src/core/libraries/np/np_handler/np_handler_friends.cpp
|
||||
src/core/libraries/np/np_handler/np_handler_invitation.cpp
|
||||
src/core/libraries/np/np_handler/np_handler_score.cpp
|
||||
src/core/libraries/np/np_handler/np_handler_trophy.cpp
|
||||
src/core/libraries/np/np_handler/np_handler_tus.cpp
|
||||
src/core/libraries/np/np_handler/np_handler_web_api.cpp
|
||||
src/core/libraries/np/signaling_handler.cpp
|
||||
src/core/libraries/np/signaling_handler.h
|
||||
src/core/libraries/np/np_commerce/np_commerce.cpp
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/invitation_dialog/invitation_dialog.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "invitation_dialog_ui.h"
|
||||
|
||||
namespace Libraries::InvitationDialog {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "core/libraries/net/net.h"
|
||||
#include "core/libraries/network/http.h"
|
||||
#include "core/libraries/network/ssl.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_web_api/np_web_api.h"
|
||||
#include "core/libraries/system/commondialog.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "core/libraries/invitation_dialog/invitation_dialog.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/system/commondialog.h"
|
||||
#include "imgui/imgui_layer.h"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,754 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019-2026 rpcs3 Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <fmt/format.h>
|
||||
#include "common/elf_info.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/net/net_p2p.h"
|
||||
#include "core/libraries/net/net_upnp.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_manager.h"
|
||||
#include "core/libraries/np/np_matching2/np_matching2_internal.h"
|
||||
#include "core/libraries/np/np_matching2/np_matching2_mm.h"
|
||||
#include "core/user_settings.h"
|
||||
#include "imgui/shadnet_notifications_layer.h"
|
||||
#include "shadnet.pb.h"
|
||||
#include "shadnet/server_probe.h"
|
||||
|
||||
namespace Libraries::Np {
|
||||
|
||||
NpHandler::NpHandler()
|
||||
: m_matching2_contexts(
|
||||
std::unique_ptr<NpMatching2::ContextManager>(new NpMatching2::ContextManager())),
|
||||
m_matching2_state(std::make_unique<NpMatching2::NpMatching2State>()) {}
|
||||
|
||||
NpHandler::~NpHandler() = default;
|
||||
|
||||
NpHandler& NpHandler::GetInstance() {
|
||||
static NpHandler s_instance;
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
NpMatching2::ContextManager& NpHandler::GetMatching2ContextManager() {
|
||||
return *m_matching2_contexts;
|
||||
}
|
||||
|
||||
NpMatching2::NpMatching2State& NpHandler::GetMatching2State() {
|
||||
return *m_matching2_state;
|
||||
}
|
||||
|
||||
NpHandler::Matching2CacheGuard NpHandler::LockMatching2Cache(
|
||||
NpMatching2::OrbisNpMatching2ContextId ctx_id) {
|
||||
std::shared_ptr<NpMatching2::Matching2ContextCache> cache;
|
||||
{
|
||||
std::lock_guard lock(m_mutex_matching2_cache);
|
||||
auto& entry = m_matching2_cache[ctx_id];
|
||||
if (!entry) {
|
||||
entry = std::make_shared<NpMatching2::Matching2ContextCache>();
|
||||
}
|
||||
cache = entry;
|
||||
}
|
||||
return Matching2CacheGuard(std::move(cache));
|
||||
}
|
||||
|
||||
void NpHandler::ResetMatching2Cache(NpMatching2::OrbisNpMatching2ContextId ctx_id) {
|
||||
std::lock_guard lock(m_mutex_matching2_cache);
|
||||
m_matching2_cache.erase(ctx_id);
|
||||
}
|
||||
|
||||
void NpHandler::ResetMatching2Caches() {
|
||||
std::lock_guard lock(m_mutex_matching2_cache);
|
||||
m_matching2_cache.clear();
|
||||
}
|
||||
|
||||
std::pair<std::string, u16> NpHandler::ParseServerAddress() const {
|
||||
const std::string server_str = EmulatorSettings.GetShadNetServer();
|
||||
u16 port = 31313; // default port
|
||||
if (server_str.empty()) {
|
||||
LOG_ERROR(NpHandler,
|
||||
"shadNet server address is empty,set the shadNet server field in settings. "
|
||||
"Connection will fail.");
|
||||
return {std::string{}, port};
|
||||
}
|
||||
std::string host = server_str;
|
||||
const auto colon = server_str.rfind(':');
|
||||
if (colon != std::string::npos) {
|
||||
host = server_str.substr(0, colon);
|
||||
const std::string port_str = server_str.substr(colon + 1);
|
||||
try {
|
||||
port = static_cast<u16>(std::stoi(port_str));
|
||||
} catch (const std::exception&) {
|
||||
LOG_WARNING(NpHandler,
|
||||
"shadNet server port '{}' is not a valid number; using default {}",
|
||||
port_str, port);
|
||||
}
|
||||
}
|
||||
return {host, port};
|
||||
}
|
||||
|
||||
bool NpHandler::ConnectUserById(s32 user_id) {
|
||||
if (!EmulatorSettings.IsShadNetEnabled())
|
||||
return false;
|
||||
|
||||
{
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
if (m_clients.find(user_id) != m_clients.end())
|
||||
return false; // already connected
|
||||
}
|
||||
|
||||
const User* u = UserManagement.GetUserByID(user_id);
|
||||
if (!u)
|
||||
return false;
|
||||
if (!u->shadnet_enabled) {
|
||||
LOG_DEBUG(NpHandler, "user_id={} ('{}') shadNet disabled,skipping", u->user_id,
|
||||
u->user_name);
|
||||
return false;
|
||||
}
|
||||
if (u->shadnet_npid.empty() || u->shadnet_password.empty()) {
|
||||
LOG_WARNING(NpHandler, "user_id={} ('{}') shadNet enabled but credentials missing,skipping",
|
||||
u->user_id, u->user_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto [host, port] = ParseServerAddress();
|
||||
return ConnectUser(u->user_id, host, port, u->shadnet_npid, u->shadnet_password,
|
||||
u->shadnet_token);
|
||||
}
|
||||
|
||||
void NpHandler::StartWorker() {
|
||||
bool expected = false;
|
||||
if (m_worker_running.compare_exchange_strong(expected, true)) {
|
||||
m_worker_thread = std::thread(&NpHandler::WorkerThread, this);
|
||||
}
|
||||
}
|
||||
|
||||
void NpHandler::Initialize() {
|
||||
if (m_initialized.exchange(true)) {
|
||||
LOG_WARNING(NpHandler, "Initialize called more than once");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EmulatorSettings.IsShadNetEnabled()) {
|
||||
LOG_INFO(NpHandler, "shadNet disabled globally we are in offline mode");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
const auto [host, port] = ParseServerAddress();
|
||||
const ShadNet::ProbeInfo probe = ShadNet::ProbeServer(host, port);
|
||||
if (probe.result != ShadNet::ProbeResult::Ok) {
|
||||
EmulatorSettings.SetShadNetSessionDisabled(true);
|
||||
switch (probe.result) {
|
||||
case ShadNet::ProbeResult::VersionMismatch:
|
||||
LOG_WARNING(NpHandler,
|
||||
"shadNet server {}:{} protocol version mismatch (server v{}, emulator "
|
||||
"v{}); disabling shadNet for this run (saved setting unchanged)",
|
||||
host, port, probe.server_version, ShadNet::SHAD_PROTOCOL_VERSION);
|
||||
ImGui::ShadNetNotify::Push(
|
||||
ImGui::ShadNetNotify::Kind::Info,
|
||||
fmt::format("shadNet protocol version mismatch (server v{}, emulator v{}). "
|
||||
"Please update shadPS4. Online features are disabled for this "
|
||||
"session.",
|
||||
probe.server_version, ShadNet::SHAD_PROTOCOL_VERSION));
|
||||
break;
|
||||
case ShadNet::ProbeResult::ProtocolError:
|
||||
LOG_WARNING(NpHandler,
|
||||
"shadNet server {}:{} sent an invalid ServerInfo handshake; disabling "
|
||||
"shadNet for this run (saved setting unchanged)",
|
||||
host, port);
|
||||
ImGui::ShadNetNotify::Push(
|
||||
ImGui::ShadNetNotify::Kind::Info,
|
||||
fmt::format("shadNet server ({}:{}) uses an incompatible protocol. Online "
|
||||
"features are disabled for this session.",
|
||||
host, port));
|
||||
break;
|
||||
default: // Unreachable
|
||||
LOG_WARNING(NpHandler,
|
||||
"shadNet server {}:{} is offline/unreachable; disabling shadNet for "
|
||||
"this run (saved setting unchanged)",
|
||||
host, port);
|
||||
ImGui::ShadNetNotify::Push(
|
||||
ImGui::ShadNetNotify::Kind::Info,
|
||||
fmt::format("shadNet server ({}:{}) is offline. Online features are disabled "
|
||||
"for this session.",
|
||||
host, port));
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto logged_in = UserManagement.GetLoggedInUsers(); // get all login users
|
||||
int connected_count = 0;
|
||||
for (int i = 0; i < Libraries::UserService::ORBIS_USER_SERVICE_MAX_LOGIN_USERS; ++i) {
|
||||
const User* u = logged_in[i];
|
||||
if (!u)
|
||||
continue;
|
||||
if (ConnectUserById(u->user_id))
|
||||
++connected_count;
|
||||
}
|
||||
|
||||
if (connected_count == 0) {
|
||||
LOG_WARNING(NpHandler, "no users connected to shadNet");
|
||||
return;
|
||||
}
|
||||
|
||||
StartWorker();
|
||||
}
|
||||
|
||||
void NpHandler::Shutdown() {
|
||||
if (!m_initialized.exchange(false))
|
||||
return;
|
||||
|
||||
m_worker_running = false;
|
||||
|
||||
// Stop any pending reconnect retries.
|
||||
{
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
m_reconnect.clear();
|
||||
}
|
||||
|
||||
// Collect user IDs to disconnect (avoid holding m_mutex_clients during Stop)
|
||||
std::vector<s32> ids;
|
||||
{
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
for (auto& [uid, _] : m_clients)
|
||||
ids.push_back(uid);
|
||||
}
|
||||
for (s32 uid : ids)
|
||||
DisconnectUser(uid);
|
||||
|
||||
if (m_worker_thread.joinable())
|
||||
m_worker_thread.join();
|
||||
|
||||
// P2P goes with NP: closes the UDP port and removes its UPnP forwarding.
|
||||
Net::StopP2P();
|
||||
|
||||
LOG_INFO(NpHandler, "Shutdown complete");
|
||||
}
|
||||
|
||||
bool NpHandler::ConnectUser(s32 user_id, const std::string& host, u16 port, const std::string& npid,
|
||||
const std::string& password, const std::string& token) {
|
||||
LOG_INFO(NpHandler, "Connecting user_id={} npid='{}' to {}:{} (timeout {}s)", user_id, npid,
|
||||
host, port, ShadNet::SHAD_CONNECT_TIMEOUT_MS / 1000);
|
||||
|
||||
auto client = std::make_shared<ShadNet::ShadNetClient>();
|
||||
|
||||
// Wire per-user notification callbacks
|
||||
client->onFriendQuery = [this, user_id](const ShadNet::NotifyFriendQuery& n) {
|
||||
OnFriendQuery(user_id, n);
|
||||
};
|
||||
client->onFriendNew = [this, user_id](const ShadNet::NotifyFriendNew& n) {
|
||||
OnFriendNew(user_id, n);
|
||||
};
|
||||
client->onFriendLost = [this, user_id](const ShadNet::NotifyFriendLost& n) {
|
||||
OnFriendLost(user_id, n);
|
||||
};
|
||||
client->onFriendStatus = [this, user_id](const ShadNet::NotifyFriendStatus& n) {
|
||||
OnFriendStatus(user_id, n);
|
||||
};
|
||||
client->onWebApiPushEvent = [this, user_id](const ShadNet::NotifyWebApiPushEvent& n) {
|
||||
OnWebApiPushEvent(user_id, n);
|
||||
};
|
||||
client->onAsyncReply = [this, user_id](ShadNet::CommandType cmd, u64 pkt_id,
|
||||
ShadNet::ErrorType err, const std::vector<u8>& body) {
|
||||
OnAsyncReply(user_id, cmd, pkt_id, err, body);
|
||||
};
|
||||
client->onLoginResult = [this, user_id](const ShadNet::LoginResult& res) {
|
||||
OnLoginResult(user_id, res);
|
||||
};
|
||||
|
||||
// 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());
|
||||
client->Start(host, port, npid, password, token);
|
||||
|
||||
// Shared handling for an incompatible-protocol failure (version mismatch or
|
||||
// corrupt/unparseable stream): tell the user and disable shadNet for this run,
|
||||
// since reconnect attempts against an incompatible server are pointless.
|
||||
const auto handle_protocol_mismatch = [&client](ShadNet::ShadNetState st) {
|
||||
if (st != ShadNet::ShadNetState::FailureProtocol)
|
||||
return;
|
||||
const u32 server_ver = client->GetServerProtocolVersion();
|
||||
EmulatorSettings.SetShadNetSessionDisabled(true);
|
||||
if (server_ver != 0) {
|
||||
LOG_ERROR(NpHandler,
|
||||
"shadNet protocol version mismatch (server v{}, emulator v{}); disabling "
|
||||
"shadNet for this run (saved setting unchanged)",
|
||||
server_ver, ShadNet::SHAD_PROTOCOL_VERSION);
|
||||
ImGui::ShadNetNotify::Push(
|
||||
ImGui::ShadNetNotify::Kind::Info,
|
||||
fmt::format("shadNet protocol version mismatch (server v{}, emulator v{}). "
|
||||
"Please update shadPS4. Online features are disabled for this "
|
||||
"session.",
|
||||
server_ver, ShadNet::SHAD_PROTOCOL_VERSION));
|
||||
} else {
|
||||
LOG_ERROR(NpHandler, "shadNet protocol error during handshake; disabling shadNet for "
|
||||
"this run (saved setting unchanged)");
|
||||
ImGui::ShadNetNotify::Push(
|
||||
ImGui::ShadNetNotify::Kind::Info,
|
||||
"shadNet server uses an incompatible protocol. Online features are disabled "
|
||||
"for this session.");
|
||||
}
|
||||
};
|
||||
|
||||
const ShadNet::ShadNetState conn_state = client->WaitForConnection();
|
||||
if (conn_state != ShadNet::ShadNetState::Ok) {
|
||||
LOG_ERROR(NpHandler, "user_id={} connection failed (state={})", user_id,
|
||||
static_cast<int>(conn_state));
|
||||
handle_protocol_mismatch(conn_state);
|
||||
client->Stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
const ShadNet::ShadNetState auth_state = client->WaitForAuthenticated();
|
||||
if (auth_state != ShadNet::ShadNetState::Ok) {
|
||||
LOG_ERROR(NpHandler, "user_id={} authentication failed (state={})", user_id,
|
||||
static_cast<int>(auth_state));
|
||||
handle_protocol_mismatch(auth_state);
|
||||
client->Stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
OrbisNpId np_id{};
|
||||
SetNpId(np_id, npid);
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
m_np_ids[user_id] = np_id;
|
||||
m_clients[user_id] = std::move(client);
|
||||
}
|
||||
|
||||
FireStateCallback(user_id, NpManager::OrbisNpState::SignedIn);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NpHandler::SetAppearOffline(bool enable) {
|
||||
m_appear_offline = enable;
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
for (auto& [uid, client] : m_clients) {
|
||||
if (client)
|
||||
client->SetAppearOffline(enable); // sends the toggle to connected sessions
|
||||
}
|
||||
}
|
||||
|
||||
void NpHandler::FailPendingRequests(s32 user_id, s32 error_code) {
|
||||
size_t failed = 0;
|
||||
{
|
||||
std::lock_guard lock(m_mutex_pending_tus);
|
||||
for (auto it = m_pending_tus.begin(); it != m_pending_tus.end();) {
|
||||
if (it->second.user_id == user_id) {
|
||||
if (it->second.req) {
|
||||
it->second.req->SetResult(error_code);
|
||||
}
|
||||
it = m_pending_tus.erase(it);
|
||||
++failed;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(m_mutex_pending_score);
|
||||
for (auto it = m_pending_score.begin(); it != m_pending_score.end();) {
|
||||
if (it->second.user_id == user_id) {
|
||||
if (it->second.req) {
|
||||
it->second.req->SetResult(error_code);
|
||||
}
|
||||
it = m_pending_score.erase(it);
|
||||
++failed;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(m_mutex_pending_lookup);
|
||||
for (auto it = m_pending_lookup.begin(); it != m_pending_lookup.end();) {
|
||||
if (it->second.user_id == user_id) {
|
||||
if (it->second.on_result) {
|
||||
it->second.on_result(error_code, 0, {});
|
||||
}
|
||||
it = m_pending_lookup.erase(it);
|
||||
++failed;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (failed > 0) {
|
||||
LOG_WARNING(NpHandler, "user_id={} failed {} pending request(s) with {:#x}", user_id,
|
||||
failed, static_cast<u32>(error_code));
|
||||
}
|
||||
}
|
||||
|
||||
void NpHandler::DisconnectUser(s32 user_id) {
|
||||
std::shared_ptr<ShadNet::ShadNetClient> client;
|
||||
{
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
auto it = m_clients.find(user_id);
|
||||
if (it == m_clients.end())
|
||||
return;
|
||||
client = std::move(it->second);
|
||||
m_clients.erase(it);
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(m_mutex_friend_state);
|
||||
m_friend_state.erase(user_id);
|
||||
}
|
||||
client->Stop();
|
||||
// The reader thread is joined, so no reply can race us: complete every
|
||||
// in-flight request now, or PollAsync spins and WaitAsync blocks forever.
|
||||
FailPendingRequests(user_id, ORBIS_NP_ERROR_SIGNED_OUT);
|
||||
FireStateCallback(user_id, NpManager::OrbisNpState::SignedOut);
|
||||
LOG_INFO(NpHandler, "user_id={} disconnected", user_id);
|
||||
}
|
||||
|
||||
void NpHandler::OnUserLoggedIn(s32 user_id) {
|
||||
if (ConnectUserById(user_id))
|
||||
StartWorker();
|
||||
}
|
||||
|
||||
void NpHandler::OnUserLoggedOut(s32 user_id) {
|
||||
{
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
m_reconnect.erase(user_id); // do not auto-reconnect
|
||||
}
|
||||
DisconnectUser(user_id);
|
||||
}
|
||||
|
||||
void NpHandler::WorkerThread() {
|
||||
constexpr auto INTERVAL = std::chrono::milliseconds(500);
|
||||
|
||||
while (m_worker_running) {
|
||||
std::this_thread::sleep_for(INTERVAL);
|
||||
|
||||
// Collect ids of dropped clients
|
||||
std::vector<s32> dropped;
|
||||
{
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
for (auto& [uid, client] : m_clients) {
|
||||
if (!client->IsConnected())
|
||||
dropped.push_back(uid);
|
||||
}
|
||||
}
|
||||
|
||||
for (s32 uid : dropped) {
|
||||
LOG_WARNING(NpHandler, "user_id={} connection dropped (network); will retry", uid);
|
||||
DisconnectUser(uid); // reports SignedOut + stops/removes the dead client
|
||||
MarkForReconnect(uid); // schedule transparent reconnect (network drop, not logout)
|
||||
}
|
||||
|
||||
TryReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void NpHandler::MarkForReconnect(s32 user_id) {
|
||||
if (!EmulatorSettings.IsShadNetEnabled())
|
||||
return; // offline mode: nothing to reconnect to
|
||||
constexpr auto kInitialBackoff = std::chrono::milliseconds(2000);
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
auto& st = m_reconnect[user_id];
|
||||
st.backoff = kInitialBackoff;
|
||||
st.next_attempt = std::chrono::steady_clock::now() + st.backoff;
|
||||
}
|
||||
|
||||
void NpHandler::TryReconnect() {
|
||||
constexpr auto kMaxBackoff = std::chrono::milliseconds(30000);
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
|
||||
std::vector<s32> due;
|
||||
{
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
for (auto& [uid, st] : m_reconnect) {
|
||||
if (m_clients.count(uid) || now >= st.next_attempt)
|
||||
due.push_back(uid);
|
||||
}
|
||||
}
|
||||
|
||||
for (s32 uid : due) {
|
||||
if (!m_worker_running)
|
||||
return;
|
||||
if (!EmulatorSettings.IsShadNetEnabled()) {
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
m_reconnect.erase(uid);
|
||||
continue;
|
||||
}
|
||||
// ConnectUserById locks m_mutex_clients internally; call it unlocked.
|
||||
const bool ok = ConnectUserById(uid);
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
if (ok || m_clients.count(uid)) {
|
||||
m_reconnect.erase(uid);
|
||||
LOG_INFO(NpHandler, "user_id={} reconnected to shadNet", uid);
|
||||
} else if (auto it = m_reconnect.find(uid); it != m_reconnect.end()) {
|
||||
it->second.backoff = std::min(it->second.backoff * 2, kMaxBackoff);
|
||||
it->second.next_attempt = std::chrono::steady_clock::now() + it->second.backoff;
|
||||
LOG_DEBUG(NpHandler, "user_id={} reconnect failed; next attempt in {}ms", uid,
|
||||
it->second.backoff.count());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool NpHandler::IsPsnSignedIn(s32 user_id) const {
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
auto it = m_clients.find(user_id);
|
||||
return it != m_clients.end() && it->second->IsAuthenticated();
|
||||
}
|
||||
|
||||
bool NpHandler::IsAnySignedIn() const {
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
for (auto& [_, client] : m_clients)
|
||||
if (client->IsAuthenticated())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
OrbisNpId NpHandler::GetNpId(s32 user_id) const {
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
auto it = m_np_ids.find(user_id);
|
||||
return it != m_np_ids.end() ? it->second : OrbisNpId{};
|
||||
}
|
||||
|
||||
OrbisNpOnlineId NpHandler::GetOnlineId(s32 user_id) const {
|
||||
return GetNpId(user_id).handle;
|
||||
}
|
||||
|
||||
std::string NpHandler::GetAvatarUrl(s32 user_id) const {
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
auto it = m_clients.find(user_id);
|
||||
return it != m_clients.end() ? it->second->GetAvatarUrl() : std::string{};
|
||||
}
|
||||
|
||||
OrbisNpAccountId NpHandler::GetAccountId(s32 user_id) const {
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
auto it = m_clients.find(user_id);
|
||||
return it != m_clients.end() ? static_cast<OrbisNpAccountId>(it->second->GetUserId()) : 0;
|
||||
}
|
||||
|
||||
std::string NpHandler::GetBearerToken(s32 user_id) const {
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
auto it = m_clients.find(user_id);
|
||||
return it != m_clients.end() ? it->second->GetBearerToken() : std::string{};
|
||||
}
|
||||
|
||||
u32 NpHandler::GetLocalIpAddr(s32 user_id) const {
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
auto it = m_clients.find(user_id);
|
||||
return it != m_clients.end() ? it->second->GetAddrLocal() : 0;
|
||||
}
|
||||
|
||||
s32 NpHandler::GetUserIdByAccountId(u64 account_id) const {
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
for (auto& [uid, client] : m_clients) {
|
||||
if (static_cast<u64>(client->GetUserId()) == account_id)
|
||||
return uid;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
s32 NpHandler::GetUserIdByOnlineId(const OrbisNpOnlineId& online_id) const {
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
for (auto& [uid, np_id] : m_np_ids) {
|
||||
if (strncmp(np_id.handle.data, online_id.data, ORBIS_NP_ONLINEID_MAX_LENGTH) == 0)
|
||||
return uid;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::vector<s32> NpHandler::GetConnectedUsers() const {
|
||||
std::vector<s32> out;
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
out.reserve(m_clients.size());
|
||||
for (const auto& [uid, c] : m_clients) {
|
||||
out.push_back(uid);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// state callbacks
|
||||
s32 NpHandler::RegisterStateCallback(StateCallback cb, void* userdata) {
|
||||
std::lock_guard lock(m_mutex_cbs);
|
||||
const s32 h = m_next_handle++;
|
||||
m_state_cbs.push_back({h, std::move(cb), userdata});
|
||||
return h;
|
||||
}
|
||||
|
||||
void NpHandler::UnregisterStateCallback(s32 handle) {
|
||||
std::lock_guard lock(m_mutex_cbs);
|
||||
m_state_cbs.erase(std::remove_if(m_state_cbs.begin(), m_state_cbs.end(),
|
||||
[handle](const CbEntry& e) { return e.handle == handle; }),
|
||||
m_state_cbs.end());
|
||||
}
|
||||
|
||||
void NpHandler::FireStateCallback(s32 user_id, NpManager::OrbisNpState state) {
|
||||
std::lock_guard lock(m_mutex_cbs);
|
||||
for (const auto& e : m_state_cbs) {
|
||||
if (e.cb)
|
||||
e.cb(user_id, state);
|
||||
}
|
||||
}
|
||||
|
||||
// A usable NP Communication ID is the 12-char "NPWRxxxxx_00" form. An empty or
|
||||
// all-NUL buffer means npbind.dat was missing or unparsed,treat it as invalid
|
||||
// so callers reject the request instead of sending a blank comm id on the wire.
|
||||
bool IsValidNpCommId(const std::string& id) {
|
||||
return !id.empty() && std::any_of(id.begin(), id.end(), [](char c) { return c != '\0'; });
|
||||
}
|
||||
|
||||
std::string NpHandler::GetNpCommId(s32 service_label) const {
|
||||
// TODO complete guess of how commid is mapping to service_label.
|
||||
constexpr size_t COM_ID_LEN = 12;
|
||||
const auto& ids = Common::ElfInfo::Instance().GetNpCommIds();
|
||||
std::string com_id;
|
||||
if (!ids.empty()) {
|
||||
const size_t idx = (service_label >= 0 && static_cast<size_t>(service_label) < ids.size())
|
||||
? static_cast<size_t>(service_label)
|
||||
: 0;
|
||||
com_id = ids[idx];
|
||||
if (static_cast<size_t>(service_label) >= ids.size()) {
|
||||
LOG_WARNING(NpHandler,
|
||||
"service_label={} >= npbind entry count {} — falling back "
|
||||
"to index 0",
|
||||
service_label, ids.size());
|
||||
}
|
||||
}
|
||||
if (com_id.size() < COM_ID_LEN) {
|
||||
com_id.resize(COM_ID_LEN, '\0');
|
||||
} else if (com_id.size() > COM_ID_LEN) {
|
||||
com_id.resize(COM_ID_LEN);
|
||||
}
|
||||
return com_id;
|
||||
}
|
||||
|
||||
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 >= 100 && cmd_val <= 200) {
|
||||
NpMatching2::OnMatchingReply(cmd, pkt_id, error, body);
|
||||
} else if (cmd_val >= 201 && cmd_val <= 300) {
|
||||
OnTusReply(user_id, cmd, pkt_id, error, body);
|
||||
} else if (cmd_val >= 301 && cmd_val <= 400) {
|
||||
OnTrophyReply(user_id, cmd, pkt_id, error, body);
|
||||
} else if (cmd == ShadNet::CommandType::LookupOnlineId) {
|
||||
OnLookupReply(user_id, cmd, pkt_id, error, body);
|
||||
} else {
|
||||
OnScoreReply(user_id, cmd, pkt_id, error, body);
|
||||
}
|
||||
}
|
||||
|
||||
void NpHandler::ResolveOnlineId(
|
||||
s32 user_id, const std::string& online_id,
|
||||
std::function<void(s32 result, u64 account_id, const std::string& canonical_online_id)>
|
||||
on_result) {
|
||||
std::shared_ptr<ShadNet::ShadNetClient> client;
|
||||
{
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
auto it = m_clients.find(user_id);
|
||||
if (it == m_clients.end()) {
|
||||
LOG_WARNING(NpHandler, "No shadNet session for user_id={}", user_id);
|
||||
if (on_result) {
|
||||
on_result(ORBIS_NP_ERROR_SIGNED_OUT, 0, {});
|
||||
}
|
||||
return;
|
||||
}
|
||||
client = it->second;
|
||||
}
|
||||
if (!client || !client->IsAuthenticated()) {
|
||||
LOG_WARNING(NpHandler, "user_id={} not authenticated", user_id);
|
||||
if (on_result) {
|
||||
on_result(ORBIS_NP_COMMUNITY_ERROR_NO_LOGIN, 0, {});
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (online_id.empty()) {
|
||||
if (on_result) {
|
||||
on_result(ORBIS_NP_COMMUNITY_SERVER_ERROR_NO_SUCH_USER_NPID, 0, {});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const u64 pkt_id = client->LookupOnlineId(online_id);
|
||||
if (on_result) {
|
||||
std::lock_guard lock(m_mutex_pending_lookup);
|
||||
PendingLookupRequest pending;
|
||||
pending.user_id = user_id;
|
||||
pending.on_result = std::move(on_result);
|
||||
m_pending_lookup.emplace(pkt_id, std::move(pending));
|
||||
}
|
||||
LOG_INFO(NpHandler, "user_id={} onlineId='{}' pkt_id={}", user_id, online_id, pkt_id);
|
||||
}
|
||||
|
||||
void NpHandler::OnLookupReply(s32 user_id, ShadNet::CommandType /*cmd*/, u64 pkt_id,
|
||||
ShadNet::ErrorType error, const std::vector<u8>& body) {
|
||||
std::function<void(s32, u64, const std::string&)> on_result;
|
||||
{
|
||||
std::lock_guard lock(m_mutex_pending_lookup);
|
||||
auto it = m_pending_lookup.find(pkt_id);
|
||||
if (it == m_pending_lookup.end()) {
|
||||
return; // no waiter (already flushed by a disconnect, or a stray reply)
|
||||
}
|
||||
on_result = std::move(it->second.on_result);
|
||||
m_pending_lookup.erase(it);
|
||||
}
|
||||
if (!on_result) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (error == ShadNet::ErrorType::NotFound) {
|
||||
LOG_INFO(NpHandler, "LookupOnlineId: user_id={} pkt_id={} -> not found", user_id, pkt_id);
|
||||
on_result(ORBIS_NP_COMMUNITY_SERVER_ERROR_NO_SUCH_USER_NPID, 0, {});
|
||||
return;
|
||||
}
|
||||
if (error == ShadNet::ErrorType::InvalidInput) {
|
||||
on_result(ORBIS_NP_COMMUNITY_ERROR_INVALID_ARGUMENT, 0, {});
|
||||
return;
|
||||
}
|
||||
if (error != ShadNet::ErrorType::NoError) {
|
||||
LOG_WARNING(NpHandler, "LookupOnlineId failed: error={}", static_cast<int>(error));
|
||||
on_result(ORBIS_NP_COMMUNITY_ERROR_BAD_RESPONSE, 0, {});
|
||||
return;
|
||||
}
|
||||
|
||||
// Reply body is u32 LE blob size followed by the proto.
|
||||
if (body.size() < 4) {
|
||||
LOG_WARNING(NpHandler, "LookupOnlineId: truncated reply");
|
||||
on_result(ORBIS_NP_COMMUNITY_ERROR_BAD_RESPONSE, 0, {});
|
||||
return;
|
||||
}
|
||||
const u32 blob_sz = static_cast<u32>(body[0]) | (static_cast<u32>(body[1]) << 8) |
|
||||
(static_cast<u32>(body[2]) << 16) | (static_cast<u32>(body[3]) << 24);
|
||||
if (body.size() < 4 + blob_sz) {
|
||||
LOG_WARNING(NpHandler, "LookupOnlineId: reply shorter than declared blob");
|
||||
on_result(ORBIS_NP_COMMUNITY_ERROR_BAD_RESPONSE, 0, {});
|
||||
return;
|
||||
}
|
||||
|
||||
shadnet::LookupOnlineIdReply pb;
|
||||
if (!pb.ParseFromArray(body.data() + 4, static_cast<int>(blob_sz))) {
|
||||
LOG_WARNING(NpHandler, "LookupOnlineId: could not parse reply");
|
||||
on_result(ORBIS_NP_COMMUNITY_ERROR_BAD_RESPONSE, 0, {});
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO(NpHandler, "LookupOnlineId: user_id={} pkt_id={} -> accountId={} npid='{}'", user_id,
|
||||
pkt_id, pb.account_id(), pb.npid());
|
||||
on_result(ORBIS_OK, pb.account_id(), pb.npid());
|
||||
}
|
||||
|
||||
} // namespace Libraries::Np
|
||||
@@ -37,6 +37,8 @@ class ContextManager;
|
||||
struct NpMatching2State;
|
||||
} // namespace NpMatching2
|
||||
|
||||
bool IsValidNpCommId(const std::string& id);
|
||||
|
||||
class NpHandler {
|
||||
public:
|
||||
class Matching2CacheGuard {
|
||||
@@ -0,0 +1,232 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019-2026 rpcs3 Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "imgui/shadnet_notifications_layer.h"
|
||||
|
||||
namespace Libraries::Np {
|
||||
|
||||
// friends calls
|
||||
u32 NpHandler::GetNumFriends(s32 user_id) const {
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
auto it = m_clients.find(user_id);
|
||||
return it != m_clients.end() ? it->second->GetNumFriends() : 0;
|
||||
}
|
||||
|
||||
std::optional<std::string> NpHandler::GetFriendNpid(s32 user_id, u32 index) const {
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
auto it = m_clients.find(user_id);
|
||||
return it != m_clients.end() ? it->second->GetFriendNpid(index) : std::nullopt;
|
||||
}
|
||||
|
||||
void NpHandler::OnFriendQuery(s32 user_id, const ShadNet::NotifyFriendQuery& n) {
|
||||
LOG_INFO(NpHandler, "user_id={} FriendQuery from '{}'", user_id, n.fromNpid);
|
||||
ImGui::ShadNetNotify::Push(ImGui::ShadNetNotify::Kind::FriendRequest,
|
||||
"Friend request from " + n.fromNpid);
|
||||
std::lock_guard lock(m_mutex_friend_state);
|
||||
auto& st = m_friend_state[user_id];
|
||||
if (std::find(st.requests_received.begin(), st.requests_received.end(), n.fromNpid) ==
|
||||
st.requests_received.end()) {
|
||||
st.requests_received.push_back(n.fromNpid);
|
||||
}
|
||||
}
|
||||
|
||||
void NpHandler::OnFriendNew(s32 user_id, const ShadNet::NotifyFriendNew& n) {
|
||||
LOG_INFO(NpHandler, "user_id={} FriendNew '{}' ({})", user_id, n.npid,
|
||||
n.online ? "online" : "offline");
|
||||
ImGui::ShadNetNotify::Push(ImGui::ShadNetNotify::Kind::FriendNew,
|
||||
n.npid + " is now your friend");
|
||||
std::lock_guard lock(m_mutex_friend_state);
|
||||
auto& st = m_friend_state[user_id];
|
||||
auto it = std::find_if(st.friends.begin(), st.friends.end(),
|
||||
[&](const FriendInfo& f) { return f.npid == n.npid; });
|
||||
if (it == st.friends.end()) {
|
||||
st.friends.push_back({n.npid, n.online});
|
||||
} else {
|
||||
it->online = n.online;
|
||||
}
|
||||
const auto drop = [&](std::vector<std::string>& v) {
|
||||
v.erase(std::remove(v.begin(), v.end(), n.npid), v.end());
|
||||
};
|
||||
drop(st.requests_received);
|
||||
drop(st.requests_sent);
|
||||
}
|
||||
|
||||
void NpHandler::OnFriendLost(s32 user_id, const ShadNet::NotifyFriendLost& n) {
|
||||
LOG_INFO(NpHandler, "user_id={} FriendLost '{}'", user_id, n.npid);
|
||||
ImGui::ShadNetNotify::Push(ImGui::ShadNetNotify::Kind::FriendLost,
|
||||
n.npid + " removed you as a friend");
|
||||
std::lock_guard lock(m_mutex_friend_state);
|
||||
auto& st = m_friend_state[user_id];
|
||||
st.friends.erase(std::remove_if(st.friends.begin(), st.friends.end(),
|
||||
[&](const FriendInfo& f) { return f.npid == n.npid; }),
|
||||
st.friends.end());
|
||||
}
|
||||
|
||||
void NpHandler::OnFriendStatus(s32 user_id, const ShadNet::NotifyFriendStatus& n) {
|
||||
LOG_DEBUG(NpHandler, "user_id={} FriendStatus '{}' is {}", user_id, n.npid,
|
||||
n.online ? "online" : "offline");
|
||||
if (n.online) {
|
||||
ImGui::ShadNetNotify::Push(ImGui::ShadNetNotify::Kind::Online, n.npid + " is online");
|
||||
}
|
||||
std::lock_guard lock(m_mutex_friend_state);
|
||||
auto& st = m_friend_state[user_id];
|
||||
auto it = std::find_if(st.friends.begin(), st.friends.end(),
|
||||
[&](const FriendInfo& f) { return f.npid == n.npid; });
|
||||
if (it != st.friends.end()) {
|
||||
it->online = n.online;
|
||||
} else {
|
||||
st.friends.push_back({n.npid, n.online});
|
||||
}
|
||||
}
|
||||
|
||||
void NpHandler::OnLoginResult(s32 user_id, const ShadNet::LoginResult& res) {
|
||||
if (res.error != ShadNet::ErrorType::NoError) {
|
||||
return;
|
||||
}
|
||||
FriendListSnapshot snap;
|
||||
snap.friends.reserve(res.friends.size());
|
||||
for (const auto& f : res.friends) {
|
||||
snap.friends.push_back({f.npid, f.online});
|
||||
}
|
||||
snap.requests_sent = res.requestsSent;
|
||||
snap.requests_received = res.requestsReceived;
|
||||
snap.blocked = res.blocked;
|
||||
{
|
||||
std::lock_guard lock(m_mutex_friend_state);
|
||||
m_friend_state[user_id] = std::move(snap);
|
||||
}
|
||||
LOG_INFO(NpHandler,
|
||||
"user_id={} friend state seeded: {} friends, {} received, {} sent, {} blocked",
|
||||
user_id, res.friends.size(), res.requestsReceived.size(), res.requestsSent.size(),
|
||||
res.blocked.size());
|
||||
|
||||
// Surface friend requests that arrived while offline (no live notification fires for these).
|
||||
if (!res.requestsReceived.empty()) {
|
||||
ImGui::ShadNetNotify::Push(ImGui::ShadNetNotify::Kind::FriendRequest,
|
||||
std::to_string(res.requestsReceived.size()) +
|
||||
" pending friend request(s)");
|
||||
}
|
||||
}
|
||||
|
||||
NpHandler::FriendListSnapshot NpHandler::GetFriendList(s32 user_id) const {
|
||||
std::lock_guard lock(m_mutex_friend_state);
|
||||
const auto it = m_friend_state.find(user_id);
|
||||
return it == m_friend_state.end() ? FriendListSnapshot{} : it->second;
|
||||
}
|
||||
|
||||
s32 NpHandler::SendFriendRequest(s32 user_id, const std::string& npid) {
|
||||
std::shared_ptr<ShadNet::ShadNetClient> client;
|
||||
{
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
const auto it = m_clients.find(user_id);
|
||||
if (it == m_clients.end()) {
|
||||
return ORBIS_NP_ERROR_SIGNED_OUT;
|
||||
}
|
||||
client = it->second;
|
||||
}
|
||||
if (!client->IsAuthenticated()) {
|
||||
return ORBIS_NP_ERROR_SIGNED_OUT;
|
||||
}
|
||||
client->AddFriend(npid);
|
||||
LOG_INFO(NpHandler, "user_id={} AddFriend '{}'", user_id, npid);
|
||||
|
||||
std::lock_guard lock(m_mutex_friend_state);
|
||||
auto& st = m_friend_state[user_id];
|
||||
const bool accepting = std::find(st.requests_received.begin(), st.requests_received.end(),
|
||||
npid) != st.requests_received.end();
|
||||
if (!accepting && std::find(st.requests_sent.begin(), st.requests_sent.end(), npid) ==
|
||||
st.requests_sent.end()) {
|
||||
st.requests_sent.push_back(npid);
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 NpHandler::RemoveFriend(s32 user_id, const std::string& npid) {
|
||||
std::shared_ptr<ShadNet::ShadNetClient> client;
|
||||
{
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
const auto it = m_clients.find(user_id);
|
||||
if (it == m_clients.end()) {
|
||||
return ORBIS_NP_ERROR_SIGNED_OUT;
|
||||
}
|
||||
client = it->second;
|
||||
}
|
||||
if (!client->IsAuthenticated()) {
|
||||
return ORBIS_NP_ERROR_SIGNED_OUT;
|
||||
}
|
||||
client->RemoveFriend(npid);
|
||||
LOG_INFO(NpHandler, "user_id={} RemoveFriend '{}'", user_id, npid);
|
||||
|
||||
std::lock_guard lock(m_mutex_friend_state);
|
||||
auto& st = m_friend_state[user_id];
|
||||
st.friends.erase(std::remove_if(st.friends.begin(), st.friends.end(),
|
||||
[&](const FriendInfo& f) { return f.npid == npid; }),
|
||||
st.friends.end());
|
||||
const auto drop = [&](std::vector<std::string>& v) {
|
||||
v.erase(std::remove(v.begin(), v.end(), npid), v.end());
|
||||
};
|
||||
drop(st.requests_received);
|
||||
drop(st.requests_sent);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 NpHandler::BlockUser(s32 user_id, const std::string& npid) {
|
||||
std::shared_ptr<ShadNet::ShadNetClient> client;
|
||||
{
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
const auto it = m_clients.find(user_id);
|
||||
if (it == m_clients.end()) {
|
||||
return ORBIS_NP_ERROR_SIGNED_OUT;
|
||||
}
|
||||
client = it->second;
|
||||
}
|
||||
if (!client->IsAuthenticated()) {
|
||||
return ORBIS_NP_ERROR_SIGNED_OUT;
|
||||
}
|
||||
client->AddBlock(npid);
|
||||
LOG_INFO(NpHandler, "user_id={} AddBlock '{}'", user_id, npid);
|
||||
|
||||
std::lock_guard lock(m_mutex_friend_state);
|
||||
auto& st = m_friend_state[user_id];
|
||||
if (std::find(st.blocked.begin(), st.blocked.end(), npid) == st.blocked.end()) {
|
||||
st.blocked.push_back(npid);
|
||||
}
|
||||
st.friends.erase(std::remove_if(st.friends.begin(), st.friends.end(),
|
||||
[&](const FriendInfo& f) { return f.npid == npid; }),
|
||||
st.friends.end());
|
||||
const auto drop = [&](std::vector<std::string>& v) {
|
||||
v.erase(std::remove(v.begin(), v.end(), npid), v.end());
|
||||
};
|
||||
drop(st.requests_received);
|
||||
drop(st.requests_sent);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 NpHandler::UnblockUser(s32 user_id, const std::string& npid) {
|
||||
std::shared_ptr<ShadNet::ShadNetClient> client;
|
||||
{
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
const auto it = m_clients.find(user_id);
|
||||
if (it == m_clients.end()) {
|
||||
return ORBIS_NP_ERROR_SIGNED_OUT;
|
||||
}
|
||||
client = it->second;
|
||||
}
|
||||
if (!client->IsAuthenticated()) {
|
||||
return ORBIS_NP_ERROR_SIGNED_OUT;
|
||||
}
|
||||
client->RemoveBlock(npid);
|
||||
LOG_INFO(NpHandler, "user_id={} RemoveBlock '{}'", user_id, npid);
|
||||
|
||||
std::lock_guard lock(m_mutex_friend_state);
|
||||
auto& st = m_friend_state[user_id];
|
||||
st.blocked.erase(std::remove(st.blocked.begin(), st.blocked.end(), npid), st.blocked.end());
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
} // namespace Libraries::Np
|
||||
@@ -0,0 +1,221 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019-2026 rpcs3 Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <httplib.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/invitation_dialog/invitation_dialog.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/system/systemservice.h"
|
||||
#include "imgui/invitation_prompt_layer.h"
|
||||
|
||||
namespace Libraries::Np {
|
||||
|
||||
// Minimal JSON string escaper for the invitation-request body (npids are safe but the user message
|
||||
// can contain quotes/backslashes/control chars).
|
||||
std::string JsonEscape(const std::string& in) {
|
||||
std::string out;
|
||||
out.reserve(in.size() + 8);
|
||||
for (const char c : in) {
|
||||
switch (c) {
|
||||
case '"':
|
||||
out += "\\\"";
|
||||
break;
|
||||
case '\\':
|
||||
out += "\\\\";
|
||||
break;
|
||||
case '\n':
|
||||
out += "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
out += "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
out += "\\t";
|
||||
break;
|
||||
default:
|
||||
if (static_cast<unsigned char>(c) < 0x20) {
|
||||
char buf[8];
|
||||
std::snprintf(buf, sizeof(buf), "\\u%04x", c);
|
||||
out += buf;
|
||||
} else {
|
||||
out += c;
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool NpHandler::SendSessionInvitation(s32 user_id, const std::string& session_id,
|
||||
const std::vector<std::string>& to,
|
||||
const std::string& message) {
|
||||
if (session_id.empty() || to.empty()) {
|
||||
LOG_ERROR(NpHandler, "empty session_id or recipient list");
|
||||
return false;
|
||||
}
|
||||
const std::string base_url = EmulatorSettings.GetShadNetWebApiServer();
|
||||
if (base_url.empty()) {
|
||||
LOG_ERROR(NpHandler, "WebAPI server address is empty");
|
||||
return false;
|
||||
}
|
||||
const std::string token = GetBearerToken(user_id);
|
||||
if (token.empty()) {
|
||||
LOG_ERROR(NpHandler, "no bearer token for user_id={}", user_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
// invitation-request JSON: {"to":[...],"message":"..."}
|
||||
std::string json = "{\"to\":[";
|
||||
for (size_t i = 0; i < to.size(); ++i) {
|
||||
if (i != 0) {
|
||||
json += ',';
|
||||
}
|
||||
json += '"';
|
||||
json += JsonEscape(to[i]);
|
||||
json += '"';
|
||||
}
|
||||
json += ']';
|
||||
if (!message.empty()) {
|
||||
json += ",\"message\":\"";
|
||||
json += JsonEscape(message);
|
||||
json += '"';
|
||||
}
|
||||
json += '}';
|
||||
|
||||
// multipart/mixed with a single JSON part; shadNet keys on the Content-Description.
|
||||
const std::string boundary = "shadps4invite" + std::to_string(user_id);
|
||||
std::string body;
|
||||
body += "--" + boundary + "\r\n";
|
||||
body += "Content-Type: application/json; charset=utf-8\r\n";
|
||||
body += "Content-Description: invitation-request\r\n\r\n";
|
||||
body += json;
|
||||
body += "\r\n--" + boundary + "--\r\n";
|
||||
|
||||
const std::string path = "/v1/sessions/" + session_id + "/invitations";
|
||||
const std::string content_type = "multipart/mixed; boundary=" + boundary;
|
||||
|
||||
httplib::Client cli(base_url);
|
||||
cli.set_connection_timeout(5);
|
||||
cli.set_read_timeout(10);
|
||||
const httplib::Headers headers = {{"Authorization", "Bearer " + token}};
|
||||
const auto res = cli.Post(path.c_str(), headers, body, content_type.c_str());
|
||||
if (!res) {
|
||||
LOG_ERROR(NpHandler, "POST {} failed (no response)", path);
|
||||
return false;
|
||||
}
|
||||
if (res->status != 200 && res->status != 204) {
|
||||
LOG_ERROR(NpHandler, "POST {} -> HTTP {}", path, res->status);
|
||||
return false;
|
||||
}
|
||||
LOG_INFO(NpHandler, "sent invite to {} recipient(s) for session '{}'", to.size(), session_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NpHandler::PostSessionInvitationEvent(s32 user_id, const std::string& session_id,
|
||||
const std::string& invitation_id,
|
||||
const std::string& accepter_online_id,
|
||||
const std::string& inviter_online_id,
|
||||
OrbisNpAccountId inviter_account_id) {
|
||||
using Libraries::InvitationDialog::ORBIS_NP_SESSION_INVITATION_EVENT_FLAG_INVITATION;
|
||||
using Libraries::InvitationDialog::OrbisNpSessionInvitationEventParam;
|
||||
|
||||
Libraries::SystemService::OrbisSystemServiceEvent event{};
|
||||
event.event_type = Libraries::SystemService::OrbisSystemServiceEventType::SessionInvitation;
|
||||
|
||||
auto* param = reinterpret_cast<OrbisNpSessionInvitationEventParam*>(event.param);
|
||||
std::memset(param, 0, sizeof(*param));
|
||||
std::strncpy(param->sessionId.data, session_id.c_str(), sizeof(param->sessionId.data) - 1);
|
||||
if (!invitation_id.empty()) {
|
||||
std::strncpy(param->invitationId.data, invitation_id.c_str(),
|
||||
sizeof(param->invitationId.data) - 1);
|
||||
param->flag = ORBIS_NP_SESSION_INVITATION_EVENT_FLAG_INVITATION;
|
||||
} else {
|
||||
param->flag = 0; // join from session info (no invitation id in the push)
|
||||
}
|
||||
// data[16] is followed by its own 'term' byte, so a full 16-char online id must not be cut.
|
||||
std::strncpy(param->onlineId.data, accepter_online_id.c_str(), sizeof(param->onlineId.data));
|
||||
param->userId = user_id;
|
||||
std::strncpy(param->referralOnlineId.data, inviter_online_id.c_str(),
|
||||
sizeof(param->referralOnlineId.data));
|
||||
param->referralAccountId = inviter_account_id;
|
||||
|
||||
Libraries::SystemService::PushSystemServiceEvent(event);
|
||||
LOG_INFO(NpHandler,
|
||||
"Posted SESSION_INVITATION user_id={} session='{}' invitation='{}' flag={} "
|
||||
"onlineId='{}' referral='{}'({})",
|
||||
user_id, session_id, invitation_id, param->flag, accepter_online_id, inviter_online_id,
|
||||
inviter_account_id);
|
||||
}
|
||||
|
||||
std::vector<NpHandler::PendingInvitation> NpHandler::GetPendingInvitations(s32 user_id) const {
|
||||
std::lock_guard lk(m_mutex_pending_invites);
|
||||
const auto it = m_pending_invites.find(user_id);
|
||||
if (it == m_pending_invites.end()) {
|
||||
return {};
|
||||
}
|
||||
const auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
std::vector<PendingInvitation> out;
|
||||
out.reserve(it->second.size());
|
||||
for (const auto& p : it->second) {
|
||||
if (p.valid_until == 0 || now <= p.valid_until) {
|
||||
out.push_back(p);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool NpHandler::AcceptSessionInvitation(s32 user_id, const std::string& invitation_id) {
|
||||
// Pull the matching stashed invite (and drop it).
|
||||
PendingInvitation inv;
|
||||
bool found = false;
|
||||
{
|
||||
std::lock_guard lk(m_mutex_pending_invites);
|
||||
const auto mit = m_pending_invites.find(user_id);
|
||||
if (mit != m_pending_invites.end()) {
|
||||
auto& v = mit->second;
|
||||
for (auto it = v.begin(); it != v.end(); ++it) {
|
||||
if (it->invitation_id == invitation_id) {
|
||||
inv = *it;
|
||||
v.erase(it);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Whatever surface handled it (RECV dialog or the emulator prompt), retire the other one.
|
||||
ImGui::InvitationPrompt::Dismiss(invitation_id);
|
||||
if (!found) {
|
||||
LOG_ERROR(NpHandler, "no pending invite '{}' for user_id={}", invitation_id, user_id);
|
||||
return false;
|
||||
}
|
||||
// Raise the join event now that the user has explicitly accepted (via the RECV dialog or the
|
||||
// emulator's system-UI equivalent).
|
||||
PostSessionInvitationEvent(user_id, inv.session_id, invitation_id, inv.to_npid, inv.from_npid,
|
||||
inv.from_account_id);
|
||||
LOG_INFO(NpHandler, "accepted '{}' session='{}'", invitation_id, inv.session_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NpHandler::DeclineSessionInvitation(s32 user_id, const std::string& invitation_id) {
|
||||
ImGui::InvitationPrompt::Dismiss(invitation_id);
|
||||
std::lock_guard lock(m_mutex_pending_invites);
|
||||
auto uit = m_pending_invites.find(user_id);
|
||||
if (uit == m_pending_invites.end()) {
|
||||
return;
|
||||
}
|
||||
auto& v = uit->second;
|
||||
v.erase(std::remove_if(
|
||||
v.begin(), v.end(),
|
||||
[&](const PendingInvitation& p) { return p.invitation_id == invitation_id; }),
|
||||
v.end());
|
||||
LOG_INFO(NpHandler, "dismissed '{}' for user_id={}", invitation_id, user_id);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Np
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,149 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019-2026 rpcs3 Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "shadnet.pb.h"
|
||||
|
||||
namespace Libraries::Np {
|
||||
|
||||
void NpHandler::ReportTrophyUnlock(s32 user_id, s32 service_label, s32 trophy_id, u64 timestamp) {
|
||||
std::shared_ptr<ShadNet::ShadNetClient> client;
|
||||
{
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
auto it = m_clients.find(user_id);
|
||||
if (it == m_clients.end()) {
|
||||
LOG_DEBUG(NpHandler, "no shadNet session for user_id={}; skipping", user_id);
|
||||
return;
|
||||
}
|
||||
client = it->second;
|
||||
}
|
||||
if (!client) {
|
||||
LOG_DEBUG(NpHandler, "null client for user_id={}; skipping", user_id);
|
||||
return;
|
||||
}
|
||||
if (!client->IsAuthenticated()) {
|
||||
LOG_WARNING(NpHandler, "not authenticated; trophy {} not mirrored", trophy_id);
|
||||
return;
|
||||
}
|
||||
if (!client->IsTrophiesEnabled()) {
|
||||
LOG_WARNING(NpHandler,
|
||||
"server has not advertised trophy support; trophy {} "
|
||||
"not mirrored",
|
||||
trophy_id);
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string com_id = GetNpCommId(service_label);
|
||||
if (!IsValidNpCommId(com_id)) {
|
||||
LOG_WARNING(NpHandler,
|
||||
"no valid NP Communication ID for service_label={}; "
|
||||
"skipping trophy {}",
|
||||
service_label, trophy_id);
|
||||
return;
|
||||
}
|
||||
|
||||
const u64 pkt_id = client->UnlockTrophy(com_id, trophy_id, timestamp);
|
||||
LOG_INFO(NpHandler, "user_id={} trophy={} com_id='{}' pkt_id={}", user_id, trophy_id, com_id,
|
||||
pkt_id);
|
||||
}
|
||||
|
||||
void NpHandler::SyncTrophies(
|
||||
s32 user_id, s32 service_label, const std::vector<std::pair<s32, u64>>& local_trophies,
|
||||
std::function<void(const std::vector<std::pair<s32, u64>>&)> on_merged) {
|
||||
std::shared_ptr<ShadNet::ShadNetClient> client;
|
||||
{
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
auto it = m_clients.find(user_id);
|
||||
if (it == m_clients.end()) {
|
||||
LOG_DEBUG(NpHandler, "no shadNet session for user_id={}; skipping", user_id);
|
||||
return;
|
||||
}
|
||||
client = it->second;
|
||||
}
|
||||
if (!client) {
|
||||
LOG_DEBUG(NpHandler, "null client for user_id={}; skipping", user_id);
|
||||
return;
|
||||
}
|
||||
if (!client->IsAuthenticated()) {
|
||||
LOG_WARNING(NpHandler, "not authenticated; skipping sync");
|
||||
return;
|
||||
}
|
||||
if (!client->IsTrophiesEnabled()) {
|
||||
LOG_WARNING(NpHandler, "server has not advertised trophy support; "
|
||||
"skipping sync");
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string com_id = GetNpCommId(service_label);
|
||||
if (!IsValidNpCommId(com_id)) {
|
||||
LOG_WARNING(NpHandler, "no valid NP Communication ID for service_label={}; skipping",
|
||||
service_label);
|
||||
return;
|
||||
}
|
||||
|
||||
const u64 pkt_id = client->SyncTrophies(com_id, local_trophies);
|
||||
if (on_merged) {
|
||||
std::lock_guard lock(m_mutex_pending_trophy);
|
||||
m_pending_trophy.emplace(pkt_id, std::move(on_merged));
|
||||
}
|
||||
LOG_INFO(NpHandler, "user_id={} uploading {} com_id='{}' pkt_id={}", user_id,
|
||||
local_trophies.size(), com_id, pkt_id);
|
||||
}
|
||||
|
||||
void NpHandler::OnTrophyReply(s32 user_id, ShadNet::CommandType cmd, u64 pkt_id,
|
||||
ShadNet::ErrorType error, const std::vector<u8>& body) {
|
||||
if (cmd == ShadNet::CommandType::UnlockTrophy) {
|
||||
if (error != ShadNet::ErrorType::NoError) {
|
||||
LOG_WARNING(NpHandler, "UnlockTrophy rejected by server: error={}",
|
||||
static_cast<int>(error));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (cmd != ShadNet::CommandType::SyncTrophies)
|
||||
return;
|
||||
|
||||
std::function<void(const std::vector<std::pair<s32, u64>>&)> on_merged;
|
||||
{
|
||||
std::lock_guard lock(m_mutex_pending_trophy);
|
||||
auto it = m_pending_trophy.find(pkt_id);
|
||||
if (it == m_pending_trophy.end())
|
||||
return;
|
||||
on_merged = std::move(it->second);
|
||||
m_pending_trophy.erase(it);
|
||||
}
|
||||
|
||||
if (error != ShadNet::ErrorType::NoError) {
|
||||
LOG_WARNING(NpHandler, "SyncTrophies failed: error={}", static_cast<int>(error));
|
||||
return; // leave local trophies exactly as they were
|
||||
}
|
||||
|
||||
// Reply body is u32 LE blob size followed by the proto.
|
||||
if (body.size() < 4) {
|
||||
LOG_WARNING(NpHandler, "SyncTrophies: truncated reply");
|
||||
return;
|
||||
}
|
||||
const u32 blob_sz = static_cast<u32>(body[0]) | (static_cast<u32>(body[1]) << 8) |
|
||||
(static_cast<u32>(body[2]) << 16) | (static_cast<u32>(body[3]) << 24);
|
||||
if (body.size() < 4 + blob_sz) {
|
||||
LOG_WARNING(NpHandler, "SyncTrophies: reply shorter than declared blob");
|
||||
return;
|
||||
}
|
||||
|
||||
shadnet::SyncTrophiesReply pb;
|
||||
if (!pb.ParseFromArray(body.data() + 4, static_cast<int>(blob_sz))) {
|
||||
LOG_WARNING(NpHandler, "could not parse reply");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::pair<s32, u64>> merged;
|
||||
merged.reserve(pb.trophies_size());
|
||||
for (const auto& t : pb.trophies())
|
||||
merged.emplace_back(t.trophyid(), t.timestamp());
|
||||
|
||||
LOG_INFO(NpHandler, "server holds {} trophies for user_id={}", merged.size(), user_id);
|
||||
on_merged(merged);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Np
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,87 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019-2026 rpcs3 Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_web_api/np_web_api.h"
|
||||
#include "imgui/invitation_prompt_layer.h"
|
||||
|
||||
namespace Libraries::Np {
|
||||
|
||||
void NpHandler::OnWebApiPushEvent(s32 user_id, const ShadNet::NotifyWebApiPushEvent& n) {
|
||||
LOG_INFO(NpHandler, "user_id={} WebApiPushEvent svc='{}' type='{}' bytes={}", user_id,
|
||||
n.npServiceName, n.dataType, n.data.size());
|
||||
NpWebApi::PushEventInput ev;
|
||||
ev.targetUserId = user_id;
|
||||
ev.npServiceName = n.npServiceName;
|
||||
ev.npServiceLabel = n.npServiceLabel;
|
||||
ev.dataType = n.dataType;
|
||||
ev.data = n.data;
|
||||
if (!n.fromNpid.empty()) {
|
||||
ev.hasFrom = true;
|
||||
SetNpOnlineId(ev.fromOnlineId, n.fromNpid);
|
||||
}
|
||||
if (!n.toNpid.empty()) {
|
||||
ev.hasTo = true;
|
||||
SetNpOnlineId(ev.toOnlineId, n.toNpid);
|
||||
}
|
||||
ev.extdData = n.extdData; // extended-data (key,value) pairs -> dispatched as pExtdData
|
||||
// Account ids for pFrom/pTo.
|
||||
ev.toAccountId = n.toAccountId != 0 ? n.toAccountId : GetAccountId(user_id);
|
||||
ev.fromAccountId = n.fromAccountId;
|
||||
if (ev.fromAccountId == 0) {
|
||||
for (const auto& kv : n.extdData) {
|
||||
if (kv.first == "fromAccountId") {
|
||||
ev.fromAccountId = std::strtoull(kv.second.c_str(), nullptr, 10);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ev.toAccountId != 0) {
|
||||
ev.hasTo = true;
|
||||
if (n.toNpid.empty()) {
|
||||
// e.g. friendlist events carry no toNpid; the recipient is still us.
|
||||
std::lock_guard lock(m_mutex_clients);
|
||||
if (const auto it = m_np_ids.find(user_id); it != m_np_ids.end()) {
|
||||
std::memcpy(ev.toOnlineId.data, it->second.handle.data, sizeof(ev.toOnlineId.data));
|
||||
}
|
||||
}
|
||||
}
|
||||
NpWebApi::EnqueuePushEvent(ev);
|
||||
|
||||
// Also surface a SESSION_INVITATION system-service event for titles that watch it instead of
|
||||
// (or in addition to) the WebAPI push callback
|
||||
if (n.npServiceName == "sessionInvitation") {
|
||||
std::string session_id, invitation_id;
|
||||
int64_t valid_until = 0;
|
||||
for (const auto& kv : n.extdData) {
|
||||
if (kv.first == "sessionId") {
|
||||
session_id = kv.second;
|
||||
} else if (kv.first == "invitationId") {
|
||||
invitation_id = kv.second;
|
||||
} else if (kv.first == "validUntil") {
|
||||
valid_until = std::strtoll(kv.second.c_str(), nullptr, 10);
|
||||
}
|
||||
}
|
||||
if (!session_id.empty()) {
|
||||
{
|
||||
std::lock_guard lk(m_mutex_pending_invites);
|
||||
auto& v = m_pending_invites[user_id];
|
||||
v.erase(std::remove_if(v.begin(), v.end(),
|
||||
[&](const PendingInvitation& p) {
|
||||
return p.invitation_id == invitation_id;
|
||||
}),
|
||||
v.end());
|
||||
PendingInvitation inv{session_id, invitation_id, n.fromNpid, n.toNpid, valid_until};
|
||||
inv.from_account_id = ev.fromAccountId;
|
||||
v.push_back(std::move(inv));
|
||||
}
|
||||
ImGui::InvitationPrompt::Push(user_id, invitation_id, session_id, n.fromNpid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Libraries::Np
|
||||
@@ -15,11 +15,11 @@
|
||||
#include "core/libraries/kernel/process.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_manager.h"
|
||||
#include "core/tls.h"
|
||||
#include "core/user_manager.h"
|
||||
#include "core/user_settings.h"
|
||||
#include "np_handler.h"
|
||||
|
||||
namespace Libraries::Np::NpManager {
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/net/net.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/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"
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "core/libraries/net/net.h"
|
||||
#include "core/libraries/np/np_common.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_matching2/np_matching2_internal.h"
|
||||
#include "shadnet.pb.h"
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "core/libraries/net/net_p2p.h"
|
||||
#include "core/libraries/net/net_upnp.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_matching2/np_matching2_internal.h"
|
||||
#include "core/libraries/np/np_matching2/np_matching2_mm.h"
|
||||
#include "core/libraries/np/np_matching2/np_matching2_signaling.h"
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "core/libraries/net/net_util.h"
|
||||
#include "core/libraries/network/netctl.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_matching2/np_matching2_internal.h"
|
||||
#include "core/libraries/np/np_matching2/np_matching2_mm.h"
|
||||
#include "core/libraries/np/np_matching2/np_matching2_signaling.h"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "core/libraries/kernel/process.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_score/np_score.h"
|
||||
#include "core/libraries/np/np_score/np_score_ctx.h"
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "core/libraries/net/net_util.h"
|
||||
#include "core/libraries/network/netctl.h"
|
||||
#include "core/libraries/np/np_common.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_manager.h"
|
||||
#include "core/libraries/np/np_signaling/np_signaling.h"
|
||||
#include "core/libraries/np/np_signaling/np_signaling_helpers.h"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "core/libraries/net/net_util.h"
|
||||
#include "core/libraries/np/np_common.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_signaling/np_signaling.h"
|
||||
#include "core/libraries/np/np_signaling/np_signaling_helpers.h"
|
||||
#include "core/libraries/np/np_signaling/np_signaling_state.h"
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "core/libraries/net/net.h"
|
||||
#include "core/libraries/net/net_util.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_signaling/np_signaling_state.h"
|
||||
#include "core/libraries/np/np_signaling/np_signaling_transport.h"
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_trophy.h"
|
||||
#include "core/libraries/np/trophy_ui.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_manager.h"
|
||||
#include "core/libraries/np/np_tus/np_tus.h"
|
||||
#include "core/libraries/np/np_types.h"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_utility/np_utility.h"
|
||||
#include "core/libraries/np/np_utility/np_utility_ctx.h"
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "core/libraries/kernel/time.h"
|
||||
#include "core/libraries/network/http.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "np_web_api_internal.h"
|
||||
|
||||
namespace Libraries::Np::NpWebApi {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "core/libraries/network/http2.h"
|
||||
#include "core/libraries/np/np_common.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_web_api2/np_web_api2_context.h"
|
||||
|
||||
namespace Libraries::Np::NpWebApi2 {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "core/libraries/net/net.h"
|
||||
#include "core/libraries/net/net_util.h"
|
||||
#include "core/libraries/np/np_error.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_matching2/np_matching2_internal.h"
|
||||
#include "core/libraries/np/np_matching2/np_matching2_signaling.h"
|
||||
#include "core/libraries/np/np_signaling/np_signaling_state.h"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "common/singleton.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_manager.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
#include "core/libraries/system/userservice_error.h"
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "core/libraries/np/np_trophy.h"
|
||||
#include "core/libraries/save_data/save_backup.h"
|
||||
#include "core/linker.h"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "imgui/friends_layer.h"
|
||||
#include "imgui/imgui_layer.h"
|
||||
#include "imgui/shadnet_notifications_layer.h"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/np/np_handler.h"
|
||||
#include "core/libraries/np/np_handler/np_handler.h"
|
||||
#include "imgui/imgui_layer.h"
|
||||
#include "imgui/invitation_prompt_layer.h"
|
||||
#include "imgui/renderer/imgui_core.h"
|
||||
|
||||
Reference in New Issue
Block a user