1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-22 02:32:36 +01:00

RPCN v0.6

This commit is contained in:
RipleyTom 2022-05-18 17:44:21 +02:00 committed by kd-11
parent 67c02e3522
commit eea73deab3
39 changed files with 3461 additions and 758 deletions

@ -1 +1 @@
Subproject commit 615616cb5549a34bdf288c04bc1b94bd7a65c396
Subproject commit 06c5c7ed0bd987a918cf88caafb094f22cdd1721

View File

@ -5,9 +5,10 @@ if(USE_SYSTEM_WOLFSSL)
target_link_libraries(wolfssl INTERFACE PkgConfig::WolfSSL)
else()
# TODO(cjj19970505@live.cn)
# OPENSSL_EXTRA, WOLFSSL_DES_ECB and HAVE_SNI are unconfigurable from CMake cache.
# OPENSSL_EXTRA, and HAVE_SNI are unconfigurable from CMake cache.
# but they do have it in a TODO list (wolfssl/CMakeList, 1021)
add_compile_definitions(OPENSSL_EXTRA WOLFSSL_DES_ECB HAVE_SNI HAVE_WRITE_DUP)
add_compile_definitions(FP_MAX_BITS=8192)
set(WOLFSSL_TLS13 "no" CACHE INTERNAL "")
set(WOLFSSL_SHA224 "yes" CACHE INTERNAL "")

View File

@ -237,7 +237,7 @@ struct netstart_hack
error_code cellNetCtlNetStartDialogLoadAsync(vm::cptr<CellNetCtlNetStartDialogParam> param)
{
cellNetCtl.error("cellNetCtlNetStartDialogLoadAsync(param=*0x%x)", param);
cellNetCtl.warning("cellNetCtlNetStartDialogLoadAsync(param=*0x%x)", param);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();

File diff suppressed because it is too large Load Diff

View File

@ -1393,14 +1393,14 @@ struct SceNpMatchingReqRange
struct SceNpScoreVariableSizeGameInfo
{
be_t<u64> infoSize;
be_t<u32> infoSize;
u8 data[SCE_NP_SCORE_VARIABLE_SIZE_GAMEINFO_MAXSIZE];
u8 pad[3];
};
struct SceNpScoreRecordOptParam
{
be_t<u64> size;
be_t<u32> size;
vm::bptr<SceNpScoreVariableSizeGameInfo> vsInfo;
vm::bptr<CellRtcTick> reserved;
};

View File

@ -319,7 +319,7 @@ error_code sceNpMatching2Term2()
error_code sceNpMatching2DestroyContext(SceNpMatching2ContextId ctxId)
{
sceNp2.todo("sceNpMatching2DestroyContext(ctxId=%d)", ctxId);
sceNp2.warning("sceNpMatching2DestroyContext(ctxId=%d)", ctxId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
@ -611,11 +611,20 @@ error_code sceNpMatching2SignalingGetConnectionInfo(
return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
if (!connInfo)
{
return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
const auto si = sigh.get_sig2_infos(roomId, memberId);
switch (code)
{
case SCE_NP_SIGNALING_CONN_INFO_RTT:
{
connInfo->rtt = 20000; // HACK
connInfo->rtt = si.rtt;
sceNp2.warning("Returning a RTT of %d microseconds", connInfo->rtt);
break;
}
case SCE_NP_SIGNALING_CONN_INFO_BANDWIDTH:
@ -625,22 +634,17 @@ error_code sceNpMatching2SignalingGetConnectionInfo(
}
case SCE_NP_SIGNALING_CONN_INFO_PEER_NPID:
{
// TODO: need an update to whole signaling as matching2 signaling ignores npids atm
sceNp2.fatal("sceNpMatching2SignalingGetConnectionInfo Unimplemented SCE_NP_SIGNALING_CONN_INFO_PEER_NPID");
connInfo->npId = si.npid;
break;
}
case SCE_NP_SIGNALING_CONN_INFO_PEER_ADDRESS:
{
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
const auto si = sigh.get_sig2_infos(roomId, memberId);
connInfo->address.port = std::bit_cast<u16, be_t<u16>>(si.port);
connInfo->address.addr.np_s_addr = si.addr;
break;
}
case SCE_NP_SIGNALING_CONN_INFO_MAPPED_ADDRESS:
{
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
const auto si = sigh.get_sig2_infos(roomId, memberId);
connInfo->address.port = std::bit_cast<u16, be_t<u16>>(si.mapped_port);
connInfo->address.addr.np_s_addr = si.mapped_addr;
break;
@ -652,8 +656,7 @@ error_code sceNpMatching2SignalingGetConnectionInfo(
}
default:
{
sceNp2.fatal("sceNpMatching2SignalingGetConnectionInfo Unimplemented code: %d", code);
return CELL_OK;
return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
}
@ -715,6 +718,16 @@ error_code sceNpMatching2AbortRequest(SceNpMatching2ContextId ctxId, SceNpMatchi
return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
if (!ctxId)
{
return SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
}
if (!check_match2_context(ctxId))
{
return SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND;
}
return CELL_OK;
}
@ -1349,7 +1362,7 @@ error_code sceNpMatching2SetDefaultRequestOptParam(SceNpMatching2ContextId ctxId
error_code sceNpMatching2RegisterRoomEventCallback(SceNpMatching2ContextId ctxId, vm::ptr<SceNpMatching2RoomEventCallback> cbFunc, vm::ptr<void> cbFuncArg)
{
sceNp2.todo("sceNpMatching2RegisterRoomEventCallback(ctxId=%d, cbFunc=*0x%x, cbFuncArg=*0x%x)", ctxId, cbFunc, cbFuncArg);
sceNp2.warning("sceNpMatching2RegisterRoomEventCallback(ctxId=%d, cbFunc=*0x%x, cbFuncArg=*0x%x)", ctxId, cbFunc, cbFuncArg);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
@ -1546,7 +1559,7 @@ error_code sceNpMatching2SetLobbyMemberDataInternal(
error_code sceNpMatching2RegisterRoomMessageCallback(SceNpMatching2ContextId ctxId, vm::ptr<SceNpMatching2RoomMessageCallback> cbFunc, vm::ptr<void> cbFuncArg)
{
sceNp2.todo("sceNpMatching2RegisterRoomMessageCallback(ctxId=%d, cbFunc=*0x%x, cbFuncArg=*0x%x)", ctxId, cbFunc, cbFuncArg);
sceNp2.warning("sceNpMatching2RegisterRoomMessageCallback(ctxId=%d, cbFunc=*0x%x, cbFuncArg=*0x%x)", ctxId, cbFunc, cbFuncArg);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();

View File

@ -822,6 +822,7 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr<void> buf, u32
{
sn_addr = res_addr;
std::memcpy(buf.get_ptr(), vec.data(), res);
sys_net_dump_data("recvfrom", vec.data(), res);
}
result = res;
lv2_obj::awake(&ppu);
@ -998,6 +999,8 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 l
return -SYS_NET_EAFNOSUPPORT;
}
sys_net_dump_data("sendto", static_cast<const u8 *>(buf.get_ptr()), len);
const std::optional<sys_net_sockaddr> sn_addr = addr ? std::optional<sys_net_sockaddr>(*addr) : std::nullopt;
const std::vector<u8> buf_copy(vm::_ptr<const char>(buf.addr()), vm::_ptr<const char>(buf.addr()) + len);
s32 result{};
@ -1222,7 +1225,9 @@ error_code sys_net_bnet_close(ppu_thread& ppu, s32 s)
}
if (sock->get_queue_size())
sys_net.error("CLOSE");
{
sock->abort_socket(0);
}
sock->close();
@ -1718,6 +1723,9 @@ error_code lv2_socket::abort_socket(s32 flags)
for (auto& [ppu, _] : qcopy)
{
if (!ppu)
continue;
sys_net.warning("lv2_socket::abort_socket(): waking up \"%s\": (func: %s, r3=0x%x, r4=0x%x, r5=0x%x, r6=0x%x)", ppu->get_name(), ppu->current_function, ppu->gpr[3], ppu->gpr[4], ppu->gpr[5], ppu->gpr[6]);
ppu->gpr[3] = static_cast<u64>(-SYS_NET_EINTR);
lv2_obj::append(ppu.get());

View File

@ -204,7 +204,7 @@ s32 lv2_socket_p2p::setsockopt(s32 level, s32 optname, const std::vector<u8>& op
return {};
}
std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>> lv2_socket_p2p::recvfrom([[maybe_unused]] s32 flags, u32 len, bool is_lock)
std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>> lv2_socket_p2p::recvfrom(s32 flags, u32 len, bool is_lock)
{
std::unique_lock<shared_mutex> lock(mutex, std::defer_lock);
@ -217,7 +217,10 @@ std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>> lv2_socket_p2p
if (data.empty())
{
return {{-SYS_NET_EWOULDBLOCK, {}, {}}};
if (so_nbio || (flags & SYS_NET_MSG_DONTWAIT))
return {{-SYS_NET_EWOULDBLOCK, {}, {}}};
return std::nullopt;
}
std::vector<u8> res_buf(len);

View File

@ -703,7 +703,7 @@ std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>> lv2_socket_p2p
if (!data_available)
{
if (so_nbio)
if (so_nbio || (flags & SYS_NET_MSG_DONTWAIT))
{
return {{-SYS_NET_EWOULDBLOCK, {}, {}}};
}

View File

@ -1,5 +1,6 @@
#include "stdafx.h"
#include "lv2_socket_raw.h"
#include "Emu/NP/vport0.h"
LOG_CHANNEL(sys_net);
@ -20,7 +21,31 @@ void lv2_socket_raw::save(utils::serial& ar)
std::tuple<bool, s32, std::shared_ptr<lv2_socket>, sys_net_sockaddr> lv2_socket_raw::accept([[maybe_unused]] bool is_lock)
{
sys_net.todo("lv2_socket_raw::accept");
sys_net.fatal("[RAW] accept() called on a RAW socket");
return {};
}
std::optional<s32> lv2_socket_raw::connect([[maybe_unused]] const sys_net_sockaddr& addr)
{
sys_net.fatal("[RAW] connect() called on a RAW socket");
return CELL_OK;
}
s32 lv2_socket_raw::connect_followup()
{
sys_net.fatal("[RAW] connect_followup() called on a RAW socket");
return CELL_OK;
}
std::pair<s32, sys_net_sockaddr> lv2_socket_raw::getpeername()
{
sys_net.todo("[RAW] getpeername() called on a RAW socket");
return {};
}
s32 lv2_socket_raw::listen([[maybe_unused]] s32 backlog)
{
sys_net.todo("[RAW] listen() called on a RAW socket");
return {};
}
@ -30,24 +55,6 @@ s32 lv2_socket_raw::bind([[maybe_unused]] const sys_net_sockaddr& addr)
return {};
}
std::optional<s32> lv2_socket_raw::connect([[maybe_unused]] const sys_net_sockaddr& addr)
{
sys_net.todo("lv2_socket_raw::connect");
return CELL_OK;
}
s32 lv2_socket_raw::connect_followup()
{
sys_net.todo("lv2_socket_raw::connect_followup");
return CELL_OK;
}
std::pair<s32, sys_net_sockaddr> lv2_socket_raw::getpeername()
{
sys_net.todo("lv2_socket_raw::getpeername");
return {};
}
std::pair<s32, sys_net_sockaddr> lv2_socket_raw::getsockname()
{
sys_net.todo("lv2_socket_raw::getsockname");
@ -66,16 +73,10 @@ s32 lv2_socket_raw::setsockopt([[maybe_unused]] s32 level, [[maybe_unused]] s32
return {};
}
s32 lv2_socket_raw::listen([[maybe_unused]] s32 backlog)
{
sys_net.todo("lv2_socket_raw::listen");
return {};
}
std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>> lv2_socket_raw::recvfrom([[maybe_unused]] s32 flags, [[maybe_unused]] u32 len, [[maybe_unused]] bool is_lock)
{
sys_net.todo("lv2_socket_raw::recvfrom");
return {{{}, {}, {}}};
return {};
}
std::optional<s32> lv2_socket_raw::sendto([[maybe_unused]] s32 flags, [[maybe_unused]] const std::vector<u8>& buf, [[maybe_unused]] std::optional<sys_net_sockaddr> opt_sn_addr, [[maybe_unused]] bool is_lock)

View File

@ -53,9 +53,9 @@ std::vector<std::vector<u8>> get_rpcn_msgs()
return msgs;
}
std::vector<std::pair<std::pair<u32, u16>, std::vector<u8>>> get_sign_msgs()
std::vector<signaling_message> get_sign_msgs()
{
std::vector<std::pair<std::pair<u32, u16>, std::vector<u8>>> msgs;
std::vector<signaling_message> msgs;
auto& nc = g_fxo->get<network_context>();
{
std::lock_guard list_lock(nc.list_p2p_ports_mutex);

View File

@ -9,6 +9,7 @@
#include "sys_net_helpers.h"
#include "Emu/NP/signaling_handler.h"
#include "sys_net_helpers.h"
#include "Emu/NP/vport0.h"
LOG_CHANNEL(sys_net);
@ -119,35 +120,48 @@ bool nt_p2p_port::recv_data()
u16 dst_vport = reinterpret_cast<le_t<u16>&>(p2p_recv_data[0]);
if (dst_vport == 0) // Reserved for messages from RPCN server
if (dst_vport == 0)
{
std::vector<u8> rpcn_msg(recv_res - sizeof(u16));
memcpy(rpcn_msg.data(), p2p_recv_data.data() + sizeof(u16), recv_res - sizeof(u16));
std::lock_guard lock(s_rpcn_mutex);
rpcn_msgs.push_back(std::move(rpcn_msg));
return true;
}
if (dst_vport == 65535) // Reserved for signaling
{
std::vector<u8> sign_msg(recv_res - sizeof(u16));
memcpy(sign_msg.data(), p2p_recv_data.data() + sizeof(u16), recv_res - sizeof(u16));
std::pair<std::pair<u32, u16>, std::vector<u8>> msg;
msg.first.first = reinterpret_cast<struct sockaddr_in*>(&native_addr)->sin_addr.s_addr;
msg.first.second = std::bit_cast<u16, be_t<u16>>(reinterpret_cast<struct sockaddr_in*>(&native_addr)->sin_port);
msg.second = std::move(sign_msg);
if (recv_res < VPORT_0_HEADER_SIZE)
{
std::lock_guard lock(s_sign_mutex);
sign_msgs.push_back(std::move(msg));
sys_net.error("Bad vport 0 packet(no subset)!");
return true;
}
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
sigh.wake_up();
const u8 subset = p2p_recv_data[2];
const auto data_size = recv_res - VPORT_0_HEADER_SIZE;
std::vector<u8> vport_0_data(p2p_recv_data.data() + VPORT_0_HEADER_SIZE, p2p_recv_data.data() + VPORT_0_HEADER_SIZE + data_size);
return true;
switch (subset)
{
case SUBSET_RPCN:
{
std::lock_guard lock(s_rpcn_mutex);
rpcn_msgs.push_back(std::move(vport_0_data));
return true;
}
case SUBSET_SIGNALING:
{
signaling_message msg;
msg.src_addr = reinterpret_cast<struct sockaddr_in*>(&native_addr)->sin_addr.s_addr;
msg.src_port = std::bit_cast<u16, be_t<u16>>(reinterpret_cast<struct sockaddr_in*>(&native_addr)->sin_port);
msg.data = std::move(vport_0_data);
{
std::lock_guard lock(s_sign_mutex);
sign_msgs.push_back(std::move(msg));
}
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
sigh.wake_up();
return true;
}
default:
{
sys_net.error("Invalid vport 0 subset!");
return true;
}
}
}
{

View File

@ -18,6 +18,14 @@
#endif
#endif
struct signaling_message
{
u32 src_addr = 0;
u16 src_port = 0;
std::vector<u8> data;
};
struct nt_p2p_port
{
// Real socket where P2P packets are received/sent
@ -36,7 +44,7 @@ struct nt_p2p_port
std::vector<std::vector<u8>> rpcn_msgs{};
// Queued signaling messages
shared_mutex s_sign_mutex;
std::vector<std::pair<std::pair<u32, u16>, std::vector<u8>>> sign_msgs{};
std::vector<signaling_message> sign_msgs{};
std::array<u8, 65535> p2p_recv_data{};

View File

@ -154,7 +154,7 @@ namespace np
SceNpMatching2RoomDataExternal* cur_room;
cur_room = (i > 0) ? edata.allocate<SceNpMatching2RoomDataExternal>(sizeof(SceNpMatching2RoomDataExternal), prev_room->next) :
edata.allocate<SceNpMatching2RoomDataExternal>(sizeof(SceNpMatching2RoomDataExternal), search_resp->roomDataExternal);
edata.allocate<SceNpMatching2RoomDataExternal>(sizeof(SceNpMatching2RoomDataExternal), search_resp->roomDataExternal);
RoomDataExternal_to_SceNpMatching2RoomDataExternal(edata, fb_room, cur_room);
prev_room = cur_room;
@ -173,7 +173,7 @@ namespace np
SceNpMatching2RoomDataExternal* cur_room;
cur_room = (i > 0) ? edata.allocate<SceNpMatching2RoomDataExternal>(sizeof(SceNpMatching2RoomDataExternal), prev_room->next) :
edata.allocate<SceNpMatching2RoomDataExternal>(sizeof(SceNpMatching2RoomDataExternal), get_resp->roomDataExternal);
edata.allocate<SceNpMatching2RoomDataExternal>(sizeof(SceNpMatching2RoomDataExternal), get_resp->roomDataExternal);
RoomDataExternal_to_SceNpMatching2RoomDataExternal(edata, fb_room, cur_room);
prev_room = cur_room;
@ -370,10 +370,10 @@ namespace np
{
rpcn_log.todo("RoomDataInternalUpdateInfo::newRoomGroup");
// TODO
//sce_update_info->newRoomGroupNum = update_info->newRoomGroup()->size();
//vm::ptr<SceNpMatching2RoomGroup> group_info(allocate(sizeof(SceNpMatching2RoomGroup) * sce_update_info->newRoomGroupNum));
//RoomGroups_to_SceNpMatching2RoomGroup(update_info->newRoomGroup(), group_info);
//sce_update_info->newRoomGroup = group_info;
// sce_update_info->newRoomGroupNum = update_info->newRoomGroup()->size();
// vm::ptr<SceNpMatching2RoomGroup> group_info(allocate(sizeof(SceNpMatching2RoomGroup) * sce_update_info->newRoomGroupNum));
// RoomGroups_to_SceNpMatching2RoomGroup(update_info->newRoomGroup(), group_info);
// sce_update_info->newRoomGroup = group_info;
}
if (update_info->newRoomBinAttrInternal() && update_info->newRoomBinAttrInternal()->size() != 0)
@ -497,5 +497,4 @@ namespace np
}
}
}
} // namespace np

View File

@ -270,3 +270,69 @@ table SendMessageRequest {
message:[uint8] (nested_flatbuffer: "MessageDetails");
npids:[string];
}
table BoardInfo {
rankLimit:uint32;
updateMode:uint32;
sortMode:uint32;
uploadNumLimit:uint32;
uploadSizeLimit:uint32;
}
table RecordScoreRequest {
boardId:uint32;
pcId:int32;
score:int64;
comment:string;
data:[uint8];
}
table GetScoreRangeRequest {
boardId:uint32;
startRank:uint32;
numRanks:uint32;
withComment:bool;
withGameInfo:bool;
}
table ScoreNpIdPcId {
npid:string;
pcId:int32;
}
table GetScoreNpIdRequest {
boardId:uint32;
npids:[ScoreNpIdPcId];
withComment:bool;
withGameInfo:bool;
}
table GetScoreFriendsRequest {
boardId:uint32;
include_self:bool;
max:uint32;
withComment:bool;
withGameInfo:bool;
}
table ScoreRankData {
npId:string;
onlineName:string;
pcId:int32;
rank:uint32;
score:int64;
hasGameData:bool;
recordDate:uint64;
}
table ScoreInfo {
data: [uint8];
}
table GetScoreResponse {
rankArray:[ScoreRankData];
commentArray:[string];
infoArray:[ScoreInfo];
lastSortDate:uint64;
totalRecord:uint32;
}

View File

@ -6,6 +6,13 @@
#include "flatbuffers/flatbuffers.h"
// Ensure the included flatbuffers.h is the same version as when this file was
// generated, otherwise it may not be compatible.
static_assert(FLATBUFFERS_VERSION_MAJOR == 2 &&
FLATBUFFERS_VERSION_MINOR == 0 &&
FLATBUFFERS_VERSION_REVISION == 8,
"Non-compatible flatbuffers version included");
struct BinAttr;
struct BinAttrBuilder;
@ -111,6 +118,33 @@ struct MessageDetailsBuilder;
struct SendMessageRequest;
struct SendMessageRequestBuilder;
struct BoardInfo;
struct BoardInfoBuilder;
struct RecordScoreRequest;
struct RecordScoreRequestBuilder;
struct GetScoreRangeRequest;
struct GetScoreRangeRequestBuilder;
struct ScoreNpIdPcId;
struct ScoreNpIdPcIdBuilder;
struct GetScoreNpIdRequest;
struct GetScoreNpIdRequestBuilder;
struct GetScoreFriendsRequest;
struct GetScoreFriendsRequestBuilder;
struct ScoreRankData;
struct ScoreRankDataBuilder;
struct ScoreInfo;
struct ScoreInfoBuilder;
struct GetScoreResponse;
struct GetScoreResponseBuilder;
struct BinAttr FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
typedef BinAttrBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
@ -3460,4 +3494,781 @@ inline flatbuffers::Offset<SendMessageRequest> CreateSendMessageRequestDirect(
npids__);
}
struct BoardInfo FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
typedef BoardInfoBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_RANKLIMIT = 4,
VT_UPDATEMODE = 6,
VT_SORTMODE = 8,
VT_UPLOADNUMLIMIT = 10,
VT_UPLOADSIZELIMIT = 12
};
uint32_t rankLimit() const {
return GetField<uint32_t>(VT_RANKLIMIT, 0);
}
uint32_t updateMode() const {
return GetField<uint32_t>(VT_UPDATEMODE, 0);
}
uint32_t sortMode() const {
return GetField<uint32_t>(VT_SORTMODE, 0);
}
uint32_t uploadNumLimit() const {
return GetField<uint32_t>(VT_UPLOADNUMLIMIT, 0);
}
uint32_t uploadSizeLimit() const {
return GetField<uint32_t>(VT_UPLOADSIZELIMIT, 0);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint32_t>(verifier, VT_RANKLIMIT, 4) &&
VerifyField<uint32_t>(verifier, VT_UPDATEMODE, 4) &&
VerifyField<uint32_t>(verifier, VT_SORTMODE, 4) &&
VerifyField<uint32_t>(verifier, VT_UPLOADNUMLIMIT, 4) &&
VerifyField<uint32_t>(verifier, VT_UPLOADSIZELIMIT, 4) &&
verifier.EndTable();
}
};
struct BoardInfoBuilder {
typedef BoardInfo Table;
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_rankLimit(uint32_t rankLimit) {
fbb_.AddElement<uint32_t>(BoardInfo::VT_RANKLIMIT, rankLimit, 0);
}
void add_updateMode(uint32_t updateMode) {
fbb_.AddElement<uint32_t>(BoardInfo::VT_UPDATEMODE, updateMode, 0);
}
void add_sortMode(uint32_t sortMode) {
fbb_.AddElement<uint32_t>(BoardInfo::VT_SORTMODE, sortMode, 0);
}
void add_uploadNumLimit(uint32_t uploadNumLimit) {
fbb_.AddElement<uint32_t>(BoardInfo::VT_UPLOADNUMLIMIT, uploadNumLimit, 0);
}
void add_uploadSizeLimit(uint32_t uploadSizeLimit) {
fbb_.AddElement<uint32_t>(BoardInfo::VT_UPLOADSIZELIMIT, uploadSizeLimit, 0);
}
explicit BoardInfoBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
flatbuffers::Offset<BoardInfo> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<BoardInfo>(end);
return o;
}
};
inline flatbuffers::Offset<BoardInfo> CreateBoardInfo(
flatbuffers::FlatBufferBuilder &_fbb,
uint32_t rankLimit = 0,
uint32_t updateMode = 0,
uint32_t sortMode = 0,
uint32_t uploadNumLimit = 0,
uint32_t uploadSizeLimit = 0) {
BoardInfoBuilder builder_(_fbb);
builder_.add_uploadSizeLimit(uploadSizeLimit);
builder_.add_uploadNumLimit(uploadNumLimit);
builder_.add_sortMode(sortMode);
builder_.add_updateMode(updateMode);
builder_.add_rankLimit(rankLimit);
return builder_.Finish();
}
struct RecordScoreRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
typedef RecordScoreRequestBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_BOARDID = 4,
VT_PCID = 6,
VT_SCORE = 8,
VT_COMMENT = 10,
VT_DATA = 12
};
uint32_t boardId() const {
return GetField<uint32_t>(VT_BOARDID, 0);
}
int32_t pcId() const {
return GetField<int32_t>(VT_PCID, 0);
}
int64_t score() const {
return GetField<int64_t>(VT_SCORE, 0);
}
const flatbuffers::String *comment() const {
return GetPointer<const flatbuffers::String *>(VT_COMMENT);
}
const flatbuffers::Vector<uint8_t> *data() const {
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_DATA);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint32_t>(verifier, VT_BOARDID, 4) &&
VerifyField<int32_t>(verifier, VT_PCID, 4) &&
VerifyField<int64_t>(verifier, VT_SCORE, 8) &&
VerifyOffset(verifier, VT_COMMENT) &&
verifier.VerifyString(comment()) &&
VerifyOffset(verifier, VT_DATA) &&
verifier.VerifyVector(data()) &&
verifier.EndTable();
}
};
struct RecordScoreRequestBuilder {
typedef RecordScoreRequest Table;
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_boardId(uint32_t boardId) {
fbb_.AddElement<uint32_t>(RecordScoreRequest::VT_BOARDID, boardId, 0);
}
void add_pcId(int32_t pcId) {
fbb_.AddElement<int32_t>(RecordScoreRequest::VT_PCID, pcId, 0);
}
void add_score(int64_t score) {
fbb_.AddElement<int64_t>(RecordScoreRequest::VT_SCORE, score, 0);
}
void add_comment(flatbuffers::Offset<flatbuffers::String> comment) {
fbb_.AddOffset(RecordScoreRequest::VT_COMMENT, comment);
}
void add_data(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data) {
fbb_.AddOffset(RecordScoreRequest::VT_DATA, data);
}
explicit RecordScoreRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
flatbuffers::Offset<RecordScoreRequest> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<RecordScoreRequest>(end);
return o;
}
};
inline flatbuffers::Offset<RecordScoreRequest> CreateRecordScoreRequest(
flatbuffers::FlatBufferBuilder &_fbb,
uint32_t boardId = 0,
int32_t pcId = 0,
int64_t score = 0,
flatbuffers::Offset<flatbuffers::String> comment = 0,
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data = 0) {
RecordScoreRequestBuilder builder_(_fbb);
builder_.add_score(score);
builder_.add_data(data);
builder_.add_comment(comment);
builder_.add_pcId(pcId);
builder_.add_boardId(boardId);
return builder_.Finish();
}
inline flatbuffers::Offset<RecordScoreRequest> CreateRecordScoreRequestDirect(
flatbuffers::FlatBufferBuilder &_fbb,
uint32_t boardId = 0,
int32_t pcId = 0,
int64_t score = 0,
const char *comment = nullptr,
const std::vector<uint8_t> *data = nullptr) {
auto comment__ = comment ? _fbb.CreateString(comment) : 0;
auto data__ = data ? _fbb.CreateVector<uint8_t>(*data) : 0;
return CreateRecordScoreRequest(
_fbb,
boardId,
pcId,
score,
comment__,
data__);
}
struct GetScoreRangeRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
typedef GetScoreRangeRequestBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_BOARDID = 4,
VT_STARTRANK = 6,
VT_NUMRANKS = 8,
VT_WITHCOMMENT = 10,
VT_WITHGAMEINFO = 12
};
uint32_t boardId() const {
return GetField<uint32_t>(VT_BOARDID, 0);
}
uint32_t startRank() const {
return GetField<uint32_t>(VT_STARTRANK, 0);
}
uint32_t numRanks() const {
return GetField<uint32_t>(VT_NUMRANKS, 0);
}
bool withComment() const {
return GetField<uint8_t>(VT_WITHCOMMENT, 0) != 0;
}
bool withGameInfo() const {
return GetField<uint8_t>(VT_WITHGAMEINFO, 0) != 0;
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint32_t>(verifier, VT_BOARDID, 4) &&
VerifyField<uint32_t>(verifier, VT_STARTRANK, 4) &&
VerifyField<uint32_t>(verifier, VT_NUMRANKS, 4) &&
VerifyField<uint8_t>(verifier, VT_WITHCOMMENT, 1) &&
VerifyField<uint8_t>(verifier, VT_WITHGAMEINFO, 1) &&
verifier.EndTable();
}
};
struct GetScoreRangeRequestBuilder {
typedef GetScoreRangeRequest Table;
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_boardId(uint32_t boardId) {
fbb_.AddElement<uint32_t>(GetScoreRangeRequest::VT_BOARDID, boardId, 0);
}
void add_startRank(uint32_t startRank) {
fbb_.AddElement<uint32_t>(GetScoreRangeRequest::VT_STARTRANK, startRank, 0);
}
void add_numRanks(uint32_t numRanks) {
fbb_.AddElement<uint32_t>(GetScoreRangeRequest::VT_NUMRANKS, numRanks, 0);
}
void add_withComment(bool withComment) {
fbb_.AddElement<uint8_t>(GetScoreRangeRequest::VT_WITHCOMMENT, static_cast<uint8_t>(withComment), 0);
}
void add_withGameInfo(bool withGameInfo) {
fbb_.AddElement<uint8_t>(GetScoreRangeRequest::VT_WITHGAMEINFO, static_cast<uint8_t>(withGameInfo), 0);
}
explicit GetScoreRangeRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
flatbuffers::Offset<GetScoreRangeRequest> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<GetScoreRangeRequest>(end);
return o;
}
};
inline flatbuffers::Offset<GetScoreRangeRequest> CreateGetScoreRangeRequest(
flatbuffers::FlatBufferBuilder &_fbb,
uint32_t boardId = 0,
uint32_t startRank = 0,
uint32_t numRanks = 0,
bool withComment = false,
bool withGameInfo = false) {
GetScoreRangeRequestBuilder builder_(_fbb);
builder_.add_numRanks(numRanks);
builder_.add_startRank(startRank);
builder_.add_boardId(boardId);
builder_.add_withGameInfo(withGameInfo);
builder_.add_withComment(withComment);
return builder_.Finish();
}
struct ScoreNpIdPcId FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
typedef ScoreNpIdPcIdBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_NPID = 4,
VT_PCID = 6
};
const flatbuffers::String *npid() const {
return GetPointer<const flatbuffers::String *>(VT_NPID);
}
int32_t pcId() const {
return GetField<int32_t>(VT_PCID, 0);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_NPID) &&
verifier.VerifyString(npid()) &&
VerifyField<int32_t>(verifier, VT_PCID, 4) &&
verifier.EndTable();
}
};
struct ScoreNpIdPcIdBuilder {
typedef ScoreNpIdPcId Table;
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_npid(flatbuffers::Offset<flatbuffers::String> npid) {
fbb_.AddOffset(ScoreNpIdPcId::VT_NPID, npid);
}
void add_pcId(int32_t pcId) {
fbb_.AddElement<int32_t>(ScoreNpIdPcId::VT_PCID, pcId, 0);
}
explicit ScoreNpIdPcIdBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
flatbuffers::Offset<ScoreNpIdPcId> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<ScoreNpIdPcId>(end);
return o;
}
};
inline flatbuffers::Offset<ScoreNpIdPcId> CreateScoreNpIdPcId(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<flatbuffers::String> npid = 0,
int32_t pcId = 0) {
ScoreNpIdPcIdBuilder builder_(_fbb);
builder_.add_pcId(pcId);
builder_.add_npid(npid);
return builder_.Finish();
}
inline flatbuffers::Offset<ScoreNpIdPcId> CreateScoreNpIdPcIdDirect(
flatbuffers::FlatBufferBuilder &_fbb,
const char *npid = nullptr,
int32_t pcId = 0) {
auto npid__ = npid ? _fbb.CreateString(npid) : 0;
return CreateScoreNpIdPcId(
_fbb,
npid__,
pcId);
}
struct GetScoreNpIdRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
typedef GetScoreNpIdRequestBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_BOARDID = 4,
VT_NPIDS = 6,
VT_WITHCOMMENT = 8,
VT_WITHGAMEINFO = 10
};
uint32_t boardId() const {
return GetField<uint32_t>(VT_BOARDID, 0);
}
const flatbuffers::Vector<flatbuffers::Offset<ScoreNpIdPcId>> *npids() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<ScoreNpIdPcId>> *>(VT_NPIDS);
}
bool withComment() const {
return GetField<uint8_t>(VT_WITHCOMMENT, 0) != 0;
}
bool withGameInfo() const {
return GetField<uint8_t>(VT_WITHGAMEINFO, 0) != 0;
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint32_t>(verifier, VT_BOARDID, 4) &&
VerifyOffset(verifier, VT_NPIDS) &&
verifier.VerifyVector(npids()) &&
verifier.VerifyVectorOfTables(npids()) &&
VerifyField<uint8_t>(verifier, VT_WITHCOMMENT, 1) &&
VerifyField<uint8_t>(verifier, VT_WITHGAMEINFO, 1) &&
verifier.EndTable();
}
};
struct GetScoreNpIdRequestBuilder {
typedef GetScoreNpIdRequest Table;
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_boardId(uint32_t boardId) {
fbb_.AddElement<uint32_t>(GetScoreNpIdRequest::VT_BOARDID, boardId, 0);
}
void add_npids(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ScoreNpIdPcId>>> npids) {
fbb_.AddOffset(GetScoreNpIdRequest::VT_NPIDS, npids);
}
void add_withComment(bool withComment) {
fbb_.AddElement<uint8_t>(GetScoreNpIdRequest::VT_WITHCOMMENT, static_cast<uint8_t>(withComment), 0);
}
void add_withGameInfo(bool withGameInfo) {
fbb_.AddElement<uint8_t>(GetScoreNpIdRequest::VT_WITHGAMEINFO, static_cast<uint8_t>(withGameInfo), 0);
}
explicit GetScoreNpIdRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
flatbuffers::Offset<GetScoreNpIdRequest> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<GetScoreNpIdRequest>(end);
return o;
}
};
inline flatbuffers::Offset<GetScoreNpIdRequest> CreateGetScoreNpIdRequest(
flatbuffers::FlatBufferBuilder &_fbb,
uint32_t boardId = 0,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ScoreNpIdPcId>>> npids = 0,
bool withComment = false,
bool withGameInfo = false) {
GetScoreNpIdRequestBuilder builder_(_fbb);
builder_.add_npids(npids);
builder_.add_boardId(boardId);
builder_.add_withGameInfo(withGameInfo);
builder_.add_withComment(withComment);
return builder_.Finish();
}
inline flatbuffers::Offset<GetScoreNpIdRequest> CreateGetScoreNpIdRequestDirect(
flatbuffers::FlatBufferBuilder &_fbb,
uint32_t boardId = 0,
const std::vector<flatbuffers::Offset<ScoreNpIdPcId>> *npids = nullptr,
bool withComment = false,
bool withGameInfo = false) {
auto npids__ = npids ? _fbb.CreateVector<flatbuffers::Offset<ScoreNpIdPcId>>(*npids) : 0;
return CreateGetScoreNpIdRequest(
_fbb,
boardId,
npids__,
withComment,
withGameInfo);
}
struct GetScoreFriendsRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
typedef GetScoreFriendsRequestBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_BOARDID = 4,
VT_INCLUDE_SELF = 6,
VT_MAX = 8,
VT_WITHCOMMENT = 10,
VT_WITHGAMEINFO = 12
};
uint32_t boardId() const {
return GetField<uint32_t>(VT_BOARDID, 0);
}
bool include_self() const {
return GetField<uint8_t>(VT_INCLUDE_SELF, 0) != 0;
}
uint32_t max() const {
return GetField<uint32_t>(VT_MAX, 0);
}
bool withComment() const {
return GetField<uint8_t>(VT_WITHCOMMENT, 0) != 0;
}
bool withGameInfo() const {
return GetField<uint8_t>(VT_WITHGAMEINFO, 0) != 0;
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint32_t>(verifier, VT_BOARDID, 4) &&
VerifyField<uint8_t>(verifier, VT_INCLUDE_SELF, 1) &&
VerifyField<uint32_t>(verifier, VT_MAX, 4) &&
VerifyField<uint8_t>(verifier, VT_WITHCOMMENT, 1) &&
VerifyField<uint8_t>(verifier, VT_WITHGAMEINFO, 1) &&
verifier.EndTable();
}
};
struct GetScoreFriendsRequestBuilder {
typedef GetScoreFriendsRequest Table;
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_boardId(uint32_t boardId) {
fbb_.AddElement<uint32_t>(GetScoreFriendsRequest::VT_BOARDID, boardId, 0);
}
void add_include_self(bool include_self) {
fbb_.AddElement<uint8_t>(GetScoreFriendsRequest::VT_INCLUDE_SELF, static_cast<uint8_t>(include_self), 0);
}
void add_max(uint32_t max) {
fbb_.AddElement<uint32_t>(GetScoreFriendsRequest::VT_MAX, max, 0);
}
void add_withComment(bool withComment) {
fbb_.AddElement<uint8_t>(GetScoreFriendsRequest::VT_WITHCOMMENT, static_cast<uint8_t>(withComment), 0);
}
void add_withGameInfo(bool withGameInfo) {
fbb_.AddElement<uint8_t>(GetScoreFriendsRequest::VT_WITHGAMEINFO, static_cast<uint8_t>(withGameInfo), 0);
}
explicit GetScoreFriendsRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
flatbuffers::Offset<GetScoreFriendsRequest> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<GetScoreFriendsRequest>(end);
return o;
}
};
inline flatbuffers::Offset<GetScoreFriendsRequest> CreateGetScoreFriendsRequest(
flatbuffers::FlatBufferBuilder &_fbb,
uint32_t boardId = 0,
bool include_self = false,
uint32_t max = 0,
bool withComment = false,
bool withGameInfo = false) {
GetScoreFriendsRequestBuilder builder_(_fbb);
builder_.add_max(max);
builder_.add_boardId(boardId);
builder_.add_withGameInfo(withGameInfo);
builder_.add_withComment(withComment);
builder_.add_include_self(include_self);
return builder_.Finish();
}
struct ScoreRankData FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
typedef ScoreRankDataBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_NPID = 4,
VT_ONLINENAME = 6,
VT_PCID = 8,
VT_RANK = 10,
VT_SCORE = 12,
VT_HASGAMEDATA = 14,
VT_RECORDDATE = 16
};
const flatbuffers::String *npId() const {
return GetPointer<const flatbuffers::String *>(VT_NPID);
}
const flatbuffers::String *onlineName() const {
return GetPointer<const flatbuffers::String *>(VT_ONLINENAME);
}
int32_t pcId() const {
return GetField<int32_t>(VT_PCID, 0);
}
uint32_t rank() const {
return GetField<uint32_t>(VT_RANK, 0);
}
int64_t score() const {
return GetField<int64_t>(VT_SCORE, 0);
}
bool hasGameData() const {
return GetField<uint8_t>(VT_HASGAMEDATA, 0) != 0;
}
uint64_t recordDate() const {
return GetField<uint64_t>(VT_RECORDDATE, 0);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_NPID) &&
verifier.VerifyString(npId()) &&
VerifyOffset(verifier, VT_ONLINENAME) &&
verifier.VerifyString(onlineName()) &&
VerifyField<int32_t>(verifier, VT_PCID, 4) &&
VerifyField<uint32_t>(verifier, VT_RANK, 4) &&
VerifyField<int64_t>(verifier, VT_SCORE, 8) &&
VerifyField<uint8_t>(verifier, VT_HASGAMEDATA, 1) &&
VerifyField<uint64_t>(verifier, VT_RECORDDATE, 8) &&
verifier.EndTable();
}
};
struct ScoreRankDataBuilder {
typedef ScoreRankData Table;
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_npId(flatbuffers::Offset<flatbuffers::String> npId) {
fbb_.AddOffset(ScoreRankData::VT_NPID, npId);
}
void add_onlineName(flatbuffers::Offset<flatbuffers::String> onlineName) {
fbb_.AddOffset(ScoreRankData::VT_ONLINENAME, onlineName);
}
void add_pcId(int32_t pcId) {
fbb_.AddElement<int32_t>(ScoreRankData::VT_PCID, pcId, 0);
}
void add_rank(uint32_t rank) {
fbb_.AddElement<uint32_t>(ScoreRankData::VT_RANK, rank, 0);
}
void add_score(int64_t score) {
fbb_.AddElement<int64_t>(ScoreRankData::VT_SCORE, score, 0);
}
void add_hasGameData(bool hasGameData) {
fbb_.AddElement<uint8_t>(ScoreRankData::VT_HASGAMEDATA, static_cast<uint8_t>(hasGameData), 0);
}
void add_recordDate(uint64_t recordDate) {
fbb_.AddElement<uint64_t>(ScoreRankData::VT_RECORDDATE, recordDate, 0);
}
explicit ScoreRankDataBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
flatbuffers::Offset<ScoreRankData> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<ScoreRankData>(end);
return o;
}
};
inline flatbuffers::Offset<ScoreRankData> CreateScoreRankData(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<flatbuffers::String> npId = 0,
flatbuffers::Offset<flatbuffers::String> onlineName = 0,
int32_t pcId = 0,
uint32_t rank = 0,
int64_t score = 0,
bool hasGameData = false,
uint64_t recordDate = 0) {
ScoreRankDataBuilder builder_(_fbb);
builder_.add_recordDate(recordDate);
builder_.add_score(score);
builder_.add_rank(rank);
builder_.add_pcId(pcId);
builder_.add_onlineName(onlineName);
builder_.add_npId(npId);
builder_.add_hasGameData(hasGameData);
return builder_.Finish();
}
inline flatbuffers::Offset<ScoreRankData> CreateScoreRankDataDirect(
flatbuffers::FlatBufferBuilder &_fbb,
const char *npId = nullptr,
const char *onlineName = nullptr,
int32_t pcId = 0,
uint32_t rank = 0,
int64_t score = 0,
bool hasGameData = false,
uint64_t recordDate = 0) {
auto npId__ = npId ? _fbb.CreateString(npId) : 0;
auto onlineName__ = onlineName ? _fbb.CreateString(onlineName) : 0;
return CreateScoreRankData(
_fbb,
npId__,
onlineName__,
pcId,
rank,
score,
hasGameData,
recordDate);
}
struct ScoreInfo FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
typedef ScoreInfoBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_DATA = 4
};
const flatbuffers::Vector<uint8_t> *data() const {
return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_DATA);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_DATA) &&
verifier.VerifyVector(data()) &&
verifier.EndTable();
}
};
struct ScoreInfoBuilder {
typedef ScoreInfo Table;
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_data(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data) {
fbb_.AddOffset(ScoreInfo::VT_DATA, data);
}
explicit ScoreInfoBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
flatbuffers::Offset<ScoreInfo> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<ScoreInfo>(end);
return o;
}
};
inline flatbuffers::Offset<ScoreInfo> CreateScoreInfo(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<flatbuffers::Vector<uint8_t>> data = 0) {
ScoreInfoBuilder builder_(_fbb);
builder_.add_data(data);
return builder_.Finish();
}
inline flatbuffers::Offset<ScoreInfo> CreateScoreInfoDirect(
flatbuffers::FlatBufferBuilder &_fbb,
const std::vector<uint8_t> *data = nullptr) {
auto data__ = data ? _fbb.CreateVector<uint8_t>(*data) : 0;
return CreateScoreInfo(
_fbb,
data__);
}
struct GetScoreResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
typedef GetScoreResponseBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_RANKARRAY = 4,
VT_COMMENTARRAY = 6,
VT_INFOARRAY = 8,
VT_LASTSORTDATE = 10,
VT_TOTALRECORD = 12
};
const flatbuffers::Vector<flatbuffers::Offset<ScoreRankData>> *rankArray() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<ScoreRankData>> *>(VT_RANKARRAY);
}
const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *commentArray() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> *>(VT_COMMENTARRAY);
}
const flatbuffers::Vector<flatbuffers::Offset<ScoreInfo>> *infoArray() const {
return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<ScoreInfo>> *>(VT_INFOARRAY);
}
uint64_t lastSortDate() const {
return GetField<uint64_t>(VT_LASTSORTDATE, 0);
}
uint32_t totalRecord() const {
return GetField<uint32_t>(VT_TOTALRECORD, 0);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_RANKARRAY) &&
verifier.VerifyVector(rankArray()) &&
verifier.VerifyVectorOfTables(rankArray()) &&
VerifyOffset(verifier, VT_COMMENTARRAY) &&
verifier.VerifyVector(commentArray()) &&
verifier.VerifyVectorOfStrings(commentArray()) &&
VerifyOffset(verifier, VT_INFOARRAY) &&
verifier.VerifyVector(infoArray()) &&
verifier.VerifyVectorOfTables(infoArray()) &&
VerifyField<uint64_t>(verifier, VT_LASTSORTDATE, 8) &&
VerifyField<uint32_t>(verifier, VT_TOTALRECORD, 4) &&
verifier.EndTable();
}
};
struct GetScoreResponseBuilder {
typedef GetScoreResponse Table;
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_rankArray(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ScoreRankData>>> rankArray) {
fbb_.AddOffset(GetScoreResponse::VT_RANKARRAY, rankArray);
}
void add_commentArray(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> commentArray) {
fbb_.AddOffset(GetScoreResponse::VT_COMMENTARRAY, commentArray);
}
void add_infoArray(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ScoreInfo>>> infoArray) {
fbb_.AddOffset(GetScoreResponse::VT_INFOARRAY, infoArray);
}
void add_lastSortDate(uint64_t lastSortDate) {
fbb_.AddElement<uint64_t>(GetScoreResponse::VT_LASTSORTDATE, lastSortDate, 0);
}
void add_totalRecord(uint32_t totalRecord) {
fbb_.AddElement<uint32_t>(GetScoreResponse::VT_TOTALRECORD, totalRecord, 0);
}
explicit GetScoreResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
flatbuffers::Offset<GetScoreResponse> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<GetScoreResponse>(end);
return o;
}
};
inline flatbuffers::Offset<GetScoreResponse> CreateGetScoreResponse(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ScoreRankData>>> rankArray = 0,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>> commentArray = 0,
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ScoreInfo>>> infoArray = 0,
uint64_t lastSortDate = 0,
uint32_t totalRecord = 0) {
GetScoreResponseBuilder builder_(_fbb);
builder_.add_lastSortDate(lastSortDate);
builder_.add_totalRecord(totalRecord);
builder_.add_infoArray(infoArray);
builder_.add_commentArray(commentArray);
builder_.add_rankArray(rankArray);
return builder_.Finish();
}
inline flatbuffers::Offset<GetScoreResponse> CreateGetScoreResponseDirect(
flatbuffers::FlatBufferBuilder &_fbb,
const std::vector<flatbuffers::Offset<ScoreRankData>> *rankArray = nullptr,
const std::vector<flatbuffers::Offset<flatbuffers::String>> *commentArray = nullptr,
const std::vector<flatbuffers::Offset<ScoreInfo>> *infoArray = nullptr,
uint64_t lastSortDate = 0,
uint32_t totalRecord = 0) {
auto rankArray__ = rankArray ? _fbb.CreateVector<flatbuffers::Offset<ScoreRankData>>(*rankArray) : 0;
auto commentArray__ = commentArray ? _fbb.CreateVector<flatbuffers::Offset<flatbuffers::String>>(*commentArray) : 0;
auto infoArray__ = infoArray ? _fbb.CreateVector<flatbuffers::Offset<ScoreInfo>>(*infoArray) : 0;
return CreateGetScoreResponse(
_fbb,
rankArray__,
commentArray__,
infoArray__,
lastSortDate,
totalRecord);
}
#endif // FLATBUFFERS_GENERATED_NP2STRUCTS_H_

View File

@ -278,7 +278,7 @@ namespace np
if (num_binattrs)
{
ptr_member->roomMemberBinAttrInternal.set(mem.allocate(sizeof(SceNpMatching2RoomMemberBinAttrInternal) * num_binattrs));
ptr_member->roomMemberBinAttrInternalNum = num_binattrs;
ptr_member->roomMemberBinAttrInternalNum = num_binattrs;
SceNpMatching2RoomMemberBinAttrInternal* bin_ptr = ptr_member->roomMemberBinAttrInternal.get_ptr();
u32 actual_cnt = 0;
@ -288,8 +288,8 @@ namespace np
{
const auto& bin = ::at32(member.bins, binattrs_list[i]);
bin_ptr[actual_cnt].updateDate.tick = bin.updateDate.tick;
bin_ptr[actual_cnt].data.id = bin.id;
bin_ptr[actual_cnt].data.size = bin.data.size();
bin_ptr[actual_cnt].data.id = bin.id;
bin_ptr[actual_cnt].data.size = bin.data.size();
bin_ptr[actual_cnt].data.ptr.set(mem.allocate(bin.data.size()));
memcpy(bin_ptr[actual_cnt].data.ptr.get_ptr(), bin.data.data(), bin.data.size());
actual_cnt++;
@ -299,4 +299,15 @@ namespace np
return needed_data_size;
}
} // namespace np
SceNpId cache_manager::get_npid(u64 room_id, u16 member_id)
{
std::lock_guard lock(mutex);
ensure(rooms.contains(room_id), "cache_manager::get_npid: Room not cached!");
ensure(::at32(rooms, room_id).members.contains(member_id), "cache_manager::get_npid: Member not cached!");
return ::at32(::at32(rooms, room_id).members, member_id).userInfo.npId;
}
} // namespace np

View File

@ -75,6 +75,7 @@ namespace np
std::pair<error_code, std::vector<SceNpMatching2RoomMemberId>> get_memberids(u64 room_id, s32 sort_method);
std::pair<error_code, std::optional<SceNpMatching2SessionPassword>> get_password(SceNpMatching2RoomId room_id);
error_code get_member_and_attrs(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id, const std::vector<SceNpMatching2AttributeId>& binattrs_list, SceNpMatching2RoomMemberDataInternal* ptr_member, u32 addr_data, u32 size_data);
SceNpId get_npid(u64 room_id, u16 member_id);
private:
shared_mutex mutex;

View File

@ -3,6 +3,8 @@
#include "Emu/IdManager.h"
LOG_CHANNEL(sceNp2);
score_ctx::score_ctx(vm::cptr<SceNpCommunicationId> communicationId, vm::cptr<SceNpCommunicationPassphrase> passphrase)
{
ensure(!communicationId->data[9] && strlen(communicationId->data) == 9);
@ -11,25 +13,72 @@ score_ctx::score_ctx(vm::cptr<SceNpCommunicationId> communicationId, vm::cptr<Sc
}
s32 create_score_context(vm::cptr<SceNpCommunicationId> communicationId, vm::cptr<SceNpCommunicationPassphrase> passphrase)
{
return static_cast<s32>(idm::make<score_ctx>(communicationId, passphrase));
s32 score_id = idm::make<score_ctx>(communicationId, passphrase);
if (score_id == id_manager::id_traits<score_ctx>::invalid)
{
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS;
}
return static_cast<s32>(score_id);
}
bool destroy_score_context(s32 ctx_id)
{
return idm::remove<score_ctx>(static_cast<u32>(ctx_id));
}
score_transaction_ctx::score_transaction_ctx(s32 score_context_id)
score_transaction_ctx::score_transaction_ctx(const std::shared_ptr<score_ctx>& score)
{
this->score_context_id = score_context_id;
pcId = score->pcId;
communicationId = score->communicationId;
passphrase = score->passphrase;
timeout = score->timeout;
}
s32 create_score_transaction_context(s32 score_context_id)
score_transaction_ctx::~score_transaction_ctx()
{
return static_cast<s32>(idm::make<score_transaction_ctx>(score_context_id));
if (thread.joinable())
thread.join();
}
s32 create_score_transaction_context(const std::shared_ptr<score_ctx>& score)
{
s32 trans_id = idm::make<score_transaction_ctx>(score);
if (trans_id == id_manager::id_traits<score_transaction_ctx>::invalid)
{
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_OBJECTS;
}
return static_cast<s32>(trans_id);
}
bool destroy_score_transaction_context(s32 ctx_id)
{
return idm::remove<score_transaction_ctx>(static_cast<u32>(ctx_id));
}
std::optional<s32> score_transaction_ctx::get_score_transaction_status()
{
std::lock_guard lock(mutex);
return result;
}
void score_transaction_ctx::abort_score_transaction()
{
std::lock_guard lock(mutex);
result = SCE_NP_COMMUNITY_ERROR_ABORTED;
wake_cond.notify_one();
}
void score_transaction_ctx::wait_for_completion()
{
std::unique_lock lock(mutex);
if (result)
{
return;
}
completion_cond.wait(lock);
}
match2_ctx::match2_ctx(vm::cptr<SceNpCommunicationId> communicationId, vm::cptr<SceNpCommunicationPassphrase> passphrase)
{
@ -39,6 +88,7 @@ match2_ctx::match2_ctx(vm::cptr<SceNpCommunicationId> communicationId, vm::cptr<
}
u16 create_match2_context(vm::cptr<SceNpCommunicationId> communicationId, vm::cptr<SceNpCommunicationPassphrase> passphrase)
{
sceNp2.notice("Creating match2 context with communicationId: <%s>", static_cast<const char *>(communicationId->data));
return static_cast<u16>(idm::make<match2_ctx>(communicationId, passphrase));
}
bool destroy_match2_context(u16 ctx_id)

View File

@ -1,5 +1,11 @@
#pragma once
#include <optional>
#include <condition_variable>
#include <thread>
#include "Utilities/mutex.h"
#include "Emu/Memory/vm_ptr.h"
#include "Emu/Cell/Modules/sceNp.h"
#include "Emu/Cell/Modules/sceNp2.h"
@ -10,29 +16,51 @@ struct score_ctx
{
score_ctx(vm::cptr<SceNpCommunicationId> communicationId, vm::cptr<SceNpCommunicationPassphrase> passphrase);
static const u32 id_base = 1;
static const u32 id_base = 0x2001;
static const u32 id_step = 1;
static const u32 id_count = 32;
SAVESTATE_INIT_POS(25);
shared_mutex mutex;
u64 timeout = 60'000'000; // 60 seconds
SceNpCommunicationId communicationId{};
SceNpCommunicationPassphrase passphrase{};
s32 pcId = 0;
};
s32 create_score_context(vm::cptr<SceNpCommunicationId> communicationId, vm::cptr<SceNpCommunicationPassphrase> passphrase);
bool destroy_score_context(s32 ctx_id);
struct score_transaction_ctx
{
score_transaction_ctx(s32 score_context_id);
score_transaction_ctx(const std::shared_ptr<score_ctx>& score);
~score_transaction_ctx();
std::optional<s32> get_score_transaction_status();
void abort_score_transaction();
void wait_for_completion();
static const u32 id_base = 1;
static const u32 id_base = 0x1001;
static const u32 id_step = 1;
static const u32 id_count = 32;
SAVESTATE_INIT_POS(26);
s32 score_context_id = 0;
shared_mutex mutex;
std::condition_variable_any wake_cond, completion_cond;
std::optional<error_code> result;
std::vector<u32> data;
std::vector<u8> game_data;
u64 timeout = 60'000'000; // 60 seconds;
SceNpCommunicationId communicationId{};
SceNpCommunicationPassphrase passphrase{};
s32 pcId = 0;
std::thread thread;
};
s32 create_score_transaction_context(s32 score_context_id);
s32 create_score_transaction_context(const std::shared_ptr<score_ctx>& score);
bool destroy_score_transaction_context(s32 ctx_id);
// Match2 related
@ -62,7 +90,7 @@ struct lookup_title_ctx
{
lookup_title_ctx(vm::cptr<SceNpCommunicationId> communicationId);
static const u32 id_base = 1;
static const u32 id_base = 0x3001;
static const u32 id_step = 1;
static const u32 id_count = 32;
SAVESTATE_INIT_POS(28);
@ -77,7 +105,7 @@ struct lookup_transaction_ctx
{
lookup_transaction_ctx(s32 lt_ctx);
static const u32 id_base = 1;
static const u32 id_base = 0x4001;
static const u32 id_step = 1;
static const u32 id_count = 32;
SAVESTATE_INIT_POS(29);
@ -91,7 +119,7 @@ struct commerce2_ctx
{
commerce2_ctx(u32 version, vm::cptr<SceNpId> npid, vm::ptr<SceNpCommerce2Handler> handler, vm::ptr<void> arg);
static const u32 id_base = 1;
static const u32 id_base = 0x5001;
static const u32 id_step = 1;
static const u32 id_count = 32;
SAVESTATE_INIT_POS(30);
@ -109,7 +137,7 @@ struct signaling_ctx
{
signaling_ctx(vm::ptr<SceNpId> npid, vm::ptr<SceNpSignalingHandler> handler, vm::ptr<void> arg);
static const u32 id_base = 1;
static const u32 id_base = 0x6001;
static const u32 id_step = 1;
static const u32 id_count = 32;
SAVESTATE_INIT_POS(31);

View File

@ -384,9 +384,7 @@ namespace np
return;
}
ar(is_NP_Lookup_init, is_NP_Score_init, is_NP2_init, is_NP2_Match2_init, is_NP_Auth_init
, manager_cb, manager_cb_arg, std::as_bytes(std::span(&basic_handler, 1)), is_connected, is_psn_active
, hostname, ether_address, local_ip_addr, public_ip_addr, dns_ip);
ar(is_NP_Lookup_init, is_NP_Score_init, is_NP2_init, is_NP2_Match2_init, is_NP_Auth_init, manager_cb, manager_cb_arg, std::as_bytes(std::span(&basic_handler, 1)), is_connected, is_psn_active, hostname, ether_address, local_ip_addr, public_ip_addr, dns_ip);
// Call init func if needed (np_memory is unaffected when an empty pool is provided)
init_NP(0, vm::null);
@ -396,6 +394,27 @@ namespace np
// TODO: IDM-tied objects are not yet saved
}
np_handler::~np_handler()
{
std::unordered_map<u32, std::shared_ptr<score_transaction_ctx>> moved_trans;
{
std::lock_guard lock(mutex_score_transactions);
moved_trans = std::move(score_transactions);
score_transactions.clear();
}
for (auto& [trans_id, trans] : moved_trans)
{
trans->abort_score_transaction();
}
for (auto& [trans_id, trans] : moved_trans)
{
if (trans->thread.joinable())
trans->thread.join();
}
}
void np_handler::save(utils::serial& ar)
{
// TODO: See ctor
@ -408,9 +427,7 @@ namespace np
USING_SERIALIZATION_VERSION(sceNp);
ar(is_NP_Lookup_init, is_NP_Score_init, is_NP2_init, is_NP2_Match2_init, is_NP_Auth_init
, manager_cb, manager_cb_arg, std::as_bytes(std::span(&basic_handler, 1)), is_connected, is_psn_active
, hostname, ether_address, local_ip_addr, public_ip_addr, dns_ip);
ar(is_NP_Lookup_init, is_NP_Score_init, is_NP2_init, is_NP2_Match2_init, is_NP_Auth_init, manager_cb, manager_cb_arg, std::as_bytes(std::span(&basic_handler, 1)), is_connected, is_psn_active, hostname, ether_address, local_ip_addr, public_ip_addr, dns_ip);
np_memory.save(ar);
}
@ -454,10 +471,9 @@ namespace np
return;
}
// First address is used for now, (TODO combobox with possible local addresses to use?)
local_ip_addr = *reinterpret_cast<u32*>(host->h_addr_list[0]);
// Set public address to local discovered address for now, may be updated later;
// Set public address to local discovered address for now, may be updated later from RPCN socket
public_ip_addr = local_ip_addr;
nph_log.notice("discover_ip_address: IP was determined to be %s", ip_to_string(local_ip_addr));
@ -662,6 +678,7 @@ namespace np
string_to_online_name(rpcn->get_online_name(), &online_name);
string_to_avatar_url(rpcn->get_avatar_url(), &avatar_url);
public_ip_addr = rpcn->get_addr_sig();
local_ip_addr = std::bit_cast<u32, be_t<u32>>(rpcn->get_addr_local());
break;
}
@ -799,6 +816,13 @@ namespace np
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::StoreScoreData: reply_store_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;
default: rpcn_log.error("Unknown reply(%d) received!", command); break;
}
}
@ -896,16 +920,8 @@ namespace np
const u32 req_id = get_req_id(optParam ? optParam->appReqId : ctx->default_match2_optparam.appReqId);
ret.ctx_id = ctx_id;
ret.cb_arg = optParam ? optParam->cbFuncArg : ctx->default_match2_optparam.cbFuncArg;
if (optParam && optParam->cbFunc)
{
ret.cb = optParam->cbFunc;
}
else
{
ret.cb = ctx->default_match2_optparam.cbFunc;
}
ret.cb_arg = (optParam && optParam->cbFuncArg) ? optParam->cbFuncArg : ctx->default_match2_optparam.cbFuncArg;
ret.cb = (optParam && optParam->cbFunc) ? optParam->cbFunc : ctx->default_match2_optparam.cbFunc;
nph_log.warning("Callback used is 0x%x", ret.cb);

View File

@ -14,6 +14,7 @@
#include "Emu/NP/np_allocator.h"
#include "Emu/NP/np_cache.h"
#include "Emu/NP/np_event_data.h"
#include "Emu/NP/np_contexts.h"
namespace np
{
@ -69,6 +70,7 @@ namespace np
SAVESTATE_INIT_POS(5);
np_handler();
~np_handler();
np_handler(utils::serial& ar);
void save(utils::serial& ar);
@ -92,13 +94,13 @@ namespace np
void init_NP(u32 poolsize, vm::ptr<void> poolptr);
void terminate_NP();
bool is_netctl_init = false;
bool is_NP_init = false;
bool is_NP_Lookup_init = false;
bool is_NP_Score_init = false;
bool is_NP2_init = false;
bool is_NP2_Match2_init = false;
bool is_NP_Auth_init = false;
atomic_t<bool> is_netctl_init = false;
atomic_t<bool> is_NP_init = false;
atomic_t<bool> is_NP_Lookup_init = false;
atomic_t<bool> is_NP_Score_init = false;
atomic_t<bool> is_NP2_init = false;
atomic_t<bool> is_NP2_Match2_init = false;
atomic_t<bool> is_NP_Auth_init = false;
// NP Handlers/Callbacks
// Seems to be global
@ -151,6 +153,14 @@ namespace np
u32 get_match2_event(SceNpMatching2EventKey event_key, u32 dest_addr, u32 size);
// Score requests
void score_async_handler(std::unique_lock<shared_mutex> lock, const std::shared_ptr<score_transaction_ctx>& trans_ctx, u32 req_id, bool async);
void get_board_infos(std::shared_ptr<score_transaction_ctx>& trans_ctx, SceNpScoreBoardId board_id, vm::ptr<SceNpScoreBoardInfo> board_info, bool async);
void record_score(std::shared_ptr<score_transaction_ctx>& trans_ctx, SceNpScoreBoardId board_id, SceNpScoreValue score, vm::cptr<SceNpScoreComment> comment, const u8* data, u32 data_size, vm::ptr<SceNpScoreRankNumber> tmp_rank, bool async);
void get_score_range(std::shared_ptr<score_transaction_ctx>& trans_ctx, SceNpScoreBoardId boardId, SceNpScoreRankNumber startSerialRank, vm::ptr<SceNpScoreRankData> rankArray, u64 rankArraySize, vm::ptr<SceNpScoreComment> commentArray, u64 commentArraySize, vm::ptr<void> infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr<CellRtcTick> lastSortDate, vm::ptr<SceNpScoreRankNumber> totalRecord, bool async);
void get_score_npid(std::shared_ptr<score_transaction_ctx>& trans_ctx, SceNpScoreBoardId boardId, const std::vector<std::pair<SceNpId, s32>>& npid_vec, vm::ptr<SceNpScorePlayerRankData> rankArray, u64 rankArraySize, vm::ptr<SceNpScoreComment> commentArray, u64 commentArraySize, vm::ptr<void> infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr<CellRtcTick> lastSortDate, vm::ptr<SceNpScoreRankNumber> totalRecord, bool async);
void get_score_friend(std::shared_ptr<score_transaction_ctx>& trans_ctx, SceNpScoreBoardId boardId, vm::ptr<SceNpScoreRankData> rankArray, u64 rankArraySize, vm::ptr<SceNpScoreComment> commentArray, u64 commentArraySize, vm::ptr<void> infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr<CellRtcTick> lastSortDate, vm::ptr<SceNpScoreRankNumber> totalRecord, bool async);
// Local functions
std::pair<error_code, std::optional<SceNpMatching2RoomSlotInfo>> local_get_room_slots(SceNpMatching2RoomId room_id);
std::pair<error_code, std::optional<SceNpMatching2SessionPassword>> local_get_room_password(SceNpMatching2RoomId room_id);
@ -173,7 +183,7 @@ namespace np
// For custom menu
struct custom_menu_action
{
s32 id = 0;
s32 id = 0;
u32 mask = SCE_NP_CUSTOM_MENU_ACTION_MASK_ME;
std::string name;
};
@ -220,6 +230,13 @@ namespace np
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_store_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);
// Helper functions(fb=>np2)
void BinAttr_to_SceNpMatching2BinAttr(event_data& edata, const BinAttr* bin_attr, SceNpMatching2BinAttr* binattr_info);
@ -240,6 +257,7 @@ namespace np
void RoomMessageInfo_to_SceNpMatching2RoomMessageInfo(event_data& edata, const RoomMessageInfo* mi, SceNpMatching2RoomMessageInfo* sce_mi);
void RoomDataInternalUpdateInfo_to_SceNpMatching2RoomDataInternalUpdateInfo(event_data& edata, const RoomDataInternalUpdateInfo* update_info, SceNpMatching2RoomDataInternalUpdateInfo* sce_update_info, const SceNpId& npid);
void RoomMemberDataInternalUpdateInfo_to_SceNpMatching2RoomMemberDataInternalUpdateInfo(event_data& edata, const RoomMemberDataInternalUpdateInfo* update_info, SceNpMatching2RoomMemberDataInternalUpdateInfo* sce_update_info);
bool handle_GetScoreResponse(u32 req_id, std::vector<u8>& reply_data);
struct callback_info
{
@ -298,6 +316,10 @@ namespace np
}
event_data& allocate_req_result(u32 event_key, u32 max_size, u32 initial_size);
// Async score threads
shared_mutex mutex_score_transactions;
std::unordered_map<u32, std::shared_ptr<score_transaction_ctx>> score_transactions; // (req_id, transaction_ctx)
// RPCN
shared_mutex mutex_rpcn;
std::shared_ptr<rpcn::rpcn_client> rpcn;

View File

@ -22,24 +22,24 @@ namespace np
return fmt::format("%02X:%02X:%02X:%02X:%02X:%02X", ether[0], ether[1], ether[2], ether[3], ether[4], ether[5]);
}
void string_to_npid(const std::string& str, SceNpId* npid)
void string_to_npid(std::string_view str, SceNpId* npid)
{
memset(npid, 0, sizeof(SceNpId));
strcpy_trunc(npid->handle.data, str);
// npid->reserved[0] = 1;
}
void string_to_online_name(const std::string& str, SceNpOnlineName* online_name)
void string_to_online_name(std::string_view str, SceNpOnlineName* online_name)
{
strcpy_trunc(online_name->data, str);
}
void string_to_avatar_url(const std::string& str, SceNpAvatarUrl* avatar_url)
void string_to_avatar_url(std::string_view str, SceNpAvatarUrl* avatar_url)
{
strcpy_trunc(avatar_url->data, str);
}
void string_to_communication_id(const std::string& str, SceNpCommunicationId* comm_id)
void string_to_communication_id(std::string_view str, SceNpCommunicationId* comm_id)
{
strcpy_trunc(comm_id->data, str);
}

View File

@ -7,8 +7,8 @@ namespace np
std::string ip_to_string(u32 addr);
std::string ether_to_string(std::array<u8, 6>& ether);
void string_to_npid(const std::string& str, SceNpId* npid);
void string_to_online_name(const std::string& str, SceNpOnlineName* online_name);
void string_to_avatar_url(const std::string& str, SceNpAvatarUrl* avatar_url);
void string_to_communication_id(const std::string& str, SceNpCommunicationId* comm_id);
void string_to_npid(std::string_view str, SceNpId* npid);
void string_to_online_name(std::string_view str, SceNpOnlineName* online_name);
void string_to_avatar_url(std::string_view str, SceNpAvatarUrl* avatar_url);
void string_to_communication_id(std::string_view str, SceNpCommunicationId* comm_id);
}

View File

@ -221,7 +221,7 @@ namespace np
// Attempt Signaling
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
sigh.set_sig2_infos(room_id, member_id, SCE_NP_SIGNALING_CONN_STATUS_PENDING, addr_p2p, port_p2p);
sigh.set_sig2_infos(room_id, member_id, SCE_NP_SIGNALING_CONN_STATUS_PENDING, addr_p2p, port_p2p, np_cache.get_npid(room_id, member_id));
sigh.start_sig2(room_id, member_id);
}
} // namespace np

View File

@ -6,6 +6,7 @@
#include "Emu/IdManager.h"
#include "np_handler.h"
#include "np_contexts.h"
#include "np_helpers.h"
#include "np_structs_extra.h"
LOG_CHANNEL(rpcn_log, "rpcn");
@ -163,6 +164,8 @@ namespace np
{
u32 req_id = generate_callback_info(ctx_id, optParam);
extra_nps::print_createjoinroom(req);
if (!rpcn->createjoin_room(req_id, get_match2_context(ctx_id)->communicationId, req))
{
rpcn_log.error("Disconnecting from RPCN!");
@ -200,7 +203,7 @@ namespace np
// Establish Matching2 self signaling info
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
sigh.set_self_sig2_info(room_info->roomId, 1);
sigh.set_sig2_infos(room_info->roomId, 1, SCE_NP_SIGNALING_CONN_STATUS_ACTIVE, rpcn->get_addr_sig(), rpcn->get_port_sig(), true);
sigh.set_sig2_infos(room_info->roomId, 1, SCE_NP_SIGNALING_CONN_STATUS_ACTIVE, rpcn->get_addr_sig(), rpcn->get_port_sig(), get_npid(), true);
// TODO? Should this send a message to Signaling CB? Is this even necessary?
extra_nps::print_create_room_resp(room_resp);
@ -221,6 +224,8 @@ namespace np
{
u32 req_id = generate_callback_info(ctx_id, optParam);
extra_nps::print_joinroom(req);
if (!rpcn->join_room(req_id, get_match2_context(ctx_id)->communicationId, req))
{
rpcn_log.error("Disconnecting from RPCN!");
@ -257,7 +262,7 @@ namespace np
// Establish Matching2 self signaling info
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
sigh.set_self_sig2_info(room_info->roomId, member_id);
sigh.set_sig2_infos(room_info->roomId, member_id, SCE_NP_SIGNALING_CONN_STATUS_ACTIVE, rpcn->get_addr_sig(), rpcn->get_port_sig(), true);
sigh.set_sig2_infos(room_info->roomId, member_id, SCE_NP_SIGNALING_CONN_STATUS_ACTIVE, rpcn->get_addr_sig(), rpcn->get_port_sig(), get_npid(), true);
// TODO? Should this send a message to Signaling CB? Is this even necessary?
if (cb_info.cb)
@ -316,6 +321,8 @@ namespace np
{
u32 req_id = generate_callback_info(ctx_id, optParam);
extra_nps::print_search_room(req);
if (!rpcn->search_room(req_id, get_match2_context(ctx_id)->communicationId, req))
{
rpcn_log.error("Disconnecting from RPCN!");
@ -342,6 +349,8 @@ namespace np
SearchRoomResponse_to_SceNpMatching2SearchRoomResponse(edata, resp, search_resp);
np_memory.shrink_allocation(edata.addr(), edata.size());
extra_nps::print_search_room_resp(search_resp);
if (cb_info.cb)
{
sysutil_register_cb([=, size = edata.size()](ppu_thread& cb_ppu) -> s32
@ -358,6 +367,8 @@ namespace np
{
u32 req_id = generate_callback_info(ctx_id, optParam);
extra_nps::print_get_roomdata_external_list_req(req);
if (!rpcn->get_roomdata_external_list(req_id, get_match2_context(ctx_id)->communicationId, req))
{
rpcn_log.error("Disconnecting from RPCN!");
@ -384,6 +395,8 @@ namespace np
GetRoomDataExternalListResponse_to_SceNpMatching2GetRoomDataExternalListResponse(edata, resp, sce_get_room_ext_resp);
np_memory.shrink_allocation(edata.addr(), edata.size());
extra_nps::print_get_roomdata_external_list_resp(sce_get_room_ext_resp);
if (cb_info.cb)
{
sysutil_register_cb([=, size = edata.size()](ppu_thread& cb_ppu) -> s32
@ -482,6 +495,8 @@ namespace np
{
u32 req_id = generate_callback_info(ctx_id, optParam);
// TODO: extra_nps::print_set_roomdata_req(req);
extra_nps::print_set_roomdata_int_req(req);
if (!rpcn->set_roomdata_internal(req_id, get_match2_context(ctx_id)->communicationId, req))
@ -515,6 +530,8 @@ namespace np
{
u32 req_id = generate_callback_info(ctx_id, optParam);
extra_nps::print_set_roommemberdata_int_req(req);
if (!rpcn->set_roommemberdata_internal(req_id, get_match2_context(ctx_id)->communicationId, req))
{
rpcn_log.error("Disconnecting from RPCN!");
@ -703,4 +720,342 @@ namespace np
return true;
}
void np_handler::score_async_handler(std::unique_lock<shared_mutex> lock, const std::shared_ptr<score_transaction_ctx>& trans_ctx, u32 req_id, bool async)
{
auto worker_function = [trans_ctx = trans_ctx, req_id, this](std::unique_lock<shared_mutex> lock)
{
auto res = trans_ctx->wake_cond.wait_for(lock, std::chrono::microseconds(trans_ctx->timeout));
{
std::lock_guard lock_threads(this->mutex_score_transactions);
this->score_transactions.erase(req_id);
}
if (res == std::cv_status::timeout)
{
trans_ctx->result = SCE_NP_COMMUNITY_ERROR_TIMEOUT;
return;
}
// Only 2 cases should be timeout or caller setting result
ensure(trans_ctx->result);
trans_ctx->completion_cond.notify_one();
};
{
std::lock_guard lock_score(mutex_score_transactions);
ensure(score_transactions.insert({req_id, trans_ctx}).second);
}
if (async)
{
trans_ctx->thread = std::thread(worker_function, std::move(lock));
return;
}
return worker_function(std::move(lock));
}
void np_handler::get_board_infos(std::shared_ptr<score_transaction_ctx>& trans_ctx, SceNpScoreBoardId board_id, vm::ptr<SceNpScoreBoardInfo> board_info, bool async)
{
std::unique_lock lock(trans_ctx->mutex);
u32 req_id = get_req_id(0x3334);
std::string comm_id(static_cast<const char*>(trans_ctx->communicationId.data));
trans_ctx->data.clear();
trans_ctx->data.push_back(board_info.addr());
rpcn->get_board_infos(req_id, trans_ctx->communicationId, board_id);
return score_async_handler(std::move(lock), trans_ctx, req_id, async);
}
bool np_handler::reply_get_board_infos(u32 req_id, std::vector<u8>& reply_data)
{
vec_stream reply(reply_data, 1);
auto raw_board_info = reply.get_rawdata();
auto* resp = flatbuffers::GetRoot<BoardInfo>(raw_board_info.data());
SceNpScoreBoardInfo board_info;
board_info.rankLimit = resp->rankLimit();
board_info.updateMode = resp->updateMode();
board_info.sortMode = resp->sortMode();
board_info.uploadNumLimit = resp->uploadNumLimit();
board_info.uploadSizeLimit = resp->uploadSizeLimit();
std::lock_guard lock_trans(mutex_score_transactions);
if (!score_transactions.count(req_id))
{
rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id);
return false;
}
auto trans = ::at32(score_transactions, req_id);
std::lock_guard lock(trans->mutex);
ensure(trans->data.size() == 1);
vm::ptr<SceNpScoreBoardInfo> boardinfo_ptr = vm::cast(trans->data[0]);
memcpy(reinterpret_cast<u8*>(boardinfo_ptr.get_ptr()), &board_info, sizeof(SceNpScoreBoardInfo));
trans->result = CELL_OK;
trans->wake_cond.notify_one();
return true;
}
void np_handler::record_score(std::shared_ptr<score_transaction_ctx>& trans_ctx, SceNpScoreBoardId board_id, SceNpScoreValue score, vm::cptr<SceNpScoreComment> comment, const u8* data, u32 data_size, vm::ptr<SceNpScoreRankNumber> tmp_rank, bool async)
{
std::unique_lock lock(trans_ctx->mutex);
u32 req_id = get_req_id(0x3334);
std::optional<std::string> str_comment = comment ? std::optional(std::string(reinterpret_cast<const char*>(comment->data))) : std::nullopt;
std::optional<std::vector<u8>> vec_data = data ? std::optional(std::vector(data, data + data_size)) : std::nullopt;
trans_ctx->data.clear();
if (tmp_rank)
{
trans_ctx->data.push_back(tmp_rank.addr());
}
rpcn->record_score(req_id, trans_ctx->communicationId, board_id, trans_ctx->pcId, score, str_comment, vec_data);
return score_async_handler(std::move(lock), trans_ctx, req_id, async);
}
bool np_handler::reply_record_score(u32 req_id, std::vector<u8>& reply_data)
{
std::lock_guard lock_trans(mutex_score_transactions);
if (!score_transactions.count(req_id))
{
rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id);
return false;
}
auto trans = ::at32(score_transactions, req_id);
std::lock_guard lock(trans->mutex);
if (rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])))
{
switch (reply_data[0])
{
case rpcn::ErrorType::ScoreNotBest:
{
trans->result = SCE_NP_COMMUNITY_SERVER_ERROR_NOT_BEST_SCORE;
trans->wake_cond.notify_one();
return true;
}
default: return false;
}
}
vec_stream reply(reply_data, 1);
auto tmp_rank = reply.get<u32>();
if (reply.is_error())
{
rpcn_log.error("Error parsing response in reply_record_score");
return false;
}
ensure(trans->data.size() == 1 || trans->data.size() == 0);
if (!trans->data.empty())
{
vm::ptr<u32> tmprank_ptr = vm::cast(trans->data[0]);
*tmprank_ptr = tmp_rank;
}
trans->result = CELL_OK;
trans->wake_cond.notify_one();
return true;
}
bool np_handler::reply_store_score_data(u32 req_id, std::vector<u8>& reply_data)
{
return true;
}
bool np_handler::reply_get_score_data(u32 req_id, std::vector<u8>& reply_data)
{
return true;
}
void np_handler::get_score_range(std::shared_ptr<score_transaction_ctx>& trans_ctx, SceNpScoreBoardId boardId, SceNpScoreRankNumber startSerialRank, vm::ptr<SceNpScoreRankData> rankArray, [[maybe_unused]] u64 rankArraySize, vm::ptr<SceNpScoreComment> commentArray, [[maybe_unused]] u64 commentArraySize, vm::ptr<void> infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr<CellRtcTick> lastSortDate, vm::ptr<SceNpScoreRankNumber> totalRecord, bool async)
{
std::unique_lock lock(trans_ctx->mutex);
u32 req_id = get_req_id(0x3334);
trans_ctx->data.clear();
trans_ctx->data.push_back(static_cast<u32>(arrayNum));
trans_ctx->data.push_back(rankArray.addr());
trans_ctx->data.push_back(commentArray.addr());
trans_ctx->data.push_back(infoArray.addr());
trans_ctx->data.push_back(static_cast<u32>((arrayNum * sizeof(SceNpScoreGameInfo)) == infoArraySize));
trans_ctx->data.push_back(lastSortDate.addr());
trans_ctx->data.push_back(totalRecord.addr());
bool with_comments = !!commentArray;
bool with_gameinfo = !!infoArray;
rpcn->get_score_range(req_id, trans_ctx->communicationId, boardId, startSerialRank, arrayNum, with_comments, with_gameinfo);
return score_async_handler(std::move(lock), trans_ctx, req_id, async);
}
bool np_handler::handle_GetScoreResponse(u32 req_id, std::vector<u8>& reply_data)
{
std::lock_guard lock_trans(mutex_score_transactions);
if (!score_transactions.count(req_id))
{
rpcn_log.error("Couldn't find transaction(%d) in trans_id!", req_id);
return false;
}
auto trans_ctx = ::at32(score_transactions, req_id);
std::lock_guard lock(trans_ctx->mutex);
if (rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])))
{
return false;
}
vec_stream reply(reply_data, 1);
auto raw_getscore_response = reply.get_rawdata();
auto* resp = flatbuffers::GetRoot<GetScoreResponse>(raw_getscore_response.data());
flatbuffers::Verifier verifier(raw_getscore_response.data(), raw_getscore_response.size());
if (reply.is_error() || !resp->Verify(verifier))
{
rpcn_log.error("Error parsing response in reply_record_score");
return false;
}
ensure(trans_ctx->data.size() == 7);
u32 arrayNum = trans_ctx->data[0];
vm::ptr<SceNpScoreRankData> rankArray = vm::cast(trans_ctx->data[1]);
vm::ptr<SceNpScoreComment> commentArray = vm::cast(trans_ctx->data[2]);
vm::ptr<void> infoArray = vm::cast(trans_ctx->data[3]);
bool info_array_is_SceNpScoreGameInfo = trans_ctx->data[4];
vm::ptr<CellRtcTick> lastSortDate = vm::cast(trans_ctx->data[5]);
vm::ptr<SceNpScoreRankNumber> totalRecord = vm::cast(trans_ctx->data[6]);
ensure(resp->rankArray() && resp->rankArray()->size() <= arrayNum);
memset(rankArray.get_ptr(), 0, sizeof(SceNpScoreRankData) * arrayNum);
auto* fb_rankarray = resp->rankArray();
for (flatbuffers::uoffset_t i = 0; i < fb_rankarray->size(); i++)
{
const auto* fb_rankdata = fb_rankarray->Get(i);
ensure(fb_rankdata->npId() && fb_rankdata->onlineName());
string_to_npid(fb_rankdata->npId()->string_view(), &rankArray[i].npId);
string_to_online_name(fb_rankdata->onlineName()->string_view(), &rankArray[i].onlineName);
rankArray[i].pcId = fb_rankdata->pcId();
rankArray[i].serialRank = fb_rankdata->rank();
rankArray[i].rank = fb_rankdata->rank();
rankArray[i].highestRank = fb_rankdata->rank();
rankArray[i].scoreValue = fb_rankdata->score();
rankArray[i].hasGameData = fb_rankdata->hasGameData();
rankArray[i].recordDate.tick = fb_rankdata->recordDate();
}
if (commentArray)
{
ensure(resp->commentArray() && resp->commentArray()->size() <= arrayNum);
memset(commentArray.get_ptr(), 0, sizeof(SceNpScoreComment) * arrayNum);
auto* fb_commentarray = resp->commentArray();
for (flatbuffers::uoffset_t i = 0; i < fb_commentarray->size(); i++)
{
const auto* fb_comment = fb_commentarray->Get(i);
strcpy_trunc(commentArray[i].data, fb_comment->string_view());
}
}
if (infoArray)
{
ensure(resp->infoArray() && resp->infoArray()->size() <= arrayNum);
auto* fb_infoarray = resp->infoArray();
if (info_array_is_SceNpScoreGameInfo)
{
vm::ptr<SceNpScoreGameInfo> ptr_gameinfo = vm::static_ptr_cast<SceNpScoreGameInfo>(infoArray);
memset(ptr_gameinfo.get_ptr(), 0, sizeof(SceNpScoreGameInfo) * arrayNum);
for (flatbuffers::uoffset_t i = 0; i < fb_infoarray->size(); i++)
{
const auto* fb_info = fb_infoarray->Get(i);
ensure(fb_info->data()->size() <= SCE_NP_SCORE_GAMEINFO_SIZE);
memcpy(ptr_gameinfo[i].nativeData, fb_info->data()->data(), fb_info->data()->size());
}
}
else
{
vm::ptr<SceNpScoreVariableSizeGameInfo> ptr_vargameinfo = vm::static_ptr_cast<SceNpScoreVariableSizeGameInfo>(infoArray);
memset(ptr_vargameinfo.get_ptr(), 0, sizeof(SceNpScoreVariableSizeGameInfo) * arrayNum);
for (flatbuffers::uoffset_t i = 0; i < fb_infoarray->size(); i++)
{
const auto* fb_info = fb_infoarray->Get(i);
ensure(fb_info->data()->size() <= SCE_NP_SCORE_VARIABLE_SIZE_GAMEINFO_MAXSIZE);
ptr_vargameinfo[i].infoSize = fb_info->data()->size();
memcpy(ptr_vargameinfo[i].data, fb_info->data(), fb_info->data()->size());
}
}
}
lastSortDate->tick = resp->lastSortDate();
*totalRecord = resp->totalRecord();
trans_ctx->result = not_an_error(fb_rankarray->size());
trans_ctx->wake_cond.notify_one();
return true;
}
bool np_handler::reply_get_score_range(u32 req_id, std::vector<u8>& reply_data)
{
return handle_GetScoreResponse(req_id, reply_data);
}
void np_handler::get_score_friend(std::shared_ptr<score_transaction_ctx>& trans_ctx, SceNpScoreBoardId boardId, vm::ptr<SceNpScoreRankData> rankArray, [[maybe_unused]] u64 rankArraySize, vm::ptr<SceNpScoreComment> commentArray, [[maybe_unused]] u64 commentArraySize, vm::ptr<void> infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr<CellRtcTick> lastSortDate, vm::ptr<SceNpScoreRankNumber> totalRecord, bool async)
{
std::unique_lock lock(trans_ctx->mutex);
u32 req_id = get_req_id(0x3334);
trans_ctx->data.clear();
trans_ctx->data.push_back(static_cast<u32>(arrayNum));
trans_ctx->data.push_back(rankArray.addr());
trans_ctx->data.push_back(commentArray.addr());
trans_ctx->data.push_back(infoArray.addr());
trans_ctx->data.push_back(static_cast<u32>((arrayNum * sizeof(SceNpScoreGameInfo)) == infoArraySize));
trans_ctx->data.push_back(lastSortDate.addr());
trans_ctx->data.push_back(totalRecord.addr());
bool with_comments = !!commentArray;
bool with_gameinfo = !!infoArray;
rpcn->get_score_friend(req_id, trans_ctx->communicationId, boardId, with_comments, with_gameinfo);
return score_async_handler(std::move(lock), trans_ctx, req_id, async);
}
bool np_handler::reply_get_score_friends(u32 req_id, std::vector<u8>& reply_data)
{
return handle_GetScoreResponse(req_id, reply_data);
}
void np_handler::get_score_npid(std::shared_ptr<score_transaction_ctx>& trans_ctx, SceNpScoreBoardId boardId, const std::vector<std::pair<SceNpId, s32>>& npid_vec, vm::ptr<SceNpScorePlayerRankData> rankArray, [[maybe_unused]] u64 rankArraySize, vm::ptr<SceNpScoreComment> commentArray, [[maybe_unused]] u64 commentArraySize, vm::ptr<void> infoArray, u64 infoArraySize, u64 arrayNum, vm::ptr<CellRtcTick> lastSortDate, vm::ptr<SceNpScoreRankNumber> totalRecord, bool async)
{
std::unique_lock lock(trans_ctx->mutex);
u32 req_id = get_req_id(0x3334);
trans_ctx->data.clear();
trans_ctx->data.push_back(static_cast<u32>(arrayNum));
trans_ctx->data.push_back(rankArray.addr());
trans_ctx->data.push_back(commentArray.addr());
trans_ctx->data.push_back(infoArray.addr());
trans_ctx->data.push_back(static_cast<u32>((arrayNum * sizeof(SceNpScoreGameInfo)) == infoArraySize));
trans_ctx->data.push_back(lastSortDate.addr());
trans_ctx->data.push_back(totalRecord.addr());
bool with_comments = !!commentArray;
bool with_gameinfo = !!infoArray;
rpcn->get_score_npid(req_id, trans_ctx->communicationId, boardId, npid_vec, with_comments, with_gameinfo);
return score_async_handler(std::move(lock), trans_ctx, req_id, async);
}
bool np_handler::reply_get_score_npid(u32 req_id, std::vector<u8>& reply_data)
{
return handle_GetScoreResponse(req_id, reply_data);
}
} // namespace np

View File

@ -8,6 +8,14 @@ LOG_CHANNEL(sceNp2);
namespace extra_nps
{
void print_userinfo2(const SceNpUserInfo2* user)
{
sceNp2.warning("SceNpUserInfo2:");
sceNp2.warning("npid: %s", static_cast<const char*>(user->npId.handle.data));
sceNp2.warning("onlineName: *0x%x(%s)", user->onlineName, user->onlineName ? static_cast<const char*>(user->onlineName->data) : "");
sceNp2.warning("avatarUrl: *0x%x(%s)", user->avatarUrl, user->avatarUrl ? static_cast<const char*>(user->avatarUrl->data) : "");
}
void print_sigoptparam(const SceNpMatching2SignalingOptParam* opt)
{
sceNp2.warning("type: %d", opt->type);
@ -49,6 +57,13 @@ namespace extra_nps
sceNp2.warning("Data: %s", dadata);
}
void print_range(const SceNpMatching2Range* range)
{
sceNp2.warning("startIndex: %d", range->startIndex);
sceNp2.warning("total: %d", range->total);
sceNp2.warning("size: %d", range->size);
}
void print_range_filter(const SceNpMatching2RangeFilter* filt)
{
sceNp2.warning("startIndex: %d", filt->startIndex);
@ -133,6 +148,20 @@ namespace extra_nps
sceNp2.warning("attrIdNum: %d", req->attrIdNum);
}
void print_search_room_resp(const SceNpMatching2SearchRoomResponse* resp)
{
sceNp2.warning("SceNpMatching2SearchRoomResponse:");
print_range(&resp->range);
const SceNpMatching2RoomDataExternal *room_ptr = resp->roomDataExternal.get_ptr();
for (u32 i = 0; i < resp->range.total; i++)
{
sceNp2.warning("SceNpMatching2SearchRoomResponse[%d]:", i);
print_room_data_external(room_ptr);
room_ptr = room_ptr->next.get_ptr();
}
}
void print_room_member_data_internal(const SceNpMatching2RoomMemberDataInternal* member)
{
sceNp2.warning("SceNpMatching2RoomMemberDataInternal:");
@ -182,6 +211,38 @@ namespace extra_nps
print_bin_attr_internal(&room->roomBinAttrInternal[i]);
}
void print_room_data_external(const SceNpMatching2RoomDataExternal* room)
{
sceNp2.warning("SceNpMatching2RoomDataExternal:");
sceNp2.warning("next: *0x%x", room->next);
sceNp2.warning("serverId: %d", room->serverId);
sceNp2.warning("worldId: %d", room->worldId);
sceNp2.warning("publicSlotNum: %d", room->publicSlotNum);
sceNp2.warning("privateSlotNum: %d", room->privateSlotNum);
sceNp2.warning("lobbyId: %d", room->lobbyId);
sceNp2.warning("roomId: %d", room->roomId);
sceNp2.warning("openPublicSlotNum: %d", room->openPublicSlotNum);
sceNp2.warning("maxSlot: %d", room->maxSlot);
sceNp2.warning("openPrivateSlotNum: %d", room->openPrivateSlotNum);
sceNp2.warning("curMemberNum: %d", room->curMemberNum);
sceNp2.warning("SceNpMatching2RoomPasswordSlotMask: 0x%x", room->passwordSlotMask);
sceNp2.warning("owner: *0x%x", room->owner);
print_userinfo2(room->owner.get_ptr());
sceNp2.warning("roomGroup: *0x%x", room->roomGroup);
// TODO: print roomGroup
sceNp2.warning("roomGroupNum: %d", room->roomGroupNum);
sceNp2.warning("flagAttr: 0x%x", room->flagAttr);
sceNp2.warning("roomSearchableIntAttrExternal: *0x%x", room->roomSearchableIntAttrExternal);
sceNp2.warning("roomSearchableIntAttrExternalNum: %d", room->roomSearchableIntAttrExternalNum);
// TODO: print roomSearchableIntAttrExternal
sceNp2.warning("roomSearchableBinAttrExternal: *0x%x", room->roomSearchableBinAttrExternal);
sceNp2.warning("roomSearchableBinAttrExternalNum: %d", room->roomSearchableBinAttrExternalNum);
// TODO: print roomSearchableBinAttrExternal
sceNp2.warning("roomBinAttrExternal: *0x%x", room->roomBinAttrExternal);
sceNp2.warning("roomBinAttrExternalNum: %d", room->roomBinAttrExternalNum);
// TODO: print roomBinAttrExternal
}
void print_create_room_resp(const SceNpMatching2CreateJoinRoomResponse* resp)
{
sceNp2.warning("SceNpMatching2CreateJoinRoomResponse:");
@ -231,4 +292,37 @@ namespace extra_nps
print_bin_attr(&req->roomMemberBinAttrInternal[i]);
}
void print_get_roomdata_external_list_req(const SceNpMatching2GetRoomDataExternalListRequest* req)
{
sceNp2.warning("SceNpMatching2GetRoomDataExternalListRequest:");
sceNp2.warning("roomId: *0x%x", req->roomId);
sceNp2.warning("roomIdNum: %d", req->roomIdNum);
for (u32 i = 0; i < req->roomIdNum; i++)
{
sceNp2.warning("RoomId[%d] = %d", i, req->roomId[i]);
}
sceNp2.warning("attrId: *0x%x", req->attrId);
sceNp2.warning("attrIdNum: %d", req->attrIdNum);
for (u32 i = 0; i < req->attrIdNum; i++)
{
sceNp2.warning("attrId[%d] = %d", i, req->attrId[i]);
}
}
void print_get_roomdata_external_list_resp(const SceNpMatching2GetRoomDataExternalListResponse* resp)
{
sceNp2.warning("SceNpMatching2GetRoomDataExternalListResponse:");
sceNp2.warning("roomDataExternal: *0x%x", resp->roomDataExternal);
sceNp2.warning("roomDataExternalNum: %d", resp->roomDataExternalNum);
const SceNpMatching2RoomDataExternal* cur_room = resp->roomDataExternal.get_ptr();
for (u32 i = 0; i < resp->roomDataExternalNum; i++)
{
sceNp2.warning("SceNpMatching2GetRoomDataExternalListResponse[%d]:", i);
print_room_data_external(cur_room);
cur_room = cur_room->next.get_ptr();
}
}
} // namespace extra_nps

View File

@ -4,18 +4,23 @@
namespace extra_nps
{
void print_userinfo2(const SceNpUserInfo2* user);
void print_sigoptparam(const SceNpMatching2SignalingOptParam* opt);
void print_bin_attr(const SceNpMatching2BinAttr* bin);
void print_presence_data(const SceNpMatching2PresenceOptionData* opt);
void print_range_filter(const SceNpMatching2RangeFilter* filt);
void print_room_data_internal(const SceNpMatching2RoomDataInternal* room);
void print_room_data_external(const SceNpMatching2RoomDataExternal* room);
void print_room_member_data_internal(const SceNpMatching2RoomMemberDataInternal* member);
void print_createjoinroom(const SceNpMatching2CreateJoinRoomRequest* req);
void print_create_room_resp(const SceNpMatching2CreateJoinRoomResponse* resp);
void print_joinroom(const SceNpMatching2JoinRoomRequest* req);
void print_search_room(const SceNpMatching2SearchRoomRequest* req);
void print_search_room_resp(const SceNpMatching2SearchRoomResponse* resp);
void print_set_roomdata_ext_req(const SceNpMatching2SetRoomDataExternalRequest* req);
void print_set_roomdata_int_req(const SceNpMatching2SetRoomDataInternalRequest* req);
void print_set_roommemberdata_int_req(const SceNpMatching2SetRoomMemberDataInternalRequest* req);
void print_get_roomdata_external_list_req(const SceNpMatching2GetRoomDataExternalListRequest* req);
void print_get_roomdata_external_list_resp(const SceNpMatching2GetRoomDataExternalListResponse* resp);
} // namespace extra_nps

View File

@ -12,6 +12,7 @@
#include "Emu/System.h"
#include "Emu/NP/rpcn_config.h"
#include "Emu/NP/np_helpers.h"
#include "Emu/NP/vport0.h"
#include "util/asm.hpp"
@ -43,10 +44,6 @@
LOG_CHANNEL(rpcn_log, "rpcn");
// Those are defined here to avoid including sys_net.h
s32 send_packet_from_p2p_port(const std::vector<u8>& data, const sockaddr_in& addr);
std::vector<std::vector<u8>> get_rpcn_msgs();
namespace rpcn
{
localized_string_id rpcn_state_to_localized_string_id(rpcn::rpcn_state state)
@ -74,10 +71,49 @@ namespace rpcn
return get_localized_string(rpcn_state_to_localized_string_id(state));
}
constexpr u32 RPCN_PROTOCOL_VERSION = 15;
constexpr usz RPCN_HEADER_SIZE = 13;
constexpr u32 RPCN_PROTOCOL_VERSION = 16;
constexpr usz RPCN_HEADER_SIZE = 15;
constexpr usz COMMUNICATION_ID_SIZE = 9;
bool is_error(ErrorType err)
{
if (err >= ErrorType::__error_last)
{
rpcn_log.error("Invalid error returned!");
return true;
}
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 AlreadyJoined: rpcn_log.error("User has already joined!"); 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 Unsupported: rpcn_log.error("An unsupported operation was attempted!"); break;
default: rpcn_log.fatal("Unhandled ErrorType reached the switch?"); break;
}
return true;
}
// Constructor, destructor & singleton manager
rpcn_client::rpcn_client()
@ -198,6 +234,7 @@ namespace rpcn
{
if (want_conn)
{
want_conn = false;
{
std::lock_guard lock(mutex_connected);
connect(g_cfg_rpcn.get_host());
@ -257,9 +294,10 @@ 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)
{
std::vector<u8> ping(9);
std::vector<u8> ping(13);
ping[0] = 1;
*utils::bless<le_t<s64, 1>>(&ping[1]) = user_id;
*utils::bless<be_t<u32, 1>>(&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!");
@ -301,8 +339,8 @@ namespace rpcn
const u8 packet_type = header[0];
const u16 command = *utils::bless<le_t<u16>>(&header[1]);
const u16 packet_size = *utils::bless<le_t<u16>>(&header[3]);
const u64 packet_id = *utils::bless<le_t<u64>>(&header[5]);
const u32 packet_size = *utils::bless<le_t<u32>>(&header[3]);
const u64 packet_id = *utils::bless<le_t<u64>>(&header[7]);
if (packet_size < RPCN_HEADER_SIZE)
return error_and_disconnect("Invalid packet size");
@ -328,7 +366,8 @@ namespace rpcn
if (command == CommandType::Login || command == CommandType::GetServerList || command == CommandType::Create ||
command == CommandType::AddFriend || command == CommandType::RemoveFriend ||
command == CommandType::AddBlock || command == CommandType::RemoveBlock ||
command == CommandType::SendMessage || command == CommandType::SendToken)
command == CommandType::SendMessage || command == CommandType::SendToken ||
command == CommandType::SendResetToken || command == CommandType::ResetPassword)
{
std::lock_guard lock(mutex_replies_sync);
replies_sync.insert(std::make_pair(packet_id, std::make_pair(command, std::move(data))));
@ -681,6 +720,16 @@ namespace rpcn
rpcn_log.notice("connect: Connection successful");
sockaddr_in client_addr;
socklen_t client_addr_size = sizeof(client_addr);
if (getsockname(sockfd, reinterpret_cast<struct sockaddr*>(&client_addr), &client_addr_size) != 0)
{
rpcn_log.error("Failed to get the client address from the socket!");
}
update_local_addr(client_addr.sin_addr.s_addr);
rpcn_log.notice("Updated local address to %s", np::ip_to_string(std::bit_cast<u32, be_t<u32>>(local_addr_sig.load())));
if (wolfSSL_set_fd(read_wssl, sockfd) != WOLFSSL_SUCCESS)
{
rpcn_log.error("connect: Failed to associate wolfssl to the socket");
@ -837,7 +886,7 @@ namespace rpcn
return true;
}
ErrorType rpcn_client::create_user(const std::string& npid, const std::string& password, const std::string& online_name, const std::string& avatar_url, const std::string& email)
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;
std::copy(npid.begin(), npid.end(), std::back_inserter(data));
@ -908,6 +957,78 @@ namespace rpcn
return ErrorType::NoError;
}
ErrorType rpcn_client::send_reset_token(std::string_view npid, std::string_view email)
{
if (authentified)
{
// If you're already logged in why do you need a password reset token?
return ErrorType::LoginAlreadyLoggedIn;
}
std::vector<u8> data;
std::copy(npid.begin(), npid.end(), std::back_inserter(data));
data.push_back(0);
std::copy(email.begin(), email.end(), std::back_inserter(data));
data.push_back(0);
u64 req_id = rpcn_request_counter.fetch_add(1);
std::vector<u8> packet_data;
if (!forge_send_reply(CommandType::SendResetToken, req_id, data, packet_data))
{
return ErrorType::Malformed;
}
vec_stream reply(packet_data);
auto error = static_cast<ErrorType>(reply.get<u8>());
if (is_error(error))
{
return error;
}
rpcn_log.success("Password reset token has successfully been sent!");
return ErrorType::NoError;
}
ErrorType rpcn_client::reset_password(std::string_view npid, std::string_view token, std::string_view password)
{
if (authentified)
{
// If you're already logged in why do you need to reset the password?
return ErrorType::LoginAlreadyLoggedIn;
}
std::vector<u8> data;
std::copy(npid.begin(), npid.end(), std::back_inserter(data));
data.push_back(0);
std::copy(token.begin(), token.end(), std::back_inserter(data));
data.push_back(0);
std::copy(password.begin(), password.end(), std::back_inserter(data));
data.push_back(0);
u64 req_id = rpcn_request_counter.fetch_add(1);
std::vector<u8> packet_data;
if (!forge_send_reply(CommandType::ResetPassword, req_id, data, packet_data))
{
return ErrorType::Malformed;
}
vec_stream reply(packet_data);
auto error = static_cast<ErrorType>(reply.get<u8>());
if (is_error(error))
{
return error;
}
rpcn_log.success("Password has successfully been reset!");
return ErrorType::NoError;
}
bool rpcn_client::add_friend(const std::string& friend_username)
{
std::vector<u8> data;
@ -1067,18 +1188,13 @@ namespace rpcn
memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE);
reinterpret_cast<le_t<u16>&>(data[COMMUNICATION_ID_SIZE]) = server_id;
if (!forge_send(CommandType::GetWorldList, req_id, data))
return false;
return true;
return forge_send(CommandType::GetWorldList, req_id, data);
}
bool rpcn_client::createjoin_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2CreateJoinRoomRequest* req)
{
std::vector<u8> data;
extra_nps::print_createjoinroom(req);
flatbuffers::FlatBufferBuilder builder(1024);
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BinAttr>>> final_binattrinternal_vec;
@ -1196,18 +1312,13 @@ namespace rpcn
reinterpret_cast<le_t<u32>&>(data[COMMUNICATION_ID_SIZE]) = static_cast<u32>(bufsize);
memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize);
if (!forge_send(CommandType::CreateRoom, req_id, data))
return false;
return true;
return forge_send(CommandType::CreateRoom, req_id, data);
}
bool rpcn_client::join_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2JoinRoomRequest* req)
{
std::vector<u8> data;
extra_nps::print_joinroom(req);
flatbuffers::FlatBufferBuilder builder(1024);
flatbuffers::Offset<flatbuffers::Vector<u8>> final_roompassword;
@ -1240,10 +1351,7 @@ namespace rpcn
reinterpret_cast<le_t<u32>&>(data[COMMUNICATION_ID_SIZE]) = static_cast<u32>(bufsize);
memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize);
if (!forge_send(CommandType::JoinRoom, req_id, data))
return false;
return true;
return forge_send(CommandType::JoinRoom, req_id, data);
}
bool rpcn_client::leave_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2LeaveRoomRequest* req)
@ -1262,18 +1370,13 @@ namespace rpcn
reinterpret_cast<le_t<u32>&>(data[COMMUNICATION_ID_SIZE]) = static_cast<u32>(bufsize);
memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize);
if (!forge_send(CommandType::LeaveRoom, req_id, data))
return false;
return true;
return forge_send(CommandType::LeaveRoom, req_id, data);
}
bool rpcn_client::search_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SearchRoomRequest* req)
{
std::vector<u8> data;
extra_nps::print_search_room(req);
flatbuffers::FlatBufferBuilder builder(1024);
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<IntSearchFilter>>> final_intfilter_vec;
if (req->intFilterNum)
@ -1299,9 +1402,17 @@ namespace rpcn
}
final_binfilter_vec = builder.CreateVector(davec);
}
flatbuffers::Offset<flatbuffers::Vector<u16>> attrid_vec;
if (req->attrIdNum)
attrid_vec = builder.CreateVector(utils::bless<const u16>(req->attrId.get_ptr()), req->attrIdNum);
{
std::vector<u16> attr_ids;
for (u32 i = 0; i < req->attrIdNum; i++)
{
attr_ids.push_back(req->attrId[i]);
}
attrid_vec = builder.CreateVector(attr_ids);
}
SearchRoomRequestBuilder s_req(builder);
s_req.add_option(req->option);
@ -1328,10 +1439,7 @@ namespace rpcn
reinterpret_cast<le_t<u32>&>(data[COMMUNICATION_ID_SIZE]) = static_cast<u32>(bufsize);
memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize);
if (!forge_send(CommandType::SearchRoom, req_id, data))
return false;
return true;
return forge_send(CommandType::SearchRoom, req_id, data);
}
bool rpcn_client::get_roomdata_external_list(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2GetRoomDataExternalListRequest* req)
@ -1361,10 +1469,7 @@ namespace rpcn
reinterpret_cast<le_t<u32>&>(data[COMMUNICATION_ID_SIZE]) = static_cast<u32>(bufsize);
memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize);
if (!forge_send(CommandType::GetRoomDataExternalList, req_id, data))
return false;
return true;
return forge_send(CommandType::GetRoomDataExternalList, req_id, data);
}
bool rpcn_client::set_roomdata_external(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SetRoomDataExternalRequest* req)
@ -1416,10 +1521,7 @@ namespace rpcn
reinterpret_cast<le_t<u32>&>(data[COMMUNICATION_ID_SIZE]) = static_cast<u32>(bufsize);
memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize);
if (!forge_send(CommandType::SetRoomDataExternal, req_id, data))
return false;
return true;
return forge_send(CommandType::SetRoomDataExternal, req_id, data);
}
bool rpcn_client::get_roomdata_internal(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2GetRoomDataInternalRequest* req)
@ -1430,7 +1532,14 @@ namespace rpcn
flatbuffers::Offset<flatbuffers::Vector<u16>> final_attr_ids_vec;
if (req->attrIdNum)
final_attr_ids_vec = builder.CreateVector(utils::bless<const u16>(req->attrId.get_ptr()), req->attrIdNum);
{
std::vector<u16> attr_ids;
for (u32 i = 0; i < req->attrIdNum; i++)
{
attr_ids.push_back(req->attrId[i]);
}
final_attr_ids_vec = builder.CreateVector(attr_ids);
}
auto req_finished = CreateGetRoomDataInternalRequest(builder, req->roomId, final_attr_ids_vec);
@ -1443,18 +1552,13 @@ namespace rpcn
reinterpret_cast<le_t<u32>&>(data[COMMUNICATION_ID_SIZE]) = static_cast<u32>(bufsize);
memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize);
if (!forge_send(CommandType::GetRoomDataInternal, req_id, data))
return false;
return true;
return forge_send(CommandType::GetRoomDataInternal, req_id, data);
}
bool rpcn_client::set_roomdata_internal(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SetRoomDataInternalRequest* req)
{
std::vector<u8> data;
// extra_nps::print_set_roomdata_req(req);
flatbuffers::FlatBufferBuilder builder(1024);
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BinAttr>>> final_binattrinternal_vec;
if (req->roomBinAttrInternalNum)
@ -1484,7 +1588,14 @@ namespace rpcn
flatbuffers::Offset<flatbuffers::Vector<u16>> final_ownerprivilege_vec;
if (req->ownerPrivilegeRankNum)
final_ownerprivilege_vec = builder.CreateVector(utils::bless<const u16>(req->ownerPrivilegeRank.get_ptr()), req->ownerPrivilegeRankNum);
{
std::vector<u16> priv_ranks;
for (u32 i = 0; i < req->ownerPrivilegeRankNum; i++)
{
priv_ranks.push_back(req->ownerPrivilegeRank[i]);
}
final_ownerprivilege_vec = builder.CreateVector(priv_ranks);
}
auto req_finished =
CreateSetRoomDataInternalRequest(builder, req->roomId, req->flagFilter, req->flagAttr, final_binattrinternal_vec, final_grouppasswordconfig_vec, final_passwordSlotMask, final_ownerprivilege_vec);
@ -1498,18 +1609,13 @@ namespace rpcn
reinterpret_cast<le_t<u32>&>(data[COMMUNICATION_ID_SIZE]) = static_cast<u32>(bufsize);
memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize);
if (!forge_send(CommandType::SetRoomDataInternal, req_id, data))
return false;
return true;
return forge_send(CommandType::SetRoomDataInternal, req_id, data);
}
bool rpcn_client::set_roommemberdata_internal(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SetRoomMemberDataInternalRequest* req)
{
std::vector<u8> data{};
extra_nps::print_set_roommemberdata_int_req(req);
flatbuffers::FlatBufferBuilder builder(1024);
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<BinAttr>>> final_binattrinternal_vec;
if (req->roomMemberBinAttrInternalNum)
@ -1534,10 +1640,7 @@ namespace rpcn
reinterpret_cast<le_t<u32>&>(data[COMMUNICATION_ID_SIZE]) = static_cast<u32>(bufsize);
memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize);
if (!forge_send(CommandType::SetRoomMemberDataInternal, req_id, data))
return false;
return true;
return forge_send(CommandType::SetRoomMemberDataInternal, req_id, data);
}
bool rpcn_client::ping_room_owner(u32 req_id, const SceNpCommunicationId& communication_id, u64 room_id)
@ -1549,10 +1652,7 @@ namespace rpcn
memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE);
*utils::bless<le_t<u64>>(&data[COMMUNICATION_ID_SIZE]) = room_id;
if (!forge_send(CommandType::PingRoomOwner, req_id, data))
return false;
return true;
return forge_send(CommandType::PingRoomOwner, req_id, data);
}
bool rpcn_client::send_room_message(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SendRoomMessageRequest* req)
@ -1587,16 +1687,13 @@ namespace rpcn
builder.Finish(req_finished);
u8* buf = builder.GetBufferPointer();
usz bufsize = builder.GetSize();
data.resize(COMMUNICATION_ID_SIZE + bufsize + sizeof(u32));
data.resize(COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize);
memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE);
reinterpret_cast<le_t<u32>&>(data[COMMUNICATION_ID_SIZE]) = static_cast<u32>(bufsize);
memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize);
if (!forge_send(CommandType::SendRoomMessage, req_id, data))
return false;
return true;
return forge_send(CommandType::SendRoomMessage, req_id, data);
}
bool rpcn_client::req_sign_infos(u32 req_id, const std::string& npid)
@ -1605,10 +1702,7 @@ namespace rpcn
std::copy(npid.begin(), npid.end(), std::back_inserter(data));
data.push_back(0);
if (!forge_send(CommandType::RequestSignalingInfos, req_id, data))
return false;
return true;
return forge_send(CommandType::RequestSignalingInfos, req_id, data);
}
bool rpcn_client::req_ticket(u32 req_id, const std::string& service_id, const std::vector<u8>& cookie)
@ -1620,10 +1714,7 @@ namespace rpcn
std::copy(reinterpret_cast<const u8*>(&size), reinterpret_cast<const u8*>(&size) + sizeof(le_t<u32>), std::back_inserter(data));
std::copy(cookie.begin(), cookie.end(), std::back_inserter(data));
if (!forge_send(CommandType::RequestTicket, req_id, data))
return false;
return true;
return forge_send(CommandType::RequestTicket, req_id, data);
}
bool rpcn_client::sendmessage(const message_data& msg_data, const std::set<std::string>& npids)
@ -1658,64 +1749,113 @@ namespace rpcn
u64 req_id = rpcn_request_counter.fetch_add(1);
if (!forge_send(CommandType::SendMessage, req_id, data))
return false;
return forge_send(CommandType::SendMessage, req_id, data);
}
return true;
bool rpcn_client::get_board_infos(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id)
{
std::vector<u8> data(COMMUNICATION_ID_SIZE + sizeof(u32));
memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE);
*utils::bless<le_t<u32>>(&data[COMMUNICATION_ID_SIZE]) = board_id;
return forge_send(CommandType::GetBoardInfos, req_id, data);
}
bool rpcn_client::record_score(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, SceNpScorePcId char_id, SceNpScoreValue score, const std::optional<std::string> comment, const std::optional<std::vector<u8>> score_data)
{
std::vector<u8> data;
flatbuffers::FlatBufferBuilder builder(1024);
auto req_finished = CreateRecordScoreRequestDirect(builder, board_id, char_id, score, comment ? (*comment).c_str() : nullptr, score_data ? &*score_data : nullptr);
builder.Finish(req_finished);
u8* buf = builder.GetBufferPointer();
usz bufsize = builder.GetSize();
data.resize(COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize);
memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE);
reinterpret_cast<le_t<u32>&>(data[COMMUNICATION_ID_SIZE]) = static_cast<u32>(bufsize);
memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize);
return forge_send(CommandType::RecordScore, req_id, data);
}
bool rpcn_client::get_score_range(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, u32 start_rank, u32 num_rank, bool with_comment, bool with_gameinfo)
{
std::vector<u8> data;
flatbuffers::FlatBufferBuilder builder(1024);
auto req_finished = CreateGetScoreRangeRequest(builder, board_id, start_rank, num_rank, with_comment, with_gameinfo);
builder.Finish(req_finished);
u8* buf = builder.GetBufferPointer();
usz bufsize = builder.GetSize();
data.resize(COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize);
memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE);
reinterpret_cast<le_t<u32>&>(data[COMMUNICATION_ID_SIZE]) = static_cast<u32>(bufsize);
memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize);
return forge_send(CommandType::GetScoreRange, req_id, data);
}
bool rpcn_client::get_score_npid(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, const std::vector<std::pair<SceNpId, s32>>& npids, bool with_comment, bool with_gameinfo)
{
std::vector<u8> data;
flatbuffers::FlatBufferBuilder builder(1024);
std::vector<flatbuffers::Offset<ScoreNpIdPcId>> davec;
for (usz i = 0; i < npids.size(); i++)
{
auto npid = CreateScoreNpIdPcId(builder, builder.CreateString(static_cast<const char*>(npids[i].first.handle.data)), npids[i].second);
davec.push_back(npid);
}
auto req_finished = CreateGetScoreNpIdRequest(builder, board_id, builder.CreateVector(davec), with_comment, with_gameinfo);
builder.Finish(req_finished);
u8* buf = builder.GetBufferPointer();
usz bufsize = builder.GetSize();
data.resize(COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize);
memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE);
reinterpret_cast<le_t<u32>&>(data[COMMUNICATION_ID_SIZE]) = static_cast<u32>(bufsize);
memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize);
return forge_send(CommandType::GetScoreNpid, req_id, data);
}
bool rpcn_client::get_score_friend(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, bool with_comment, bool with_gameinfo)
{
std::vector<u8> data;
flatbuffers::FlatBufferBuilder builder(1024);
auto req_finished = CreateGetScoreFriendsRequest(builder, board_id, with_comment, with_gameinfo);
builder.Finish(req_finished);
u8* buf = builder.GetBufferPointer();
usz bufsize = builder.GetSize();
data.resize(COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize);
memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE);
reinterpret_cast<le_t<u32>&>(data[COMMUNICATION_ID_SIZE]) = static_cast<u32>(bufsize);
memcpy(data.data() + COMMUNICATION_ID_SIZE + sizeof(u32), buf, bufsize);
return forge_send(CommandType::GetScoreFriends, req_id, data);
}
std::vector<u8> rpcn_client::forge_request(u16 command, u64 packet_id, const std::vector<u8>& data) const
{
u16 packet_size = data.size() + RPCN_HEADER_SIZE;
u32 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;
reinterpret_cast<le_t<u16>&>(packet[3]) = packet_size;
reinterpret_cast<le_t<u64>&>(packet[5]) = packet_id;
reinterpret_cast<le_t<u32>&>(packet[3]) = packet_size;
reinterpret_cast<le_t<u64>&>(packet[7]) = packet_id;
memcpy(packet.data() + RPCN_HEADER_SIZE, data.data(), data.size());
return packet;
}
bool rpcn_client::is_error(ErrorType err) const
{
if (err >= ErrorType::__error_last)
{
rpcn_log.error("Invalid error returned!");
return true;
}
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 AlreadyJoined: rpcn_log.error("User has already joined!"); 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 Unsupported: rpcn_log.error("An unsupported operation was attempted!"); break;
default: rpcn_log.fatal("Unhandled ErrorType reached the switch?"); break;
}
return true;
}
bool rpcn_client::error_and_disconnect(const std::string& error_msg)
{
connected = false;
@ -1760,6 +1900,7 @@ namespace rpcn
{
return state;
}
want_auth = true;
sem_rpcn.release();
}

View File

@ -118,6 +118,8 @@ namespace rpcn
Terminate,
Create,
SendToken,
SendResetToken,
ResetPassword,
AddFriend,
RemoveFriend,
AddBlock,
@ -138,6 +140,13 @@ namespace rpcn
RequestSignalingInfos,
RequestTicket,
SendMessage,
GetBoardInfos,
RecordScore,
StoreScoreData,
GetScoreData,
GetScoreRange,
GetScoreFriends,
GetScoreNpid,
};
enum NotificationType : u16
@ -204,6 +213,7 @@ namespace rpcn
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
Unsupported,
__error_last
};
@ -221,6 +231,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);
class rpcn_client
{
@ -290,7 +301,7 @@ namespace rpcn
public:
~rpcn_client();
rpcn_client(rpcn_client& other) = delete;
rpcn_client(rpcn_client& other) = delete;
void operator=(const rpcn_client&) = delete;
static std::shared_ptr<rpcn_client> get_instance();
rpcn_state wait_for_connection();
@ -299,8 +310,10 @@ namespace rpcn
void get_friends_and_register_cb(friend_data& friend_infos, friend_cb_func cb_func, void* cb_param);
void remove_friend_cb(friend_cb_func, void* cb_param);
ErrorType create_user(const std::string& npid, const std::string& password, const std::string& online_name, const std::string& avatar_url, const std::string& email);
ErrorType create_user(std::string_view npid, std::string_view password, std::string_view online_name, std::string_view avatar_url, std::string_view email);
ErrorType resend_token(const std::string& npid, const std::string& password);
ErrorType send_reset_token(std::string_view npid, std::string_view email);
ErrorType reset_password(std::string_view npid, std::string_view token, std::string_view password);
bool add_friend(const std::string& friend_username);
bool remove_friend(const std::string& friend_username);
@ -350,6 +363,11 @@ namespace rpcn
bool req_sign_infos(u32 req_id, const std::string& npid);
bool req_ticket(u32 req_id, const std::string& service_id, const std::vector<u8>& cookie);
bool sendmessage(const message_data& msg_data, const std::set<std::string>& npids);
bool get_board_infos(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id);
bool record_score(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, SceNpScorePcId char_id, SceNpScoreValue score, const std::optional<std::string> comment, const std::optional<std::vector<u8>> score_data);
bool get_score_range(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, u32 start_rank, u32 num_rank, bool with_comment, bool with_gameinfo);
bool get_score_npid(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, const std::vector<std::pair<SceNpId, s32>>& npids, bool with_comment, bool with_gameinfo);
bool get_score_friend(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, bool with_comment, bool with_gameinfo);
const std::string& get_online_name() const
{
@ -368,6 +386,15 @@ namespace rpcn
{
return port_sig.load();
}
u32 get_addr_local() const
{
return local_addr_sig.load();
}
void update_local_addr(u32 addr)
{
local_addr_sig = std::bit_cast<u32, be_t<u32>>(addr);
}
private:
bool get_reply(u64 expected_id, std::vector<u8>& data);
@ -376,7 +403,6 @@ namespace rpcn
bool forge_send(u16 command, u64 packet_id, const std::vector<u8>& data);
bool forge_send_reply(u16 command, u64 packet_id, const std::vector<u8>& data, std::vector<u8>& reply_data);
bool is_error(ErrorType err) const;
bool error_and_disconnect(const std::string& error_mgs);
std::string get_wolfssl_error(WOLFSSL* wssl, int error) const;
@ -432,6 +458,7 @@ namespace rpcn
atomic_t<u32> addr_sig{};
atomic_t<u16> port_sig{};
atomic_t<u32> local_addr_sig{};
};
} // namespace rpcn

View File

@ -21,6 +21,14 @@ void cfg_rpcn::load()
rpcn_log.notice("RPCN config missing. Using default settings. Path: %s", path);
from_default();
}
// Update config from old version(s)
if (version == 1)
{
password.from_string("");
version.set(2);
save();
}
}
void cfg_rpcn::save() const
@ -55,7 +63,7 @@ std::string cfg_rpcn::generate_npid()
std::string gen_npid = "RPCS3_";
const char list_chars[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
std::srand(time(nullptr));
@ -72,6 +80,32 @@ std::string cfg_rpcn::get_host() const
return host.to_string();
}
std::vector<std::pair<std::string, std::string>> cfg_rpcn::get_hosts()
{
std::vector<std::pair<std::string, std::string>> vec_hosts;
auto hosts_list = fmt::split(hosts.to_string(), {"|||"});
for (const auto& cur_host : hosts_list)
{
auto desc_and_host = fmt::split(cur_host, {"|"});
if (desc_and_host.size() != 2)
{
rpcn_log.error("Invalid host in the list of hosts: %s", cur_host);
continue;
}
vec_hosts.push_back(std::make_pair(std::move(desc_and_host[0]), std::move(desc_and_host[1])));
}
if (vec_hosts.empty())
{
hosts.from_default();
save();
return get_hosts();
}
return vec_hosts;
}
std::string cfg_rpcn::get_npid()
{
std::string final_npid = npid.to_string();
@ -94,22 +128,80 @@ std::string cfg_rpcn::get_token() const
return token.to_string();
}
void cfg_rpcn::set_host(const std::string& host)
void cfg_rpcn::set_host(std::string_view host)
{
this->host.from_string(host);
}
void cfg_rpcn::set_npid(const std::string& npid)
void cfg_rpcn::set_npid(std::string_view npid)
{
this->npid.from_string(npid);
}
void cfg_rpcn::set_password(const std::string& password)
void cfg_rpcn::set_password(std::string_view password)
{
this->password.from_string(password);
}
void cfg_rpcn::set_token(const std::string& token)
void cfg_rpcn::set_token(std::string_view token)
{
this->token.from_string(token);
}
void cfg_rpcn::set_hosts(const std::vector<std::pair<std::string, std::string>>& vec_hosts)
{
std::string final_string;
for (const auto& [cur_desc, cur_host] : vec_hosts)
{
fmt::append(final_string, "%s|%s|||", cur_desc, cur_host);
}
if (final_string.empty())
{
hosts.from_default();
return;
}
final_string.resize(final_string.size() - 3);
hosts.from_string(final_string);
}
bool cfg_rpcn::add_host(std::string_view new_description, std::string_view new_host)
{
auto cur_hosts = get_hosts();
for (const auto& [cur_desc, cur_host] : cur_hosts)
{
if (cur_desc == new_description && cur_host == new_host)
return false;
}
cur_hosts.push_back(std::make_pair(std::string(new_description), std::string(new_host)));
set_hosts(cur_hosts);
return true;
}
bool cfg_rpcn::del_host(std::string_view del_description, std::string_view del_host)
{
// Do not delete default servers
if ((del_description == "Official RPCN Server" && del_host == "np.rpcs3.net") ||
(del_description == "RPCN Test Server" && del_host == "test-np.rpcs3.net"))
{
return true;
}
auto cur_hosts = get_hosts();
for (auto it = cur_hosts.begin(); it != cur_hosts.end(); it++)
{
if (it->first == del_description && it->second == del_host)
{
cur_hosts.erase(it);
set_hosts(cur_hosts);
return true;
}
}
return false;
}

View File

@ -4,10 +4,12 @@
struct cfg_rpcn : cfg::node
{
cfg::uint32 version{this, "Version", 1};
cfg::string host{this, "Host", "np.rpcs3.net"};
cfg::string npid{this, "NPID", ""};
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"};
void load();
void save() const;
@ -16,15 +18,19 @@ 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;
std::vector<std::pair<std::string, std::string>> get_hosts(); // saves default if no valid server in the list
void set_host(const std::string& host);
void set_npid(const std::string& npid);
void set_password(const std::string& password);
void set_token(const std::string& token);
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);
bool add_host(std::string_view description, std::string_view host);
bool del_host(std::string_view description, std::string_view host);
private:
private:
static std::string get_path();
static std::string generate_npid();
void set_hosts(const std::vector<std::pair<std::string, std::string>>& vec_hosts);
};
extern cfg_rpcn g_cfg_rpcn;

View File

@ -4,6 +4,7 @@
#include "Emu/IdManager.h"
#include "Emu/Cell/Modules/cellSysutil.h"
#include "np_handler.h"
#include "Emu/NP/vport0.h"
#ifdef _WIN32
#include <winsock2.h>
@ -15,28 +16,26 @@
LOG_CHANNEL(sign_log, "Signaling");
std::vector<std::pair<std::pair<u32, u16>, std::vector<u8>>> get_sign_msgs();
s32 send_packet_from_p2p_port(const std::vector<u8>& data, const sockaddr_in& addr);
void need_network();
template <>
void fmt_class_string<SignalingCommand>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto value)
{
switch (value)
{
case signal_ping: return "PING";
case signal_pong: return "PONG";
case signal_connect: return "CONNECT";
case signal_connect_ack: return "CONNECT_ACK";
case signal_confirm: return "CONFIRM";
case signal_finished: return "FINISHED";
case signal_finished_ack: return "FINISHED_ACK";
}
switch (value)
{
case signal_ping: return "PING";
case signal_pong: return "PONG";
case signal_connect: return "CONNECT";
case signal_connect_ack: return "CONNECT_ACK";
case signal_confirm: return "CONFIRM";
case signal_finished: return "FINISHED";
case signal_finished_ack: return "FINISHED_ACK";
}
return unknown;
});
return unknown;
});
}
signaling_handler::signaling_handler()
@ -77,10 +76,10 @@ void signaling_handler::signal_sig_callback(u32 conn_id, int event)
if (sig_cb)
{
sysutil_register_cb([sig_cb = this->sig_cb, sig_cb_ctx = this->sig_cb_ctx, conn_id, event, sig_cb_arg = this->sig_cb_arg](ppu_thread& cb_ppu) -> s32
{
sig_cb(cb_ppu, sig_cb_ctx, conn_id, event, 0, sig_cb_arg);
return 0;
});
{
sig_cb(cb_ppu, sig_cb_ctx, conn_id, event, 0, sig_cb_arg);
return 0;
});
sign_log.notice("Called sig CB: 0x%x (conn_id: %d)", event, conn_id);
}
@ -93,11 +92,11 @@ void signaling_handler::signal_ext_sig_callback(u32 conn_id, int event) const
if (sig_ext_cb)
{
sysutil_register_cb([sig_ext_cb = this->sig_ext_cb, sig_ext_cb_ctx = this->sig_ext_cb_ctx, conn_id, event, sig_ext_cb_arg = this->sig_ext_cb_arg](ppu_thread& cb_ppu) -> s32
{
sig_ext_cb(cb_ppu, sig_ext_cb_ctx, conn_id, event, 0, sig_ext_cb_arg);
return 0;
});
sign_log.notice("Called EXT sig CB: 0x%x (conn_id: %d, member_id: %d)", event, conn_id);
{
sig_ext_cb(cb_ppu, sig_ext_cb_ctx, conn_id, event, 0, sig_ext_cb_arg);
return 0;
});
sign_log.notice("Called EXT sig CB: 0x%x (conn_id: %d)", event, conn_id);
}
}
@ -107,10 +106,10 @@ void signaling_handler::signal_sig2_callback(u64 room_id, u16 member_id, SceNpMa
if (sig2_cb)
{
sysutil_register_cb([sig2_cb = this->sig2_cb, sig2_cb_ctx = this->sig2_cb_ctx, room_id, member_id, event, sig2_cb_arg = this->sig2_cb_arg](ppu_thread& cb_ppu) -> s32
{
sig2_cb(cb_ppu, sig2_cb_ctx, room_id, member_id, event, 0, sig2_cb_arg);
return 0;
});
{
sig2_cb(cb_ppu, sig2_cb_ctx, room_id, member_id, event, 0, sig2_cb_arg);
return 0;
});
}
sign_log.notice("Called sig2 CB: 0x%x (room_id: %d, member_id: %d)", event, room_id, member_id);
@ -180,15 +179,15 @@ void signaling_handler::process_incoming_messages()
for (const auto& msg : msgs)
{
if (msg.second.size() != sizeof(signaling_packet))
if (msg.data.size() != sizeof(signaling_packet))
{
sign_log.error("Received an invalid signaling packet");
continue;
}
auto op_addr = msg.first.first;
auto op_port = msg.first.second;
auto* sp = reinterpret_cast<const signaling_packet*>(msg.second.data());
auto op_addr = msg.src_addr;
auto op_port = msg.src_port;
auto* sp = reinterpret_cast<const signaling_packet*>(msg.data.data());
if (!validate_signaling_packet(sp))
continue;
@ -231,8 +230,8 @@ void signaling_handler::process_incoming_messages()
si = ::at32(sig1_peers, conn_id);
// Activate the connection without triggering the main CB
si->connStatus = SCE_NP_SIGNALING_CONN_STATUS_ACTIVE;
si->addr = op_addr;
si->port = op_port;
si->addr = op_addr;
si->port = op_port;
si->ext_status = ext_sign_peer;
// Notify extended callback that peer activated
signal_ext_sig_callback(conn_id, SCE_NP_SIGNALING_EVENT_EXT_PEER_ACTIVATED);
@ -259,30 +258,51 @@ void signaling_handler::process_incoming_messages()
}
}
sent_packet.command = signal_ping;
sent_packet.command = signal_ping;
sent_packet.timestamp = now.time_since_epoch().count();
send_signaling_packet(sent_packet, si->addr, si->port);
queue_signaling_packet(sent_packet, si, now + REPEAT_PING_DELAY);
};
const auto update_rtt = [&]()
{
u32 rtt = now.time_since_epoch().count() - sp->timestamp;
si->last_rtts[(si->rtt_counters % 6)] = rtt;
si->rtt_counters++;
std::size_t num_rtts = std::min(static_cast<std::size_t>(6), si->rtt_counters);
u64 sum = 0;
for (std::size_t index = 0; index < num_rtts; index++)
{
sum += si->last_rtts[index];
}
si->rtt = (sum / num_rtts) / 1000;
};
switch (sp->command)
{
case signal_ping:
reply = true;
schedule_repeat = false;
sent_packet.command = signal_pong;
reply = true;
schedule_repeat = false;
sent_packet.command = signal_pong;
sent_packet.timestamp = sp->timestamp;
break;
case signal_pong:
update_rtt();
reply = false;
schedule_repeat = false;
reschedule_packet(si, signal_ping, now + 15s);
reschedule_packet(si, signal_ping, now + 10s);
break;
case signal_connect:
reply = true;
schedule_repeat = true;
sent_packet.command = signal_connect_ack;
reply = true;
schedule_repeat = true;
sent_packet.command = signal_connect_ack;
sent_packet.timestamp = sp->timestamp;
// connection is established
// TODO: notify extended callback!
break;
case signal_connect_ack:
update_rtt();
reply = true;
schedule_repeat = false;
setup_ping();
@ -357,6 +377,17 @@ void signaling_handler::operator()()
break; // qpackets will be emptied of all packets from this user so we're requeuing
}
// Update the timestamp if necessary
switch (it->second.packet.command)
{
case signal_connect:
case signal_ping:
it->second.packet.timestamp = now.time_since_epoch().count();
break;
default:
break;
}
// Resend the packet
send_signaling_packet(it->second.packet, it->second.sig_info->addr, it->second.sig_info->port);
@ -427,7 +458,7 @@ void signaling_handler::update_si_mapped_addr(std::shared_ptr<signaling_info>& s
if (si->addr != new_addr || si->port != new_port)
{
in_addr addr_old, addr_new;
addr_old.s_addr = si->addr;
addr_old.s_addr = si->mapped_addr;
addr_new.s_addr = new_addr;
char ip_str_old[16];
@ -435,7 +466,7 @@ void signaling_handler::update_si_mapped_addr(std::shared_ptr<signaling_info>& s
inet_ntop(AF_INET, &addr_old, ip_str_old, sizeof(ip_str_old));
inet_ntop(AF_INET, &addr_new, ip_str_new, sizeof(ip_str_new));
sign_log.trace("Updated Mapped Address from %s:%d to %s:%d", ip_str_old, si->port, ip_str_new, new_port);
sign_log.trace("Updated Mapped Address from %s:%d to %s:%d", ip_str_old, si->mapped_port, ip_str_new, new_port);
si->mapped_addr = new_addr;
si->mapped_port = new_port;
}
@ -493,9 +524,12 @@ void signaling_handler::set_self_sig2_info(u64 room_id, u16 member_id)
void signaling_handler::send_signaling_packet(signaling_packet& sp, u32 addr, u16 port) const
{
std::vector<u8> packet(sizeof(signaling_packet) + sizeof(u16));
reinterpret_cast<le_t<u16>&>(packet[0]) = 65535;
memcpy(packet.data() + sizeof(u16), &sp, sizeof(signaling_packet));
std::vector<u8> packet(sizeof(signaling_packet) + VPORT_0_HEADER_SIZE);
reinterpret_cast<le_t<u16>&>(packet[0]) = 0; // VPort 0
packet[2] = SUBSET_SIGNALING;
sp.sent_addr = addr;
sp.sent_port = port;
memcpy(packet.data() + VPORT_0_HEADER_SIZE, &sp, sizeof(signaling_packet));
sockaddr_in dest;
memset(&dest, 0, sizeof(sockaddr_in));
@ -508,9 +542,6 @@ 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);
sp.sent_addr = addr;
sp.sent_port = port;
if (send_packet_from_p2p_port(packet, dest) == -1)
{
sign_log.error("Failed to send signaling packet to %s:%d", ip_str, port);
@ -534,7 +565,7 @@ std::shared_ptr<signaling_info> signaling_handler::get_signaling_ptr(const signa
memcpy(npid_buf, sp->V1.npid.handle.data, 16);
std::string npid(npid_buf);
if (!npid_to_conn_id.count(npid))
if (!npid_to_conn_id.contains(npid))
return nullptr;
const u32 conn_id = ::at32(npid_to_conn_id, npid);
@ -566,6 +597,7 @@ void signaling_handler::start_sig_nl(u32 conn_id, u32 addr, u16 port)
{
auto& sent_packet = sig1_packet;
sent_packet.command = signal_connect;
sent_packet.timestamp = steady_clock::now().time_since_epoch().count();
ensure(sig1_peers.contains(conn_id));
std::shared_ptr<signaling_info> si = ::at32(sig1_peers, conn_id);
@ -578,12 +610,29 @@ void signaling_handler::start_sig_nl(u32 conn_id, u32 addr, u16 port)
wake_up();
}
void signaling_handler::stop_sig(u32 conn_id)
{
std::lock_guard lock(data_mutex);
if (!sig1_peers.contains(conn_id))
return;
auto& sent_packet = sig1_packet;
sent_packet.command = signal_finished;
std::shared_ptr<signaling_info> si = ::at32(sig1_peers, conn_id);
send_signaling_packet(sent_packet, si->addr, si->port);
queue_signaling_packet(sent_packet, si, steady_clock::now() + REPEAT_FINISHED_DELAY);
}
void signaling_handler::start_sig2(u64 room_id, u16 member_id)
{
std::lock_guard lock(data_mutex);
auto& sent_packet = sig2_packet;
sent_packet.command = signal_connect;
sent_packet.timestamp = steady_clock::now().time_since_epoch().count();
ensure(sig2_peers.contains(room_id));
const auto& sp = ::at32(sig2_peers, room_id);
@ -633,6 +682,7 @@ u32 signaling_handler::create_sig_infos(const SceNpId* npid)
sig1_peers.emplace(conn_id, std::make_shared<signaling_info>());
::at32(sig1_peers, conn_id)->version = 1;
::at32(sig1_peers, conn_id)->conn_id = conn_id;
::at32(sig1_peers, conn_id)->npid = *npid;
return conn_id;
}
@ -671,10 +721,33 @@ u32 signaling_handler::init_sig_infos(const SceNpId* npid)
signaling_info signaling_handler::get_sig_infos(u32 conn_id)
{
std::lock_guard lock(data_mutex);
return *sig1_peers[conn_id];
}
void signaling_handler::set_sig2_infos(u64 room_id, u16 member_id, s32 status, u32 addr, u16 port, bool self)
std::optional<u32> signaling_handler::get_conn_id_from_npid(const SceNpId* npid)
{
std::lock_guard lock(data_mutex);
// Diff behaviour here depending on SDK version, 420+ always succeeds
return create_sig_infos(npid);
}
std::optional<u32> signaling_handler::get_conn_id_from_addr(u32 addr, u16 port)
{
std::lock_guard lock(data_mutex);
for (const auto& [conn_id, conn_info] : sig1_peers)
{
if (conn_info && std::bit_cast<u32, be_t<u32>>(conn_info->addr) == addr && conn_info->port == port)
{
return conn_id;
}
}
return std::nullopt;
}
void signaling_handler::set_sig2_infos(u64 room_id, u16 member_id, s32 status, u32 addr, u16 port, const SceNpId& npid, bool self)
{
std::lock_guard lock(data_mutex);
if (!sig2_peers[room_id][member_id])
@ -688,6 +761,7 @@ void signaling_handler::set_sig2_infos(u64 room_id, u16 member_id, s32 status, u
peer->version = 2;
peer->room_id = room_id;
peer->member_id = member_id;
peer->npid = npid;
}
signaling_info signaling_handler::get_sig2_infos(u64 room_id, u16 member_id)
@ -697,10 +771,10 @@ signaling_info signaling_handler::get_sig2_infos(u64 room_id, u16 member_id)
if (!sig2_peers[room_id][member_id])
{
sig2_peers[room_id][member_id] = std::make_shared<signaling_info>();
auto& peer = sig2_peers[room_id][member_id];
peer->room_id = room_id;
peer->member_id = member_id;
peer->version = 2;
auto& peer = sig2_peers[room_id][member_id];
peer->room_id = room_id;
peer->member_id = member_id;
peer->version = 2;
}
return *sig2_peers[room_id][member_id];

View File

@ -7,11 +7,12 @@
#include <unordered_map>
#include <condition_variable>
#include <chrono>
#include <optional>
enum ext_signaling_status : u8
{
ext_sign_none = 0,
ext_sign_peer = 1,
ext_sign_none = 0,
ext_sign_peer = 1,
ext_sign_mutual = 2,
};
@ -27,15 +28,24 @@ struct signaling_info
// For handler
steady_clock::time_point time_last_msg_recvd = steady_clock::now();
bool self = false;
u32 version = 0;
SceNpId npid{};
// Signaling
u32 conn_id = 0;
ext_signaling_status ext_status = ext_sign_none;
// Matching2
u64 room_id = 0;
u16 member_id = 0;
// Stats
u64 last_rtts[6] = {};
std::size_t rtt_counters = 0;
u32 rtt = 0;
u32 pings_sent = 1, lost_pings = 0;
u32 packet_loss = 0;
};
enum SignalingCommand : u32
@ -62,8 +72,10 @@ public:
u32 init_sig_infos(const SceNpId* npid);
signaling_info get_sig_infos(u32 conn_id);
std::optional<u32> get_conn_id_from_npid(const SceNpId* npid);
std::optional<u32> get_conn_id_from_addr(u32 addr, u16 port);
void set_sig2_infos(u64 room_id, u16 member_id, s32 status, u32 addr, u16 port, bool self = false);
void set_sig2_infos(u64 room_id, u16 member_id, s32 status, u32 addr, u16 port, const SceNpId& npid, bool self = false);
signaling_info get_sig2_infos(u64 room_id, u16 member_id);
void set_sig_cb(u32 sig_cb_ctx, vm::ptr<SceNpSignalingHandler> sig_cb, vm::ptr<void> sig_cb_arg);
@ -71,6 +83,7 @@ public:
void set_sig2_cb(u16 sig2_cb_ctx, vm::ptr<SceNpMatching2SignalingCallback> sig2_cb, vm::ptr<void> sig2_cb_arg);
void start_sig(u32 conn_id, u32 addr, u16 port);
void stop_sig(u32 conn_id);
void start_sig2(u64 room_id, u16 member_id);
void disconnect_sig2_users(u64 room_id);
@ -78,19 +91,21 @@ public:
static constexpr auto thread_name = "Signaling Manager Thread"sv;
private:
static constexpr auto REPEAT_CONNECT_DELAY = std::chrono::milliseconds(200);
static constexpr auto REPEAT_PING_DELAY = std::chrono::milliseconds(500);
static constexpr auto REPEAT_FINISHED_DELAY = std::chrono::milliseconds(500);
static constexpr auto REPEAT_CONNECT_DELAY = std::chrono::milliseconds(200);
static constexpr auto REPEAT_PING_DELAY = std::chrono::milliseconds(500);
static constexpr auto REPEAT_FINISHED_DELAY = std::chrono::milliseconds(500);
static constexpr be_t<u32> SIGNALING_SIGNATURE = (static_cast<u32>('S') << 24 | static_cast<u32>('I') << 16 | static_cast<u32>('G') << 8 | static_cast<u32>('N'));
struct signaling_packet
{
be_t<u32> signature = SIGNALING_SIGNATURE;
le_t<u32> version;
le_t<u64> timestamp;
le_t<SignalingCommand> command;
le_t<u32> sent_addr;
le_t<u16> sent_port;
union {
union
{
struct
{
SceNpId npid;

26
rpcs3/Emu/NP/vport0.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include <vector>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <netinet/in.h>
#endif
#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);
std::vector<signaling_message> get_sign_msgs();
std::vector<std::vector<u8>> get_rpcn_msgs();
constexpr s32 VPORT_0_HEADER_SIZE = sizeof(u16) + sizeof(u8);
// VPort 0 is invalid for sys_net so we use it for:
// Subset 0: Messages from RPCN server, IP retrieval / UDP hole punching
// Subset 1: Signaling
enum VPORT_0_SUBSET : u8
{
SUBSET_RPCN = 0,
SUBSET_SIGNALING = 1,
};

View File

@ -504,6 +504,7 @@
<ClInclude Include="Emu\NP\generated\np2_structs_generated.h" />
<ClInclude Include="Emu\NP\np_handler.h" />
<ClInclude Include="Emu\NP\signaling_handler.h" />
<ClInclude Include="Emu\NP\vport0.h" />
<ClInclude Include="Emu\NP\np_allocator.h" />
<ClInclude Include="Emu\NP\np_cache.h" />
<ClInclude Include="Emu\NP\np_dnshook.h" />

View File

@ -1923,6 +1923,9 @@
<ClInclude Include="Emu\NP\signaling_handler.h">
<Filter>Emu\NP</Filter>
</ClInclude>
<ClInclude Include="Emu\NP\vport0.h">
<Filter>Emu\NP</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\Overlays\overlay_osk_panel.h">
<Filter>Emu\GPU\RSX\Overlays</Filter>
</ClInclude>

View File

@ -6,11 +6,13 @@
#include <QInputDialog>
#include <QGroupBox>
#include <QMenu>
#include <QDialogButtonBox>
#include <thread>
#include "qt_utils.h"
#include "rpcn_settings_dialog.h"
#include "Emu/System.h"
#include "Emu/NP/rpcn_config.h"
#include <wolfssl/ssl.h>
@ -18,13 +20,50 @@
LOG_CHANNEL(rpcn_settings_log, "rpcn settings dlg");
bool validate_rpcn_username(const std::string& input)
bool validate_rpcn_username(std::string_view username)
{
if (input.length() < 3 || input.length() > 16)
if (username.length() < 3 || username.length() > 16)
return false;
return std::all_of(input.cbegin(), input.cend(), [](const char c)
{ return std::isalnum(c) || c == '-' || c == '_'; });
return std::all_of(username.cbegin(), username.cend(), [](const char c)
{
return std::isalnum(static_cast<unsigned char>(c)) || c == '-' || c == '_';
});
}
bool validate_email(std::string_view email)
{
const QRegularExpressionValidator simple_email_validator(QRegularExpression("^[a-zA-Z0-9.!#$%&*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$"));
QString qstr_email = QString::fromStdString(std::string(email));
int pos = 0;
if (qstr_email.isEmpty() || qstr_email.contains(' ') || qstr_email.contains('\t') || simple_email_validator.validate(qstr_email, pos) != QValidator::Acceptable)
return false;
return true;
}
bool validate_token(std::string_view token)
{
return token.size() == 16 && std::all_of(token.cbegin(), token.cend(), [](const char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z'); });
}
std::string derive_password(std::string_view user_password)
{
std::string_view salt_str = "No matter where you go, everybody's connected.";
u8 derived_password_digest[SHA3_256_DIGEST_LENGTH];
ensure(!wc_PBKDF2(derived_password_digest, reinterpret_cast<const u8*>(user_password.data()), user_password.size(), reinterpret_cast<const u8*>(salt_str.data()), salt_str.size(), 200'000, SHA3_256_DIGEST_LENGTH, WC_SHA3_256));
std::string derived_password("0000000000000000000000000000000000000000000000000000000000000000");
for (u32 i = 0; i < SHA3_256_DIGEST_LENGTH; i++)
{
constexpr auto pal = "0123456789ABCDEF";
derived_password[i * 2] = pal[derived_password_digest[i] >> 4];
derived_password[(i * 2) + 1] = pal[derived_password_digest[i] & 15];
}
return derived_password;
}
rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent)
@ -51,6 +90,11 @@ rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent)
connect(btn_account, &QPushButton::clicked, this, [this]()
{
if (!Emu.IsStopped())
{
QMessageBox::critical(this, tr("Error: Emulation Running"), tr("You need to stop the emulator before editing RPCN account information!"), QMessageBox::Ok);
return;
}
rpcn_account_dialog dlg(this);
dlg.exec();
});
@ -66,8 +110,509 @@ rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent)
rpcn_account_dialog::rpcn_account_dialog(QWidget* parent)
: QDialog(parent)
{
setWindowTitle(tr("RPCN: Configuration"));
setWindowTitle(tr("RPCN: Account"));
setObjectName("rpcn_account_dialog");
QVBoxLayout* vbox_global = new QVBoxLayout();
QGroupBox* grp_server = new QGroupBox(tr("Server:"));
QVBoxLayout* vbox_server = new QVBoxLayout();
QHBoxLayout* hbox_lbl_combo = new QHBoxLayout();
QLabel* lbl_server = new QLabel(tr("Server:"));
cbx_servers = new QComboBox();
refresh_combobox();
hbox_lbl_combo->addWidget(lbl_server);
hbox_lbl_combo->addWidget(cbx_servers);
QHBoxLayout* hbox_buttons = new QHBoxLayout();
QPushButton* btn_add_server = new QPushButton(tr("Add"));
QPushButton* btn_del_server = new QPushButton(tr("Del"));
hbox_buttons->addStretch();
hbox_buttons->addWidget(btn_add_server);
hbox_buttons->addWidget(btn_del_server);
vbox_server->addLayout(hbox_lbl_combo);
vbox_server->addLayout(hbox_buttons);
grp_server->setLayout(vbox_server);
vbox_global->addWidget(grp_server);
QGroupBox* grp_buttons = new QGroupBox();
QVBoxLayout* vbox_buttons = new QVBoxLayout();
QPushButton* btn_create = new QPushButton(tr("Create Account"));
QPushButton* btn_edit = new QPushButton(tr("Edit Account"));
QPushButton* btn_test = new QPushButton(tr("Test Account"));
vbox_buttons->addSpacing(10);
vbox_buttons->addWidget(btn_create);
vbox_buttons->addSpacing(10);
vbox_buttons->addWidget(btn_edit);
vbox_buttons->addSpacing(10);
vbox_buttons->addWidget(btn_test);
vbox_buttons->addSpacing(10);
grp_buttons->setLayout(vbox_buttons);
vbox_global->addWidget(grp_buttons);
setLayout(vbox_global);
connect(cbx_servers, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index)
{
if (index < 0)
return;
QVariant host = cbx_servers->itemData(index);
if (!host.isValid() || !host.canConvert<QString>())
return;
g_cfg_rpcn.set_host(host.toString().toStdString());
g_cfg_rpcn.save();
});
connect(btn_add_server, &QAbstractButton::clicked, this, [this]()
{
rpcn_add_server_dialog dlg(this);
dlg.exec();
const auto new_server = dlg.get_new_server();
if (new_server)
{
if (!g_cfg_rpcn.add_host(new_server->first, new_server->second))
{
QMessageBox::critical(this, tr("Existing Server"), tr("You already have a server with this description & hostname in the list."), QMessageBox::Ok);
return;
}
g_cfg_rpcn.save();
refresh_combobox();
}
});
connect(btn_del_server, &QAbstractButton::clicked, this, [this]()
{
const int index = cbx_servers->currentIndex();
if (index < 0)
return;
const auto desc = cbx_servers->itemText(index).toStdString();
const auto host = cbx_servers->itemData(index).toString().toStdString();
ensure(g_cfg_rpcn.del_host(desc, host));
g_cfg_rpcn.save();
refresh_combobox();
});
connect(btn_create, &QAbstractButton::clicked, this, [this]()
{
rpcn_ask_username_dialog dlg_username(this, tr("Please enter your username.\n\n"
"Note that these restrictions apply:\n"
"- Username must be between 3 and 16 characters\n"
"- Username can only contain a-z A-Z 0-9 '-' '_'\n"
"- Username is case sensitive\n"));
dlg_username.exec();
auto username = dlg_username.get_username();
if (!username)
return;
rpcn_ask_password_dialog dlg_password(this, tr("Please choose your password:\n\n"));
dlg_password.exec();
auto password = dlg_password.get_password();
if (!password)
return;
rpcn_ask_email_dialog dlg_email(this, tr("An email address is required, please note:\n"
"- A valid email is needed to receive the token that validates your account.\n"
"- Your email won't be used for anything beyond sending you this token or the password reset token.\n\n"));
dlg_email.exec();
auto email = dlg_email.get_email();
if (!email)
return;
if (QMessageBox::question(this, tr("RPCN: Account Creation"), tr("You are about to create an account with:\n-Username:%0\n-Email:%1\n\nIs this correct?").arg(QString::fromStdString(*username)).arg(QString::fromStdString(*email))) != QMessageBox::Yes)
return;
{
const auto rpcn = rpcn::rpcn_client::get_instance();
const auto avatar_url = "https://rpcs3.net/cdn/netplay/DefaultAvatar.png";
if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure)
{
const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result)));
QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok);
return;
}
if (auto error = rpcn->create_user(*username, *password, *username, avatar_url, *email); error != rpcn::ErrorType::NoError)
{
QString error_message;
switch (error)
{
case rpcn::ErrorType::CreationExistingUsername: error_message = tr("An account with that username already exists!"); break;
case rpcn::ErrorType::CreationBannedEmailProvider: error_message = tr("This email provider is banned!"); break;
case rpcn::ErrorType::CreationExistingEmail: error_message = tr("An account with that email already exists!"); break;
case rpcn::ErrorType::CreationError: error_message = tr("Unknown creation error"); break;
default: error_message = tr("Unknown error"); break;
}
QMessageBox::critical(this, tr("Error Creating Account"), tr("Failed to create the account:\n%0").arg(error_message), QMessageBox::Ok);
return;
}
}
g_cfg_rpcn.set_npid(*username);
g_cfg_rpcn.set_password(*password);
g_cfg_rpcn.save();
rpcn_ask_token_dialog token_dlg(this, tr("Your account has been created successfully!\n"
"Your account authentification was saved.\n"
"Now all you need is to enter the token that was sent to your email.\n"
"You can skip this step by leaving it empty and entering it later in the Edit Account section too.\n"));
token_dlg.exec();
auto token = token_dlg.get_token();
if (!token)
return;
g_cfg_rpcn.set_token(*token);
g_cfg_rpcn.save();
});
connect(btn_edit, &QAbstractButton::clicked, this, [this]()
{
rpcn_account_edit_dialog dlg_edit(this);
dlg_edit.exec();
});
connect(btn_test, &QAbstractButton::clicked, this, [this]()
{
auto rpcn = rpcn::rpcn_client::get_instance();
if (auto res = rpcn->wait_for_connection(); res != rpcn::rpcn_state::failure_no_failure)
{
const QString error_msg = tr("Failed to connect to RPCN:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(res)));
QMessageBox::warning(this, tr("Error connecting to RPCN!"), error_msg, QMessageBox::Ok);
return;
}
if (auto res = rpcn->wait_for_authentified(); res != rpcn::rpcn_state::failure_no_failure)
{
const QString error_msg = tr("Failed to authentify to RPCN:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(res)));
QMessageBox::warning(this, tr("Error authentifying to RPCN!"), error_msg, QMessageBox::Ok);
return;
}
QMessageBox::information(this, tr("RPCN Account Valid!"), tr("Your account is valid!"), QMessageBox::Ok);
});
}
void rpcn_account_dialog::refresh_combobox()
{
g_cfg_rpcn.load();
const auto vec_hosts = g_cfg_rpcn.get_hosts();
auto cur_host = g_cfg_rpcn.get_host();
int i = 0, index = 0;
cbx_servers->clear();
for (const auto& [desc, host] : vec_hosts)
{
cbx_servers->addItem(QString::fromStdString(desc), QString::fromStdString(host));
if (cur_host == host)
index = i;
i++;
}
cbx_servers->setCurrentIndex(index);
}
rpcn_add_server_dialog::rpcn_add_server_dialog(QWidget* parent)
: QDialog(parent)
{
setWindowTitle(tr("RPCN: Add Server"));
setObjectName("rpcn_add_server_dialog");
setMinimumSize(QSize(400, 200));
QVBoxLayout* vbox_global = new QVBoxLayout();
QLabel* lbl_description = new QLabel(tr("Description:"));
QLineEdit* edt_description = new QLineEdit();
QLabel* lbl_host = new QLabel(tr("Host:"));
QLineEdit* edt_host = new QLineEdit();
QDialogButtonBox* btn_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
vbox_global->addWidget(lbl_description);
vbox_global->addWidget(edt_description);
vbox_global->addWidget(lbl_host);
vbox_global->addWidget(edt_host);
vbox_global->addWidget(btn_box);
setLayout(vbox_global);
connect(btn_box, &QDialogButtonBox::accepted, this, [this, edt_description, edt_host]()
{
auto description = edt_description->text();
auto host = edt_host->text();
if (description.isEmpty())
{
QMessageBox::critical(this, tr("Missing Description"), tr("You must enter a description!"), QMessageBox::Ok);
return;
}
if (host.isEmpty())
{
QMessageBox::critical(this, tr("Missing Hostname"), tr("You must enter a hostname for the server!"), QMessageBox::Ok);
return;
}
m_new_server = std::make_pair(description.toStdString(), host.toStdString());
QDialog::accept();
});
connect(btn_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
const std::optional<std::pair<std::string, std::string>>& rpcn_add_server_dialog::get_new_server() const
{
return m_new_server;
}
rpcn_ask_username_dialog::rpcn_ask_username_dialog(QWidget* parent, const QString& description)
: QDialog(parent)
{
setWindowTitle(tr("RPCN: Username"));
setObjectName("rpcn_ask_username_dialog");
QVBoxLayout* vbox_global = new QVBoxLayout();
QLabel* lbl_username = new QLabel(description);
QGroupBox* grp_username = new QGroupBox(tr("Username:"));
QHBoxLayout* hbox_grp_username = new QHBoxLayout();
QLineEdit* edt_username = new QLineEdit(QString::fromStdString(g_cfg_rpcn.get_npid()));
edt_username->setMaxLength(16);
edt_username->setValidator(new QRegularExpressionValidator(QRegularExpression("^[a-zA-Z0-9_\\-]*$"), this));
hbox_grp_username->addWidget(edt_username);
grp_username->setLayout(hbox_grp_username);
QDialogButtonBox* btn_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
vbox_global->addWidget(lbl_username);
vbox_global->addWidget(grp_username);
vbox_global->addWidget(btn_box);
setLayout(vbox_global);
connect(btn_box, &QDialogButtonBox::accepted, this, [this, edt_username]()
{
const auto username = edt_username->text().toStdString();
if (username.empty())
{
QMessageBox::critical(this, tr("Missing Username"), tr("You must enter a username!"), QMessageBox::Ok);
return;
}
if (!validate_rpcn_username(username))
{
QMessageBox::critical(this, tr("Invalid Username"), tr("Please enter a valid username!"), QMessageBox::Ok);
}
m_username = username;
QDialog::accept();
});
connect(btn_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
const std::optional<std::string>& rpcn_ask_username_dialog::get_username() const
{
return m_username;
}
rpcn_ask_password_dialog::rpcn_ask_password_dialog(QWidget* parent, const QString& description)
: QDialog(parent)
{
setWindowTitle(tr("RPCN: Password"));
setObjectName("rpcn_ask_password_dialog");
QVBoxLayout* vbox_global = new QVBoxLayout();
QLabel* lbl_description = new QLabel(description);
QGroupBox* gbox_password = new QGroupBox();
QVBoxLayout* vbox_gbox = new QVBoxLayout();
QLabel* lbl_pass1 = new QLabel(tr("Enter your password:"));
QLineEdit* m_edit_pass1 = new QLineEdit();
m_edit_pass1->setEchoMode(QLineEdit::Password);
QLabel* lbl_pass2 = new QLabel(tr("Enter your password a second time:"));
QLineEdit* m_edit_pass2 = new QLineEdit();
m_edit_pass2->setEchoMode(QLineEdit::Password);
vbox_gbox->addWidget(lbl_pass1);
vbox_gbox->addWidget(m_edit_pass1);
vbox_gbox->addWidget(lbl_pass2);
vbox_gbox->addWidget(m_edit_pass2);
gbox_password->setLayout(vbox_gbox);
QDialogButtonBox* btn_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
vbox_global->addWidget(lbl_description);
vbox_global->addWidget(gbox_password);
vbox_global->addWidget(btn_box);
setLayout(vbox_global);
connect(btn_box, &QDialogButtonBox::accepted, this, [this, m_edit_pass1, m_edit_pass2]()
{
if (m_edit_pass1->text() != m_edit_pass2->text())
{
QMessageBox::critical(this, tr("Wrong Input"), tr("The two passwords you entered don't match!"), QMessageBox::Ok);
return;
}
if (m_edit_pass1->text().isEmpty())
{
QMessageBox::critical(this, tr("Missing Password"), tr("You need to enter a password!"), QMessageBox::Ok);
return;
}
m_password = derive_password(m_edit_pass1->text().toStdString());
QDialog::accept();
});
connect(btn_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
const std::optional<std::string>& rpcn_ask_password_dialog::get_password() const
{
return m_password;
}
rpcn_ask_email_dialog::rpcn_ask_email_dialog(QWidget* parent, const QString& description)
: QDialog(parent)
{
setWindowTitle(tr("RPCN: Email"));
setObjectName("rpcn_ask_email_dialog");
QVBoxLayout* vbox_global = new QVBoxLayout();
QLabel* lbl_emailinfo = new QLabel(description);
QGroupBox* gbox_password = new QGroupBox();
QVBoxLayout* vbox_gbox = new QVBoxLayout();
QLabel* lbl_pass1 = new QLabel(tr("Enter your email:"));
QLineEdit* m_edit_pass1 = new QLineEdit();
QLabel* lbl_pass2 = new QLabel(tr("Enter your email a second time:"));
QLineEdit* m_edit_pass2 = new QLineEdit();
vbox_gbox->addWidget(lbl_pass1);
vbox_gbox->addWidget(m_edit_pass1);
vbox_gbox->addWidget(lbl_pass2);
vbox_gbox->addWidget(m_edit_pass2);
gbox_password->setLayout(vbox_gbox);
QDialogButtonBox* btn_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
vbox_global->addWidget(lbl_emailinfo);
vbox_global->addWidget(gbox_password);
vbox_global->addWidget(btn_box);
setLayout(vbox_global);
connect(btn_box, &QDialogButtonBox::accepted, this, [this, m_edit_pass1, m_edit_pass2]()
{
if (m_edit_pass1->text() != m_edit_pass2->text())
{
QMessageBox::critical(this, tr("Wrong Input"), tr("The two emails you entered don't match!"), QMessageBox::Ok);
return;
}
if (m_edit_pass1->text().isEmpty())
{
QMessageBox::critical(this, tr("Missing Email"), tr("You need to enter an email!"), QMessageBox::Ok);
return;
}
auto email = m_edit_pass1->text().toStdString();
if (!validate_email(email))
{
QMessageBox::critical(this, tr("Invalid Email"), tr("You need to enter a valid email!"), QMessageBox::Ok);
return;
}
m_email = email;
QDialog::accept();
});
connect(btn_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
const std::optional<std::string>& rpcn_ask_email_dialog::get_email() const
{
return m_email;
}
rpcn_ask_token_dialog::rpcn_ask_token_dialog(QWidget* parent, const QString& description)
: QDialog(parent)
{
setWindowTitle(tr("RPCN: Username"));
setObjectName("rpcn_ask_token_dialog");
QVBoxLayout* vbox_global = new QVBoxLayout();
QLabel* lbl_token = new QLabel(description);
QGroupBox* grp_token = new QGroupBox(tr("Token:"));
QHBoxLayout* hbox_grp_token = new QHBoxLayout();
QLineEdit* edt_token = new QLineEdit();
edt_token->setMaxLength(16);
hbox_grp_token->addWidget(edt_token);
grp_token->setLayout(hbox_grp_token);
QDialogButtonBox* btn_box = new QDialogButtonBox(QDialogButtonBox::Ok);
vbox_global->addWidget(lbl_token);
vbox_global->addWidget(grp_token);
vbox_global->addWidget(btn_box);
setLayout(vbox_global);
connect(btn_box, &QDialogButtonBox::accepted, this, [this, edt_token]()
{
const auto token = edt_token->text().toStdString();
if (!token.empty())
{
if (!validate_token(token))
{
QMessageBox::critical(this, tr("Invalid Token"), tr("The token appears to be invalid:\n"
"-Token should be 16 characters long\n"
"-Token should only contain 0-9 and A-F"),
QMessageBox::Ok);
return;
}
m_token = token;
}
QDialog::accept();
return;
});
}
const std::optional<std::string>& rpcn_ask_token_dialog::get_token() const
{
return m_token;
}
rpcn_account_edit_dialog::rpcn_account_edit_dialog(QWidget* parent)
: QDialog(parent)
{
setWindowTitle(tr("RPCN: Edit Account"));
setObjectName("rpcn_account_edit_dialog");
setMinimumSize(QSize(400, 200));
QVBoxLayout* vbox_global = new QVBoxLayout();
@ -76,37 +621,30 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent)
QVBoxLayout* vbox_edits = new QVBoxLayout();
QHBoxLayout* hbox_buttons = new QHBoxLayout();
QLabel* label_host = new QLabel(tr("Host:"));
m_edit_host = new QLineEdit();
QLabel* label_npid = new QLabel(tr("NPID (username):"));
m_edit_npid = new QLineEdit();
m_edit_npid->setMaxLength(16);
m_edit_npid->setValidator(new QRegularExpressionValidator(QRegularExpression("^[a-zA-Z0-9_\\-]*$"), this));
QLabel* label_pass = new QLabel(tr("Password:"));
QLabel* lbl_username = new QLabel(tr("Username:"));
m_edit_username = new QLineEdit();
m_edit_username->setMaxLength(16);
m_edit_username->setValidator(new QRegularExpressionValidator(QRegularExpression("^[a-zA-Z0-9_\\-]*$"), this));
QLabel* lbl_pass = new QLabel(tr("Password:"));
QPushButton* btn_chg_pass = new QPushButton(tr("Set Password"));
QLabel* label_token = new QLabel(tr("Token:"));
QLabel* lbl_token = new QLabel(tr("Token:"));
m_edit_token = new QLineEdit();
m_edit_token->setMaxLength(16);
QPushButton* btn_create = new QPushButton(tr("Create Account"), this);
QPushButton* btn_resendtoken = new QPushButton(tr("Resend Token"), this);
QPushButton* btn_changepass = new QPushButton(tr("Change Password"), this);
btn_changepass->setEnabled(false);
QPushButton* btn_save = new QPushButton(tr("Save"), this);
QPushButton* btn_resendtoken = new QPushButton(tr("Resend Token"), this);
QPushButton* btn_change_password = new QPushButton(tr("Change Password"), this);
QPushButton* btn_save = new QPushButton(tr("Save"), this);
vbox_labels->addWidget(label_host);
vbox_labels->addWidget(label_npid);
vbox_labels->addWidget(label_pass);
vbox_labels->addWidget(label_token);
vbox_labels->addWidget(lbl_username);
vbox_labels->addWidget(lbl_pass);
vbox_labels->addWidget(lbl_token);
vbox_edits->addWidget(m_edit_host);
vbox_edits->addWidget(m_edit_npid);
vbox_edits->addWidget(m_edit_username);
vbox_edits->addWidget(btn_chg_pass);
vbox_edits->addWidget(m_edit_token);
hbox_buttons->addWidget(btn_create);
hbox_buttons->addWidget(btn_resendtoken);
hbox_buttons->addWidget(btn_changepass);
hbox_buttons->addWidget(btn_change_password);
hbox_buttons->addStretch();
hbox_buttons->addWidget(btn_save);
@ -120,29 +658,14 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent)
connect(btn_chg_pass, &QAbstractButton::clicked, this, [this]()
{
rpcn_ask_password_dialog ask_pass;
rpcn_ask_password_dialog ask_pass(this, tr("Please enter your password:"));
ask_pass.exec();
auto password = ask_pass.get_password();
if (!password)
return;
const std::string pass_str = password.value();
const std::string salt_str = "No matter where you go, everybody's connected.";
u8 salted_pass[SHA_DIGEST_SIZE];
wolfSSL_PKCS5_PBKDF2_HMAC_SHA1(pass_str.c_str(), pass_str.size(), reinterpret_cast<const u8*>(salt_str.c_str()), salt_str.size(), 1000, SHA_DIGEST_SIZE, salted_pass);
std::string hash("0000000000000000000000000000000000000000");
for (u32 i = 0; i < 20; i++)
{
constexpr auto pal = "0123456789abcdef";
hash[i * 2] = pal[salted_pass[i] >> 4];
hash[1 + i * 2] = pal[salted_pass[i] & 15];
}
g_cfg_rpcn.set_password(hash);
g_cfg_rpcn.set_password(*password);
g_cfg_rpcn.save();
QMessageBox::information(this, tr("RPCN Password Saved"), tr("Your password was saved successfully!"), QMessageBox::Ok);
@ -153,48 +676,39 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent)
if (save_config())
close();
});
connect(btn_create, &QAbstractButton::clicked, this, &rpcn_account_dialog::create_account);
connect(btn_resendtoken, &QAbstractButton::clicked, this, &rpcn_account_dialog::resend_token);
connect(btn_resendtoken, &QAbstractButton::clicked, this, &rpcn_account_edit_dialog::resend_token);
connect(btn_change_password, &QAbstractButton::clicked, this, &rpcn_account_edit_dialog::change_password);
g_cfg_rpcn.load();
m_edit_host->setText(QString::fromStdString(g_cfg_rpcn.get_host()));
m_edit_npid->setText(QString::fromStdString(g_cfg_rpcn.get_npid()));
m_edit_username->setText(QString::fromStdString(g_cfg_rpcn.get_npid()));
m_edit_token->setText(QString::fromStdString(g_cfg_rpcn.get_token()));
}
bool rpcn_account_dialog::save_config()
bool rpcn_account_edit_dialog::save_config()
{
const auto host = m_edit_host->text().toStdString();
const auto npid = m_edit_npid->text().toStdString();
const auto token = m_edit_token->text().toStdString();
const auto username = m_edit_username->text().toStdString();
const auto token = m_edit_token->text().toStdString();
if (host.empty())
if (username.empty() || g_cfg_rpcn.get_password().empty())
{
QMessageBox::critical(this, tr("Missing host"), tr("You need to enter a host for rpcn!"), QMessageBox::Ok);
QMessageBox::critical(this, tr("Missing Input"), tr("You need to enter a username and a password!"), QMessageBox::Ok);
return false;
}
if (npid.empty() || g_cfg_rpcn.get_password().empty())
if (!validate_rpcn_username(username))
{
QMessageBox::critical(this, tr("Wrong input"), tr("You need to enter a username and a password!"), QMessageBox::Ok);
QMessageBox::critical(this, tr("Invalid Username"), tr("Username must be between 3 and 16 characters and can only contain '-', '_' or alphanumeric characters."), QMessageBox::Ok);
return false;
}
if (!validate_rpcn_username(npid))
if (!token.empty() && !validate_token(token))
{
QMessageBox::critical(this, tr("Invalid character"), tr("NPID must be between 3 and 16 characters and can only contain '-', '_' or alphanumeric characters."), QMessageBox::Ok);
QMessageBox::critical(this, tr("Invalid Token"), tr("The token you have received should be 16 characters long and contain only 0-9 A-F."), QMessageBox::Ok);
return false;
}
if (!token.empty() && token.size() != 16)
{
QMessageBox::critical(this, tr("Invalid token"), tr("The token you have received should be 16 characters long."), QMessageBox::Ok);
return false;
}
g_cfg_rpcn.set_host(host);
g_cfg_rpcn.set_npid(npid);
g_cfg_rpcn.set_npid(username);
g_cfg_rpcn.set_token(token);
g_cfg_rpcn.save();
@ -202,66 +716,7 @@ bool rpcn_account_dialog::save_config()
return true;
}
void rpcn_account_dialog::create_account()
{
// Validate and save
if (!save_config())
return;
QString email;
const QRegularExpressionValidator simple_email_validator(QRegularExpression("^[a-zA-Z0-9.!#$%&*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$"));
while (true)
{
bool clicked_ok = false;
email = QInputDialog::getText(this, tr("Email address"), tr("An email address is required, please note:\n*A valid email is needed to validate your account.\n*Your email won't be used for anything beyond sending you the token.\n*Upon successful creation a token will be sent to your email which you'll need to login.\n\n"), QLineEdit::Normal, "", &clicked_ok);
if (!clicked_ok)
return;
int pos = 0;
if (email.isEmpty() || simple_email_validator.validate(email, pos) != QValidator::Acceptable)
{
QMessageBox::critical(this, tr("Wrong input"), tr("You need to enter a valid email!"), QMessageBox::Ok);
}
else
{
break;
}
}
const auto rpcn = rpcn::rpcn_client::get_instance();
const auto npid = g_cfg_rpcn.get_npid();
const auto online_name = npid;
const auto avatar_url = "https://rpcs3.net/cdn/netplay/DefaultAvatar.png";
const auto password = g_cfg_rpcn.get_password();
if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure)
{
const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result)));
QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok);
return;
}
if (auto error = rpcn->create_user(npid, password, online_name, avatar_url, email.toStdString()); error != rpcn::ErrorType::NoError)
{
QString error_message;
switch (error)
{
case rpcn::ErrorType::CreationExistingUsername: error_message = tr("An account with that username already exists!"); break;
case rpcn::ErrorType::CreationBannedEmailProvider: error_message = tr("This email provider is banned!"); break;
case rpcn::ErrorType::CreationExistingEmail: error_message = tr("An account with that email already exists!"); break;
case rpcn::ErrorType::CreationError: error_message = tr("Unknown creation error"); break;
default: error_message = tr("Unknown error"); break;
}
QMessageBox::critical(this, tr("Error Creating Account"), tr("Failed to create the account:\n%0").arg(error_message), QMessageBox::Ok);
return;
}
QMessageBox::information(this, tr("Account created!"), tr("Your account has been created successfully!\nCheck your email for your token!"), QMessageBox::Ok);
}
void rpcn_account_dialog::resend_token()
void rpcn_account_edit_dialog::resend_token()
{
if (!save_config())
return;
@ -284,11 +739,10 @@ void rpcn_account_dialog::resend_token()
switch (error)
{
case rpcn::ErrorType::Invalid: error_message = tr("The server has no email verification and doesn't need a token!"); break;
case rpcn::ErrorType::LoginAlreadyLoggedIn: error_message = tr("You can't ask for your token while authentified!"); break;
case rpcn::ErrorType::DbFail: error_message = tr("A database related error happened on the server!"); break;
case rpcn::ErrorType::TooSoon: error_message = tr("You can only ask for a token mail once every 24 hours!"); break;
case rpcn::ErrorType::EmailFail: error_message = tr("The mail couldn't be sent successfully!"); break;
case rpcn::ErrorType::LoginError: error_message = tr("The login/password pair is invalid!"); break;
case rpcn::ErrorType::LoginError: error_message = tr("The username/password pair is invalid!"); break;
default: error_message = tr("Unknown error"); break;
}
QMessageBox::critical(this, tr("Error Sending Token"), tr("Failed to send the token:\n%0").arg(error_message), QMessageBox::Ok);
@ -298,64 +752,102 @@ void rpcn_account_dialog::resend_token()
QMessageBox::information(this, tr("Token Sent!"), tr("Your token was successfully resent to the email associated with your account!"), QMessageBox::Ok);
}
rpcn_ask_password_dialog::rpcn_ask_password_dialog(QWidget* parent)
: QDialog(parent)
void rpcn_account_edit_dialog::change_password()
{
QVBoxLayout* vbox_global = new QVBoxLayout();
QHBoxLayout* hbox_buttons = new QHBoxLayout();
rpcn_ask_username_dialog dlg_username(this, tr("Please confirm your username:"));
dlg_username.exec();
auto username = dlg_username.get_username();
QGroupBox* gbox_password = new QGroupBox();
QVBoxLayout* vbox_gbox = new QVBoxLayout();
if (!username)
return;
QLabel* label_pass1 = new QLabel(tr("Enter your password:"));
m_edit_pass1 = new QLineEdit();
m_edit_pass1->setEchoMode(QLineEdit::Password);
QLabel* label_pass2 = new QLabel(tr("Enter your password a second time:"));
m_edit_pass2 = new QLineEdit();
m_edit_pass2->setEchoMode(QLineEdit::Password);
switch (QMessageBox::question(this, tr("RPCN: Change Password"), tr("Do you already have a reset password token?\n"
"Note that the reset password token is different from the email verification token.")))
{
case QMessageBox::No:
{
rpcn_ask_email_dialog dlg_email(this, tr("Please enter the email you used to create the account:"));
dlg_email.exec();
const auto email = dlg_email.get_email();
vbox_gbox->addWidget(label_pass1);
vbox_gbox->addWidget(m_edit_pass1);
vbox_gbox->addWidget(label_pass2);
vbox_gbox->addWidget(m_edit_pass2);
gbox_password->setLayout(vbox_gbox);
if (!email)
return;
QPushButton* btn_ok = new QPushButton(tr("Ok"));
QPushButton* btn_cancel = new QPushButton(tr("Cancel"));
hbox_buttons->addStretch();
hbox_buttons->addWidget(btn_ok);
hbox_buttons->addWidget(btn_cancel);
vbox_global->addWidget(gbox_password);
vbox_global->addLayout(hbox_buttons);
setLayout(vbox_global);
connect(btn_ok, &QAbstractButton::clicked, this, [this]()
{
if (m_edit_pass1->text() != m_edit_pass2->text())
const auto rpcn = rpcn::rpcn_client::get_instance();
if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure)
{
QMessageBox::critical(this, tr("Wrong input"), tr("The two passwords you entered don't match!"), QMessageBox::Ok);
const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result)));
QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok);
return;
}
if (m_edit_pass1->text().isEmpty())
if (auto error = rpcn->send_reset_token(*username, *email); error != rpcn::ErrorType::NoError)
{
QMessageBox::critical(this, tr("Wrong input"), tr("You need to enter a password!"), QMessageBox::Ok);
QString error_message;
switch (error)
{
case rpcn::ErrorType::Invalid: error_message = tr("The server has no email verification and doesn't support password changes!"); break;
case rpcn::ErrorType::DbFail: error_message = tr("A database related error happened on the server!"); break;
case rpcn::ErrorType::TooSoon: error_message = tr("You can only ask for a reset password token once every 24 hours!"); break;
case rpcn::ErrorType::EmailFail: error_message = tr("The mail couldn't be sent successfully!"); break;
case rpcn::ErrorType::LoginError: error_message = tr("The username/email pair is invalid!"); break;
default: error_message = tr("Unknown error"); break;
}
QMessageBox::critical(this, tr("Error Sending Password Reset Token"), tr("Failed to send the password reset token:\n%0").arg(error_message), QMessageBox::Ok);
return;
}
m_password = m_edit_pass1->text().toStdString();
close();
});
connect(btn_cancel, &QAbstractButton::clicked, this, [this]()
{ this->close(); });
}
QMessageBox::information(this, tr("Password Reset Token Sent!"), tr("The reset password token has successfully been sent!"), QMessageBox::Ok);
}
}
case QMessageBox::Yes:
{
rpcn_ask_token_dialog dlg_token(this, tr("Please enter the password reset token you received:"));
dlg_token.exec();
const auto token = dlg_token.get_token();
std::optional<std::string> rpcn_ask_password_dialog::get_password()
{
return m_password;
if (!token)
return;
rpcn_ask_password_dialog dlg_password(this, tr("Please enter your new password:"));
dlg_password.exec();
const auto password = dlg_password.get_password();
if (!password)
return;
{
const auto rpcn = rpcn::rpcn_client::get_instance();
if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure)
{
const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result)));
QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok);
return;
}
if (auto error = rpcn->reset_password(*username, *token, *password); error != rpcn::ErrorType::NoError)
{
QString error_message;
switch (error)
{
case rpcn::ErrorType::Invalid: error_message = tr("The server has no email verification and doesn't support password changes!"); break;
case rpcn::ErrorType::DbFail: error_message = tr("A database related error happened on the server!"); break;
case rpcn::ErrorType::TooSoon: error_message = tr("You can only ask for a reset password token once every 24 hours!"); break;
case rpcn::ErrorType::EmailFail: error_message = tr("The mail couldn't be sent successfully!"); break;
case rpcn::ErrorType::LoginError: error_message = tr("The username/token pair is invalid!"); break;
default: error_message = tr("Unknown error"); break;
}
QMessageBox::critical(this, tr("Error Sending Password Reset Token"), tr("Failed to change the password:\n%0").arg(error_message), QMessageBox::Ok);
return;
}
QMessageBox::information(this, tr("Password Successfully Changed!"), tr("Your password has been successfully changed!"), QMessageBox::Ok);
}
}
default:
return;
}
}
void friend_callback(void* param, rpcn::NotificationType ntype, const std::string& username, bool status)
@ -636,7 +1128,7 @@ void rpcn_friends_dialog::callback_handler(rpcn::NotificationType ntype, std::st
}
default:
{
rpcn_settings_log.fatal("An unhandled notification type was received by the rpcn friends dialog callback!");
rpcn_settings_log.fatal("An unhandled notification type was received by the RPCN friends dialog callback!");
break;
}
}

View File

@ -22,28 +22,84 @@ public:
rpcn_account_dialog(QWidget* parent = nullptr);
private:
bool save_config();
void refresh_combobox();
private Q_SLOTS:
void create_account();
void resend_token();
private:
QComboBox* cbx_servers = nullptr;
};
protected:
QLineEdit *m_edit_host, *m_edit_npid, *m_edit_token;
class rpcn_add_server_dialog : public QDialog
{
Q_OBJECT
public:
rpcn_add_server_dialog(QWidget* parent = nullptr);
const std::optional<std::pair<std::string, std::string>>& get_new_server() const;
private:
std::optional<std::pair<std::string, std::string>> m_new_server;
};
class rpcn_ask_username_dialog : public QDialog
{
Q_OBJECT
public:
rpcn_ask_username_dialog(QWidget* parent, const QString& description);
const std::optional<std::string>& get_username() const;
private:
std::optional<std::string> m_username;
};
class rpcn_ask_password_dialog : public QDialog
{
Q_OBJECT
public:
rpcn_ask_password_dialog(QWidget* parent = nullptr);
std::optional<std::string> get_password();
rpcn_ask_password_dialog(QWidget* parent, const QString& description);
const std::optional<std::string>& get_password() const;
private:
QLineEdit *m_edit_pass1, *m_edit_pass2;
std::optional<std::string> m_password;
};
class rpcn_ask_email_dialog : public QDialog
{
Q_OBJECT
public:
rpcn_ask_email_dialog(QWidget* parent, const QString& description);
const std::optional<std::string>& get_email() const;
private:
std::optional<std::string> m_email;
};
class rpcn_ask_token_dialog : public QDialog
{
Q_OBJECT
public:
rpcn_ask_token_dialog(QWidget* parent, const QString& description);
const std::optional<std::string>& get_token() const;
private:
std::optional<std::string> m_token;
};
class rpcn_account_edit_dialog : public QDialog
{
Q_OBJECT
public:
rpcn_account_edit_dialog(QWidget* parent = nullptr);
private:
bool save_config();
private Q_SLOTS:
void resend_token();
void change_password();
protected:
QLineEdit *m_edit_username, *m_edit_token;
};
class rpcn_friends_dialog : public QDialog
{
Q_OBJECT