RPCN v1.4

This commit is contained in:
RipleyTom 2025-02-05 20:28:08 +01:00
parent cd87a64621
commit 0ad81094fe
No known key found for this signature in database
GPG key ID: FC2B5DEF76BF4CC8
41 changed files with 2150 additions and 1197 deletions

View file

@ -462,6 +462,7 @@ target_sources(rpcs3_emu PRIVATE
NP/rpcn_countries.cpp
NP/upnp_config.cpp
NP/upnp_handler.cpp
NP/ip_address.cpp
)
# Memory

View file

@ -1,5 +1,6 @@
#pragma once
#include "Emu/NP/rpcn_types.h"
#include "cellRtc.h"
#include "Emu/Cell/ErrorCodes.h"
#include "util/shared_ptr.hpp"
@ -1826,7 +1827,7 @@ public:
virtual ~SendMessageDialogBase() = default;
virtual error_code Exec(message_data& msg_data, std::set<std::string>& npids) = 0;
virtual void callback_handler(u16 ntype, const std::string& username, bool status) = 0;
virtual void callback_handler(rpcn::NotificationType ntype, const std::string& username, bool status) = 0;
protected:
std::shared_ptr<rpcn::rpcn_client> m_rpcn;

View file

@ -23,7 +23,6 @@
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#ifdef __clang__
#pragma GCC diagnostic pop

View file

@ -35,7 +35,7 @@ std::size_t lv2_socket::get_queue_size() const
}
socket_type lv2_socket::get_socket() const
{
return socket;
return native_socket;
}
#ifdef _WIN32

View file

@ -6,6 +6,7 @@
#include "Utilities/mutex.h"
#include "Emu/IdManager.h"
#include "Emu/Cell/lv2/sys_net.h"
#include "Emu/NP/ip_address.h"
#ifdef _WIN32
#include <winsock2.h>
@ -21,12 +22,6 @@
#endif
#endif
#ifdef _WIN32
using socket_type = uptr;
#else
using socket_type = int;
#endif
enum class thread_state : u32;
class lv2_socket
@ -126,7 +121,7 @@ protected:
shared_mutex mutex;
s32 lv2_id = 0;
socket_type socket = 0;
socket_type native_socket = 0;
lv2_socket_family family{};
lv2_socket_type type{};

View file

@ -87,9 +87,9 @@ s32 lv2_socket_native::create_socket()
return CELL_OK;
}
void lv2_socket_native::set_socket(socket_type socket, lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol)
void lv2_socket_native::set_socket(socket_type native_socket, lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol)
{
this->socket = socket;
this->native_socket = native_socket;
this->family = family;
this->type = type;
this->protocol = protocol;
@ -115,12 +115,12 @@ std::tuple<bool, s32, shared_ptr<lv2_socket>, sys_net_sockaddr> lv2_socket_nativ
sys_net.error("Calling socket::accept() from a previously connected socket!");
}
socket_type native_socket = ::accept(socket, reinterpret_cast<struct sockaddr*>(&native_addr), &native_addrlen);
socket_type client_socket = ::accept(native_socket, reinterpret_cast<struct sockaddr*>(&native_addr), &native_addrlen);
if (native_socket != invalid_socket)
if (client_socket != invalid_socket)
{
auto newsock = make_single<lv2_socket_native>(family, type, protocol);
newsock->set_socket(native_socket, family, type, protocol);
newsock->set_socket(client_socket, family, type, protocol);
// Sockets inherit non blocking behaviour from their parent
newsock->so_nbio = so_nbio;
@ -173,7 +173,7 @@ s32 lv2_socket_native::bind(const sys_net_sockaddr& addr)
sys_net.warning("[Native] Trying to bind %s:%d", native_addr.sin_addr, std::bit_cast<be_t<u16>, u16>(native_addr.sin_port));
if (::bind(socket, reinterpret_cast<struct sockaddr*>(&native_addr), native_addr_len) == 0)
if (::bind(native_socket, reinterpret_cast<struct sockaddr*>(&native_addr), native_addr_len) == 0)
{
// Only UPNP port forward binds to 0.0.0.0
if (saddr == 0)
@ -182,7 +182,7 @@ s32 lv2_socket_native::bind(const sys_net_sockaddr& addr)
{
sockaddr_in client_addr;
socklen_t client_addr_size = sizeof(client_addr);
ensure(::getsockname(socket, reinterpret_cast<struct sockaddr*>(&client_addr), &client_addr_size) == 0);
ensure(::getsockname(native_socket, reinterpret_cast<struct sockaddr*>(&client_addr), &client_addr_size) == 0);
bound_port = std::bit_cast<u16, be_t<u16>>(client_addr.sin_port);
}
else
@ -245,7 +245,7 @@ std::optional<s32> lv2_socket_native::connect(const sys_net_sockaddr& addr)
return -SYS_NET_EALREADY;
}
if (::connect(socket, reinterpret_cast<struct sockaddr*>(&native_addr), native_addr_len) == 0)
if (::connect(native_socket, reinterpret_cast<struct sockaddr*>(&native_addr), native_addr_len) == 0)
{
return CELL_OK;
}
@ -272,7 +272,7 @@ std::optional<s32> lv2_socket_native::connect(const sys_net_sockaddr& addr)
{
int native_error;
::socklen_t size = sizeof(native_error);
if (::getsockopt(socket, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&native_error), &size) != 0 || size != sizeof(int))
if (::getsockopt(native_socket, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&native_error), &size) != 0 || size != sizeof(int))
{
so_error = 1;
}
@ -304,7 +304,7 @@ s32 lv2_socket_native::connect_followup()
{
int native_error;
::socklen_t size = sizeof(native_error);
if (::getsockopt(socket, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&native_error), &size) != 0 || size != sizeof(int))
if (::getsockopt(native_socket, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&native_error), &size) != 0 || size != sizeof(int))
{
return -1;
}
@ -320,7 +320,7 @@ std::pair<s32, sys_net_sockaddr> lv2_socket_native::getpeername()
::sockaddr_storage native_addr;
::socklen_t native_addrlen = sizeof(native_addr);
if (::getpeername(socket, reinterpret_cast<struct sockaddr*>(&native_addr), &native_addrlen) == 0)
if (::getpeername(native_socket, reinterpret_cast<struct sockaddr*>(&native_addr), &native_addrlen) == 0)
{
ensure(native_addr.ss_family == AF_INET);
@ -339,7 +339,7 @@ std::pair<s32, sys_net_sockaddr> lv2_socket_native::getsockname()
::sockaddr_storage native_addr;
::socklen_t native_addrlen = sizeof(native_addr);
if (::getsockname(socket, reinterpret_cast<struct sockaddr*>(&native_addr), &native_addrlen) == 0)
if (::getsockname(native_socket, reinterpret_cast<struct sockaddr*>(&native_addr), &native_addrlen) == 0)
{
ensure(native_addr.ss_family == AF_INET);
@ -581,7 +581,7 @@ std::tuple<s32, lv2_socket::sockopt_data, u32> lv2_socket_native::getsockopt(s32
return {-SYS_NET_EINVAL, {}, {}};
}
if (::getsockopt(socket, native_level, native_opt, native_val.ch, &native_len) != 0)
if (::getsockopt(native_socket, native_level, native_opt, native_val.ch, &native_len) != 0)
{
return {-get_last_error(false), {}, {}};
}
@ -864,7 +864,7 @@ s32 lv2_socket_native::setsockopt(s32 level, s32 optname, const std::vector<u8>&
return -SYS_NET_EINVAL;
}
if (::setsockopt(socket, native_level, native_opt, static_cast<const char*>(native_val), native_len) == 0)
if (::setsockopt(native_socket, native_level, native_opt, static_cast<const char*>(native_val), native_len) == 0)
{
return {};
}
@ -876,7 +876,7 @@ s32 lv2_socket_native::listen(s32 backlog)
{
std::lock_guard lock(mutex);
if (::listen(socket, backlog) == 0)
if (::listen(native_socket, backlog) == 0)
{
return CELL_OK;
}
@ -930,7 +930,7 @@ std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>> lv2_socket_nat
native_flags |= MSG_WAITALL;
}
auto native_result = ::recvfrom(socket, reinterpret_cast<char*>(res_buf.data()), len, native_flags, reinterpret_cast<struct sockaddr*>(&native_addr), &native_addrlen);
auto native_result = ::recvfrom(native_socket, reinterpret_cast<char*>(res_buf.data()), len, native_flags, reinterpret_cast<struct sockaddr*>(&native_addr), &native_addrlen);
if (native_result >= 0)
{
@ -1021,7 +1021,7 @@ std::optional<s32> lv2_socket_native::sendto(s32 flags, const std::vector<u8>& b
}
}
native_result = ::sendto(socket, reinterpret_cast<const char*>(buf.data()), ::narrow<int>(buf.size()), native_flags, native_addr ? reinterpret_cast<struct sockaddr*>(&native_addr.value()) : nullptr, native_addr ? sizeof(sockaddr_in) : 0);
native_result = ::sendto(native_socket, reinterpret_cast<const char*>(buf.data()), ::narrow<int>(buf.size()), native_flags, native_addr ? reinterpret_cast<struct sockaddr*>(&native_addr.value()) : nullptr, native_addr ? sizeof(sockaddr_in) : 0);
if (native_result >= 0)
{
@ -1075,7 +1075,7 @@ std::optional<s32> lv2_socket_native::sendmsg(s32 flags, const sys_net_msghdr& m
const u32 len = msg.msg_iov[i].iov_len;
const std::vector<u8> buf_copy(vm::_ptr<const char>(iov_base.addr()), vm::_ptr<const char>(iov_base.addr()) + len);
native_result = ::send(socket, reinterpret_cast<const char*>(buf_copy.data()), ::narrow<int>(buf_copy.size()), native_flags);
native_result = ::send(native_socket, reinterpret_cast<const char*>(buf_copy.data()), ::narrow<int>(buf_copy.size()), native_flags);
if (native_result >= 0)
{
@ -1096,15 +1096,9 @@ std::optional<s32> lv2_socket_native::sendmsg(s32 flags, const sys_net_msghdr& m
void lv2_socket_native::close()
{
std::lock_guard lock(mutex);
if (socket)
{
#ifdef _WIN32
::closesocket(socket);
#else
::close(socket);
#endif
socket = {};
}
np::close_socket(native_socket);
native_socket = {};
if (auto dnshook = g_fxo->try_get<np::dnshook>())
{
@ -1141,7 +1135,7 @@ s32 lv2_socket_native::shutdown(s32 how)
SHUT_RDWR;
#endif
if (::shutdown(socket, native_how) == 0)
if (::shutdown(native_socket, native_how) == 0)
{
return CELL_OK;
}
@ -1163,7 +1157,7 @@ s32 lv2_socket_native::poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd)
sys_net.warning("sys_net_bnet_poll(fd=%d): events=0x%x", sn_pfd.fd, sn_pfd.events);
}
native_pfd.fd = socket;
native_pfd.fd = native_socket;
if (sn_pfd.events & SYS_NET_POLLIN)
{
@ -1179,7 +1173,7 @@ s32 lv2_socket_native::poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd)
std::tuple<bool, bool, bool> lv2_socket_native::select(bs_t<lv2_socket::poll_t> selected, pollfd& native_pfd)
{
native_pfd.fd = socket;
native_pfd.fd = native_socket;
if (selected & lv2_socket::poll_t::read)
{
native_pfd.events |= POLLIN;
@ -1196,12 +1190,12 @@ void lv2_socket_native::set_default_buffers()
{
// Those are the default PS3 values
u32 default_RCVBUF = (type == SYS_NET_SOCK_STREAM) ? 65535 : 9216;
if (::setsockopt(socket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<const char*>(&default_RCVBUF), sizeof(default_RCVBUF)) != 0)
if (::setsockopt(native_socket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<const char*>(&default_RCVBUF), sizeof(default_RCVBUF)) != 0)
{
sys_net.error("Error setting default SO_RCVBUF on sys_net_bnet_socket socket");
}
u32 default_SNDBUF = 131072;
if (::setsockopt(socket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<const char*>(&default_SNDBUF), sizeof(default_SNDBUF)) != 0)
if (::setsockopt(native_socket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<const char*>(&default_SNDBUF), sizeof(default_SNDBUF)) != 0)
{
sys_net.error("Error setting default SO_SNDBUF on sys_net_bnet_socket socket");
}
@ -1212,12 +1206,7 @@ void lv2_socket_native::set_non_blocking()
// Set non-blocking
// This is done to avoid having threads stuck on blocking socket functions
// Blocking functions just put the thread to sleep and delegate the waking up to network_thread which polls the sockets
#ifdef _WIN32
u_long _true = 1;
::ioctlsocket(socket, FIONBIO, &_true);
#else
::fcntl(socket, F_SETFL, ::fcntl(socket, F_GETFL, 0) | O_NONBLOCK);
#endif
np::set_socket_non_blocking(native_socket);
}
bool lv2_socket_native::is_socket_connected()
@ -1232,7 +1221,7 @@ bool lv2_socket_native::is_socket_connected()
int listening = 0;
socklen_t len = sizeof(listening);
if (::getsockopt(socket, SOL_SOCKET, SO_ACCEPTCONN, reinterpret_cast<char*>(&listening), &len) == -1)
if (::getsockopt(native_socket, SOL_SOCKET, SO_ACCEPTCONN, reinterpret_cast<char*>(&listening), &len) == -1)
{
return false;
}
@ -1248,8 +1237,8 @@ bool lv2_socket_native::is_socket_connected()
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_SET(socket, &readfds);
FD_SET(socket, &writefds);
FD_SET(native_socket, &readfds);
FD_SET(native_socket, &writefds);
// Use select to check for readability and writability
const int result = ::select(1, &readfds, &writefds, NULL, &timeout);
@ -1261,5 +1250,5 @@ bool lv2_socket_native::is_socket_connected()
}
// Socket is connected if it's readable or writable
return FD_ISSET(socket, &readfds) || FD_ISSET(socket, &writefds);
return FD_ISSET(native_socket, &readfds) || FD_ISSET(native_socket, &writefds);
}

View file

@ -17,7 +17,6 @@
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#ifdef __clang__
#pragma GCC diagnostic pop
@ -61,7 +60,7 @@ public:
s32 shutdown(s32 how) override;
private:
void set_socket(socket_type socket, lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol);
void set_socket(socket_type native_socket, lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol);
void set_default_buffers();
void set_non_blocking();

View file

@ -164,7 +164,7 @@ s32 lv2_socket_p2p::bind(const sys_net_sockaddr& addr)
std::lock_guard lock(mutex);
port = p2p_port;
vport = p2p_vport;
socket = real_socket;
native_socket = real_socket;
bound_addr = psa_in_p2p->sin_addr;
}
@ -176,7 +176,7 @@ std::pair<s32, sys_net_sockaddr> lv2_socket_p2p::getsockname()
std::lock_guard lock(mutex);
// Unbound socket
if (!socket)
if (!native_socket)
{
return {CELL_OK, {}};
}
@ -299,7 +299,7 @@ std::optional<s32> lv2_socket_p2p::sendto(s32 flags, const std::vector<u8>& buf,
native_flags |= MSG_WAITALL;
}
auto native_result = ::sendto(socket, reinterpret_cast<const char*>(p2p_data.data()), ::size32(p2p_data), native_flags, reinterpret_cast<struct sockaddr*>(&native_addr), sizeof(native_addr));
auto native_result = np::sendto_possibly_ipv6(native_socket, reinterpret_cast<const char*>(p2p_data.data()), ::size32(p2p_data), &native_addr, native_flags);
if (native_result >= 0)
{

View file

@ -1,7 +1,6 @@
#include "stdafx.h"
#include "Utilities/Thread.h"
#include "util/asm.hpp"
#include "util/atomic.hpp"
#include "lv2_socket_p2ps.h"
#include "Emu/NP/np_helpers.h"
@ -275,10 +274,10 @@ lv2_socket_p2ps::lv2_socket_p2ps(lv2_socket_family family, lv2_socket_type type,
sockopts[(static_cast<u64>(SYS_NET_SOL_SOCKET) << 32ull) | SYS_NET_SO_TYPE] = cache_type;
}
lv2_socket_p2ps::lv2_socket_p2ps(socket_type socket, u16 port, u16 vport, u32 op_addr, u16 op_port, u16 op_vport, u64 cur_seq, u64 data_beg_seq, s32 so_nbio)
lv2_socket_p2ps::lv2_socket_p2ps(socket_type native_socket, u16 port, u16 vport, u32 op_addr, u16 op_port, u16 op_vport, u64 cur_seq, u64 data_beg_seq, s32 so_nbio)
: lv2_socket_p2p(SYS_NET_AF_INET, SYS_NET_SOCK_STREAM_P2P, SYS_NET_IPPROTO_IP)
{
this->socket = socket;
this->native_socket = native_socket;
this->port = port;
this->vport = vport;
this->op_addr = op_addr;
@ -467,7 +466,7 @@ bool lv2_socket_p2ps::handle_listening(p2ps_encapsulated_tcp* tcp_header, [[mayb
const u16 new_op_vport = tcp_header->src_port;
const u64 new_cur_seq = send_hdr.seq + 1;
const u64 new_data_beg_seq = send_hdr.ack;
auto sock_lv2 = make_shared<lv2_socket_p2ps>(socket, port, vport, new_op_addr, new_op_port, new_op_vport, new_cur_seq, new_data_beg_seq, so_nbio);
auto sock_lv2 = make_shared<lv2_socket_p2ps>(native_socket, port, vport, new_op_addr, new_op_port, new_op_vport, new_cur_seq, new_data_beg_seq, so_nbio);
const s32 new_sock_id = idm::import_existing<lv2_socket>(sock_lv2);
sock_lv2->set_lv2_id(new_sock_id);
const u64 key_connected = (reinterpret_cast<struct sockaddr_in*>(op_addr)->sin_addr.s_addr) | (static_cast<u64>(tcp_header->src_port) << 48) | (static_cast<u64>(tcp_header->dst_port) << 32);
@ -518,8 +517,9 @@ void lv2_socket_p2ps::send_u2s_packet(std::vector<u8> data, const ::sockaddr_in*
{
char ip_str[16];
inet_ntop(AF_INET, &dst->sin_addr, ip_str, sizeof(ip_str));
sys_net.trace("[P2PS] Sending U2S packet on socket %d(id:%d): data(%d, seq %d, require_ack %d) to %s:%d", socket, lv2_id, data.size(), seq, require_ack, ip_str, std::bit_cast<u16, be_t<u16>>(dst->sin_port));
while (::sendto(socket, reinterpret_cast<char*>(data.data()), ::size32(data), 0, reinterpret_cast<const sockaddr*>(dst), sizeof(sockaddr_in)) == -1)
sys_net.trace("[P2PS] Sending U2S packet on socket %d(id:%d): data(%d, seq %d, require_ack %d) to %s:%d", native_socket, lv2_id, data.size(), seq, require_ack, ip_str, std::bit_cast<u16, be_t<u16>>(dst->sin_port));
while (np::sendto_possibly_ipv6(native_socket, reinterpret_cast<char*>(data.data()), ::size32(data), dst, 0) == -1)
{
const sys_net_error err = get_last_error(false);
// concurrency on the socket can result in EAGAIN error in which case we try again
@ -707,7 +707,7 @@ s32 lv2_socket_p2ps::bind(const sys_net_sockaddr& addr)
port = p2p_port;
vport = p2p_vport;
socket = real_socket;
native_socket = real_socket;
bound_addr = psa_in_p2p->sin_addr;
}
}
@ -720,7 +720,7 @@ std::pair<s32, sys_net_sockaddr> lv2_socket_p2ps::getsockname()
std::lock_guard lock(mutex);
// Unbound socket
if (!socket)
if (!native_socket)
{
return {CELL_OK, {}};
}
@ -783,7 +783,7 @@ std::optional<s32> lv2_socket_p2ps::connect(const sys_net_sockaddr& addr)
}
}
socket = real_socket;
native_socket = real_socket;
send_hdr.src_port = vport;
send_hdr.dst_port = dst_vport;

View file

@ -1,35 +1,73 @@
#include "Emu/NP/ip_address.h"
#include "stdafx.h"
#include "Emu/Cell/lv2/sys_sync.h"
#include "Emu/Cell/Modules/sceNp.h" // for SCE_NP_PORT
#include "network_context.h"
#include "Emu/system_config.h"
#include "sys_net_helpers.h"
LOG_CHANNEL(sys_net);
// Used by RPCN to send signaling packets to RPCN server(for UDP hole punching)
s32 send_packet_from_p2p_port(const std::vector<u8>& data, const sockaddr_in& addr)
bool send_packet_from_p2p_port_ipv4(const std::vector<u8>& data, const sockaddr_in& addr)
{
s32 res{};
auto& nc = g_fxo->get<p2p_context>();
{
std::lock_guard list_lock(nc.list_p2p_ports_mutex);
if (nc.list_p2p_ports.contains(SCE_NP_PORT))
{
auto& def_port = ::at32(nc.list_p2p_ports, SCE_NP_PORT);
res = ::sendto(def_port.p2p_socket, reinterpret_cast<const char*>(data.data()), ::size32(data), 0, reinterpret_cast<const sockaddr*>(&addr), sizeof(sockaddr_in));
if (res == -1)
sys_net.error("Failed to send signaling packet: %s", get_last_error(false, false));
if (def_port.is_ipv6)
{
const auto addr6 = np::sockaddr_to_sockaddr6(addr);
if (::sendto(def_port.p2p_socket, reinterpret_cast<const char*>(data.data()), ::size32(data), 0, reinterpret_cast<const sockaddr*>(&addr6), sizeof(sockaddr_in6)) == -1)
{
sys_net.error("Failed to send IPv4 signaling packet on IPv6 socket: %s", get_last_error(false, false));
return false;
}
}
else if (::sendto(def_port.p2p_socket, reinterpret_cast<const char*>(data.data()), ::size32(data), 0, reinterpret_cast<const sockaddr*>(&addr), sizeof(sockaddr_in)) == -1)
{
sys_net.error("Failed to send signaling packet on IPv4 socket: %s", get_last_error(false, false));
return false;
}
}
else
{
sys_net.error("send_packet_from_p2p_port: port %d not present", +SCE_NP_PORT);
sys_net.error("send_packet_from_p2p_port_ipv4: port %d not present", +SCE_NP_PORT);
return false;
}
}
return res;
return true;
}
bool send_packet_from_p2p_port_ipv6(const std::vector<u8>& data, const sockaddr_in6& addr)
{
auto& nc = g_fxo->get<p2p_context>();
{
std::lock_guard list_lock(nc.list_p2p_ports_mutex);
if (nc.list_p2p_ports.contains(SCE_NP_PORT))
{
auto& def_port = ::at32(nc.list_p2p_ports, SCE_NP_PORT);
ensure(def_port.is_ipv6);
if (::sendto(def_port.p2p_socket, reinterpret_cast<const char*>(data.data()), ::size32(data), 0, reinterpret_cast<const sockaddr*>(&addr), sizeof(sockaddr_in6)) == -1)
{
sys_net.error("Failed to send signaling packet on IPv6 socket: %s", get_last_error(false, false));
return false;
}
}
else
{
sys_net.error("send_packet_from_p2p_port_ipv6: port %d not present", +SCE_NP_PORT);
return false;
}
}
return true;
}
std::vector<std::vector<u8>> get_rpcn_msgs()
@ -241,13 +279,18 @@ void p2p_thread::operator()()
auto num_p2p_sockets = 0;
std::memset(p2p_fd.data(), 0, p2p_fd.size() * sizeof(::pollfd));
{
std::lock_guard lock(list_p2p_ports_mutex);
for (const auto& p2p_port : list_p2p_ports)
auto set_fd = [&](socket_type socket)
{
p2p_fd[num_p2p_sockets].events = POLLIN;
p2p_fd[num_p2p_sockets].revents = 0;
p2p_fd[num_p2p_sockets].fd = p2p_port.second.p2p_socket;
p2p_fd[num_p2p_sockets].fd = socket;
num_p2p_sockets++;
};
std::lock_guard lock(list_p2p_ports_mutex);
for (const auto& [_, p2p_port] : list_p2p_ports)
{
set_fd(p2p_port.p2p_socket);
}
}
@ -260,14 +303,20 @@ void p2p_thread::operator()()
{
std::lock_guard lock(list_p2p_ports_mutex);
auto fd_index = 0;
for (auto& p2p_port : list_p2p_ports)
auto process_fd = [&](nt_p2p_port& p2p_port)
{
if ((p2p_fd[fd_index].revents & POLLIN) == POLLIN || (p2p_fd[fd_index].revents & POLLRDNORM) == POLLRDNORM)
{
while (p2p_port.second.recv_data())
while (p2p_port.recv_data())
;
}
fd_index++;
};
for (auto& [_, p2p_port] : list_p2p_ports)
{
process_fd(p2p_port);
}
wake_threads();

View file

@ -1,11 +1,7 @@
#include "stdafx.h"
#include <fcntl.h>
#include "Emu/NP/ip_address.h"
#include "nt_p2p_port.h"
#include "lv2_socket_native.h"
#include "lv2_socket_p2ps.h"
#include "util/asm.hpp"
#include "sys_net_helpers.h"
#include "Emu/NP/signaling_handler.h"
#include "sys_net_helpers.h"
@ -44,9 +40,10 @@ namespace sys_net_helpers
nt_p2p_port::nt_p2p_port(u16 port)
: port(port)
{
// Creates and bind P2P Socket
p2p_socket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
is_ipv6 = np::is_ipv6_supported();
// Creates and bind P2P Socket
p2p_socket = is_ipv6 ? ::socket(AF_INET6, SOCK_DGRAM, 0) : ::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
#ifdef _WIN32
if (p2p_socket == INVALID_SOCKET)
#else
@ -54,22 +51,30 @@ nt_p2p_port::nt_p2p_port(u16 port)
#endif
fmt::throw_exception("Failed to create DGRAM socket for P2P socket: %s!", get_last_error(true));
#ifdef _WIN32
u_long _true = 1;
::ioctlsocket(p2p_socket, FIONBIO, &_true);
#else
::fcntl(p2p_socket, F_SETFL, ::fcntl(p2p_socket, F_GETFL, 0) | O_NONBLOCK);
#endif
np::set_socket_non_blocking(p2p_socket);
u32 optval = 131072; // value obtained from DECR for a SOCK_DGRAM_P2P socket(should maybe be bigger for actual socket?)
if (setsockopt(p2p_socket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<const char*>(&optval), sizeof(optval)) != 0)
fmt::throw_exception("Error setsockopt SO_RCVBUF on P2P socket: %s", get_last_error(true));
::sockaddr_in p2p_saddr{};
p2p_saddr.sin_family = AF_INET;
p2p_saddr.sin_port = std::bit_cast<u16, be_t<u16>>(port); // htons(port);
p2p_saddr.sin_addr.s_addr = 0; // binds to 0.0.0.0
const auto ret_bind = ::bind(p2p_socket, reinterpret_cast<sockaddr*>(&p2p_saddr), sizeof(p2p_saddr));
int ret_bind = 0;
const u16 be_port = std::bit_cast<u16, be_t<u16>>(port);
if (is_ipv6)
{
// Some OS(Windows, maybe more) will only support IPv6 adressing by default and we need IPv4 over IPv6
optval = 0;
if (setsockopt(p2p_socket, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char*>(&optval), sizeof(optval)) != 0)
fmt::throw_exception("Error setsockopt IPV6_V6ONLY on P2P socket: %s", get_last_error(true));
::sockaddr_in6 p2p_ipv6_addr{.sin6_family = AF_INET6, .sin6_port = be_port};
ret_bind = ::bind(p2p_socket, reinterpret_cast<sockaddr*>(&p2p_ipv6_addr), sizeof(p2p_ipv6_addr));
}
else
{
::sockaddr_in p2p_ipv4_addr{.sin_family = AF_INET, .sin_port = be_port};
ret_bind = ::bind(p2p_socket, reinterpret_cast<sockaddr*>(&p2p_ipv4_addr), sizeof(p2p_ipv4_addr));
}
if (ret_bind == -1)
fmt::throw_exception("Failed to bind DGRAM socket to %d for P2P: %s!", port, get_last_error(true));
@ -82,14 +87,7 @@ nt_p2p_port::nt_p2p_port(u16 port)
nt_p2p_port::~nt_p2p_port()
{
if (p2p_socket)
{
#ifdef _WIN32
::closesocket(p2p_socket);
#else
::close(p2p_socket);
#endif
}
np::close_socket(p2p_socket);
}
void nt_p2p_port::dump_packet(p2ps_encapsulated_tcp* tcph)
@ -153,7 +151,7 @@ bool nt_p2p_port::recv_data()
{
auto lerr = get_last_error(false);
if (lerr != SYS_NET_EINPROGRESS && lerr != SYS_NET_EWOULDBLOCK)
sys_net.error("Error recvfrom on P2P socket: %d", lerr);
sys_net.error("Error recvfrom on %s P2P socket: %d", is_ipv6 ? "IPv6" : "IPv4", lerr);
return false;
}
@ -166,6 +164,14 @@ bool nt_p2p_port::recv_data()
u16 dst_vport = reinterpret_cast<le_t<u16>&>(p2p_recv_data[0]);
if (is_ipv6)
{
const auto* addr_ipv6 = reinterpret_cast<sockaddr_in6*>(&native_addr);
const auto addr_ipv4 = np::sockaddr6_to_sockaddr(*addr_ipv6);
native_addr = {};
std::memcpy(&native_addr, &addr_ipv4, sizeof(addr_ipv4));
}
if (dst_vport == 0)
{
if (recv_res < VPORT_0_HEADER_SIZE)
@ -339,7 +345,7 @@ bool nt_p2p_port::recv_data()
send_hdr.flags = p2ps_tcp_flags::RST;
auto packet = generate_u2s_packet(send_hdr, nullptr, 0);
if (::sendto(p2p_socket, reinterpret_cast<char*>(packet.data()), ::size32(packet), 0, reinterpret_cast<const sockaddr*>(&native_addr), sizeof(sockaddr_in)) == -1)
if (np::sendto_possibly_ipv6(p2p_socket, reinterpret_cast<char*>(packet.data()), ::size32(packet), reinterpret_cast<const sockaddr_in*>(&native_addr), 0) == -1)
{
sys_net.error("[P2PS] Error sending RST to sender to unbound P2PS: %s", get_last_error(false));
return true;

View file

@ -48,6 +48,8 @@ struct nt_p2p_port
socket_type p2p_socket = 0;
u16 port = 0;
bool is_ipv6 = false;
shared_mutex bound_p2p_vports_mutex;
// For DGRAM_P2P sockets (vport, sock_ids)
std::map<u16, std::set<s32>> bound_p2p_vports{};

View file

@ -1,11 +1,8 @@
#include "stdafx.h"
#include "Emu/IdManager.h"
#include "Emu/Cell/PPUThread.h"
#include "lv2_socket.h"
#include "sys_net_helpers.h"
#include "network_context.h"
LOG_CHANNEL(sys_net);
@ -32,13 +29,13 @@ sys_net_error convert_error(bool is_blocking, int native_error, [[maybe_unused]]
#define ERROR_CASE(error) \
case WSA##error: \
result = SYS_NET_##error; \
name = #error; \
name = #error; \
break;
#else
#define ERROR_CASE(error) \
case error: \
result = SYS_NET_##error; \
name = #error; \
name = #error; \
break;
#endif
switch (native_error)
@ -86,11 +83,11 @@ sys_net_error convert_error(bool is_blocking, int native_error, [[maybe_unused]]
ERROR_CASE(EHOSTDOWN);
ERROR_CASE(EHOSTUNREACH);
#ifdef _WIN32
// Windows likes to be special with unique errors
case WSAENETRESET:
result = SYS_NET_ECONNRESET;
name = "WSAENETRESET";
break;
// Windows likes to be special with unique errors
case WSAENETRESET:
result = SYS_NET_ECONNRESET;
name = "WSAENETRESET";
break;
#endif
default:
fmt::throw_exception("sys_net get_last_error(is_blocking=%d, native_error=%d): Unknown/illegal socket error", is_blocking, native_error);
@ -137,11 +134,11 @@ sys_net_sockaddr native_addr_to_sys_net_addr(const ::sockaddr_storage& native_ad
sys_net_sockaddr_in* paddr = reinterpret_cast<sys_net_sockaddr_in*>(&sn_addr);
paddr->sin_len = sizeof(sys_net_sockaddr_in);
paddr->sin_len = sizeof(sys_net_sockaddr_in);
paddr->sin_family = SYS_NET_AF_INET;
paddr->sin_port = std::bit_cast<be_t<u16>, u16>(reinterpret_cast<const sockaddr_in*>(&native_addr)->sin_port);
paddr->sin_addr = std::bit_cast<be_t<u32>, u32>(reinterpret_cast<const sockaddr_in*>(&native_addr)->sin_addr.s_addr);
paddr->sin_zero = 0;
paddr->sin_port = std::bit_cast<be_t<u16>, u16>(reinterpret_cast<const sockaddr_in*>(&native_addr)->sin_port);
paddr->sin_addr = std::bit_cast<be_t<u32>, u32>(reinterpret_cast<const sockaddr_in*>(&native_addr)->sin_addr.s_addr);
paddr->sin_zero = 0;
return sn_addr;
}
@ -153,8 +150,8 @@ sys_net_sockaddr native_addr_to_sys_net_addr(const ::sockaddr_storage& native_ad
const sys_net_sockaddr_in* psa_in = reinterpret_cast<const sys_net_sockaddr_in*>(&sn_addr);
::sockaddr_in native_addr{};
native_addr.sin_family = AF_INET;
native_addr.sin_port = std::bit_cast<u16>(psa_in->sin_port);
native_addr.sin_family = AF_INET;
native_addr.sin_port = std::bit_cast<u16>(psa_in->sin_port);
native_addr.sin_addr.s_addr = std::bit_cast<u32>(psa_in->sin_addr);
#ifdef _WIN32
@ -189,9 +186,9 @@ u32 network_clear_queue(ppu_thread& ppu)
u32 cleared = 0;
idm::select<lv2_socket>([&](u32, lv2_socket& sock)
{
cleared += sock.clear_queue(&ppu);
});
{
cleared += sock.clear_queue(&ppu);
});
return cleared;
}
@ -204,6 +201,7 @@ void clear_ppu_to_awake(ppu_thread& ppu)
#ifdef _WIN32
// Workaround function for WSAPoll not reporting failed connections
// Note that this was fixed in Windows 10 version 2004 (after more than 10 years lol)
void windows_poll(std::vector<pollfd>& fds, unsigned long nfds, int timeout, std::vector<bool>& connecting)
{
ensure(fds.size() >= nfds);
@ -237,12 +235,12 @@ void windows_poll(std::vector<pollfd>& fds, unsigned long nfds, int timeout, std
{
if (!fds[i].revents)
{
int error = 0;
int error = 0;
socklen_t intlen = sizeof(error);
if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&error), &intlen) == -1 || error != 0)
{
// Connection silently failed
connecting[i] = false;
connecting[i] = false;
fds[i].revents = POLLERR | POLLHUP | (fds[i].events & (POLLIN | POLLOUT));
}
}

View file

@ -45,7 +45,7 @@ namespace np
{
sce_group->groupId = fb_group->groupId();
sce_group->withPassword = fb_group->withPassword();
sce_group->withLabel = fb_group->withLabel();
sce_group->withLabel = fb_group->label() ? 1 : 0;
if (fb_group->label())
{
for (flatbuffers::uoffset_t l_index = 0; l_index < fb_group->label()->size(); l_index++)

View file

@ -1,3 +1,18 @@
table SignalingAddr {
ip:[uint8];
port:uint16;
}
table MatchingSignalingInfo {
npid:string;
addr:SignalingAddr;
}
table Matching2SignalingInfo {
member_id:uint16;
addr:SignalingAddr;
}
table BinAttr {
id:uint16;
data:[uint8];
@ -27,7 +42,6 @@ table OptParam {
table GroupConfig {
slotNum:uint32;
withLabel:bool;
label:[uint8];
withPassword:bool;
}
@ -52,7 +66,6 @@ table RoomMemberDataInternal {
table RoomGroup {
groupId:uint8;
withPassword:bool;
withLabel:bool;
label:[uint8];
slotNum:uint32;
curGroupMemberNum:uint32;
@ -108,7 +121,7 @@ table PresenceOptionData {
}
table RoomGroupPasswordConfig {
groupId:[uint8];
groupId:uint8;
withPassword:bool;
}
@ -161,6 +174,11 @@ table JoinRoomRequest {
teamId:uint8;
}
table JoinRoomResponse {
room_data: RoomDataInternal;
signaling_data: [Matching2SignalingInfo];
}
table LeaveRoomRequest {
roomId:uint64;
optData:PresenceOptionData;
@ -221,6 +239,12 @@ table RoomMemberUpdateInfo {
optData:PresenceOptionData;
}
table NotificationUserJoinedRoom {
room_id:uint64;
update_info:RoomMemberUpdateInfo;
signaling:SignalingAddr;
}
table RoomUpdateInfo {
eventCause:uint8;
errorCode:int32;

View file

@ -13,6 +13,15 @@ static_assert(FLATBUFFERS_VERSION_MAJOR == 24 &&
FLATBUFFERS_VERSION_REVISION == 25,
"Non-compatible flatbuffers version included");
struct SignalingAddr;
struct SignalingAddrBuilder;
struct MatchingSignalingInfo;
struct MatchingSignalingInfoBuilder;
struct Matching2SignalingInfo;
struct Matching2SignalingInfoBuilder;
struct BinAttr;
struct BinAttrBuilder;
@ -70,6 +79,9 @@ struct CreateJoinRoomRequestBuilder;
struct JoinRoomRequest;
struct JoinRoomRequestBuilder;
struct JoinRoomResponse;
struct JoinRoomResponseBuilder;
struct LeaveRoomRequest;
struct LeaveRoomRequestBuilder;
@ -100,6 +112,9 @@ struct GetRoomDataInternalRequestBuilder;
struct RoomMemberUpdateInfo;
struct RoomMemberUpdateInfoBuilder;
struct NotificationUserJoinedRoom;
struct NotificationUserJoinedRoomBuilder;
struct RoomUpdateInfo;
struct RoomUpdateInfoBuilder;
@ -256,6 +271,185 @@ struct SearchJoinRoomGUIRequestBuilder;
struct MatchingSearchJoinRoomInfo;
struct MatchingSearchJoinRoomInfoBuilder;
struct SignalingAddr FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef SignalingAddrBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_IP = 4,
VT_PORT = 6
};
const ::flatbuffers::Vector<uint8_t> *ip() const {
return GetPointer<const ::flatbuffers::Vector<uint8_t> *>(VT_IP);
}
uint16_t port() const {
return GetField<uint16_t>(VT_PORT, 0);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_IP) &&
verifier.VerifyVector(ip()) &&
VerifyField<uint16_t>(verifier, VT_PORT, 2) &&
verifier.EndTable();
}
};
struct SignalingAddrBuilder {
typedef SignalingAddr Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_ip(::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> ip) {
fbb_.AddOffset(SignalingAddr::VT_IP, ip);
}
void add_port(uint16_t port) {
fbb_.AddElement<uint16_t>(SignalingAddr::VT_PORT, port, 0);
}
explicit SignalingAddrBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<SignalingAddr> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<SignalingAddr>(end);
return o;
}
};
inline ::flatbuffers::Offset<SignalingAddr> CreateSignalingAddr(
::flatbuffers::FlatBufferBuilder &_fbb,
::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> ip = 0,
uint16_t port = 0) {
SignalingAddrBuilder builder_(_fbb);
builder_.add_ip(ip);
builder_.add_port(port);
return builder_.Finish();
}
inline ::flatbuffers::Offset<SignalingAddr> CreateSignalingAddrDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
const std::vector<uint8_t> *ip = nullptr,
uint16_t port = 0) {
auto ip__ = ip ? _fbb.CreateVector<uint8_t>(*ip) : 0;
return CreateSignalingAddr(
_fbb,
ip__,
port);
}
struct MatchingSignalingInfo FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef MatchingSignalingInfoBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_NPID = 4,
VT_ADDR = 6
};
const ::flatbuffers::String *npid() const {
return GetPointer<const ::flatbuffers::String *>(VT_NPID);
}
const SignalingAddr *addr() const {
return GetPointer<const SignalingAddr *>(VT_ADDR);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_NPID) &&
verifier.VerifyString(npid()) &&
VerifyOffset(verifier, VT_ADDR) &&
verifier.VerifyTable(addr()) &&
verifier.EndTable();
}
};
struct MatchingSignalingInfoBuilder {
typedef MatchingSignalingInfo Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_npid(::flatbuffers::Offset<::flatbuffers::String> npid) {
fbb_.AddOffset(MatchingSignalingInfo::VT_NPID, npid);
}
void add_addr(::flatbuffers::Offset<SignalingAddr> addr) {
fbb_.AddOffset(MatchingSignalingInfo::VT_ADDR, addr);
}
explicit MatchingSignalingInfoBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<MatchingSignalingInfo> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<MatchingSignalingInfo>(end);
return o;
}
};
inline ::flatbuffers::Offset<MatchingSignalingInfo> CreateMatchingSignalingInfo(
::flatbuffers::FlatBufferBuilder &_fbb,
::flatbuffers::Offset<::flatbuffers::String> npid = 0,
::flatbuffers::Offset<SignalingAddr> addr = 0) {
MatchingSignalingInfoBuilder builder_(_fbb);
builder_.add_addr(addr);
builder_.add_npid(npid);
return builder_.Finish();
}
inline ::flatbuffers::Offset<MatchingSignalingInfo> CreateMatchingSignalingInfoDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
const char *npid = nullptr,
::flatbuffers::Offset<SignalingAddr> addr = 0) {
auto npid__ = npid ? _fbb.CreateString(npid) : 0;
return CreateMatchingSignalingInfo(
_fbb,
npid__,
addr);
}
struct Matching2SignalingInfo FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef Matching2SignalingInfoBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_MEMBER_ID = 4,
VT_ADDR = 6
};
uint16_t member_id() const {
return GetField<uint16_t>(VT_MEMBER_ID, 0);
}
const SignalingAddr *addr() const {
return GetPointer<const SignalingAddr *>(VT_ADDR);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint16_t>(verifier, VT_MEMBER_ID, 2) &&
VerifyOffset(verifier, VT_ADDR) &&
verifier.VerifyTable(addr()) &&
verifier.EndTable();
}
};
struct Matching2SignalingInfoBuilder {
typedef Matching2SignalingInfo Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_member_id(uint16_t member_id) {
fbb_.AddElement<uint16_t>(Matching2SignalingInfo::VT_MEMBER_ID, member_id, 0);
}
void add_addr(::flatbuffers::Offset<SignalingAddr> addr) {
fbb_.AddOffset(Matching2SignalingInfo::VT_ADDR, addr);
}
explicit Matching2SignalingInfoBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<Matching2SignalingInfo> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<Matching2SignalingInfo>(end);
return o;
}
};
inline ::flatbuffers::Offset<Matching2SignalingInfo> CreateMatching2SignalingInfo(
::flatbuffers::FlatBufferBuilder &_fbb,
uint16_t member_id = 0,
::flatbuffers::Offset<SignalingAddr> addr = 0) {
Matching2SignalingInfoBuilder builder_(_fbb);
builder_.add_addr(addr);
builder_.add_member_id(member_id);
return builder_.Finish();
}
struct BinAttr FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef BinAttrBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
@ -549,16 +743,12 @@ struct GroupConfig FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef GroupConfigBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_SLOTNUM = 4,
VT_WITHLABEL = 6,
VT_LABEL = 8,
VT_WITHPASSWORD = 10
VT_LABEL = 6,
VT_WITHPASSWORD = 8
};
uint32_t slotNum() const {
return GetField<uint32_t>(VT_SLOTNUM, 0);
}
bool withLabel() const {
return GetField<uint8_t>(VT_WITHLABEL, 0) != 0;
}
const ::flatbuffers::Vector<uint8_t> *label() const {
return GetPointer<const ::flatbuffers::Vector<uint8_t> *>(VT_LABEL);
}
@ -568,7 +758,6 @@ struct GroupConfig FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint32_t>(verifier, VT_SLOTNUM, 4) &&
VerifyField<uint8_t>(verifier, VT_WITHLABEL, 1) &&
VerifyOffset(verifier, VT_LABEL) &&
verifier.VerifyVector(label()) &&
VerifyField<uint8_t>(verifier, VT_WITHPASSWORD, 1) &&
@ -583,9 +772,6 @@ struct GroupConfigBuilder {
void add_slotNum(uint32_t slotNum) {
fbb_.AddElement<uint32_t>(GroupConfig::VT_SLOTNUM, slotNum, 0);
}
void add_withLabel(bool withLabel) {
fbb_.AddElement<uint8_t>(GroupConfig::VT_WITHLABEL, static_cast<uint8_t>(withLabel), 0);
}
void add_label(::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> label) {
fbb_.AddOffset(GroupConfig::VT_LABEL, label);
}
@ -606,28 +792,24 @@ struct GroupConfigBuilder {
inline ::flatbuffers::Offset<GroupConfig> CreateGroupConfig(
::flatbuffers::FlatBufferBuilder &_fbb,
uint32_t slotNum = 0,
bool withLabel = false,
::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> label = 0,
bool withPassword = false) {
GroupConfigBuilder builder_(_fbb);
builder_.add_label(label);
builder_.add_slotNum(slotNum);
builder_.add_withPassword(withPassword);
builder_.add_withLabel(withLabel);
return builder_.Finish();
}
inline ::flatbuffers::Offset<GroupConfig> CreateGroupConfigDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
uint32_t slotNum = 0,
bool withLabel = false,
const std::vector<uint8_t> *label = nullptr,
bool withPassword = false) {
auto label__ = label ? _fbb.CreateVector<uint8_t>(*label) : 0;
return CreateGroupConfig(
_fbb,
slotNum,
withLabel,
label__,
withPassword);
}
@ -854,10 +1036,9 @@ struct RoomGroup FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_GROUPID = 4,
VT_WITHPASSWORD = 6,
VT_WITHLABEL = 8,
VT_LABEL = 10,
VT_SLOTNUM = 12,
VT_CURGROUPMEMBERNUM = 14
VT_LABEL = 8,
VT_SLOTNUM = 10,
VT_CURGROUPMEMBERNUM = 12
};
uint8_t groupId() const {
return GetField<uint8_t>(VT_GROUPID, 0);
@ -865,9 +1046,6 @@ struct RoomGroup FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
bool withPassword() const {
return GetField<uint8_t>(VT_WITHPASSWORD, 0) != 0;
}
bool withLabel() const {
return GetField<uint8_t>(VT_WITHLABEL, 0) != 0;
}
const ::flatbuffers::Vector<uint8_t> *label() const {
return GetPointer<const ::flatbuffers::Vector<uint8_t> *>(VT_LABEL);
}
@ -881,7 +1059,6 @@ struct RoomGroup FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
return VerifyTableStart(verifier) &&
VerifyField<uint8_t>(verifier, VT_GROUPID, 1) &&
VerifyField<uint8_t>(verifier, VT_WITHPASSWORD, 1) &&
VerifyField<uint8_t>(verifier, VT_WITHLABEL, 1) &&
VerifyOffset(verifier, VT_LABEL) &&
verifier.VerifyVector(label()) &&
VerifyField<uint32_t>(verifier, VT_SLOTNUM, 4) &&
@ -900,9 +1077,6 @@ struct RoomGroupBuilder {
void add_withPassword(bool withPassword) {
fbb_.AddElement<uint8_t>(RoomGroup::VT_WITHPASSWORD, static_cast<uint8_t>(withPassword), 0);
}
void add_withLabel(bool withLabel) {
fbb_.AddElement<uint8_t>(RoomGroup::VT_WITHLABEL, static_cast<uint8_t>(withLabel), 0);
}
void add_label(::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> label) {
fbb_.AddOffset(RoomGroup::VT_LABEL, label);
}
@ -927,7 +1101,6 @@ inline ::flatbuffers::Offset<RoomGroup> CreateRoomGroup(
::flatbuffers::FlatBufferBuilder &_fbb,
uint8_t groupId = 0,
bool withPassword = false,
bool withLabel = false,
::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> label = 0,
uint32_t slotNum = 0,
uint32_t curGroupMemberNum = 0) {
@ -935,7 +1108,6 @@ inline ::flatbuffers::Offset<RoomGroup> CreateRoomGroup(
builder_.add_curGroupMemberNum(curGroupMemberNum);
builder_.add_slotNum(slotNum);
builder_.add_label(label);
builder_.add_withLabel(withLabel);
builder_.add_withPassword(withPassword);
builder_.add_groupId(groupId);
return builder_.Finish();
@ -945,7 +1117,6 @@ inline ::flatbuffers::Offset<RoomGroup> CreateRoomGroupDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
uint8_t groupId = 0,
bool withPassword = false,
bool withLabel = false,
const std::vector<uint8_t> *label = nullptr,
uint32_t slotNum = 0,
uint32_t curGroupMemberNum = 0) {
@ -954,7 +1125,6 @@ inline ::flatbuffers::Offset<RoomGroup> CreateRoomGroupDirect(
_fbb,
groupId,
withPassword,
withLabel,
label__,
slotNum,
curGroupMemberNum);
@ -1565,16 +1735,15 @@ struct RoomGroupPasswordConfig FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::
VT_GROUPID = 4,
VT_WITHPASSWORD = 6
};
const ::flatbuffers::Vector<uint8_t> *groupId() const {
return GetPointer<const ::flatbuffers::Vector<uint8_t> *>(VT_GROUPID);
uint8_t groupId() const {
return GetField<uint8_t>(VT_GROUPID, 0);
}
bool withPassword() const {
return GetField<uint8_t>(VT_WITHPASSWORD, 0) != 0;
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_GROUPID) &&
verifier.VerifyVector(groupId()) &&
VerifyField<uint8_t>(verifier, VT_GROUPID, 1) &&
VerifyField<uint8_t>(verifier, VT_WITHPASSWORD, 1) &&
verifier.EndTable();
}
@ -1584,8 +1753,8 @@ struct RoomGroupPasswordConfigBuilder {
typedef RoomGroupPasswordConfig Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_groupId(::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> groupId) {
fbb_.AddOffset(RoomGroupPasswordConfig::VT_GROUPID, groupId);
void add_groupId(uint8_t groupId) {
fbb_.AddElement<uint8_t>(RoomGroupPasswordConfig::VT_GROUPID, groupId, 0);
}
void add_withPassword(bool withPassword) {
fbb_.AddElement<uint8_t>(RoomGroupPasswordConfig::VT_WITHPASSWORD, static_cast<uint8_t>(withPassword), 0);
@ -1603,25 +1772,14 @@ struct RoomGroupPasswordConfigBuilder {
inline ::flatbuffers::Offset<RoomGroupPasswordConfig> CreateRoomGroupPasswordConfig(
::flatbuffers::FlatBufferBuilder &_fbb,
::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> groupId = 0,
uint8_t groupId = 0,
bool withPassword = false) {
RoomGroupPasswordConfigBuilder builder_(_fbb);
builder_.add_groupId(groupId);
builder_.add_withPassword(withPassword);
builder_.add_groupId(groupId);
return builder_.Finish();
}
inline ::flatbuffers::Offset<RoomGroupPasswordConfig> CreateRoomGroupPasswordConfigDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
const std::vector<uint8_t> *groupId = nullptr,
bool withPassword = false) {
auto groupId__ = groupId ? _fbb.CreateVector<uint8_t>(*groupId) : 0;
return CreateRoomGroupPasswordConfig(
_fbb,
groupId__,
withPassword);
}
struct SearchRoomRequest FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef SearchRoomRequestBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
@ -2250,6 +2408,71 @@ inline ::flatbuffers::Offset<JoinRoomRequest> CreateJoinRoomRequestDirect(
teamId);
}
struct JoinRoomResponse FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef JoinRoomResponseBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_ROOM_DATA = 4,
VT_SIGNALING_DATA = 6
};
const RoomDataInternal *room_data() const {
return GetPointer<const RoomDataInternal *>(VT_ROOM_DATA);
}
const ::flatbuffers::Vector<::flatbuffers::Offset<Matching2SignalingInfo>> *signaling_data() const {
return GetPointer<const ::flatbuffers::Vector<::flatbuffers::Offset<Matching2SignalingInfo>> *>(VT_SIGNALING_DATA);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_ROOM_DATA) &&
verifier.VerifyTable(room_data()) &&
VerifyOffset(verifier, VT_SIGNALING_DATA) &&
verifier.VerifyVector(signaling_data()) &&
verifier.VerifyVectorOfTables(signaling_data()) &&
verifier.EndTable();
}
};
struct JoinRoomResponseBuilder {
typedef JoinRoomResponse Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_room_data(::flatbuffers::Offset<RoomDataInternal> room_data) {
fbb_.AddOffset(JoinRoomResponse::VT_ROOM_DATA, room_data);
}
void add_signaling_data(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<Matching2SignalingInfo>>> signaling_data) {
fbb_.AddOffset(JoinRoomResponse::VT_SIGNALING_DATA, signaling_data);
}
explicit JoinRoomResponseBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<JoinRoomResponse> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<JoinRoomResponse>(end);
return o;
}
};
inline ::flatbuffers::Offset<JoinRoomResponse> CreateJoinRoomResponse(
::flatbuffers::FlatBufferBuilder &_fbb,
::flatbuffers::Offset<RoomDataInternal> room_data = 0,
::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<Matching2SignalingInfo>>> signaling_data = 0) {
JoinRoomResponseBuilder builder_(_fbb);
builder_.add_signaling_data(signaling_data);
builder_.add_room_data(room_data);
return builder_.Finish();
}
inline ::flatbuffers::Offset<JoinRoomResponse> CreateJoinRoomResponseDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
::flatbuffers::Offset<RoomDataInternal> room_data = 0,
const std::vector<::flatbuffers::Offset<Matching2SignalingInfo>> *signaling_data = nullptr) {
auto signaling_data__ = signaling_data ? _fbb.CreateVector<::flatbuffers::Offset<Matching2SignalingInfo>>(*signaling_data) : 0;
return CreateJoinRoomResponse(
_fbb,
room_data,
signaling_data__);
}
struct LeaveRoomRequest FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef LeaveRoomRequestBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
@ -2995,6 +3218,69 @@ inline ::flatbuffers::Offset<RoomMemberUpdateInfo> CreateRoomMemberUpdateInfo(
return builder_.Finish();
}
struct NotificationUserJoinedRoom FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef NotificationUserJoinedRoomBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_ROOM_ID = 4,
VT_UPDATE_INFO = 6,
VT_SIGNALING = 8
};
uint64_t room_id() const {
return GetField<uint64_t>(VT_ROOM_ID, 0);
}
const RoomMemberUpdateInfo *update_info() const {
return GetPointer<const RoomMemberUpdateInfo *>(VT_UPDATE_INFO);
}
const SignalingAddr *signaling() const {
return GetPointer<const SignalingAddr *>(VT_SIGNALING);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint64_t>(verifier, VT_ROOM_ID, 8) &&
VerifyOffset(verifier, VT_UPDATE_INFO) &&
verifier.VerifyTable(update_info()) &&
VerifyOffset(verifier, VT_SIGNALING) &&
verifier.VerifyTable(signaling()) &&
verifier.EndTable();
}
};
struct NotificationUserJoinedRoomBuilder {
typedef NotificationUserJoinedRoom Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_room_id(uint64_t room_id) {
fbb_.AddElement<uint64_t>(NotificationUserJoinedRoom::VT_ROOM_ID, room_id, 0);
}
void add_update_info(::flatbuffers::Offset<RoomMemberUpdateInfo> update_info) {
fbb_.AddOffset(NotificationUserJoinedRoom::VT_UPDATE_INFO, update_info);
}
void add_signaling(::flatbuffers::Offset<SignalingAddr> signaling) {
fbb_.AddOffset(NotificationUserJoinedRoom::VT_SIGNALING, signaling);
}
explicit NotificationUserJoinedRoomBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<NotificationUserJoinedRoom> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<NotificationUserJoinedRoom>(end);
return o;
}
};
inline ::flatbuffers::Offset<NotificationUserJoinedRoom> CreateNotificationUserJoinedRoom(
::flatbuffers::FlatBufferBuilder &_fbb,
uint64_t room_id = 0,
::flatbuffers::Offset<RoomMemberUpdateInfo> update_info = 0,
::flatbuffers::Offset<SignalingAddr> signaling = 0) {
NotificationUserJoinedRoomBuilder builder_(_fbb);
builder_.add_room_id(room_id);
builder_.add_signaling(signaling);
builder_.add_update_info(update_info);
return builder_.Finish();
}
struct RoomUpdateInfo FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef RoomUpdateInfoBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {

249
rpcs3/Emu/NP/ip_address.cpp Normal file
View file

@ -0,0 +1,249 @@
#include "stdafx.h"
#include "ip_address.h"
#include "Utilities/StrFmt.h"
#include "Emu/IdManager.h"
#include "util/endian.hpp"
#include "util/types.hpp"
#include "Emu/NP/rpcn_config.h"
#include <algorithm>
#ifndef _WIN32
#include <unistd.h>
#include <fcntl.h>
#endif
LOG_CHANNEL(IPv6_log, "IPv6_layer");
namespace np
{
bool ip_address_translator::is_ipv6(u32 addr_be)
{
const u32 addr = std::bit_cast<u32, be_t<u32>>(addr_be);
return (addr >= translation_range_begin && addr <= translation_range_end);
}
u32 ip_address_translator::register_ipv6(const std::array<u8, 16>& ipv6_addr)
{
if (ipv6_addr[10] == 0xFF && ipv6_addr[11] == 0xFF &&
std::all_of(ipv6_addr.begin(), ipv6_addr.begin() + 10, [](u8 val)
{
return val == 0;
}))
{
// IPv4 over IPv6
u32 ip_addr{};
std::memcpy(&ip_addr, &ipv6_addr[12], sizeof(u32));
return ip_addr;
}
std::lock_guard lock(mutex);
const auto it = ipv6_to_ipv4.find(ipv6_addr);
if (it == ipv6_to_ipv4.end())
{
ensure(cur_addr != translation_range_end);
const u32 translation_ip = cur_addr++;
const u32 translation_ip_be = std::bit_cast<u32, be_t<u32>>(translation_ip);
ipv6_to_ipv4.emplace(ipv6_addr, translation_ip_be);
ipv4_to_ipv6.push_back(ipv6_addr);
return translation_ip_be;
}
return it->second;
}
sockaddr_in6 ip_address_translator::get_ipv6_sockaddr(u32 addr_be, u16 port_be)
{
const u32 addr_native = std::bit_cast<u32, be_t<u32>>(addr_be);
const auto ipv6_addr = ::at32(ipv4_to_ipv6, addr_native - translation_range_begin);
sockaddr_in6 sockaddr_ipv6{.sin6_family = AF_INET6, .sin6_port = port_be};
std::memcpy(&sockaddr_ipv6.sin6_addr, ipv6_addr.data(), ipv6_addr.size());
return sockaddr_ipv6;
}
u32 register_ip(const flatbuffers::Vector<std::uint8_t>* vec)
{
if (vec->size() == 4)
{
const u32 ip = static_cast<u32>(vec->Get(0)) << 24 | static_cast<u32>(vec->Get(1)) << 16 |
static_cast<u32>(vec->Get(2)) << 8 | static_cast<u32>(vec->Get(3));
u32 result_ip = std::bit_cast<u32, be_t<u32>>(ip);
return result_ip;
}
else if (vec->size() == 16)
{
std::array<u8, 16> ipv6_addr{};
std::memcpy(ipv6_addr.data(), vec->Data(), 16);
auto& translator = g_fxo->get<np::ip_address_translator>();
return translator.register_ipv6(ipv6_addr);
}
else
{
fmt::throw_exception("Received ip address with size = %d", vec->size());
}
}
bool is_ipv6_supported(std::optional<IPV6_SUPPORT> force_state)
{
static atomic_t<IPV6_SUPPORT> ipv6_status = IPV6_SUPPORT::IPV6_UNKNOWN;
if (force_state)
ipv6_status = *force_state;
if (ipv6_status != IPV6_SUPPORT::IPV6_UNKNOWN)
return ipv6_status == IPV6_SUPPORT::IPV6_SUPPORTED;
static shared_mutex mtx;
std::lock_guard lock(mtx);
if (ipv6_status != IPV6_SUPPORT::IPV6_UNKNOWN)
return ipv6_status == IPV6_SUPPORT::IPV6_SUPPORTED;
// IPv6 feature is only used by RPCN
if (!g_cfg_rpcn.get_ipv6_support())
{
IPv6_log.notice("is_ipv6_supported(): disabled through config");
ipv6_status = IPV6_SUPPORT::IPV6_UNSUPPORTED;
return false;
}
// We try to connect to ipv6.google.com:8080
addrinfo* addr_info{};
socket_type socket_ipv6{};
auto cleanup = [&]()
{
if (socket_ipv6)
close_socket(socket_ipv6);
if (addr_info)
freeaddrinfo(addr_info);
};
auto error_and_disable = [&](const char* message) -> bool
{
IPv6_log.error("is_ipv6_supported(): %s", message);
ipv6_status = IPV6_SUPPORT::IPV6_UNSUPPORTED;
cleanup();
return false;
};
addrinfo hints{.ai_family = AF_INET6};
if (getaddrinfo("ipv6.google.com", nullptr, &hints, &addr_info))
return error_and_disable("Failed to resolve ipv6.google.com!");
addrinfo* found = addr_info;
while (found != nullptr)
{
if (found->ai_family == AF_INET6)
break;
found = found->ai_next;
}
if (found == nullptr)
return error_and_disable("Failed to find IPv6 for ipv6.google.com");
socket_type socket_or_err = ::socket(AF_INET6, SOCK_STREAM, 0);
#ifdef _WIN32
if (socket_or_err == INVALID_SOCKET)
#else
if (socket_or_err == -1)
#endif
return error_and_disable("Failed to create IPv6 socket!");
socket_ipv6 = socket_or_err;
sockaddr_in6 ipv6_addr = *reinterpret_cast<const sockaddr_in6*>(found->ai_addr);
ipv6_addr.sin6_port = std::bit_cast<u16, be_t<u16>>(443);
if (::connect(socket_ipv6, reinterpret_cast<const sockaddr*>(&ipv6_addr), sizeof(ipv6_addr)) != 0)
return error_and_disable("Failed to connect to ipv6.google.com");
cleanup();
IPv6_log.success("Successfully tested IPv6 support!");
ipv6_status = IPV6_SUPPORT::IPV6_SUPPORTED;
return true;
}
s32 sendto_possibly_ipv6(socket_type native_socket, const char* data, u32 size, const sockaddr_in* addr, int native_flags)
{
if (is_ipv6_supported())
{
sockaddr_in6 addr_ipv6{};
if (np::ip_address_translator::is_ipv6(addr->sin_addr.s_addr))
{
auto& translator = g_fxo->get<np::ip_address_translator>();
addr_ipv6 = translator.get_ipv6_sockaddr(addr->sin_addr.s_addr, addr->sin_port);
}
else
{
addr_ipv6 = sockaddr_to_sockaddr6(*addr);
}
return ::sendto(native_socket, data, size, native_flags, reinterpret_cast<const sockaddr*>(&addr_ipv6), sizeof(sockaddr_in6));
}
return ::sendto(native_socket, data, size, native_flags, reinterpret_cast<const sockaddr*>(addr), sizeof(sockaddr_in));
}
sockaddr_in6 sockaddr_to_sockaddr6(const sockaddr_in& addr)
{
sockaddr_in6 addr_ipv6{.sin6_family = AF_INET6, .sin6_port = addr.sin_port};
std::array<u8, 16> ipv6{};
// IPv4 over IPv6
ipv6[10] = 0xFF;
ipv6[11] = 0xFF;
std::memcpy(ipv6.data() + 12, &addr.sin_addr.s_addr, 4);
std::memcpy(&addr_ipv6.sin6_addr, ipv6.data(), ipv6.size());
return addr_ipv6;
}
sockaddr_in sockaddr6_to_sockaddr(const sockaddr_in6& addr)
{
sockaddr_in addr_ipv4{.sin_family = AF_INET, .sin_port = addr.sin6_port};
std::array<u8, 16> ipv6{};
std::memcpy(ipv6.data(), &addr.sin6_addr, 16);
auto& translator = g_fxo->get<np::ip_address_translator>();
addr_ipv4.sin_addr.s_addr = translator.register_ipv6(ipv6);
return addr_ipv4;
}
void close_socket(socket_type socket)
{
if (socket)
{
#ifdef _WIN32
::closesocket(socket);
#else
::close(socket);
#endif
}
}
void set_socket_non_blocking(socket_type socket)
{
if (socket)
{
#ifdef _WIN32
u_long _true = 1;
::ioctlsocket(socket, FIONBIO, &_true);
#else
::fcntl(socket, F_SETFL, ::fcntl(socket, F_GETFL, 0) | O_NONBLOCK);
#endif
}
}
} // namespace np

86
rpcs3/Emu/NP/ip_address.h Normal file
View file

@ -0,0 +1,86 @@
#pragma once
#include <array>
#include <unordered_map>
#include <flatbuffers/vector.h>
#include "util/types.hpp"
#include "Utilities/mutex.h"
#ifdef _WIN32
#include <winsock2.h>
#include <WS2tcpip.h>
#else
#ifdef __clang__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#ifdef __clang__
#pragma GCC diagnostic pop
#endif
#endif
#ifdef _WIN32
using socket_type = uptr;
#else
using socket_type = int;
#endif
template <>
struct std::hash<std::array<u8, 16>>
{
std::size_t operator()(const std::array<u8, 16>& array) const noexcept
{
std::size_t hash = 0;
std::hash<std::uint8_t> hasher;
for (auto byte : array)
{
hash ^= hasher(byte) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
}
return hash;
}
};
namespace np
{
class ip_address_translator
{
public:
static bool is_ipv6(u32 addr_be);
u32 register_ipv6(const std::array<u8, 16>& ipv6_addr);
sockaddr_in6 get_ipv6_sockaddr(u32 addr_be, u16 port_be);
private:
static constexpr u32 translation_range_begin = 0x00'00'00'01;
static constexpr u32 translation_range_end = 0x00'00'FF'FF;
shared_mutex mutex;
u32 cur_addr = translation_range_begin;
std::unordered_map<std::array<u8, 16>, u32> ipv6_to_ipv4;
std::vector<std::array<u8, 16>> ipv4_to_ipv6;
};
u32 register_ip(const flatbuffers::Vector<std::uint8_t>* vec);
enum class IPV6_SUPPORT : u8
{
IPV6_UNKNOWN,
IPV6_UNSUPPORTED,
IPV6_SUPPORTED,
};
bool is_ipv6_supported(std::optional<IPV6_SUPPORT> force_state = std::nullopt);
s32 sendto_possibly_ipv6(socket_type native_socket, const char* data, u32 size, const sockaddr_in* addr, int native_flags);
sockaddr_in6 sockaddr_to_sockaddr6(const sockaddr_in& addr);
sockaddr_in sockaddr6_to_sockaddr(const sockaddr_in6& addr);
void close_socket(socket_type socket);
void set_socket_non_blocking(socket_type socket);
} // namespace np

View file

@ -45,11 +45,10 @@ error_code generic_async_transaction_context::wait_for_completion()
return *result;
}
bool generic_async_transaction_context::set_result_and_wake(error_code err)
void generic_async_transaction_context::set_result_and_wake(error_code err)
{
result = err;
wake_cond.notify_one();
return true;
}
tus_ctx::tus_ctx(vm::cptr<SceNpCommunicationId> communicationId, vm::cptr<SceNpCommunicationPassphrase> passphrase)

View file

@ -23,7 +23,7 @@ struct generic_async_transaction_context
std::optional<s32> get_transaction_status();
void abort_transaction();
error_code wait_for_completion();
bool set_result_and_wake(error_code err);
void set_result_and_wake(error_code err);
shared_mutex mutex;
std::condition_variable_any wake_cond, completion_cond;

View file

@ -788,7 +788,14 @@ namespace np
break;
std::lock_guard lock(mutex_rpcn);
rpcn = rpcn::rpcn_client::get_instance();
bool was_already_started = true;
if (!rpcn)
{
rpcn = rpcn::rpcn_client::get_instance();
was_already_started = false;
}
// Make sure we're connected
@ -808,7 +815,8 @@ namespace np
return;
}
rsx::overlays::queue_message(localized_string_id::RPCN_SUCCESS_LOGGED_ON);
if (!was_already_started)
rsx::overlays::queue_message(localized_string_id::RPCN_SUCCESS_LOGGED_ON);
string_to_online_name(rpcn->get_online_name(), online_name);
string_to_avatar_url(rpcn->get_avatar_url(), avatar_url);
@ -836,11 +844,20 @@ namespace np
{
np_memory.release();
manager_cb = {};
manager_cb_arg = {};
basic_handler_registered = false;
room_event_cb = {};
room_event_cb_ctx = 0;
room_event_cb_arg = {};
room_msg_cb = {};
room_msg_cb_ctx = 0;
room_msg_cb_arg = {};
if (g_cfg.net.psn_status == np_psn_status::psn_rpcn)
{
rpcn_log.notice("Disconnecting from RPCN!");
std::lock_guard lock(mutex_rpcn);
rpcn.reset();
rpcn_log.notice("Setting RPCN state to disconnected!");
rpcn->reset_state();
}
}
@ -854,10 +871,13 @@ namespace np
auto& data = ::at32(match2_req_results, event_key);
data.apply_relocations(dest_addr);
vm::ptr<void> dest = vm::cast(dest_addr);
const u32 size_copied = std::min(size, data.size());
u32 size_copied = std::min(size, data.size());
memcpy(dest.get_ptr(), data.data(), size_copied);
if (dest_addr && size_copied)
{
vm::ptr<void> dest = vm::cast(dest_addr);
memcpy(dest.get_ptr(), data.data(), size_copied);
}
np_memory.free(data.addr());
match2_req_results.erase(event_key);
@ -1073,61 +1093,63 @@ namespace np
auto replies = rpcn->get_replies();
for (auto& reply : replies)
{
const u16 command = reply.second.first;
const rpcn::CommandType command = static_cast<rpcn::CommandType>(reply.second.first);
const u32 req_id = reply.first;
std::vector<u8>& data = reply.second.second;
// Every reply should at least contain a return value/error code
ensure(data.size() >= 1);
const auto error = static_cast<rpcn::ErrorType>(data[0]);
vec_stream reply_data(data, 1);
switch (command)
{
case rpcn::CommandType::GetWorldList: reply_get_world_list(req_id, data); break;
case rpcn::CommandType::CreateRoom: reply_create_join_room(req_id, data); break;
case rpcn::CommandType::JoinRoom: reply_join_room(req_id, data); break;
case rpcn::CommandType::LeaveRoom: reply_leave_room(req_id, data); break;
case rpcn::CommandType::SearchRoom: reply_search_room(req_id, data); break;
case rpcn::CommandType::GetRoomDataExternalList: reply_get_roomdata_external_list(req_id, data); break;
case rpcn::CommandType::SetRoomDataExternal: reply_set_roomdata_external(req_id, data); break;
case rpcn::CommandType::GetRoomDataInternal: reply_get_roomdata_internal(req_id, data); break;
case rpcn::CommandType::SetRoomDataInternal: reply_set_roomdata_internal(req_id, data); break;
case rpcn::CommandType::GetRoomMemberDataInternal: reply_get_roommemberdata_internal(req_id, data); break;
case rpcn::CommandType::SetRoomMemberDataInternal: reply_set_roommemberdata_internal(req_id, data); break;
case rpcn::CommandType::SetUserInfo: reply_set_userinfo(req_id, data); break;
case rpcn::CommandType::PingRoomOwner: reply_get_ping_info(req_id, data); break;
case rpcn::CommandType::SendRoomMessage: reply_send_room_message(req_id, data); break;
case rpcn::CommandType::RequestSignalingInfos: reply_req_sign_infos(req_id, data); break;
case rpcn::CommandType::RequestTicket: reply_req_ticket(req_id, data); break;
case rpcn::CommandType::GetBoardInfos: reply_get_board_infos(req_id, data); break;
case rpcn::CommandType::RecordScore: reply_record_score(req_id, data); break;
case rpcn::CommandType::RecordScoreData: reply_record_score_data(req_id, data); break;
case rpcn::CommandType::GetScoreData: reply_get_score_data(req_id, data); break;
case rpcn::CommandType::GetScoreRange: reply_get_score_range(req_id, data); break;
case rpcn::CommandType::GetScoreFriends: reply_get_score_friends(req_id, data); break;
case rpcn::CommandType::GetScoreNpid: reply_get_score_npid(req_id, data); break;
case rpcn::CommandType::TusSetMultiSlotVariable: reply_tus_set_multislot_variable(req_id, data); break;
case rpcn::CommandType::TusGetMultiSlotVariable: reply_tus_get_multislot_variable(req_id, data); break;
case rpcn::CommandType::TusGetMultiUserVariable: reply_tus_get_multiuser_variable(req_id, data); break;
case rpcn::CommandType::TusGetFriendsVariable: reply_tus_get_friends_variable(req_id, data); break;
case rpcn::CommandType::TusAddAndGetVariable: reply_tus_add_and_get_variable(req_id, data); break;
case rpcn::CommandType::TusTryAndSetVariable: reply_tus_try_and_set_variable(req_id, data); break;
case rpcn::CommandType::TusDeleteMultiSlotVariable: reply_tus_delete_multislot_variable(req_id, data); break;
case rpcn::CommandType::TusSetData: reply_tus_set_data(req_id, data); break;
case rpcn::CommandType::TusGetData: reply_tus_get_data(req_id, data); break;
case rpcn::CommandType::TusGetMultiSlotDataStatus: reply_tus_get_multislot_data_status(req_id, data); break;
case rpcn::CommandType::TusGetMultiUserDataStatus: reply_tus_get_multiuser_data_status(req_id, data); break;
case rpcn::CommandType::TusGetFriendsDataStatus: reply_tus_get_friends_data_status(req_id, data); break;
case rpcn::CommandType::TusDeleteMultiSlotData: reply_tus_delete_multislot_data(req_id, data); break;
case rpcn::CommandType::CreateRoomGUI: reply_create_room_gui(req_id, data); break;
case rpcn::CommandType::JoinRoomGUI: reply_join_room_gui(req_id, data); break;
case rpcn::CommandType::LeaveRoomGUI: reply_leave_room_gui(req_id, data); break;
case rpcn::CommandType::GetRoomListGUI: reply_get_room_list_gui(req_id, data); break;
case rpcn::CommandType::SetRoomSearchFlagGUI: reply_set_room_search_flag_gui(req_id, data); break;
case rpcn::CommandType::GetRoomSearchFlagGUI: reply_get_room_search_flag_gui(req_id, data); break;
case rpcn::CommandType::SetRoomInfoGUI: reply_set_room_info_gui(req_id, data); break;
case rpcn::CommandType::GetRoomInfoGUI: reply_get_room_info_gui(req_id, data); break;
case rpcn::CommandType::QuickMatchGUI: reply_quickmatch_gui(req_id, data); break;
case rpcn::CommandType::SearchJoinRoomGUI: reply_searchjoin_gui(req_id, data); break;
case rpcn::CommandType::GetWorldList: reply_get_world_list(req_id, error, reply_data); break;
case rpcn::CommandType::CreateRoom: reply_create_join_room(req_id, error, reply_data); break;
case rpcn::CommandType::JoinRoom: reply_join_room(req_id, error, reply_data); break;
case rpcn::CommandType::LeaveRoom: reply_leave_room(req_id, error, reply_data); break;
case rpcn::CommandType::SearchRoom: reply_search_room(req_id, error, reply_data); break;
case rpcn::CommandType::GetRoomDataExternalList: reply_get_roomdata_external_list(req_id, error, reply_data); break;
case rpcn::CommandType::SetRoomDataExternal: reply_set_roomdata_external(req_id, error); break;
case rpcn::CommandType::GetRoomDataInternal: reply_get_roomdata_internal(req_id, error, reply_data); break;
case rpcn::CommandType::SetRoomDataInternal: reply_set_roomdata_internal(req_id, error); break;
case rpcn::CommandType::GetRoomMemberDataInternal: reply_get_roommemberdata_internal(req_id, error, reply_data); break;
case rpcn::CommandType::SetRoomMemberDataInternal: reply_set_roommemberdata_internal(req_id, error); break;
case rpcn::CommandType::SetUserInfo: reply_set_userinfo(req_id, error); break;
case rpcn::CommandType::PingRoomOwner: reply_get_ping_info(req_id, error, reply_data); break;
case rpcn::CommandType::SendRoomMessage: reply_send_room_message(req_id, error); break;
case rpcn::CommandType::RequestSignalingInfos: reply_req_sign_infos(req_id, error, reply_data); break;
case rpcn::CommandType::RequestTicket: reply_req_ticket(req_id, error, reply_data); break;
case rpcn::CommandType::GetBoardInfos: reply_get_board_infos(req_id, error, reply_data); break;
case rpcn::CommandType::RecordScore: reply_record_score(req_id, error, reply_data); break;
case rpcn::CommandType::RecordScoreData: reply_record_score_data(req_id, error); break;
case rpcn::CommandType::GetScoreData: reply_get_score_data(req_id, error, reply_data); break;
case rpcn::CommandType::GetScoreRange: reply_get_score_range(req_id, error, reply_data); break;
case rpcn::CommandType::GetScoreFriends: reply_get_score_friends(req_id, error, reply_data); break;
case rpcn::CommandType::GetScoreNpid: reply_get_score_npid(req_id, error, reply_data); break;
case rpcn::CommandType::TusSetMultiSlotVariable: reply_tus_set_multislot_variable(req_id, error); break;
case rpcn::CommandType::TusGetMultiSlotVariable: reply_tus_get_multislot_variable(req_id, error, reply_data); break;
case rpcn::CommandType::TusGetMultiUserVariable: reply_tus_get_multiuser_variable(req_id, error, reply_data); break;
case rpcn::CommandType::TusGetFriendsVariable: reply_tus_get_friends_variable(req_id, error, reply_data); break;
case rpcn::CommandType::TusAddAndGetVariable: reply_tus_add_and_get_variable(req_id, error, reply_data); break;
case rpcn::CommandType::TusTryAndSetVariable: reply_tus_try_and_set_variable(req_id, error, reply_data); break;
case rpcn::CommandType::TusDeleteMultiSlotVariable: reply_tus_delete_multislot_variable(req_id, error); break;
case rpcn::CommandType::TusSetData: reply_tus_set_data(req_id, error); break;
case rpcn::CommandType::TusGetData: reply_tus_get_data(req_id, error, reply_data); break;
case rpcn::CommandType::TusGetMultiSlotDataStatus: reply_tus_get_multislot_data_status(req_id, error, reply_data); break;
case rpcn::CommandType::TusGetMultiUserDataStatus: reply_tus_get_multiuser_data_status(req_id, error, reply_data); break;
case rpcn::CommandType::TusGetFriendsDataStatus: reply_tus_get_friends_data_status(req_id, error, reply_data); break;
case rpcn::CommandType::TusDeleteMultiSlotData: reply_tus_delete_multislot_data(req_id, error); break;
case rpcn::CommandType::CreateRoomGUI: reply_create_room_gui(req_id, error, reply_data); break;
case rpcn::CommandType::JoinRoomGUI: reply_join_room_gui(req_id, error, reply_data); break;
case rpcn::CommandType::LeaveRoomGUI: reply_leave_room_gui(req_id, error, reply_data); break;
case rpcn::CommandType::GetRoomListGUI: reply_get_room_list_gui(req_id, error, reply_data); break;
case rpcn::CommandType::SetRoomSearchFlagGUI: reply_set_room_search_flag_gui(req_id, error); break;
case rpcn::CommandType::GetRoomSearchFlagGUI: reply_get_room_search_flag_gui(req_id, error, reply_data); break;
case rpcn::CommandType::SetRoomInfoGUI: reply_set_room_info_gui(req_id, error); break;
case rpcn::CommandType::GetRoomInfoGUI: reply_get_room_info_gui(req_id, error, reply_data); break;
case rpcn::CommandType::QuickMatchGUI: reply_quickmatch_gui(req_id, error, reply_data); break;
case rpcn::CommandType::SearchJoinRoomGUI: reply_searchjoin_gui(req_id, error, reply_data); break;
default: fmt::throw_exception("Unknown reply(%d) received!", command); break;
}
}
@ -1142,9 +1164,8 @@ namespace np
case rpcn::NotificationType::RoomDestroyed: notif_room_destroyed(notif.second); break;
case rpcn::NotificationType::UpdatedRoomDataInternal: notif_updated_room_data_internal(notif.second); break;
case rpcn::NotificationType::UpdatedRoomMemberDataInternal: notif_updated_room_member_data_internal(notif.second); break;
case rpcn::NotificationType::SignalP2PConnect: notif_p2p_connect(notif.second); break;
case rpcn::NotificationType::RoomMessageReceived: notif_room_message_received(notif.second); break;
case rpcn::NotificationType::SignalingInfo: notif_signaling_info(notif.second); break;
case rpcn::NotificationType::SignalingHelper: notif_signaling_helper(notif.second); break;
case rpcn::NotificationType::MemberJoinedRoomGUI: notif_member_joined_room_gui(notif.second); break;
case rpcn::NotificationType::MemberLeftRoomGUI: notif_member_left_room_gui(notif.second); break;
case rpcn::NotificationType::RoomDisappearedGUI: notif_room_disappeared_gui(notif.second); break;

View file

@ -298,8 +298,7 @@ namespace np
void notif_room_destroyed(std::vector<u8>& data);
void notif_updated_room_data_internal(std::vector<u8>& data);
void notif_updated_room_member_data_internal(std::vector<u8>& data);
void notif_p2p_connect(std::vector<u8>& data);
void notif_signaling_info(std::vector<u8>& data);
void notif_signaling_helper(std::vector<u8>& data);
void notif_room_message_received(std::vector<u8>& data);
void generic_gui_notification_handler(std::vector<u8>& data, std::string_view name, s32 notification_type);
@ -312,60 +311,60 @@ namespace np
void notif_quickmatch_complete_gui(std::vector<u8>& data);
// Reply handlers
bool reply_get_world_list(u32 req_id, std::vector<u8>& reply_data);
bool reply_create_join_room(u32 req_id, std::vector<u8>& reply_data);
bool reply_join_room(u32 req_id, std::vector<u8>& reply_data);
bool reply_leave_room(u32 req_id, std::vector<u8>& reply_data);
bool reply_search_room(u32 req_id, std::vector<u8>& reply_data);
bool reply_get_roomdata_external_list(u32 req_id, std::vector<u8>& reply_data);
bool reply_set_roomdata_external(u32 req_id, std::vector<u8>& reply_data);
bool reply_get_roomdata_internal(u32 req_id, std::vector<u8>& reply_data);
bool reply_set_roomdata_internal(u32 req_id, std::vector<u8>& reply_data);
bool reply_set_roommemberdata_internal(u32 req_id, std::vector<u8>& reply_data);
bool reply_get_roommemberdata_internal(u32 req_id, std::vector<u8>& reply_data);
bool reply_set_userinfo(u32 req_id, std::vector<u8>& reply_data);
bool reply_get_ping_info(u32 req_id, std::vector<u8>& reply_data);
bool reply_send_room_message(u32 req_id, std::vector<u8>& reply_data);
bool reply_req_sign_infos(u32 req_id, std::vector<u8>& reply_data);
bool reply_req_ticket(u32 req_id, std::vector<u8>& reply_data);
bool reply_get_board_infos(u32 req_id, std::vector<u8>& reply_data);
bool reply_record_score(u32 req_id, std::vector<u8>& reply_data);
bool reply_record_score_data(u32 req_id, std::vector<u8>& reply_data);
bool reply_get_score_data(u32 req_id, std::vector<u8>& reply_data);
bool reply_get_score_range(u32 req_id, std::vector<u8>& reply_data);
bool reply_get_score_friends(u32 req_id, std::vector<u8>& reply_data);
bool reply_get_score_npid(u32 req_id, std::vector<u8>& reply_data);
bool reply_tus_set_multislot_variable(u32 req_id, std::vector<u8>& reply_data);
bool reply_tus_get_multislot_variable(u32 req_id, std::vector<u8>& reply_data);
bool reply_tus_get_multiuser_variable(u32 req_id, std::vector<u8>& reply_data);
bool reply_tus_get_friends_variable(u32 req_id, std::vector<u8>& reply_data);
bool reply_tus_add_and_get_variable(u32 req_id, std::vector<u8>& reply_data);
bool reply_tus_try_and_set_variable(u32 req_id, std::vector<u8>& reply_data);
bool reply_tus_delete_multislot_variable(u32 req_id, std::vector<u8>& reply_data);
bool reply_tus_set_data(u32 req_id, std::vector<u8>& reply_data);
bool reply_tus_get_data(u32 req_id, std::vector<u8>& reply_data);
bool reply_tus_get_multislot_data_status(u32 req_id, std::vector<u8>& reply_data);
bool reply_tus_get_multiuser_data_status(u32 req_id, std::vector<u8>& reply_data);
bool reply_tus_get_friends_data_status(u32 req_id, std::vector<u8>& reply_data);
bool reply_tus_delete_multislot_data(u32 req_id, std::vector<u8>& reply_data);
bool reply_create_room_gui(u32 req_id, std::vector<u8>& reply_data);
bool reply_join_room_gui(u32 req_id, std::vector<u8>& reply_data);
bool reply_leave_room_gui(u32 req_id, std::vector<u8>& reply_data);
bool reply_get_room_list_gui(u32 req_id, std::vector<u8>& reply_data);
bool reply_set_room_search_flag_gui(u32 req_id, std::vector<u8>& reply_data);
bool reply_get_room_search_flag_gui(u32 req_id, std::vector<u8>& reply_data);
bool reply_set_room_info_gui(u32 req_id, std::vector<u8>& reply_data);
bool reply_get_room_info_gui(u32 req_id, std::vector<u8>& reply_data);
bool reply_quickmatch_gui(u32 req_id, std::vector<u8>& reply_data);
bool reply_searchjoin_gui(u32 req_id, std::vector<u8>& reply_data);
void reply_get_world_list(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_create_join_room(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_join_room(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_leave_room(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_search_room(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_get_roomdata_external_list(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_set_roomdata_external(u32 req_id, rpcn::ErrorType error);
void reply_get_roomdata_internal(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_set_roomdata_internal(u32 req_id, rpcn::ErrorType error);
void reply_set_roommemberdata_internal(u32 req_id, rpcn::ErrorType error);
void reply_get_roommemberdata_internal(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_set_userinfo(u32 req_id, rpcn::ErrorType error);
void reply_get_ping_info(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_send_room_message(u32 req_id, rpcn::ErrorType error);
void reply_req_sign_infos(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_req_ticket(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_get_board_infos(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_record_score(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_record_score_data(u32 req_id, rpcn::ErrorType error);
void reply_get_score_data(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_get_score_range(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_get_score_friends(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_get_score_npid(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_tus_set_multislot_variable(u32 req_id, rpcn::ErrorType error);
void reply_tus_get_multislot_variable(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_tus_get_multiuser_variable(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_tus_get_friends_variable(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_tus_add_and_get_variable(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_tus_try_and_set_variable(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_tus_delete_multislot_variable(u32 req_id, rpcn::ErrorType error);
void reply_tus_set_data(u32 req_id, rpcn::ErrorType error);
void reply_tus_get_data(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_tus_get_multislot_data_status(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_tus_get_multiuser_data_status(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_tus_get_friends_data_status(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_tus_delete_multislot_data(u32 req_id, rpcn::ErrorType error);
void reply_create_room_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_join_room_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_leave_room_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_get_room_list_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_set_room_search_flag_gui(u32 req_id, rpcn::ErrorType error);
void reply_get_room_search_flag_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_set_room_info_gui(u32 req_id, rpcn::ErrorType error);
void reply_get_room_info_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_quickmatch_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_searchjoin_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
// Helper functions
std::pair<bool, bool> get_match2_context_options(u32 ctx_id);
bool handle_GetScoreResponse(u32 req_id, std::vector<u8>& reply_data, bool simple_result = false);
bool handle_tus_no_data(u32 req_id, std::vector<u8>& reply_data);
bool handle_TusVarResponse(u32 req_id, std::vector<u8>& reply_data);
bool handle_TusVariable(u32 req_id, std::vector<u8>& reply_data);
bool handle_TusDataStatusResponse(u32 req_id, std::vector<u8>& reply_data);
void handle_GetScoreResponse(u32 req_id, rpcn::ErrorType error, vec_stream& reply, bool simple_result = false);
void handle_tus_no_data(u32 req_id, rpcn::ErrorType error);
void handle_TusVarResponse(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void handle_TusVariable(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void handle_TusDataStatusResponse(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
struct callback_info
{

View file

@ -7,6 +7,7 @@
#include "Emu/NP/np_structs_extra.h"
#include "Emu/NP/fb_helpers.h"
#include "Emu/NP/signaling_handler.h"
#include "Emu/NP/ip_address.h"
LOG_CHANNEL(rpcn_log, "rpcn");
@ -15,8 +16,7 @@ namespace np
void np_handler::notif_user_joined_room(std::vector<u8>& data)
{
vec_stream noti(data);
u64 room_id = noti.get<u64>();
const auto* update_info = noti.get_flatbuffer<RoomMemberUpdateInfo>();
const auto* notification = noti.get_flatbuffer<NotificationUserJoinedRoom>();
if (noti.is_error())
{
@ -24,20 +24,44 @@ namespace np
return;
}
ensure(notification->update_info());
const u32 event_key = get_event_key();
auto [include_onlinename, include_avatarurl] = get_match2_context_options(room_event_cb_ctx);
const auto [include_onlinename, include_avatarurl] = get_match2_context_options(room_event_cb_ctx);
const auto room_id = notification->room_id();
auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_RoomMemberUpdateInfo, sizeof(SceNpMatching2RoomMemberUpdateInfo));
auto* notif_data = reinterpret_cast<SceNpMatching2RoomMemberUpdateInfo*>(edata.data());
RoomMemberUpdateInfo_to_SceNpMatching2RoomMemberUpdateInfo(edata, update_info, notif_data, include_onlinename, include_avatarurl);
RoomMemberUpdateInfo_to_SceNpMatching2RoomMemberUpdateInfo(edata, notification->update_info(), notif_data, include_onlinename, include_avatarurl);
np_memory.shrink_allocation(edata.addr(), edata.size());
// Ensures we do not call the callback if the room is not in the cache(ie we left the room already)
if (!np_cache.add_member(room_id, notif_data->roomMemberDataInternal.get_ptr()))
{
get_match2_event(event_key, 0, 0);
return;
}
rpcn_log.notice("Received notification that user %s(%d) joined the room(%d)", notif_data->roomMemberDataInternal->userInfo.npId.handle.data, notif_data->roomMemberDataInternal->memberId, room_id);
extra_nps::print_SceNpMatching2RoomMemberDataInternal(notif_data->roomMemberDataInternal.get_ptr());
// We initiate signaling if necessary
if (const auto* signaling_info = notification->signaling())
{
const u32 addr_p2p = register_ip(signaling_info->ip());
const u16 port_p2p = signaling_info->port();
const u16 member_id = notif_data->roomMemberDataInternal->memberId;
const SceNpId& npid = notif_data->roomMemberDataInternal->userInfo.npId;
rpcn_log.notice("Join notification told to connect to member(%d=%s) of room(%d): %s:%d", member_id, reinterpret_cast<const char*>(npid.handle.data), room_id, ip_to_string(addr_p2p), port_p2p);
// Attempt Signaling
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
const u32 conn_id = sigh.init_sig2(npid, room_id, member_id);
sigh.start_sig(conn_id, addr_p2p, port_p2p);
}
if (room_event_cb)
{
sysutil_register_cb([room_event_cb = this->room_event_cb, room_id, event_key, room_event_cb_ctx = this->room_event_cb_ctx, room_event_cb_arg = this->room_event_cb_arg, size = edata.size()](ppu_thread& cb_ppu) -> s32
@ -68,8 +92,12 @@ namespace np
RoomMemberUpdateInfo_to_SceNpMatching2RoomMemberUpdateInfo(edata, update_info, notif_data, include_onlinename, include_avatarurl);
np_memory.shrink_allocation(edata.addr(), edata.size());
// Ensures we do not call the callback if the room is not in the cache(ie we left the room already)
if (!np_cache.del_member(room_id, notif_data->roomMemberDataInternal->memberId))
{
get_match2_event(event_key, 0, 0);
return;
}
rpcn_log.notice("Received notification that user %s(%d) left the room(%d)", notif_data->roomMemberDataInternal->userInfo.npId.handle.data, notif_data->roomMemberDataInternal->memberId, room_id);
extra_nps::print_SceNpMatching2RoomMemberDataInternal(notif_data->roomMemberDataInternal.get_ptr());
@ -175,7 +203,10 @@ namespace np
np_memory.shrink_allocation(edata.addr(), edata.size());
if (!np_cache.add_member(room_id, notif_data->newRoomMemberDataInternal.get_ptr()))
{
get_match2_event(event_key, 0, 0);
return;
}
rpcn_log.notice("Received notification that user's %s(%d) room (%d) data was updated", notif_data->newRoomMemberDataInternal->userInfo.npId.handle.data, notif_data->newRoomMemberDataInternal->memberId, room_id);
extra_nps::print_SceNpMatching2RoomMemberDataInternal(notif_data->newRoomMemberDataInternal.get_ptr());
@ -223,33 +254,7 @@ namespace np
}
}
void np_handler::notif_p2p_connect(std::vector<u8>& data)
{
vec_stream noti(data);
const u64 room_id = noti.get<u64>();
const u16 member_id = noti.get<u16>();
const u16 port_p2p = noti.get<u16>();
const u32 addr_p2p = noti.get<u32>();
if (noti.is_error())
{
rpcn_log.error("Received faulty SignalP2PConnect notification");
return;
}
auto [res, npid] = np_cache.get_npid(room_id, member_id);
if (!npid)
return;
rpcn_log.notice("Received notification to connect to member(%d=%s) of room(%d): %s:%d", member_id, reinterpret_cast<const char*>((*npid).handle.data), room_id, ip_to_string(addr_p2p), port_p2p);
// Attempt Signaling
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
const u32 conn_id = sigh.init_sig2(*npid, room_id, member_id);
sigh.start_sig(conn_id, addr_p2p, port_p2p);
}
void np_handler::notif_signaling_info(std::vector<u8>& data)
void np_handler::notif_signaling_helper(std::vector<u8>& data)
{
vec_stream noti(data);
const u32 addr_p2p = noti.get<u32>();
@ -258,7 +263,7 @@ namespace np
if (noti.is_error())
{
rpcn_log.error("Received faulty SignalingInfo notification");
rpcn_log.error("Received faulty SignalingHelper notification");
return;
}

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,4 @@
#include "Emu/NP/rpcn_types.h"
#include "stdafx.h"
#include "Emu/Cell/PPUCallback.h"
#include "Emu/Memory/vm_ptr.h"
@ -156,20 +157,16 @@ namespace np
return CELL_OK;
}
bool np_handler::reply_create_room_gui(u32 req_id, std::vector<u8>& reply_data)
void np_handler::reply_create_room_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
return;
ensure(!rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])), "Unexpected error in CreateRoomGUI reply");
vec_stream reply(reply_data, 1);
ensure(error == rpcn::ErrorType::NoError, "Unexpected error in CreateRoomGUI reply");
const auto* resp = reply.get_flatbuffer<MatchingRoomStatus>();
if (reply.is_error())
return error_and_disconnect("Malformed reply to CreateRoomGUI command");
ensure(!reply.is_error(), "Malformed reply to CreateRoomGUI command");
event_data edata(np_memory.allocate(MAX_SceNpMatchingJoinedRoomInfo_SIZE), sizeof(SceNpMatchingJoinedRoomInfo), MAX_SceNpMatchingJoinedRoomInfo_SIZE);
auto* room_info = reinterpret_cast<SceNpMatchingJoinedRoomInfo*>(edata.data());
@ -182,8 +179,6 @@ namespace np
set_gui_result(SCE_NP_MATCHING_GUI_EVENT_CREATE_ROOM, std::move(edata));
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_CREATE_ROOM, 0);
gui_epilog(ctx);
return true;
}
error_code np_handler::join_room_gui(u32 ctx_id, vm::ptr<SceNpRoomId> roomid, vm::ptr<SceNpMatchingGUIHandler> handler, vm::ptr<void> arg)
@ -201,44 +196,42 @@ namespace np
return CELL_OK;
}
bool np_handler::reply_join_room_gui(u32 req_id, std::vector<u8>& reply_data)
void np_handler::reply_join_room_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
return;
if (rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])))
s32 error_code = CELL_OK;
switch (error)
{
s32 error = -1;
switch (static_cast<rpcn::ErrorType>(reply_data[0]))
{
case rpcn::ErrorType::RoomMissing:
error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM;
break;
case rpcn::ErrorType::RoomFull:
// Might also be SCE_NP_MATCHING_SERVER_ERROR_ACCESS_FORBIDDEN or SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED ?
error = SCE_NP_MATCHING_SERVER_ERROR_ROOM_CLOSED;
break;
case rpcn::ErrorType::RoomAlreadyJoined:
error = SCE_NP_MATCHING_SERVER_ERROR_ACCESS_FORBIDDEN;
break;
default:
fmt::throw_exception("Unexpected error in JoinRoomGUI reply: %d", reply_data[0]);
break;
}
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_JOIN_ROOM, error);
gui_epilog(ctx);
return true;
case rpcn::ErrorType::NoError: break;
case rpcn::ErrorType::RoomMissing:
error_code = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM;
break;
case rpcn::ErrorType::RoomFull:
// Might also be SCE_NP_MATCHING_SERVER_ERROR_ACCESS_FORBIDDEN or SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED ?
error_code = SCE_NP_MATCHING_SERVER_ERROR_ROOM_CLOSED;
break;
case rpcn::ErrorType::RoomAlreadyJoined:
error_code = SCE_NP_MATCHING_SERVER_ERROR_ACCESS_FORBIDDEN;
break;
default:
fmt::throw_exception("Unexpected error in JoinRoomGUI reply: %d", static_cast<u8>(error));
break;
}
vec_stream reply(reply_data, 1);
const auto* resp = reply.get_flatbuffer<MatchingRoomStatus>();
if (error_code != 0)
{
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_JOIN_ROOM, error_code);
gui_epilog(ctx);
return;
}
if (reply.is_error())
return error_and_disconnect("Malformed reply to JoinRoomGUI command");
const auto* resp = reply.get_flatbuffer<MatchingRoomStatus>();
ensure(!reply.is_error(), "Malformed reply to JoinRoomGUI command");
event_data edata(np_memory.allocate(MAX_SceNpMatchingJoinedRoomInfo_SIZE), sizeof(SceNpMatchingJoinedRoomInfo), MAX_SceNpMatchingJoinedRoomInfo_SIZE);
auto* room_info = reinterpret_cast<SceNpMatchingJoinedRoomInfo*>(edata.data());
@ -257,8 +250,6 @@ namespace np
set_gui_result(SCE_NP_MATCHING_GUI_EVENT_JOIN_ROOM, std::move(edata));
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_JOIN_ROOM, 0);
gui_epilog(ctx);
return true;
}
error_code np_handler::leave_room_gui(u32 ctx_id, vm::cptr<SceNpRoomId> roomid)
@ -278,36 +269,29 @@ namespace np
return not_an_error(req_id);
}
bool np_handler::reply_leave_room_gui(u32 req_id, std::vector<u8>& reply_data)
void np_handler::reply_leave_room_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
return;
if (rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])))
s32 error_code = CELL_OK;
switch (error)
{
s32 error = -1;
switch (static_cast<rpcn::ErrorType>(reply_data[0]))
{
case rpcn::ErrorType::NotFound:
error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM;
break;
default:
fmt::throw_exception("Unexpected error in LeaveRoomGUI reply: %d", reply_data[0]);
break;
}
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_LEAVE_ROOM_DONE, error);
return true;
case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; break;
default: fmt::throw_exception("Unexpected error in LeaveRoomGUI reply: %d", static_cast<u8>(error));
}
vec_stream reply(reply_data, 1);
const auto* resp = reply.get_flatbuffer<MatchingRoomStatus>();
if (error_code != CELL_OK)
{
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_LEAVE_ROOM_DONE, error_code);
return;
}
if (reply.is_error())
return error_and_disconnect("Malformed reply to LeaveRoomGUI command");
const auto* resp = reply.get_flatbuffer<MatchingRoomStatus>();
ensure(!reply.is_error(), "Malformed reply to LeaveRoomGUI command");
event_data edata(np_memory.allocate(MAX_SceNpMatchingRoomStatus_SIZE), sizeof(SceNpMatchingRoomStatus), MAX_SceNpMatchingRoomStatus_SIZE);
auto* room_status = reinterpret_cast<SceNpMatchingRoomStatus*>(edata.data());
@ -320,7 +304,6 @@ namespace np
gui_notifications.list.emplace(std::make_pair(gui_notifications.current_gui_ctx_id, req_id), gui_notification{.event = SCE_NP_MATCHING_EVENT_LEAVE_ROOM_DONE, .edata = std::move(edata)});
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_LEAVE_ROOM_DONE, 0);
return true;
}
error_code np_handler::get_room_list_gui(u32 ctx_id, vm::cptr<SceNpCommunicationId> communicationId, vm::ptr<SceNpMatchingReqRange> range, vm::ptr<SceNpMatchingSearchCondition> cond, vm::ptr<SceNpMatchingAttr> attr, vm::ptr<SceNpMatchingGUIHandler> handler, vm::ptr<void> arg, bool limit)
@ -351,20 +334,16 @@ namespace np
return CELL_OK;
}
bool np_handler::reply_get_room_list_gui(u32 req_id, std::vector<u8>& reply_data)
void np_handler::reply_get_room_list_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
return;
ensure(!rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])), "Unexpected error in GetRoomListGUI reply");
vec_stream reply(reply_data, 1);
ensure(error == rpcn::ErrorType::NoError, "Unexpected error in GetRoomListGUI reply");
const auto* resp = reply.get_flatbuffer<MatchingRoomList>();
if (reply.is_error())
return error_and_disconnect("Malformed reply to GetRoomListGUI command");
ensure(!reply.is_error(), "Malformed reply to GetRoomListGUI command");
event_data edata(np_memory.allocate(MAX_SceNpMatchingRoomList_SIZE), sizeof(SceNpMatchingRoomList), MAX_SceNpMatchingRoomList_SIZE);
auto* room_list = reinterpret_cast<SceNpMatchingRoomList*>(edata.data());
@ -385,7 +364,6 @@ namespace np
}
gui_epilog(ctx);
return true;
}
error_code np_handler::set_room_search_flag_gui(u32 ctx_id, vm::ptr<SceNpLobbyId> /* lobby_id */, vm::ptr<SceNpRoomId> room_id, s32 flag)
@ -405,33 +383,24 @@ namespace np
return not_an_error(req_id);
}
bool np_handler::reply_set_room_search_flag_gui(u32 req_id, std::vector<u8>& reply_data)
void np_handler::reply_set_room_search_flag_gui(u32 req_id, rpcn::ErrorType error)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
return;
s32 error = 0;
s32 error_code = CELL_OK;
if (rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])))
switch (error)
{
switch (static_cast<rpcn::ErrorType>(reply_data[0]))
{
case rpcn::ErrorType::NotFound:
error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM;
break;
case rpcn::ErrorType::Unauthorized:
error = SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED;
break;
default:
fmt::throw_exception("Unexpected error in SetRoomSearchFlagGUI reply: %d", reply_data[0]);
break;
}
case rpcn::ErrorType::NoError: break;
case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; break;
case rpcn::ErrorType::Unauthorized: error_code = SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED; break;
default: fmt::throw_exception("Unexpected error in SetRoomSearchFlagGUI reply: %d", static_cast<u8>(error));
}
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_SET_ROOM_SEARCH_FLAG_DONE, error);
return true;
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_SET_ROOM_SEARCH_FLAG_DONE, error_code);
}
error_code np_handler::get_room_search_flag_gui(u32 ctx_id, vm::ptr<SceNpLobbyId> /* lobby_id */, vm::ptr<SceNpRoomId> room_id)
@ -451,36 +420,30 @@ namespace np
return not_an_error(req_id);
}
bool np_handler::reply_get_room_search_flag_gui(u32 req_id, std::vector<u8>& reply_data)
void np_handler::reply_get_room_search_flag_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
return;
if (rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])))
s32 error_code = CELL_OK;
switch (error)
{
s32 error = -1;
switch (static_cast<rpcn::ErrorType>(reply_data[0]))
{
case rpcn::ErrorType::NotFound:
error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM;
break;
default:
fmt::throw_exception("Unexpected error in GetRoomSearchFlagGUI reply: %d", reply_data[0]);
break;
}
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_SEARCH_FLAG_DONE, error);
return true;
case rpcn::ErrorType::NoError: break;
case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; break;
default: fmt::throw_exception("Unexpected error in GetRoomSearchFlagGUI reply: %d", static_cast<u8>(error));
}
vec_stream reply(reply_data, 1);
const auto* resp = reply.get_flatbuffer<MatchingRoom>();
if (error_code != CELL_OK)
{
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_SEARCH_FLAG_DONE, error_code);
return;
}
if (reply.is_error())
return error_and_disconnect("Malformed reply to GetRoomSearchFlagGUI command");
const auto* resp = reply.get_flatbuffer<MatchingRoom>();
ensure(!reply.is_error(), "Malformed reply to GetRoomSearchFlagGUI command");
event_data edata(np_memory.allocate(MAX_SceNpMatchingRoom_SIZE), sizeof(SceNpMatchingRoom), MAX_SceNpMatchingRoom_SIZE);
auto* room_info = reinterpret_cast<SceNpMatchingRoom*>(edata.data());
@ -491,7 +454,6 @@ namespace np
gui_notifications.list.emplace(std::make_pair(gui_notifications.current_gui_ctx_id, req_id), gui_notification{.event = SCE_NP_MATCHING_EVENT_GET_ROOM_SEARCH_FLAG_DONE, .edata = std::move(edata)});
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_SEARCH_FLAG_DONE, 0);
return true;
}
error_code np_handler::set_room_info_gui(u32 ctx_id, vm::ptr<SceNpLobbyId> /* lobby_id */, vm::ptr<SceNpRoomId> room_id, vm::ptr<SceNpMatchingAttr> attr)
@ -516,33 +478,23 @@ namespace np
return not_an_error(req_id);
}
bool np_handler::reply_set_room_info_gui(u32 req_id, std::vector<u8>& reply_data)
void np_handler::reply_set_room_info_gui(u32 req_id, rpcn::ErrorType error)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
return;
s32 error = 0;
s32 error_code = CELL_OK;
if (rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])))
switch (error)
{
switch (static_cast<rpcn::ErrorType>(reply_data[0]))
{
case rpcn::ErrorType::NotFound:
error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM;
break;
case rpcn::ErrorType::Unauthorized:
error = SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED;
break;
default:
fmt::throw_exception("Unexpected error in SetRoomInfoGUI reply: %d", reply_data[0]);
break;
}
case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; break;
case rpcn::ErrorType::Unauthorized: error_code = SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED; break;
default: fmt::throw_exception("Unexpected error in SetRoomInfoGUI reply: %d", static_cast<u8>(error));
}
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_SET_ROOM_INFO_DONE, error);
return true;
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_SET_ROOM_INFO_DONE, error_code);
}
error_code np_handler::get_room_info_gui(u32 ctx_id, vm::ptr<SceNpLobbyId> /* lobby_id */, vm::ptr<SceNpRoomId> room_id, vm::ptr<SceNpMatchingAttr> attr)
@ -562,36 +514,30 @@ namespace np
return not_an_error(req_id);
}
bool np_handler::reply_get_room_info_gui(u32 req_id, std::vector<u8>& reply_data)
void np_handler::reply_get_room_info_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
return;
if (rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])))
s32 error_code = CELL_OK;
switch (error)
{
s32 error = -1;
switch (static_cast<rpcn::ErrorType>(reply_data[0]))
{
case rpcn::ErrorType::NotFound:
error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM;
break;
default:
fmt::throw_exception("Unexpected error in GetRoomInfoGUI reply: %d", reply_data[0]);
break;
}
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_INFO_DONE, error);
return true;
case rpcn::ErrorType::NoError: break;
case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM; break;
default: fmt::throw_exception("Unexpected error in GetRoomInfoGUI reply: %d", static_cast<u8>(error));
}
vec_stream reply(reply_data, 1);
const auto* resp = reply.get_flatbuffer<MatchingRoom>();
if (error_code != CELL_OK)
{
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_INFO_DONE, error_code);
return;
}
if (reply.is_error())
return error_and_disconnect("Malformed reply to GetRoomInfoGUI command");
const auto* resp = reply.get_flatbuffer<MatchingRoom>();
ensure(!reply.is_error(), "Malformed reply to GetRoomInfoGUI command");
event_data edata(np_memory.allocate(MAX_SceNpMatchingRoom_SIZE), sizeof(SceNpMatchingRoom), MAX_SceNpMatchingRoom_SIZE);
auto* room_info = reinterpret_cast<SceNpMatchingRoom*>(edata.data());
@ -602,7 +548,6 @@ namespace np
gui_notifications.list.emplace(std::make_pair(gui_notifications.current_gui_ctx_id, req_id), gui_notification{.event = SCE_NP_MATCHING_EVENT_GET_ROOM_INFO_DONE, .edata = std::move(edata)});
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_INFO_DONE, 0);
return true;
}
error_code np_handler::quickmatch_gui(u32 ctx_id, vm::cptr<SceNpCommunicationId> communicationId, vm::cptr<SceNpMatchingSearchCondition> cond, s32 available_num, s32 timeout, vm::ptr<SceNpMatchingGUIHandler> handler, vm::ptr<void> arg)
@ -621,20 +566,16 @@ namespace np
return CELL_OK;
}
bool np_handler::reply_quickmatch_gui(u32 req_id, std::vector<u8>& reply_data)
void np_handler::reply_quickmatch_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
return;
ensure(!rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])), "Unexpected error in QuickMatchGUI reply");
vec_stream reply(reply_data, 1);
ensure(error == rpcn::ErrorType::NoError, "Unexpected error in QuickMatchGUI reply");
const auto* resp = reply.get_flatbuffer<MatchingGuiRoomId>();
if (reply.is_error())
return error_and_disconnect("Malformed reply to QuickMatchGUI command");
ensure(!reply.is_error(), "Malformed reply to QuickMatchGUI command");
SceNpRoomId room_id{};
ensure(resp->id() && resp->id()->size() == sizeof(SceNpRoomId::opt));
@ -675,7 +616,6 @@ namespace np
ctx->wakey = 0;
auto& thread = *ctx->thread;
thread(room_id);
return true;
}
error_code np_handler::searchjoin_gui(u32 ctx_id, vm::cptr<SceNpCommunicationId> communicationId, vm::cptr<SceNpMatchingSearchCondition> cond, vm::cptr<SceNpMatchingAttr> attr, vm::ptr<SceNpMatchingGUIHandler> handler, vm::ptr<void> arg)
@ -692,37 +632,30 @@ namespace np
return CELL_OK;
}
bool np_handler::reply_searchjoin_gui(u32 req_id, std::vector<u8>& reply_data)
void np_handler::reply_searchjoin_gui(u32 req_id, rpcn::ErrorType error, vec_stream& reply)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
return;
if (rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])))
s32 error_code = CELL_OK;
switch (error)
{
s32 error = -1;
switch (static_cast<rpcn::ErrorType>(reply_data[0]))
{
case rpcn::ErrorType::NotFound:
error = SCE_NP_MATCHING_ERROR_SEARCH_JOIN_ROOM_NOT_FOUND;
break;
default:
fmt::throw_exception("Unexpected error in SearchJoinRoomGUI reply: %d", reply_data[0]);
break;
}
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_SEARCH_JOIN, error);
gui_epilog(ctx);
return true;
case rpcn::ErrorType::NotFound: error_code = SCE_NP_MATCHING_ERROR_SEARCH_JOIN_ROOM_NOT_FOUND; break;
default: fmt::throw_exception("Unexpected error in SearchJoinRoomGUI reply: %d", static_cast<u8>(error));
}
vec_stream reply(reply_data, 1);
const auto* resp = reply.get_flatbuffer<MatchingSearchJoinRoomInfo>();
if (error_code != CELL_OK)
{
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_SEARCH_JOIN, error_code);
gui_epilog(ctx);
return;
}
if (reply.is_error())
return error_and_disconnect("Malformed reply to SearchJoinRoomGUI command");
const auto* resp = reply.get_flatbuffer<MatchingSearchJoinRoomInfo>();
ensure(!reply.is_error(), "Malformed reply to SearchJoinRoomGUI command");
event_data edata(np_memory.allocate(MAX_SceNpMatchingSearchJoinRoomInfo_SIZE), sizeof(SceNpMatchingSearchJoinRoomInfo), MAX_SceNpMatchingSearchJoinRoomInfo_SIZE);
auto* room_info = reinterpret_cast<SceNpMatchingSearchJoinRoomInfo*>(edata.data());
@ -741,8 +674,6 @@ namespace np
set_gui_result(SCE_NP_MATCHING_GUI_EVENT_SEARCH_JOIN, std::move(edata));
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_SEARCH_JOIN, 0);
gui_epilog(ctx);
return true;
}
// Local cache requests

View file

@ -1,3 +1,4 @@
#include "Utilities/StrUtil.h"
#include "stdafx.h"
#include <span>
#include "np_structs_extra.h"
@ -117,9 +118,13 @@ namespace extra_nps
print_SceNpMatching2BinAttr(&req->roomBinAttrExternal[i]);
sceNp2.warning("roomPassword: *0x%x", req->roomPassword);
if (req->roomPassword)
sceNp2.warning("data: %s", fmt::buf_to_hexstring(req->roomPassword->data, sizeof(req->roomPassword->data)));
sceNp2.warning("groupConfig: *0x%x", req->groupConfig);
sceNp2.warning("groupConfigNum: %d", req->groupConfigNum);
sceNp2.warning("passwordSlotMask: *0x%x", req->passwordSlotMask);
sceNp2.warning("passwordSlotMask: *0x%x, value: 0x%x", req->passwordSlotMask, req->passwordSlotMask ? static_cast<u64>(*req->passwordSlotMask) : 0ull);
sceNp2.warning("allowedUser: *0x%x", req->allowedUser);
sceNp2.warning("allowedUserNum: %d", req->allowedUserNum);
sceNp2.warning("blockedUser: *0x%x", req->blockedUser);

View file

@ -1,3 +1,5 @@
#include "Emu/Cell/lv2/sys_net/sys_net_helpers.h"
#include "Emu/NP/ip_address.h"
#include "stdafx.h"
#include <string>
#include <mutex>
@ -5,6 +7,7 @@
#include <chrono>
#include "rpcn_client.h"
#include "Utilities/StrUtil.h"
#include "Utilities/StrFmt.h"
#include "Utilities/Thread.h"
#include "Emu/System.h"
#include "Emu/NP/rpcn_config.h"
@ -42,6 +45,160 @@ LOG_CHANNEL(rpcn_log, "rpcn");
int get_native_error();
template <>
void fmt_class_string<rpcn::ErrorType>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto value)
{
switch (value)
{
case rpcn::ErrorType::NoError: return "NoError";
case rpcn::ErrorType::Malformed: return "Malformed";
case rpcn::ErrorType::Invalid: return "Invalid";
case rpcn::ErrorType::InvalidInput: return "InvalidInput";
case rpcn::ErrorType::TooSoon: return "TooSoon";
case rpcn::ErrorType::LoginError: return "LoginError";
case rpcn::ErrorType::LoginAlreadyLoggedIn: return "LoginAlreadyLoggedIn";
case rpcn::ErrorType::LoginInvalidUsername: return "LoginInvalidUsername";
case rpcn::ErrorType::LoginInvalidPassword: return "LoginInvalidPassword";
case rpcn::ErrorType::LoginInvalidToken: return "LoginInvalidToken";
case rpcn::ErrorType::CreationError: return "CreationError";
case rpcn::ErrorType::CreationExistingUsername: return "CreationExistingUsername";
case rpcn::ErrorType::CreationBannedEmailProvider: return "CreationBannedEmailProvider";
case rpcn::ErrorType::CreationExistingEmail: return "CreationExistingEmail";
case rpcn::ErrorType::RoomMissing: return "RoomMissing";
case rpcn::ErrorType::RoomAlreadyJoined: return "RoomAlreadyJoined";
case rpcn::ErrorType::RoomFull: return "RoomFull";
case rpcn::ErrorType::RoomPasswordMismatch: return "RoomPasswordMismatch";
case rpcn::ErrorType::RoomPasswordMissing: return "RoomPasswordMissing";
case rpcn::ErrorType::RoomGroupNoJoinLabel: return "RoomGroupNoJoinLabel";
case rpcn::ErrorType::RoomGroupFull: return "RoomGroupFull";
case rpcn::ErrorType::RoomGroupJoinLabelNotFound: return "RoomGroupJoinLabelNotFound";
case rpcn::ErrorType::RoomGroupMaxSlotMismatch: return "RoomGroupMaxSlotMismatch";
case rpcn::ErrorType::Unauthorized: return "Unauthorized";
case rpcn::ErrorType::DbFail: return "DbFail";
case rpcn::ErrorType::EmailFail: return "EmailFail";
case rpcn::ErrorType::NotFound: return "NotFound";
case rpcn::ErrorType::Blocked: return "Blocked";
case rpcn::ErrorType::AlreadyFriend: return "AlreadyFriend";
case rpcn::ErrorType::ScoreNotBest: return "ScoreNotBest";
case rpcn::ErrorType::ScoreInvalid: return "ScoreInvalid";
case rpcn::ErrorType::ScoreHasData: return "ScoreHasData";
case rpcn::ErrorType::CondFail: return "CondFail";
case rpcn::ErrorType::Unsupported: return "Unsupported";
default: break;
}
return unknown;
});
}
template <>
void fmt_class_string<rpcn::CommandType>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto value)
{
switch (value)
{
case rpcn::CommandType::Login: return "Login";
case rpcn::CommandType::Terminate: return "Terminate";
case rpcn::CommandType::Create: return "Create";
case rpcn::CommandType::SendToken: return "SendToken";
case rpcn::CommandType::SendResetToken: return "SendResetToken";
case rpcn::CommandType::ResetPassword: return "ResetPassword";
case rpcn::CommandType::ResetState: return "ResetState";
case rpcn::CommandType::AddFriend: return "AddFriend";
case rpcn::CommandType::RemoveFriend: return "RemoveFriend";
case rpcn::CommandType::AddBlock: return "AddBlock";
case rpcn::CommandType::RemoveBlock: return "RemoveBlock";
case rpcn::CommandType::GetServerList: return "GetServerList";
case rpcn::CommandType::GetWorldList: return "GetWorldList";
case rpcn::CommandType::CreateRoom: return "CreateRoom";
case rpcn::CommandType::JoinRoom: return "JoinRoom";
case rpcn::CommandType::LeaveRoom: return "LeaveRoom";
case rpcn::CommandType::SearchRoom: return "SearchRoom";
case rpcn::CommandType::GetRoomDataExternalList: return "GetRoomDataExternalList";
case rpcn::CommandType::SetRoomDataExternal: return "SetRoomDataExternal";
case rpcn::CommandType::GetRoomDataInternal: return "GetRoomDataInternal";
case rpcn::CommandType::SetRoomDataInternal: return "SetRoomDataInternal";
case rpcn::CommandType::GetRoomMemberDataInternal: return "GetRoomMemberDataInternal";
case rpcn::CommandType::SetRoomMemberDataInternal: return "SetRoomMemberDataInternal";
case rpcn::CommandType::SetUserInfo: return "SetUserInfo";
case rpcn::CommandType::PingRoomOwner: return "PingRoomOwner";
case rpcn::CommandType::SendRoomMessage: return "SendRoomMessage";
case rpcn::CommandType::RequestSignalingInfos: return "RequestSignalingInfos";
case rpcn::CommandType::RequestTicket: return "RequestTicket";
case rpcn::CommandType::SendMessage: return "SendMessage";
case rpcn::CommandType::GetBoardInfos: return "GetBoardInfos";
case rpcn::CommandType::RecordScore: return "RecordScore";
case rpcn::CommandType::RecordScoreData: return "RecordScoreData";
case rpcn::CommandType::GetScoreData: return "GetScoreData";
case rpcn::CommandType::GetScoreRange: return "GetScoreRange";
case rpcn::CommandType::GetScoreFriends: return "GetScoreFriends";
case rpcn::CommandType::GetScoreNpid: return "GetScoreNpid";
case rpcn::CommandType::GetNetworkTime: return "GetNetworkTime";
case rpcn::CommandType::TusSetMultiSlotVariable: return "TusSetMultiSlotVariable";
case rpcn::CommandType::TusGetMultiSlotVariable: return "TusGetMultiSlotVariable";
case rpcn::CommandType::TusGetMultiUserVariable: return "TusGetMultiUserVariable";
case rpcn::CommandType::TusGetFriendsVariable: return "TusGetFriendsVariable";
case rpcn::CommandType::TusAddAndGetVariable: return "TusAddAndGetVariable";
case rpcn::CommandType::TusTryAndSetVariable: return "TusTryAndSetVariable";
case rpcn::CommandType::TusDeleteMultiSlotVariable: return "TusDeleteMultiSlotVariable";
case rpcn::CommandType::TusSetData: return "TusSetData";
case rpcn::CommandType::TusGetData: return "TusGetData";
case rpcn::CommandType::TusGetMultiSlotDataStatus: return "TusGetMultiSlotDataStatus";
case rpcn::CommandType::TusGetMultiUserDataStatus: return "TusGetMultiUserDataStatus";
case rpcn::CommandType::TusGetFriendsDataStatus: return "TusGetFriendsDataStatus";
case rpcn::CommandType::TusDeleteMultiSlotData: return "TusDeleteMultiSlotData";
case rpcn::CommandType::SetPresence: return "SetPresence";
case rpcn::CommandType::CreateRoomGUI: return "CreateRoomGUI";
case rpcn::CommandType::JoinRoomGUI: return "JoinRoomGUI";
case rpcn::CommandType::LeaveRoomGUI: return "LeaveRoomGUI";
case rpcn::CommandType::GetRoomListGUI: return "GetRoomListGUI";
case rpcn::CommandType::SetRoomSearchFlagGUI: return "SetRoomSearchFlagGUI";
case rpcn::CommandType::GetRoomSearchFlagGUI: return "GetRoomSearchFlagGUI";
case rpcn::CommandType::SetRoomInfoGUI: return "SetRoomInfoGUI";
case rpcn::CommandType::GetRoomInfoGUI: return "GetRoomInfoGUI";
case rpcn::CommandType::QuickMatchGUI: return "QuickMatchGUI";
case rpcn::CommandType::SearchJoinRoomGUI: return "SearchJoinRoomGUI";
}
return unknown;
});
}
template <>
void fmt_class_string<rpcn::NotificationType>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto value)
{
switch (value)
{
case rpcn::NotificationType::UserJoinedRoom: return "UserJoinedRoom";
case rpcn::NotificationType::UserLeftRoom: return "UserLeftRoom";
case rpcn::NotificationType::RoomDestroyed: return "RoomDestroyed";
case rpcn::NotificationType::UpdatedRoomDataInternal: return "UpdatedRoomDataInternal";
case rpcn::NotificationType::UpdatedRoomMemberDataInternal: return "UpdatedRoomMemberDataInternal";
case rpcn::NotificationType::FriendQuery: return "FriendQuery";
case rpcn::NotificationType::FriendNew: return "FriendNew";
case rpcn::NotificationType::FriendLost: return "FriendLost";
case rpcn::NotificationType::FriendStatus: return "FriendStatus";
case rpcn::NotificationType::RoomMessageReceived: return "RoomMessageReceived";
case rpcn::NotificationType::MessageReceived: return "MessageReceived";
case rpcn::NotificationType::FriendPresenceChanged: return "FriendPresenceChanged";
case rpcn::NotificationType::SignalingHelper: return "SignalingHelper";
case rpcn::NotificationType::MemberJoinedRoomGUI: return "MemberJoinedRoomGUI";
case rpcn::NotificationType::MemberLeftRoomGUI: return "MemberLeftRoomGUI";
case rpcn::NotificationType::RoomDisappearedGUI: return "RoomDisappearedGUI";
case rpcn::NotificationType::RoomOwnerChangedGUI: return "RoomOwnerChangedGUI";
case rpcn::NotificationType::UserKickedGUI: return "UserKickedGUI";
case rpcn::NotificationType::QuickMatchCompleteGUI: return "QuickMatchCompleteGUI";
}
return unknown;
});
}
void vec_stream::dump() const
{
rpcn_log.error("vec_stream dump:\n%s", fmt::buf_to_hexstring(vec.data(), vec.size()));
@ -99,51 +256,64 @@ namespace rpcn
rpcn_log.notice("online: %s, pr_com_id: %s, pr_title: %s, pr_status: %s, pr_comment: %s, pr_data: %s", online ? "true" : "false", pr_com_id.data, pr_title, pr_status, pr_comment, fmt::buf_to_hexstring(pr_data.data(), pr_data.size()));
}
constexpr u32 RPCN_PROTOCOL_VERSION = 25;
constexpr usz RPCN_HEADER_SIZE = 15;
constexpr u32 RPCN_PROTOCOL_VERSION = 26;
constexpr usz RPCN_HEADER_SIZE = 15;
bool is_error(ErrorType err)
const char* error_to_explanation(rpcn::ErrorType error)
{
if (err >= ErrorType::__error_last)
switch (error)
{
rpcn_log.error("Invalid error returned!");
return true;
case rpcn::ErrorType::NoError: return "No error";
case rpcn::ErrorType::Malformed: return "Sent packet was malformed!";
case rpcn::ErrorType::Invalid: return "Sent command was invalid!";
case rpcn::ErrorType::InvalidInput: return "Sent data was invalid!";
case rpcn::ErrorType::TooSoon: return "Request happened too soon!";
case rpcn::ErrorType::LoginError: return "Unknown login error!";
case rpcn::ErrorType::LoginAlreadyLoggedIn: return "User is already logged in!";
case rpcn::ErrorType::LoginInvalidUsername: return "Login error: invalid username!";
case rpcn::ErrorType::LoginInvalidPassword: return "Login error: invalid password!";
case rpcn::ErrorType::LoginInvalidToken: return "Login error: invalid token!";
case rpcn::ErrorType::CreationError: return "Error creating an account!";
case rpcn::ErrorType::CreationExistingUsername: return "Error creating an account: existing username!";
case rpcn::ErrorType::CreationBannedEmailProvider: return "Error creating an account: banned email provider!";
case rpcn::ErrorType::CreationExistingEmail: return "Error creating an account: an account with that email already exist!";
case rpcn::ErrorType::RoomMissing: return "User tried to join a non-existent room!";
case rpcn::ErrorType::RoomAlreadyJoined: return "User has already joined!";
case rpcn::ErrorType::RoomFull: return "User tried to join a full room!";
case rpcn::ErrorType::RoomPasswordMismatch: return "Room password used was invalid";
case rpcn::ErrorType::RoomPasswordMissing: return "Room password was missing during room creation";
case rpcn::ErrorType::RoomGroupNoJoinLabel: return "Tried to join a group room without a label";
case rpcn::ErrorType::RoomGroupFull: return "Room group is full";
case rpcn::ErrorType::RoomGroupJoinLabelNotFound: return "Join label was invalid";
case rpcn::ErrorType::RoomGroupMaxSlotMismatch: return "Mismatch between max_slot and the listed slots in groups";
case rpcn::ErrorType::Unauthorized: return "User attempted an unauthorized operation!";
case rpcn::ErrorType::DbFail: return "A db query failed on the server!";
case rpcn::ErrorType::EmailFail: return "An email action failed on the server!";
case rpcn::ErrorType::NotFound: return "A request replied not found!";
case rpcn::ErrorType::Blocked: return "You're blocked!";
case rpcn::ErrorType::AlreadyFriend: return "You're already friends!";
case rpcn::ErrorType::ScoreNotBest: return "Attempted to register a score that is not better!";
case rpcn::ErrorType::ScoreInvalid: return "Score for player was found but wasn't what was expected!";
case rpcn::ErrorType::ScoreHasData: return "Score already has game data associated with it!";
case rpcn::ErrorType::CondFail: return "Condition related to the query failed!";
case rpcn::ErrorType::Unsupported: return "An unsupported operation was attempted!";
}
switch (err)
{
case NoError: return false;
case Malformed: rpcn_log.error("Sent packet was malformed!"); break;
case Invalid: rpcn_log.error("Sent command was invalid!"); break;
case InvalidInput: rpcn_log.error("Sent data was invalid!"); break;
case TooSoon: rpcn_log.error("Request happened too soon!"); break;
case LoginError: rpcn_log.error("Unknown login error!"); break;
case LoginAlreadyLoggedIn: rpcn_log.error("User is already logged in!"); break;
case LoginInvalidUsername: rpcn_log.error("Login error: invalid username!"); break;
case LoginInvalidPassword: rpcn_log.error("Login error: invalid password!"); break;
case LoginInvalidToken: rpcn_log.error("Login error: invalid token!"); break;
case CreationError: rpcn_log.error("Error creating an account!"); break;
case CreationExistingUsername: rpcn_log.error("Error creating an account: existing username!"); break;
case CreationBannedEmailProvider: rpcn_log.error("Error creating an account: banned email provider!"); break;
case CreationExistingEmail: rpcn_log.error("Error creating an account: an account with that email already exist!"); break;
case RoomMissing: rpcn_log.error("User tried to join a non-existent room!"); break;
case RoomAlreadyJoined: rpcn_log.error("User has already joined!"); break;
case RoomFull: rpcn_log.error("User tried to join a full room!"); break;
case Unauthorized: rpcn_log.error("User attempted an unauthorized operation!"); break;
case DbFail: rpcn_log.error("A db query failed on the server!"); break;
case EmailFail: rpcn_log.error("An email action failed on the server!"); break;
case NotFound: rpcn_log.error("A request replied not found!"); break;
case Blocked: rpcn_log.error("You're blocked!"); break;
case AlreadyFriend: rpcn_log.error("You're already friends!"); break;
case ScoreNotBest: rpcn_log.error("Attempted to register a score that is not better!"); break;
case ScoreInvalid: rpcn_log.error("Score for player was found but wasn't what was expected!"); break;
case ScoreHasData: rpcn_log.error("Score already has game data associated with it!"); break;
case CondFail: rpcn_log.error("Condition related to the query failed!"); break;
case Unsupported: rpcn_log.error("An unsupported operation was attempted!"); break;
default: rpcn_log.fatal("Unhandled ErrorType reached the switch?"); break;
}
fmt::throw_exception("Unknown error returned: %d", static_cast<u8>(error));
}
return true;
void print_error(rpcn::CommandType command, rpcn::ErrorType error)
{
const std::string error_message = fmt::format("command: %s result: %s, explanation: %s", command, error, error_to_explanation(error));
if (error == rpcn::ErrorType::NoError)
{
rpcn_log.trace("%s", error_message);
}
else
{
rpcn_log.warning("%s", error_message);
}
}
// Constructor, destructor & singleton manager
@ -267,6 +437,10 @@ namespace rpcn
{
thread_base::set_name("RPCN Client");
// UDP Signaling related
steady_clock::time_point last_ping_time_ipv4{}, last_pong_time_ipv4{};
steady_clock::time_point last_ping_time_ipv6{}, last_pong_time_ipv6{};
while (true)
{
sem_rpcn.acquire();
@ -321,16 +495,15 @@ namespace rpcn
if (authentified && !Emu.IsStopped())
{
// Ping the UDP Signaling Server if we're authentified & ingame
auto now = steady_clock::now();
auto rpcn_msgs = get_rpcn_msgs();
const auto now = steady_clock::now();
const auto rpcn_msgs = get_rpcn_msgs();
for (const auto& msg : rpcn_msgs)
{
if (msg.size() == 6)
{
const u32 new_addr_sig = read_from_ptr<le_t<u32>>(&msg[0]);
const u32 new_port_sig = read_from_ptr<be_t<u16>>(&msg[4]);
const u16 new_port_sig = read_from_ptr<be_t<u16>>(&msg[4]);
const u32 old_addr_sig = addr_sig;
const u32 old_port_sig = port_sig;
@ -352,7 +525,16 @@ namespace rpcn
}
}
last_pong_time = now;
last_pong_time_ipv4 = now;
}
else if (msg.size() == 18)
{
// We don't really need ipv6 info stored so we just update the pong data
// std::array<u8, 16> new_ipv6_addr;
// std::memcpy(new_ipv6_addr.data(), &msg[3], 16);
// const u32 new_ipv6_port = read_from_ptr<be_t<u16>>(&msg[16]);
last_pong_time_ipv6 = now;
}
else
{
@ -360,36 +542,64 @@ namespace rpcn
}
}
// Send a packet every 5 seconds and then every 500 ms until reply is received
if (now - last_pong_time >= 5s && now - last_ping_time > 500ms)
const std::chrono::nanoseconds time_since_last_ipv4_ping = now - last_ping_time_ipv4;
const std::chrono::nanoseconds time_since_last_ipv4_pong = now - last_pong_time_ipv4;
const std::chrono::nanoseconds time_since_last_ipv6_ping = now - last_ping_time_ipv6;
const std::chrono::nanoseconds time_since_last_ipv6_pong = now - last_pong_time_ipv6;
auto forge_ping_packet = [&]() -> std::vector<u8>
{
std::vector<u8> ping(13);
ping[0] = 1;
ping[0] = 1;
write_to_ptr<le_t<s64>>(ping, 1, user_id);
write_to_ptr<be_t<u32>>(ping, 9, +local_addr_sig);
if (send_packet_from_p2p_port(ping, addr_rpcn_udp) == -1)
{
rpcn_log.error("Failed to send ping to RPCN!");
}
last_ping_time = now;
}
else
return ping;
};
// Send a packet every 5 seconds and then every 500 ms until reply is received
if (time_since_last_ipv4_pong >= 5s && time_since_last_ipv4_ping > 500ms)
{
const auto ping = forge_ping_packet();
if (!send_packet_from_p2p_port_ipv4(ping, addr_rpcn_udp_ipv4))
rpcn_log.error("Failed to send IPv4 ping to RPCN!");
last_ping_time_ipv4 = now;
continue;
}
if (np::is_ipv6_supported() && time_since_last_ipv6_pong >= 5s && time_since_last_ipv6_ping > 500ms)
{
const auto ping = forge_ping_packet();
if (!send_packet_from_p2p_port_ipv6(ping, addr_rpcn_udp_ipv6))
rpcn_log.error("Failed to send IPv6 ping to RPCN!");
last_ping_time_ipv6 = now;
continue;
}
auto min_duration_for = [&](const auto last_ping_time, const auto last_pong_time) -> std::chrono::nanoseconds
{
std::chrono::nanoseconds duration;
if ((now - last_pong_time) < 5s)
{
duration = 5s - (now - last_pong_time);
return (5s - (now - last_pong_time));
}
else
{
duration = 500ms - (now - last_ping_time);
return (500ms - (now - last_ping_time));
}
};
if (!sem_rpcn.try_acquire_for(duration))
{
// TODO
}
auto duration = min_duration_for(last_ping_time_ipv4, last_pong_time_ipv4);
if (np::is_ipv6_supported())
{
const auto duration_ipv6 = min_duration_for(last_ping_time_ipv6, last_pong_time_ipv6);
duration = std::min(duration, duration_ipv6);
}
sem_rpcn.try_acquire_for(duration);
}
}
}
@ -410,10 +620,10 @@ namespace rpcn
case recvn_result::recvn_terminate: return error_and_disconnect_notice("Recvn was forcefully aborted");
}
const u8 packet_type = header[0];
const u16 command = read_from_ptr<le_t<u16>>(&header[1]);
const u8 packet_type = header[0];
const auto command = static_cast<rpcn::CommandType>(static_cast<u16>(read_from_ptr<le_t<u16>>(&header[1])));
const u32 packet_size = read_from_ptr<le_t<u32>>(&header[3]);
const u64 packet_id = read_from_ptr<le_t<u64>>(&header[7]);
const u64 packet_id = read_from_ptr<le_t<u64>>(&header[7]);
if (packet_size < RPCN_HEADER_SIZE)
return error_and_disconnect("Invalid packet size");
@ -435,6 +645,13 @@ namespace rpcn
if (data.empty())
return error_and_disconnect("Reply packet without result");
// Internal commands without feedback
if (command == CommandType::ResetState)
{
ensure(data[0] == static_cast<u8>(ErrorType::NoError));
break;
}
// Those commands are handled synchronously and won't be forwarded to NP Handler
if (command == CommandType::Login || command == CommandType::GetServerList || command == CommandType::Create ||
command == CommandType::AddFriend || command == CommandType::RemoveFriend ||
@ -463,7 +680,9 @@ namespace rpcn
}
case PacketType::Notification:
{
switch (command)
auto notif_type = static_cast<rpcn::NotificationType>(command);
switch (notif_type)
{
case NotificationType::FriendNew:
case NotificationType::FriendLost:
@ -471,7 +690,7 @@ namespace rpcn
case NotificationType::FriendStatus:
case NotificationType::FriendPresenceChanged:
{
handle_friend_notification(command, std::move(data));
handle_friend_notification(notif_type, std::move(data));
break;
}
case NotificationType::MessageReceived:
@ -482,7 +701,7 @@ namespace rpcn
default:
{
std::lock_guard lock(mutex_notifs);
notifications.emplace_back(std::make_pair(command, std::move(data)));
notifications.emplace_back(std::make_pair(notif_type, std::move(data)));
break;
}
}
@ -493,7 +712,7 @@ namespace rpcn
if (data.size() != 4)
return error_and_disconnect("Invalid size of ServerInfo packet");
received_version = reinterpret_cast<le_t<u32>&>(data[0]);
received_version = reinterpret_cast<le_t<u32>&>(data[0]);
server_info_received = true;
break;
}
@ -623,7 +842,7 @@ namespace rpcn
bool rpcn_client::send_packet(const std::vector<u8>& packet)
{
u32 num_timeouts = 0;
usz n_sent = 0;
usz n_sent = 0;
while (n_sent != packet.size())
{
if (terminate)
@ -682,7 +901,7 @@ namespace rpcn
// Helper functions
bool rpcn_client::forge_send(u16 command, u64 packet_id, const std::vector<u8>& data)
bool rpcn_client::forge_send(rpcn::CommandType command, u64 packet_id, const std::vector<u8>& data)
{
// TODO: add a check for status?
@ -691,7 +910,7 @@ namespace rpcn
return true;
}
bool rpcn_client::forge_send_reply(u16 command, u64 packet_id, const std::vector<u8>& data, std::vector<u8>& reply_data)
bool rpcn_client::forge_send_reply(rpcn::CommandType command, u64 packet_id, const std::vector<u8>& data, std::vector<u8>& reply_data)
{
if (!forge_send(command, packet_id, data))
return false;
@ -738,8 +957,8 @@ namespace rpcn
sockfd = 0;
}
connected = false;
authentified = false;
connected = false;
authentified = false;
server_info_received = false;
}
@ -811,28 +1030,63 @@ namespace rpcn
memset(&addr_rpcn, 0, sizeof(addr_rpcn));
addr_rpcn.sin_port = std::bit_cast<u16, be_t<u16>>(port); // htons
addr_rpcn.sin_port = std::bit_cast<u16, be_t<u16>>(port); // htons
addr_rpcn.sin_family = AF_INET;
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996)
#endif
hostent* host_addr = gethostbyname(splithost[0].c_str());
#ifdef _MSC_VER
#pragma warning(pop)
#endif
if (!host_addr)
addrinfo* addr_info{};
if (getaddrinfo(splithost[0].c_str(), nullptr, nullptr, &addr_info))
{
rpcn_log.error("connect: Failed to resolve %s", host);
rpcn_log.error("connect: Failed to getaddrinfo %s", host);
state = rpcn_state::failure_resolve;
return false;
}
addr_rpcn.sin_addr.s_addr = *reinterpret_cast<u32*>(host_addr->h_addr_list[0]);
bool found_ipv4 = false, found_ipv6 = false;
addrinfo* found = addr_info;
memcpy(&addr_rpcn_udp, &addr_rpcn, sizeof(addr_rpcn_udp));
addr_rpcn_udp.sin_port = std::bit_cast<u16, be_t<u16>>(3657); // htons
while (found != nullptr)
{
switch (found->ai_family)
{
case AF_INET:
{
addr_rpcn.sin_addr = reinterpret_cast<sockaddr_in*>(found->ai_addr)->sin_addr;
found_ipv4 = true;
break;
}
case AF_INET6:
{
if (np::is_ipv6_supported())
{
addr_rpcn_udp_ipv6.sin6_family = AF_INET6;
addr_rpcn_udp_ipv6.sin6_port = std::bit_cast<u16, be_t<u16>>(3657);
addr_rpcn_udp_ipv6.sin6_addr = reinterpret_cast<sockaddr_in6*>(found->ai_addr)->sin6_addr;
found_ipv6 = true;
}
break;
}
default: break;
}
found = found->ai_next;
}
if (!found_ipv4)
{
rpcn_log.error("connect: Failed to find IPv4 for %s", host);
state = rpcn_state::failure_resolve;
return false;
}
if (np::is_ipv6_supported() && !found_ipv6)
{
rpcn_log.warning("IPv6 seems supported but no IPv6 could be found for the RPCN server, IPv6 is disabled!");
is_ipv6_supported(np::IPV6_SUPPORT::IPV6_UNSUPPORTED);
}
memcpy(&addr_rpcn_udp_ipv4, &addr_rpcn, sizeof(addr_rpcn_udp_ipv4));
addr_rpcn_udp_ipv4.sin_port = std::bit_cast<u16, be_t<u16>>(3657); // htons
sockfd = socket(AF_INET, SOCK_STREAM, 0);
#ifdef _WIN32
@ -926,10 +1180,6 @@ namespace rpcn
}
rpcn_log.notice("connect: Protocol version matches");
last_ping_time = steady_clock::now() - 5s;
last_pong_time = last_ping_time;
return true;
}
@ -968,10 +1218,10 @@ namespace rpcn
}
vec_stream reply(packet_data);
auto error = static_cast<ErrorType>(reply.get<u8>());
auto error = static_cast<ErrorType>(reply.get<u8>());
online_name = reply.get_string(false);
avatar_url = reply.get_string(false);
user_id = reply.get<s64>();
avatar_url = reply.get_string(false);
user_id = reply.get<s64>();
auto get_usernames_and_status = [](vec_stream& stream, std::map<std::string, friend_online_data>& friends)
{
@ -1016,15 +1266,15 @@ namespace rpcn
get_usernames(reply, friend_infos.blocked);
}
if (is_error(error))
if (error != rpcn::ErrorType::NoError)
{
switch (error)
{
case LoginError: state = rpcn_state::failure_id; break;
case LoginAlreadyLoggedIn: state = rpcn_state::failure_id_already_logged_in; break;
case LoginInvalidUsername: state = rpcn_state::failure_id_username; break;
case LoginInvalidPassword: state = rpcn_state::failure_id_password; break;
case LoginInvalidToken: state = rpcn_state::failure_id_token; break;
case rpcn::ErrorType::LoginError: state = rpcn_state::failure_id; break;
case rpcn::ErrorType::LoginAlreadyLoggedIn: state = rpcn_state::failure_id_already_logged_in; break;
case rpcn::ErrorType::LoginInvalidUsername: state = rpcn_state::failure_id_username; break;
case rpcn::ErrorType::LoginInvalidPassword: state = rpcn_state::failure_id_password; break;
case rpcn::ErrorType::LoginInvalidToken: state = rpcn_state::failure_id_token; break;
default: state = rpcn_state::failure_id; break;
}
@ -1056,6 +1306,15 @@ namespace rpcn
return true;
}
void rpcn_client::reset_state()
{
if (!connected || !authentified)
return;
std::vector<u8> data;
forge_send(CommandType::ResetState, rpcn_request_counter.fetch_add(1), data);
}
ErrorType rpcn_client::create_user(std::string_view npid, std::string_view password, std::string_view online_name, std::string_view avatar_url, std::string_view email)
{
std::vector<u8> data;
@ -1082,14 +1341,12 @@ namespace rpcn
vec_stream reply(packet_data);
auto error = static_cast<ErrorType>(reply.get<u8>());
if (is_error(error))
if (error == rpcn::ErrorType::NoError)
{
return error;
rpcn_log.success("You have successfully created a RPCN account(%s | %s)!", npid, online_name);
}
rpcn_log.success("You have successfully created a RPCN account(%s | %s)!", npid, online_name);
return ErrorType::NoError;
return error;
}
ErrorType rpcn_client::resend_token(const std::string& npid, const std::string& password)
@ -1117,14 +1374,12 @@ namespace rpcn
vec_stream reply(packet_data);
auto error = static_cast<ErrorType>(reply.get<u8>());
if (is_error(error))
if (error == rpcn::ErrorType::NoError)
{
return error;
rpcn_log.success("Token has successfully been resent!");
}
rpcn_log.success("Token has successfully been resent!");
return ErrorType::NoError;
return error;
}
ErrorType rpcn_client::send_reset_token(std::string_view npid, std::string_view email)
@ -1152,14 +1407,12 @@ namespace rpcn
vec_stream reply(packet_data);
auto error = static_cast<ErrorType>(reply.get<u8>());
if (is_error(error))
if (error == rpcn::ErrorType::NoError)
{
return error;
rpcn_log.success("Password reset token has successfully been sent!");
}
rpcn_log.success("Password reset token has successfully been sent!");
return ErrorType::NoError;
return error;
}
ErrorType rpcn_client::reset_password(std::string_view npid, std::string_view token, std::string_view password)
@ -1189,14 +1442,12 @@ namespace rpcn
vec_stream reply(packet_data);
auto error = static_cast<ErrorType>(reply.get<u8>());
if (is_error(error))
if (error == rpcn::ErrorType::NoError)
{
return error;
rpcn_log.success("Password has successfully been reset!");
}
rpcn_log.success("Password has successfully been reset!");
return ErrorType::NoError;
return error;
}
bool rpcn_client::add_friend(const std::string& friend_username)
@ -1216,7 +1467,7 @@ namespace rpcn
vec_stream reply(packet_data);
auto error = static_cast<ErrorType>(reply.get<u8>());
if (is_error(error))
if (error != rpcn::ErrorType::NoError)
{
return false;
}
@ -1242,7 +1493,7 @@ namespace rpcn
vec_stream reply(packet_data);
auto error = static_cast<ErrorType>(reply.get<u8>());
if (is_error(error))
if (error != rpcn::ErrorType::NoError)
{
return false;
}
@ -1251,18 +1502,18 @@ namespace rpcn
return true;
}
std::vector<std::pair<u16, std::vector<u8>>> rpcn_client::get_notifications()
std::vector<std::pair<rpcn::NotificationType, std::vector<u8>>> rpcn_client::get_notifications()
{
std::lock_guard lock(mutex_notifs);
std::vector<std::pair<u16, std::vector<u8>>> notifs = std::move(notifications);
auto notifs = std::move(notifications);
notifications.clear();
return notifs;
}
std::unordered_map<u32, std::pair<u16, std::vector<u8>>> rpcn_client::get_replies()
std::unordered_map<u32, std::pair<rpcn::CommandType, std::vector<u8>>> rpcn_client::get_replies()
{
std::lock_guard lock(mutex_replies);
std::unordered_map<u32, std::pair<u16, std::vector<u8>>> ret_replies = std::move(replies);
auto ret_replies = std::move(replies);
replies.clear();
return ret_replies;
}
@ -1331,7 +1582,7 @@ namespace rpcn
vec_stream reply(reply_data);
auto error = static_cast<ErrorType>(reply.get<u8>());
if (is_error(error))
if (error != rpcn::ErrorType::NoError)
{
return false;
}
@ -1364,7 +1615,7 @@ namespace rpcn
vec_stream reply(reply_data);
auto error = static_cast<ErrorType>(reply.get<u8>());
if (is_error(error))
if (error != rpcn::ErrorType::NoError)
{
return 0;
}
@ -1380,7 +1631,6 @@ namespace rpcn
return network_time;
}
bool rpcn_client::get_world_list(u32 req_id, const SceNpCommunicationId& communication_id, u16 server_id)
{
std::vector<u8> data(COMMUNICATION_ID_SIZE + sizeof(u16));
@ -1447,7 +1697,7 @@ namespace rpcn
std::vector<flatbuffers::Offset<GroupConfig>> davec;
for (u32 i = 0; i < req->groupConfigNum; i++)
{
auto bin = CreateGroupConfig(builder, req->groupConfig[i].slotNum, req->groupConfig[i].withLabel, builder.CreateVector(req->groupConfig[i].label.data, 8), req->groupConfig[i].withPassword);
auto bin = CreateGroupConfig(builder, req->groupConfig[i].slotNum, req->groupConfig[i].withLabel ? builder.CreateVector(req->groupConfig[i].label.data, 8) : 0, req->groupConfig[i].withPassword);
davec.push_back(bin);
}
final_groupconfigs_vec = builder.CreateVector(davec);
@ -1549,7 +1799,7 @@ namespace rpcn
{
flatbuffers::FlatBufferBuilder builder(1024);
flatbuffers::Offset<PresenceOptionData> final_optdata = CreatePresenceOptionData(builder, builder.CreateVector(req->optData.data, 16), req->optData.length);
auto req_finished = CreateLeaveRoomRequest(builder, req->roomId, final_optdata);
auto req_finished = CreateLeaveRoomRequest(builder, req->roomId, final_optdata);
builder.Finish(req_finished);
return forge_request_with_com_id(builder, communication_id, CommandType::LeaveRoom, req_id);
@ -1565,7 +1815,7 @@ namespace rpcn
for (u32 i = 0; i < req->intFilterNum; i++)
{
auto int_attr = CreateIntAttr(builder, req->intFilter[i].attr.id, req->intFilter[i].attr.num);
auto bin = CreateIntSearchFilter(builder, req->intFilter[i].searchOperator, int_attr);
auto bin = CreateIntSearchFilter(builder, req->intFilter[i].searchOperator, int_attr);
davec.push_back(bin);
}
final_intfilter_vec = builder.CreateVector(davec);
@ -1577,7 +1827,7 @@ namespace rpcn
for (u32 i = 0; i < req->binFilterNum; i++)
{
auto bin_attr = CreateBinAttr(builder, req->binFilter[i].attr.id, builder.CreateVector(req->binFilter[i].attr.ptr.get_ptr(), req->binFilter[i].attr.size));
auto bin = CreateBinSearchFilter(builder, req->binFilter[i].searchOperator, bin_attr);
auto bin = CreateBinSearchFilter(builder, req->binFilter[i].searchOperator, bin_attr);
davec.push_back(bin);
}
final_binfilter_vec = builder.CreateVector(davec);
@ -1892,7 +2142,7 @@ namespace rpcn
auto fb_sendmessage = CreateSendMessageRequest(builder, nested_flatbuffer_vector, npids_vector);
builder.Finish(fb_sendmessage);
const u8* buf = builder.GetBufferPointer();
const u8* buf = builder.GetBufferPointer();
const usz bufsize = builder.GetSize();
std::vector<u8> data(bufsize + sizeof(u32));
@ -1963,7 +2213,7 @@ namespace rpcn
auto req_finished = CreateRecordScoreGameDataRequest(builder, board_id, pc_id, score);
builder.Finish(req_finished);
const u8* buf = builder.GetBufferPointer();
const u8* buf = builder.GetBufferPointer();
const usz bufsize = builder.GetSize();
std::vector<u8> data(COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize + sizeof(u32) + score_data.size());
@ -2385,11 +2635,7 @@ namespace rpcn
num = cur_attr->value.num;
break;
}
default:
{
fmt::throw_exception("Invalid attr type reached set_room_info_gui");
break;
}
default: fmt::throw_exception("Invalid attr type reached set_room_info_gui");
}
auto fb_attr = CreateMatchingAttr(builder, cur_attr->type, cur_attr->id, num, fb_vec_data);
@ -2468,7 +2714,7 @@ namespace rpcn
bool rpcn_client::forge_request_with_com_id(const flatbuffers::FlatBufferBuilder& builder, const SceNpCommunicationId& com_id, CommandType command, u64 packet_id)
{
const u8* buf = builder.GetBufferPointer();
const u8* buf = builder.GetBufferPointer();
const usz bufsize = builder.GetSize();
std::vector<u8> data(COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize);
@ -2492,13 +2738,13 @@ namespace rpcn
return forge_send(command, packet_id, data);
}
std::vector<u8> rpcn_client::forge_request(u16 command, u64 packet_id, const std::vector<u8>& data) const
std::vector<u8> rpcn_client::forge_request(rpcn::CommandType command, u64 packet_id, const std::vector<u8>& data) const
{
const usz packet_size = data.size() + RPCN_HEADER_SIZE;
std::vector<u8> packet(packet_size);
packet[0] = PacketType::Request;
reinterpret_cast<le_t<u16>&>(packet[1]) = command;
packet[0] = static_cast<u8>(PacketType::Request);
reinterpret_cast<le_t<u16>&>(packet[1]) = static_cast<u16>(command);
reinterpret_cast<le_t<u32>&>(packet[3]) = ::narrow<u32>(packet_size);
reinterpret_cast<le_t<u64>&>(packet[7]) = packet_id;
@ -2601,11 +2847,10 @@ namespace rpcn
}
}
void rpcn_client::handle_friend_notification(u16 command, std::vector<u8> data)
void rpcn_client::handle_friend_notification(rpcn::NotificationType ntype, std::vector<u8> data)
{
std::lock_guard lock(mutex_friends);
NotificationType ntype = static_cast<NotificationType>(command);
vec_stream vdata(data);
const auto call_callbacks = [&](NotificationType ntype, const std::string& username, bool status)
@ -2666,7 +2911,7 @@ namespace rpcn
case NotificationType::FriendStatus: // Set status of friend to Offline or Online
{
const bool online = !!vdata.get<u8>();
const u64 timestamp = vdata.get<u64>();
const u64 timestamp = vdata.get<u64>();
const std::string username = vdata.get_string(false);
if (vdata.is_error())
{
@ -2740,7 +2985,7 @@ namespace rpcn
// Unserialize the message
vec_stream sdata(data);
std::string sender = sdata.get_string(false);
auto* fb_mdata = sdata.get_flatbuffer<MessageDetails>();
auto* fb_mdata = sdata.get_flatbuffer<MessageDetails>();
if (sdata.is_error())
{
@ -2755,12 +3000,12 @@ namespace rpcn
}
message_data mdata = {
.msgId = message_counter,
.mainType = fb_mdata->mainType(),
.subType = fb_mdata->subType(),
.msgId = message_counter,
.mainType = fb_mdata->mainType(),
.subType = fb_mdata->subType(),
.msgFeatures = fb_mdata->msgFeatures(),
.subject = fb_mdata->subject()->str(),
.body = fb_mdata->body()->str()};
.subject = fb_mdata->subject()->str(),
.body = fb_mdata->body()->str()};
strcpy_trunc(mdata.commId.data, fb_mdata->communicationId()->str());
mdata.data.assign(fb_mdata->data()->Data(), fb_mdata->data()->Data() + fb_mdata->data()->size());
@ -2769,7 +3014,7 @@ namespace rpcn
{
std::lock_guard lock(mutex_messages);
const u64 msg_id = message_counter++;
auto id_and_msg = stx::make_shared<std::pair<std::string, message_data>>(std::make_pair(std::move(sender), std::move(mdata)));
auto id_and_msg = stx::make_shared<std::pair<std::string, message_data>>(std::make_pair(std::move(sender), std::move(mdata)));
messages.emplace(msg_id, id_and_msg);
new_messages.push_back(msg_id);
active_messages.insert(msg_id);
@ -2805,7 +3050,7 @@ namespace rpcn
for (auto id : active_messages)
{
const auto& entry = ::at32(messages, id);
const auto& msg = entry->second;
const auto& msg = entry->second;
if (msg.mainType == type_filter && (include_bootable || !(msg.msgFeatures & SCE_NP_BASIC_MESSAGE_FEATURES_BOOTABLE)))
{
vec_messages.push_back(std::make_pair(id, entry));
@ -2813,9 +3058,9 @@ namespace rpcn
}
message_cbs.insert(message_cb_t{
.cb_func = cb_func,
.cb_param = cb_param,
.type_filter = type_filter,
.cb_func = cb_func,
.cb_param = cb_param,
.type_filter = type_filter,
.inc_bootable = include_bootable,
});
}

View file

@ -30,12 +30,15 @@
#ifdef __clang__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#pragma GCC diagnostic ignored "-Wextern-c-compat"
#endif
#include <wolfssl/ssl.h>
#ifdef __clang__
#pragma GCC diagnostic pop
#endif
#include "rpcn_types.h"
// COMID is sent as 9 chars - + '_' + 2 digits
constexpr usz COMMUNICATION_ID_COMID_COMPONENT_SIZE = 9;
constexpr usz COMMUNICATION_ID_SUBID_COMPONENT_SIZE = 2;
@ -180,153 +183,6 @@ protected:
namespace rpcn
{
enum CommandType : u16
{
Login,
Terminate,
Create,
SendToken,
SendResetToken,
ResetPassword,
AddFriend,
RemoveFriend,
AddBlock,
RemoveBlock,
GetServerList,
GetWorldList,
CreateRoom,
JoinRoom,
LeaveRoom,
SearchRoom,
GetRoomDataExternalList,
SetRoomDataExternal,
GetRoomDataInternal,
SetRoomDataInternal,
GetRoomMemberDataInternal,
SetRoomMemberDataInternal,
SetUserInfo,
PingRoomOwner,
SendRoomMessage,
RequestSignalingInfos,
RequestTicket,
SendMessage,
GetBoardInfos,
RecordScore,
RecordScoreData,
GetScoreData,
GetScoreRange,
GetScoreFriends,
GetScoreNpid,
GetNetworkTime,
TusSetMultiSlotVariable,
TusGetMultiSlotVariable,
TusGetMultiUserVariable,
TusGetFriendsVariable,
TusAddAndGetVariable,
TusTryAndSetVariable,
TusDeleteMultiSlotVariable,
TusSetData,
TusGetData,
TusGetMultiSlotDataStatus,
TusGetMultiUserDataStatus,
TusGetFriendsDataStatus,
TusDeleteMultiSlotData,
ClearPresence,
SetPresence,
CreateRoomGUI,
JoinRoomGUI,
LeaveRoomGUI,
GetRoomListGUI,
SetRoomSearchFlagGUI,
GetRoomSearchFlagGUI,
SetRoomInfoGUI,
GetRoomInfoGUI,
QuickMatchGUI,
SearchJoinRoomGUI,
};
enum NotificationType : u16
{
UserJoinedRoom,
UserLeftRoom,
RoomDestroyed,
UpdatedRoomDataInternal,
UpdatedRoomMemberDataInternal,
SignalP2PConnect,
_SignalP2PDisconnect,
FriendQuery, // Other user sent a friend request
FriendNew, // Add a friend to the friendlist(either accepted a friend request or friend accepted it)
FriendLost, // Remove friend from the friendlist(user removed friend or friend removed friend)
FriendStatus, // Set status of friend to Offline or Online
RoomMessageReceived,
MessageReceived,
FriendPresenceChanged,
SignalingInfo,
MemberJoinedRoomGUI,
MemberLeftRoomGUI,
RoomDisappearedGUI,
RoomOwnerChangedGUI,
UserKickedGUI,
QuickMatchCompleteGUI,
};
enum class rpcn_state
{
failure_no_failure,
failure_input,
failure_wolfssl,
failure_resolve,
failure_connect,
failure_id,
failure_id_already_logged_in,
failure_id_username,
failure_id_password,
failure_id_token,
failure_protocol,
failure_other,
};
enum PacketType : u8
{
Request,
Reply,
Notification,
ServerInfo,
};
enum ErrorType : u8
{
NoError, // No error
Malformed, // Query was malformed, critical error that should close the connection
Invalid, // The request type is invalid(wrong stage?)
InvalidInput, // The Input doesn't fit the constraints of the request
TooSoon, // Time limited operation attempted too soon
LoginError, // An error happened related to login
LoginAlreadyLoggedIn, // Can't log in because you're already logged in
LoginInvalidUsername, // Invalid username
LoginInvalidPassword, // Invalid password
LoginInvalidToken, // Invalid token
CreationError, // An error happened related to account creation
CreationExistingUsername, // Specific to Account Creation: username exists already
CreationBannedEmailProvider, // Specific to Account Creation: the email provider is banned
CreationExistingEmail, // Specific to Account Creation: that email is already registered to an account
RoomMissing, // User tried to join a non existing room
RoomAlreadyJoined, // User tried to join a room he's already part of
RoomFull, // User tried to join a full room
Unauthorized, // User attempted an unauthorized operation
DbFail, // Generic failure on db side
EmailFail, // Generic failure related to email
NotFound, // Object of the query was not found(room, user, etc)
Blocked, // The operation can't complete because you've been blocked
AlreadyFriend, // Can't add friend because already friend
ScoreNotBest, // A better score is already registered for that user/character_id
ScoreInvalid, // Score for player was found but wasn't what was expected
ScoreHasData, // Score already has data
CondFail, // Condition related to query failed
Unsupported,
__error_last
};
using friend_cb_func = void (*)(void* param, NotificationType ntype, const std::string& username, bool status);
using message_cb_func = void (*)(void* param, const shared_ptr<std::pair<std::string, message_data>> new_msg, u64 msg_id);
@ -359,7 +215,7 @@ namespace rpcn
localized_string_id rpcn_state_to_localized_string_id(rpcn::rpcn_state state);
std::string rpcn_state_to_string(rpcn::rpcn_state state);
bool is_error(ErrorType err);
void print_error(rpcn::CommandType command, rpcn::ErrorType error);
class rpcn_client
{
@ -392,7 +248,7 @@ namespace rpcn
std::set<std::pair<friend_cb_func, void*>> friend_cbs;
friend_data friend_infos;
void handle_friend_notification(u16 command, std::vector<u8> data);
void handle_friend_notification(rpcn::NotificationType ntype, std::vector<u8> data);
void handle_message(std::vector<u8> data);
@ -435,6 +291,7 @@ namespace rpcn
rpcn_state wait_for_connection();
rpcn_state wait_for_authentified();
bool terminate_connection();
void reset_state();
void get_friends(friend_data& friend_infos);
void get_friends_and_register_cb(friend_data& friend_infos, friend_cb_func cb_func, void* cb_param);
@ -454,8 +311,8 @@ namespace rpcn
std::optional<std::pair<std::string, friend_online_data>> get_friend_presence_by_index(u32 index);
std::optional<std::pair<std::string, friend_online_data>> get_friend_presence_by_npid(const std::string& npid);
std::vector<std::pair<u16, std::vector<u8>>> get_notifications();
std::unordered_map<u32, std::pair<u16, std::vector<u8>>> get_replies();
std::vector<std::pair<rpcn::NotificationType, std::vector<u8>>> get_notifications();
std::unordered_map<u32, std::pair<rpcn::CommandType, std::vector<u8>>> get_replies();
std::unordered_map<std::string, friend_online_data> get_presence_updates();
std::map<std::string, friend_online_data> get_presence_states();
@ -537,11 +394,11 @@ namespace rpcn
static void write_communication_id(const SceNpCommunicationId& com_id, std::vector<u8>& data);
std::vector<u8> forge_request(u16 command, u64 packet_id, const std::vector<u8>& data) const;
bool forge_send(u16 command, u64 packet_id, const std::vector<u8>& data);
std::vector<u8> forge_request(rpcn::CommandType command, u64 packet_id, const std::vector<u8>& data) const;
bool forge_send(rpcn::CommandType command, u64 packet_id, const std::vector<u8>& data);
bool forge_request_with_com_id(const flatbuffers::FlatBufferBuilder& builder, const SceNpCommunicationId& com_id, CommandType command, u64 packet_id);
bool forge_request_with_data(const flatbuffers::FlatBufferBuilder& builder, CommandType command, u64 packet_id);
bool forge_send_reply(u16 command, u64 packet_id, const std::vector<u8>& data, std::vector<u8>& reply_data);
bool forge_send_reply(rpcn::CommandType command, u64 packet_id, const std::vector<u8>& data, std::vector<u8>& reply_data);
bool error_and_disconnect(const std::string& error_mgs);
bool error_and_disconnect_notice(const std::string& error_msg);
@ -556,11 +413,9 @@ namespace rpcn
atomic_t<bool> server_info_received = false;
u32 received_version = 0;
// UDP Signaling related
steady_clock::time_point last_ping_time{}, last_pong_time{};
sockaddr_in addr_rpcn{};
sockaddr_in addr_rpcn_udp{};
sockaddr_in addr_rpcn_udp_ipv4{};
sockaddr_in6 addr_rpcn_udp_ipv6{};
#ifdef _WIN32
SOCKET sockfd = 0;
#else
@ -570,10 +425,10 @@ namespace rpcn
atomic_t<u64> rpcn_request_counter = 0x100000001; // Counter used for commands whose result is not forwarded to NP handler(login, create, sendmessage, etc)
shared_mutex mutex_notifs, mutex_replies, mutex_replies_sync, mutex_presence_updates;
std::vector<std::pair<u16, std::vector<u8>>> notifications; // notif type / data
std::unordered_map<u32, std::pair<u16, std::vector<u8>>> replies; // req id / (command / data)
std::unordered_map<u64, std::pair<u16, std::vector<u8>>> replies_sync; // same but for sync replies(see handle_input())
std::unordered_map<std::string, friend_online_data> presence_updates; // npid / presence data
std::vector<std::pair<rpcn::NotificationType, std::vector<u8>>> notifications; // notif type / data
std::unordered_map<u32, std::pair<rpcn::CommandType, std::vector<u8>>> replies; // req id / (command / data)
std::unordered_map<u64, std::pair<rpcn::CommandType, std::vector<u8>>> replies_sync; // same but for sync replies(see handle_input())
std::unordered_map<std::string, friend_online_data> presence_updates; // npid / presence data
// Messages
struct message_cb_t

View file

@ -124,6 +124,11 @@ std::string cfg_rpcn::get_token() const
return token.to_string();
}
bool cfg_rpcn::get_ipv6_support() const
{
return ipv6_support.get();
}
void cfg_rpcn::set_host(std::string_view host)
{
this->host.from_string(host);
@ -144,6 +149,11 @@ void cfg_rpcn::set_token(std::string_view token)
this->token.from_string(token);
}
void cfg_rpcn::set_ipv6_support(bool ipv6_support)
{
this->ipv6_support.set(ipv6_support);
}
void cfg_rpcn::set_hosts(const std::vector<std::pair<std::string, std::string>>& vec_hosts)
{
std::string final_string;

View file

@ -10,6 +10,7 @@ struct cfg_rpcn : cfg::node
cfg::string password{this, "Password", ""};
cfg::string token{this, "Token", ""};
cfg::string hosts{this, "Hosts", "Official RPCN Server|np.rpcs3.net|||RPCN Test Server|test-np.rpcs3.net"};
cfg::_bool ipv6_support{this, "IPv6 support", true};
void load();
void save() const;
@ -18,12 +19,14 @@ struct cfg_rpcn : cfg::node
std::string get_npid(); // not const because it can save if npid is requested and it has never been set
std::string get_password() const;
std::string get_token() const;
bool get_ipv6_support() const;
std::vector<std::pair<std::string, std::string>> get_hosts(); // saves default if no valid server in the list
void set_host(std::string_view host);
void set_npid(std::string_view npid);
void set_password(std::string_view password);
void set_token(std::string_view token);
void set_ipv6_support(bool ipv6_support);
bool add_host(std::string_view description, std::string_view host);
bool del_host(std::string_view description, std::string_view host);

156
rpcs3/Emu/NP/rpcn_types.h Normal file
View file

@ -0,0 +1,156 @@
#pragma once
#include "util/types.hpp"
namespace rpcn
{
enum class CommandType : u16
{
Login,
Terminate,
Create,
SendToken,
SendResetToken,
ResetPassword,
ResetState,
AddFriend,
RemoveFriend,
AddBlock,
RemoveBlock,
GetServerList,
GetWorldList,
CreateRoom,
JoinRoom,
LeaveRoom,
SearchRoom,
GetRoomDataExternalList,
SetRoomDataExternal,
GetRoomDataInternal,
SetRoomDataInternal,
GetRoomMemberDataInternal,
SetRoomMemberDataInternal,
SetUserInfo,
PingRoomOwner,
SendRoomMessage,
RequestSignalingInfos,
RequestTicket,
SendMessage,
GetBoardInfos,
RecordScore,
RecordScoreData,
GetScoreData,
GetScoreRange,
GetScoreFriends,
GetScoreNpid,
GetNetworkTime,
TusSetMultiSlotVariable,
TusGetMultiSlotVariable,
TusGetMultiUserVariable,
TusGetFriendsVariable,
TusAddAndGetVariable,
TusTryAndSetVariable,
TusDeleteMultiSlotVariable,
TusSetData,
TusGetData,
TusGetMultiSlotDataStatus,
TusGetMultiUserDataStatus,
TusGetFriendsDataStatus,
TusDeleteMultiSlotData,
SetPresence,
CreateRoomGUI,
JoinRoomGUI,
LeaveRoomGUI,
GetRoomListGUI,
SetRoomSearchFlagGUI,
GetRoomSearchFlagGUI,
SetRoomInfoGUI,
GetRoomInfoGUI,
QuickMatchGUI,
SearchJoinRoomGUI,
};
enum class NotificationType : u16
{
UserJoinedRoom,
UserLeftRoom,
RoomDestroyed,
UpdatedRoomDataInternal,
UpdatedRoomMemberDataInternal,
FriendQuery, // Other user sent a friend request
FriendNew, // Add a friend to the friendlist(either accepted a friend request or friend accepted it)
FriendLost, // Remove friend from the friendlist(user removed friend or friend removed friend)
FriendStatus, // Set status of friend to Offline or Online
RoomMessageReceived,
MessageReceived,
FriendPresenceChanged,
SignalingHelper,
MemberJoinedRoomGUI,
MemberLeftRoomGUI,
RoomDisappearedGUI,
RoomOwnerChangedGUI,
UserKickedGUI,
QuickMatchCompleteGUI,
};
enum class rpcn_state
{
failure_no_failure,
failure_input,
failure_wolfssl,
failure_resolve,
failure_connect,
failure_id,
failure_id_already_logged_in,
failure_id_username,
failure_id_password,
failure_id_token,
failure_protocol,
failure_other,
};
enum class PacketType : u8
{
Request,
Reply,
Notification,
ServerInfo,
};
enum class ErrorType : u8
{
NoError, // No error
Malformed, // Query was malformed, critical error that should close the connection
Invalid, // The request type is invalid(wrong stage?)
InvalidInput, // The Input doesn't fit the constraints of the request
TooSoon, // Time limited operation attempted too soon
LoginError, // An error happened related to login
LoginAlreadyLoggedIn, // Can't log in because you're already logged in
LoginInvalidUsername, // Invalid username
LoginInvalidPassword, // Invalid password
LoginInvalidToken, // Invalid token
CreationError, // An error happened related to account creation
CreationExistingUsername, // Specific to Account Creation: username exists already
CreationBannedEmailProvider, // Specific to Account Creation: the email provider is banned
CreationExistingEmail, // Specific to Account Creation: that email is already registered to an account
RoomMissing, // User tried to interact with a non existing room
RoomAlreadyJoined, // User tried to join a room he's already part of
RoomFull, // User tried to join a full room
RoomPasswordMismatch, // Room password didn't match
RoomPasswordMissing, // A password was missing during room creation
RoomGroupNoJoinLabel, // Tried to join a group room without a label
RoomGroupFull, // Room group is full
RoomGroupJoinLabelNotFound, // Join label was invalid in some way
RoomGroupMaxSlotMismatch, // Mismatch between max_slot and the listed slots in groups
Unauthorized, // User attempted an unauthorized operation
DbFail, // Generic failure on db side
EmailFail, // Generic failure related to email
NotFound, // Object of the query was not found(user, etc), use RoomMissing for rooms instead
Blocked, // The operation can't complete because you've been blocked
AlreadyFriend, // Can't add friend because already friend
ScoreNotBest, // A better score is already registered for that user/character_id
ScoreInvalid, // Score for player was found but wasn't what was expected
ScoreHasData, // Score already has data
CondFail, // Condition related to query failed
Unsupported,
};
} // namespace rpcn

View file

@ -1,3 +1,4 @@
#include "Emu/NP/ip_address.h"
#include "stdafx.h"
#include "Emu/Cell/PPUCallback.h"
#include "signaling_handler.h"
@ -547,6 +548,13 @@ void signaling_handler::update_si_mapped_addr(std::shared_ptr<signaling_info>& s
{
ensure(si);
// If the address given to us by op is a translation IP, just replace it with our public ip(v4)
if (np::is_ipv6_supported() && np::ip_address_translator::is_ipv6(new_addr))
{
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
new_addr = nph.get_public_ip_addr();
}
if (si->mapped_addr != new_addr || si->mapped_port != new_port)
{
if (sign_log.trace)
@ -640,7 +648,15 @@ void signaling_handler::send_signaling_packet(signaling_packet& sp, u32 addr, u1
sign_log.trace("Sending %s packet to %s:%d", sp.command, ip_str, port);
if (send_packet_from_p2p_port(packet, dest) == -1)
if (np::is_ipv6_supported() && np::ip_address_translator::is_ipv6(dest.sin_addr.s_addr))
{
auto& translator = g_fxo->get<np::ip_address_translator>();
const auto addr6 = translator.get_ipv6_sockaddr(dest.sin_addr.s_addr, dest.sin_port);
if (!send_packet_from_p2p_port_ipv6(packet, addr6))
sign_log.error("Failed to send signaling packet to %s:%d", ip_str, port);
}
else if (!send_packet_from_p2p_port_ipv4(packet, dest))
{
sign_log.error("Failed to send signaling packet to %s:%d", ip_str, port);
}

View file

@ -10,7 +10,8 @@
#include "Emu/Cell/lv2/sys_net/nt_p2p_port.h"
s32 send_packet_from_p2p_port(const std::vector<u8>& data, const sockaddr_in& addr);
bool send_packet_from_p2p_port_ipv4(const std::vector<u8>& data, const sockaddr_in& addr);
bool send_packet_from_p2p_port_ipv6(const std::vector<u8>& data, const sockaddr_in6& addr);
std::vector<signaling_message> get_sign_msgs();
std::vector<std::vector<u8>> get_rpcn_msgs();

View file

@ -383,7 +383,7 @@ namespace rsx
}
}
void sendmessage_dialog::callback_handler(u16 ntype, const std::string& username, bool status)
void sendmessage_dialog::callback_handler(rpcn::NotificationType ntype, const std::string& username, bool status)
{
std::lock_guard lock(m_mutex);

View file

@ -40,7 +40,7 @@ namespace rsx
compiled_resource get_compiled() override;
error_code Exec(message_data& msg_data, std::set<std::string>& npids) override;
void callback_handler(u16 ntype, const std::string& username, bool status) override;
void callback_handler(rpcn::NotificationType ntype, const std::string& username, bool status) override;
};
}
}

View file

@ -267,7 +267,7 @@ enum class np_internet_status
enabled,
};
enum np_psn_status
enum class np_psn_status
{
disabled,
psn_fake,

View file

@ -175,6 +175,7 @@
<ClCompile Include="Emu\NP\signaling_handler.cpp" />
<ClCompile Include="Emu\NP\np_structs_extra.cpp" />
<ClCompile Include="Emu\NP\rpcn_client.cpp" />
<ClCompile Include="Emu\NP\ip_address.cpp" />
<ClCompile Include="Emu\vfs_config.cpp" />
<ClCompile Include="Loader\disc.cpp" />
<ClCompile Include="util\serialization_ext.cpp">
@ -618,6 +619,7 @@
<ClInclude Include="Emu\NP\np_structs_extra.h" />
<ClInclude Include="Emu\NP\rpcn_client.h" />
<ClInclude Include="Emu\NP\rpcn_config.h" />
<ClInclude Include="Emu\NP\ip_address.h" />
<ClInclude Include="Emu\perf_monitor.hpp" />
<ClInclude Include="Emu\RSX\color_utils.h" />
<ClInclude Include="Emu\RSX\Common\bitfield.hpp" />

View file

@ -1029,6 +1029,9 @@
<ClCompile Include="Emu\NP\rpcn_client.cpp">
<Filter>Emu\NP</Filter>
</ClCompile>
<ClCompile Include="Emu\NP\ip_address.cpp">
<Filter>Emu\NP</Filter>
</ClCompile>
<ClCompile Include="Emu\NP\rpcn_config.cpp">
<Filter>Emu\NP</Filter>
</ClCompile>
@ -2683,6 +2686,9 @@
<ClInclude Include="Emu\NP\np_gui_cache.h">
<Filter>Emu\NP</Filter>
</ClInclude>
<ClInclude Include="Emu\NP\ip_address.h">
<Filter>Emu\NP</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\Core\RSXDrawCommands.h">
<Filter>Emu\GPU\RSX\Core</Filter>
</ClInclude>

View file

@ -7,12 +7,14 @@
#include <QGroupBox>
#include <QMenu>
#include <QDialogButtonBox>
#include <QCheckBox>
#include "qt_utils.h"
#include "rpcn_settings_dialog.h"
#include "Emu/System.h"
#include "Emu/NP/rpcn_config.h"
#include "Emu/NP/ip_address.h"
#include <wolfssl/ssl.h>
#include <wolfssl/openssl/evp.h>
@ -160,6 +162,9 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent)
QPushButton* btn_test = new QPushButton(tr("Test Account"));
QLabel* label_npid = new QLabel();
QCheckBox* checkbox_disable_ipv6 = new QCheckBox("Disable IPv6");
checkbox_disable_ipv6->setCheckState(g_cfg_rpcn.get_ipv6_support() ? Qt::Unchecked : Qt::Checked);
const auto update_npid_label = [label_npid]()
{
const std::string npid = g_cfg_rpcn.get_npid();
@ -178,6 +183,7 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent)
grp_buttons->setLayout(vbox_buttons);
vbox_global->addWidget(grp_buttons);
vbox_global->addWidget(checkbox_disable_ipv6);
setLayout(vbox_global);
@ -193,6 +199,10 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent)
g_cfg_rpcn.set_host(host.toString().toStdString());
g_cfg_rpcn.save();
// Resets the state in case the support was limited by the RPCN server
if (!np::is_ipv6_supported())
np::is_ipv6_supported(np::IPV6_SUPPORT::IPV6_UNKNOWN);
});
connect(btn_add_server, &QAbstractButton::clicked, this, [this]()
@ -333,6 +343,12 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent)
QMessageBox::information(this, tr("RPCN Account Valid!"), tr("Your account is valid!"), QMessageBox::Ok);
});
connect(checkbox_disable_ipv6, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state)
{
g_cfg_rpcn.set_ipv6_support(state == Qt::Unchecked);
g_cfg_rpcn.save();
});
}
void rpcn_account_dialog::refresh_combobox()

View file

@ -156,7 +156,7 @@ void sendmessage_dialog_frame::slot_remove_friend(QString name)
remove_friend(m_lst_friends, name);
}
void sendmessage_dialog_frame::callback_handler(u16 ntype, const std::string& username, bool status)
void sendmessage_dialog_frame::callback_handler(rpcn::NotificationType ntype, const std::string& username, bool status)
{
QString qtr_username = QString::fromStdString(username);
switch (ntype)

View file

@ -16,7 +16,7 @@ public:
sendmessage_dialog_frame() = default;
~sendmessage_dialog_frame();
error_code Exec(message_data& msg_data, std::set<std::string>& npids) override;
void callback_handler(u16 ntype, const std::string& username, bool status) override;
void callback_handler(rpcn::NotificationType ntype, const std::string& username, bool status) override;
private:
void add_friend(QListWidget* list, const QString& name);