1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-25 12:12:50 +01:00

rpcn v0.4.0

This commit is contained in:
RipleyTom 2020-11-17 02:19:17 +01:00 committed by Megamouse
parent 15af1bca69
commit ea9dc9317d
34 changed files with 4907 additions and 2234 deletions

@ -1 +1 @@
Subproject commit 9e7e8cbe9f675123dd41b7c62868acad39188cae
Subproject commit a9a295fecf3fbd5a4f571f53b01f63202a3e2113

View File

@ -7,7 +7,7 @@ else()
# TODO(cjj19970505@live.cn)
# OPENSSL_EXTRA, WOLFSSL_DES_ECB 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)
add_compile_definitions(OPENSSL_EXTRA WOLFSSL_DES_ECB HAVE_SNI HAVE_WRITE_DUP)
set(WOLFSSL_TLS13 "no" CACHE INTERNAL "")
set(WOLFSSL_SHA224 "yes" CACHE INTERNAL "")

View File

@ -48,7 +48,7 @@
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>./wolfssl;./wolfssl/IDE/WIN;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WOLFSSL_LIB;WOLFSSL_DES_ECB;HAVE_FFDHE_2048;TFM_TIMING_RESISTANT;ECC_TIMING_RESISTANT;WC_RSA_BLINDING;HAVE_AESGCM;WOLFSSL_SHA512;WOLFSSL_SHA384;NO_DSA;HAVE_ECC;TFM_ECC256;ECC_SHAMIR;NO_RC4;NO_HC128;NO_RABBIT;WOLFSSL_SHA224;WOLFSSL_SHA3;WOLFSSL_SHAKE256;HAVE_POLY1305;HAVE_ONE_TIME_AUTH;HAVE_CHACHA;HAVE_HASHDRBG;HAVE_TLS_EXTENSIONS;HAVE_SNI;HAVE_SUPPORTED_CURVES;HAVE_EXTENDED_MASTER;NO_RC4;HAVE_ENCRYPT_THEN_MAC;NO_PSK;NO_MD4;WC_NO_ASYNC_THREADING;OPENSSL_EXTRA;WOLFSSL_USER_SETTINGS;CYASSL_USER_SETTINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WOLFSSL_LIB;WOLFSSL_DES_ECB;HAVE_FFDHE_2048;TFM_TIMING_RESISTANT;NO_DSA;TFM_ECC256;NO_RC4;NO_HC128;NO_RABBIT;WOLFSSL_SHA224;WOLFSSL_SHA3;WOLFSSL_SHAKE256;HAVE_POLY1305;HAVE_ONE_TIME_AUTH;HAVE_CHACHA;HAVE_HASHDRBG;HAVE_SNI;HAVE_ENCRYPT_THEN_MAC;NO_MD4;WC_NO_ASYNC_THREADING;CYASSL_USER_SETTINGS;WC_NO_HARDEN;HAVE_WRITE_DUP;WC_RSA_BLINDING;NO_MULTIBYTE_PRINT;OPENSSL_EXTRA;WOLFSSL_RIPEMD;NO_PSK;HAVE_EXTENDED_MASTER;WOLFSSL_SNIFFER;HAVE_AESGCM;WOLFSSL_SHA384;WOLFSSL_SHA512;HAVE_SUPPORTED_CURVES;HAVE_TLS_EXTENSIONS;HAVE_ECC;ECC_SHAMIR;ECC_TIMING_RESISTANT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<PrecompiledHeader>
</PrecompiledHeader>
@ -62,7 +62,7 @@
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>./wolfssl;./wolfssl/IDE/WIN;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WOLFSSL_LIB;WOLFSSL_DES_ECB;HAVE_FFDHE_2048;TFM_TIMING_RESISTANT;NO_DSA;TFM_ECC256;NO_RC4;NO_HC128;NO_RABBIT;WOLFSSL_SHA224;WOLFSSL_SHA3;WOLFSSL_SHAKE256;HAVE_POLY1305;HAVE_ONE_TIME_AUTH;HAVE_CHACHA;HAVE_HASHDRBG;HAVE_SNI;HAVE_ENCRYPT_THEN_MAC;NO_MD4;WC_NO_ASYNC_THREADING;WOLFSSL_USER_SETTINGS;CYASSL_USER_SETTINGS;WC_NO_HARDEN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WOLFSSL_LIB;WOLFSSL_DES_ECB;HAVE_FFDHE_2048;TFM_TIMING_RESISTANT;NO_DSA;TFM_ECC256;NO_RC4;NO_HC128;NO_RABBIT;WOLFSSL_SHA224;WOLFSSL_SHA3;WOLFSSL_SHAKE256;HAVE_POLY1305;HAVE_ONE_TIME_AUTH;HAVE_CHACHA;HAVE_HASHDRBG;HAVE_SNI;HAVE_ENCRYPT_THEN_MAC;NO_MD4;WC_NO_ASYNC_THREADING;CYASSL_USER_SETTINGS;WC_NO_HARDEN;HAVE_WRITE_DUP;WC_RSA_BLINDING;NO_MULTIBYTE_PRINT;OPENSSL_EXTRA;WOLFSSL_RIPEMD;NO_PSK;HAVE_EXTENDED_MASTER;WOLFSSL_SNIFFER;HAVE_AESGCM;WOLFSSL_SHA384;WOLFSSL_SHA512;HAVE_SUPPORTED_CURVES;HAVE_TLS_EXTENSIONS;HAVE_ECC;ECC_SHAMIR;ECC_TIMING_RESISTANT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>

View File

@ -110,6 +110,15 @@ error_code cellSysutilAvc2StartVoiceDetection()
error_code cellSysutilAvc2UnloadAsync()
{
cellSysutilAvc2.todo("cellSysutilAvc2UnloadAsync()");
if (avc2_cb)
{
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 {
avc2_cb(cb_ppu, CELL_AVC2_EVENT_UNLOAD_SUCCEEDED, 0, avc2_cb_arg);
return 0;
});
}
return CELL_OK;
}

View File

@ -3,6 +3,8 @@
#include "Emu/system_utils.hpp"
#include "Emu/VFS.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Io/interception.h"
#include "Utilities/StrUtil.h"
#include "sysPrxForUser.h"
#include "Emu/IdManager.h"
@ -396,6 +398,11 @@ void fmt_class_string<SceNpError>::format(std::string& out, u64 arg)
});
}
void message_data::print() const
{
sceNp.notice("commId: %s, msgId: %d, mainType: %d, subType: %d, subject: %s, body: %s, data_size: %d", static_cast<const char *>(commId.data), msgId, mainType, subType, subject, body, data.size());
}
error_code sceNpInit(u32 poolsize, vm::ptr<void> poolptr)
{
sceNp.warning("sceNpInit(poolsize=0x%x, poolptr=*0x%x)", poolsize, poolptr);
@ -629,7 +636,7 @@ error_code sceNpDrmProcessExitSpawn2(ppu_thread& ppu, vm::cptr<u8> klicensee, vm
error_code sceNpBasicRegisterHandler(vm::cptr<SceNpCommunicationId> context, vm::ptr<SceNpBasicEventHandler> handler, vm::ptr<void> arg)
{
sceNp.warning("sceNpBasicRegisterHandler(context=*0x%x, handler=*0x%x, arg=*0x%x)", context, handler, arg);
sceNp.warning("sceNpBasicRegisterHandler(context=*0x%x(%s), handler=*0x%x, arg=*0x%x)", context, context ? static_cast<const char *>(context->data) : "", handler, arg);
auto& nph = g_fxo->get<named_thread<np_handler>>();
@ -638,13 +645,21 @@ error_code sceNpBasicRegisterHandler(vm::cptr<SceNpCommunicationId> context, vm:
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
}
if (!context || !handler)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
nph.basic_handler = handler;
nph.basic_handler_arg = arg;
memcpy(&nph.basic_handler.context, context.get_ptr(), sizeof(nph.basic_handler.context));
nph.basic_handler.handler_func = handler;
nph.basic_handler.handler_arg = arg;
nph.basic_handler.registered = true;
nph.basic_handler.context_sensitive = false;
return CELL_OK;
}
@ -660,11 +675,22 @@ error_code sceNpBasicRegisterContextSensitiveHandler(vm::cptr<SceNpCommunication
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
}
if (!context || !handler)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
memcpy(&nph.basic_handler.context, context.get_ptr(), sizeof(nph.basic_handler.context));
nph.basic_handler.handler_func = handler;
nph.basic_handler.handler_arg = arg;
nph.basic_handler.registered = true;
nph.basic_handler.context_sensitive = true;
return CELL_OK;
}
@ -679,6 +705,13 @@ error_code sceNpBasicUnregisterHandler()
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
nph.basic_handler.registered = false;
return CELL_OK;
}
@ -775,7 +808,18 @@ error_code sceNpBasicSendMessage(vm::cptr<SceNpId> to, vm::cptr<void> data, u64
error_code sceNpBasicSendMessageGui(vm::cptr<SceNpBasicMessageDetails> msg, sys_memory_container_t containerId)
{
sceNp.todo("sceNpBasicSendMessageGui(msg=*0x%x, containerId=%d)", msg, containerId);
sceNp.warning("sceNpBasicSendMessageGui(msg=*0x%x, containerId=%d)", msg, containerId);
if (msg)
{
sceNp.notice("sceNpBasicSendMessageGui: msgId: %d, mainType: %d, subType: %d, msgFeatures: %d, count: %d", msg->msgId, msg->mainType, msg->subType, msg->msgFeatures, msg->count);
for (u32 i = 0; i < msg->count; i++)
{
sceNp.trace("sceNpBasicSendMessageGui: NpId[%d] = %s", i, static_cast<char*>(&msg->npids[i].handle.data[0]));
}
sceNp.notice("sceNpBasicSendMessageGui: subject: %s", msg->subject);
sceNp.notice("sceNpBasicSendMessageGui: body: %s", msg->body);
}
auto& nph = g_fxo->get<named_thread<np_handler>>();
@ -784,7 +828,13 @@ error_code sceNpBasicSendMessageGui(vm::cptr<SceNpBasicMessageDetails> msg, sys_
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!msg || msg->count > SCE_NP_BASIC_SEND_MESSAGE_MAX_RECIPIENTS || msg->npids.handle.data[0] == '\0' || !(msg->msgFeatures & SCE_NP_BASIC_MESSAGE_FEATURES_ALL_FEATURES))
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!msg || msg->count > SCE_NP_BASIC_SEND_MESSAGE_MAX_RECIPIENTS || (msg->msgFeatures & ~SCE_NP_BASIC_MESSAGE_FEATURES_ALL_FEATURES) ||
msg->mainType > SCE_NP_BASIC_MESSAGE_MAIN_TYPE_URL_ATTACHMENT || msg->msgId != 0ull)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
@ -799,6 +849,82 @@ error_code sceNpBasicSendMessageGui(vm::cptr<SceNpBasicMessageDetails> msg, sys_
return not_an_error(SCE_NP_BASIC_ERROR_NOT_CONNECTED);
}
// Prepare message data
message_data msg_data = {
.commId = nph.basic_handler.context,
.msgId = msg->msgId,
.mainType = msg->mainType,
.subType = msg->subType,
.msgFeatures = msg->msgFeatures};
std::set<std::string> npids;
for (u32 i = 0; i < msg->count; i++)
{
npids.insert(std::string(msg->npids[i].handle.data));
}
if (msg->subject)
{
msg_data.subject = std::string(msg->subject.get_ptr());
}
if (msg->body)
{
msg_data.body = std::string(msg->body.get_ptr());
}
if (msg->size)
{
msg_data.data.assign(msg->data.get_ptr(), msg->data.get_ptr() + msg->size);
}
atomic_t<bool> wake_up = false;
bool result = false;
input::SetIntercepted(true);
Emu.CallAfter([=, &wake_up, &result, msg_data = std::move(msg_data), npids = std::move(npids)]() mutable
{
auto send_dlg = Emu.GetCallbacks().get_sendmessage_dialog();
result = send_dlg->Exec(msg_data, npids);
wake_up = true;
wake_up.notify_one();
});
while (!wake_up && !Emu.IsStopped())
{
thread_ctrl::wait_on(wake_up, false);
}
input::SetIntercepted(false);
s32 callback_result = result ? 0 : -1;
s32 event = 0;
switch (msg->mainType)
{
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_DATA_ATTACHMENT:
event = SCE_NP_BASIC_EVENT_SEND_ATTACHMENT_RESULT;
break;
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_GENERAL:
event = SCE_NP_BASIC_EVENT_SEND_MESSAGE_RESULT;
break;
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_ADD_FRIEND:
event = SCE_NP_BASIC_EVENT_ADD_FRIEND_RESULT;
break;
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE:
event = SCE_NP_BASIC_EVENT_SEND_INVITATION_RESULT;
break;
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_CUSTOM_DATA:
event = SCE_NP_BASIC_EVENT_SEND_CUSTOM_DATA_RESULT;
break;
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_URL_ATTACHMENT:
event = SCE_NP_BASIC_EVENT_SEND_URL_ATTACHMENT_RESULT;
break;
}
nph.send_basic_event(event, callback_result, 0);
return CELL_OK;
}
@ -845,9 +971,9 @@ error_code sceNpBasicRecvMessageAttachment(sys_memory_container_t containerId)
return CELL_OK;
}
error_code sceNpBasicRecvMessageAttachmentLoad(u32 id, vm::ptr<void> buffer, vm::ptr<u64> size)
error_code sceNpBasicRecvMessageAttachmentLoad(u32 id, vm::ptr<void> buffer, vm::ptr<u32> size)
{
sceNp.todo("sceNpBasicRecvMessageAttachmentLoad(id=%d, buffer=*0x%x, size=*0x%x)", id, buffer, size);
sceNp.warning("sceNpBasicRecvMessageAttachmentLoad(id=%d, buffer=*0x%x, size=*0x%x)", id, buffer, size);
auto& nph = g_fxo->get<named_thread<np_handler>>();
@ -856,22 +982,41 @@ error_code sceNpBasicRecvMessageAttachmentLoad(u32 id, vm::ptr<void> buffer, vm:
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!buffer || !size)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
if (id > SCE_NP_BASIC_SELECTED_MESSAGE_DATA)
const auto opt_msg = nph.get_message(id);
if (!opt_msg)
{
return SCE_NP_BASIC_ERROR_INVALID_DATA_ID;
}
const auto msg_pair = opt_msg.value();
const auto msg = msg_pair->second;
const u32 orig_size = *size;
const u32 size_to_copy = std::min(static_cast<u32>(msg.data.size()), orig_size);
memcpy(buffer.get_ptr(), msg.data.data(), size_to_copy);
*size = size_to_copy;
if (size_to_copy < msg.data.size())
{
return SCE_NP_BASIC_ERROR_DATA_LOST;
}
return CELL_OK;
}
error_code sceNpBasicRecvMessageCustom(u16 mainType, u32 recvOptions, sys_memory_container_t containerId)
{
sceNp.todo("sceNpBasicRecvMessageCustom(mainType=%d, recvOptions=%d, containerId=%d)", mainType, recvOptions, containerId);
sceNp.warning("sceNpBasicRecvMessageCustom(mainType=%d, recvOptions=%d, containerId=%d)", mainType, recvOptions, containerId);
auto& nph = g_fxo->get<named_thread<np_handler>>();
@ -880,11 +1025,66 @@ error_code sceNpBasicRecvMessageCustom(u16 mainType, u32 recvOptions, sys_memory
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!(recvOptions & SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_ALL_OPTIONS))
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if ((recvOptions & ~SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_ALL_OPTIONS))
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
// TODO: SCE_NP_BASIC_ERROR_NOT_SUPPORTED
atomic_t<bool> wake_up = false;
bool result = false;
input::SetIntercepted(true);
SceNpBasicMessageRecvAction recv_result;
u64 chosen_msg_id;
Emu.CallAfter([=, &wake_up, &result, &recv_result, &chosen_msg_id]()
{
auto recv_dlg = Emu.GetCallbacks().get_recvmessage_dialog();
result = recv_dlg->Exec(static_cast<SceNpBasicMessageMainType>(mainType), static_cast<SceNpBasicMessageRecvOptions>(recvOptions), recv_result, chosen_msg_id);
wake_up = true;
wake_up.notify_one();
});
while (!wake_up && !Emu.IsStopped())
{
thread_ctrl::wait_on(wake_up, false);
}
input::SetIntercepted(false);
if (!result)
{
return SCE_NP_BASIC_ERROR_CANCEL;
}
const auto msg_pair = nph.get_message(chosen_msg_id).value();
const auto& msg = msg_pair->second;
const u32 event_to_send = (mainType == SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE) ? SCE_NP_BASIC_EVENT_RECV_INVITATION_RESULT : SCE_NP_BASIC_EVENT_RECV_CUSTOM_DATA_RESULT;
np_handler::basic_event to_add{};
to_add.event = event_to_send;
strcpy_trunc(to_add.from.userId.handle.data, msg_pair->first);
strcpy_trunc(to_add.from.name.data, msg_pair->first);
to_add.data.resize(sizeof(SceNpBasicExtendedAttachmentData));
SceNpBasicExtendedAttachmentData* att_data = reinterpret_cast<SceNpBasicExtendedAttachmentData*>(to_add.data.data());
att_data->flags = 0; // ?
att_data->msgId = chosen_msg_id;
att_data->data.id = static_cast<u32>(chosen_msg_id);
att_data->data.size = static_cast<u32>(msg.data.size());
att_data->userAction = recv_result;
att_data->markedAsUsed = (recvOptions & SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_PRESERVE) ? 0 : 1;
nph.queue_basic_event(to_add);
nph.send_basic_event(event_to_send, 0, 0);
return CELL_OK;
}
@ -972,8 +1172,7 @@ error_code sceNpBasicGetFriendListEntryCount(vm::ptr<u32> count)
return SCE_NP_ERROR_ID_NOT_FOUND;
}
// TODO: Check if there are any friends
*count = 0;
*count = nph.get_num_friends();
return CELL_OK;
}
@ -1258,7 +1457,7 @@ error_code sceNpBasicGetBlockListEntryCount(vm::ptr<u32> count)
}
// TODO: Check if there are block lists
*count = 0;
*count = nph.get_num_blocks();
return CELL_OK;
}
@ -1560,15 +1759,19 @@ error_code sceNpBasicGetEvent(vm::ptr<s32> event, vm::ptr<SceNpUserInfo> from, v
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!event || !from || !data || !size)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
// TODO: Check for other error and pass other events
//*event = SCE_NP_BASIC_EVENT_OFFLINE; // This event only indicates a contact is offline, not the current status of the connection
return not_an_error(SCE_NP_BASIC_ERROR_NO_EVENT);
return nph.get_basic_event(event, from, data, size);
}
error_code sceNpCommerceCreateCtx(u32 version, vm::ptr<SceNpId> npId, vm::ptr<SceNpCommerceHandler> handler, vm::ptr<void> arg, vm::ptr<u32> ctx_id)
@ -2913,10 +3116,10 @@ error_code sceNpManagerGetTicket(vm::ptr<void> buffer, vm::ptr<u32> bufferSize)
}
const auto& ticket = nph.get_ticket();
*bufferSize = static_cast<u32>(ticket.size());
if (!buffer)
{
*bufferSize = static_cast<u32>(ticket.size());
return CELL_OK;
}

View File

@ -3,6 +3,8 @@
#include "cellRtc.h"
#include "Emu/Cell/ErrorCodes.h"
#include <set>
error_code sceNpInit(u32 poolsize, vm::ptr<void> poolptr);
error_code sceNpTerm();
@ -1022,11 +1024,11 @@ struct SceNpBasicMessageDetails
be_t<u16> mainType;
be_t<u16> subType;
be_t<u32> msgFeatures;
const SceNpId npids;
vm::bptr<SceNpId> npids;
be_t<u32> count;
const s8 subject;
const s8 body;
const be_t<u32> data;
vm::bptr<char> subject;
vm::bptr<char> body;
vm::bptr<u8> data;
be_t<u32> size;
};
@ -1359,3 +1361,33 @@ using SceNpMatchingGUIHandler = void(u32 ctx_id, s32 event, s32 error_code, vm::
using SceNpProfileResultHandler = s32(s32 result, vm::ptr<void> arg);
using SceNpManagerSubSigninCallback = void(s32 result, vm::ptr<SceNpId> npId, vm::ptr<void> cb_arg);
// Used to pass data to UI/RPCN
struct message_data
{
SceNpCommunicationId commId{};
u64 msgId = 0;
u16 mainType = 0;
u16 subType = 0;
u32 msgFeatures = 0;
std::string subject;
std::string body;
std::vector<u8> data;
void print() const;
};
class SendMessageDialogBase
{
public:
virtual ~SendMessageDialogBase() = default;
virtual bool Exec(message_data& msg_data, std::set<std::string>& npids) = 0;
};
class RecvMessageDialogBase
{
public:
virtual ~RecvMessageDialogBase() = default;
virtual bool Exec(SceNpBasicMessageMainType type, SceNpBasicMessageRecvOptions options, SceNpBasicMessageRecvAction& recv_result, u64& chosen_msg_id) = 0;
};

View File

@ -613,17 +613,23 @@ error_code sceNpMatching2SignalingGetConnectionInfo(
switch (code)
{
case 1:
case SCE_NP_SIGNALING_CONN_INFO_RTT:
{
connInfo->rtt = 20000; // HACK
break;
}
case 2:
case SCE_NP_SIGNALING_CONN_INFO_BANDWIDTH:
{
connInfo->bandwidth = 10'000'000; // 10 MBPS HACK
break;
}
case 5:
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");
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);
@ -631,7 +637,15 @@ error_code sceNpMatching2SignalingGetConnectionInfo(
connInfo->address.addr.np_s_addr = si.addr;
break;
}
case 6:
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;
}
case SCE_NP_SIGNALING_CONN_INFO_PACKET_LOSS:
{
connInfo->packet_loss = 1; // HACK
break;
@ -1274,6 +1288,8 @@ error_code sceNpMatching2GetRoomDataExternalList(
return res;
}
*assignedReqId = nph.get_roomdata_external_list(ctxId, optParam, reqParam.get_ptr());
return CELL_OK;
}

View File

@ -978,7 +978,7 @@ struct network_thread
WSADATA wsa_data;
WSAStartup(MAKEWORD(2, 2), &wsa_data);
#endif
if (g_cfg.net.psn_status == np_psn_status::rpcn)
if (g_cfg.net.psn_status == np_psn_status::psn_rpcn)
list_p2p_ports.emplace(std::piecewise_construct, std::forward_as_tuple(3658), std::forward_as_tuple(3658));
}

View File

@ -55,7 +55,68 @@ void np_handler::UserInfo2_to_SceNpUserInfo2(const UserInfo2* user, SceNpUserInf
}
}
void np_handler::SearchRoomReponse_to_SceNpMatching2SearchRoomResponse(const SearchRoomResponse* resp, SceNpMatching2SearchRoomResponse* search_resp)
void np_handler::RoomDataExternal_to_SceNpMatching2RoomDataExternal(const RoomDataExternal* room, SceNpMatching2RoomDataExternal* room_info)
{
room_info->serverId = room->serverId();
room_info->worldId = room->worldId();
room_info->publicSlotNum = room->publicSlotNum();
room_info->privateSlotNum = room->privateSlotNum();
room_info->lobbyId = room->lobbyId();
room_info->roomId = room->roomId();
room_info->openPublicSlotNum = room->openPublicSlotNum();
room_info->maxSlot = room->maxSlot();
room_info->openPrivateSlotNum = room->openPrivateSlotNum();
room_info->curMemberNum = room->curMemberNum();
room_info->passwordSlotMask = room->passwordSlotMask();
if (auto owner = room->owner())
{
vm::ptr<SceNpUserInfo2> owner_info(allocate(sizeof(SceNpUserInfo2)));
UserInfo2_to_SceNpUserInfo2(owner, owner_info.get_ptr());
room_info->owner = owner_info;
}
if (room->roomGroup() && room->roomGroup()->size() != 0)
{
room_info->roomGroupNum = room->roomGroup()->size();
vm::ptr<SceNpMatching2RoomGroup> group_info(allocate(sizeof(SceNpMatching2RoomGroup) * room_info->roomGroupNum));
RoomGroup_to_SceNpMatching2RoomGroup(room->roomGroup(), group_info);
room_info->roomGroup = group_info;
}
room_info->flagAttr = room->flagAttr();
if (room->roomSearchableIntAttrExternal() && room->roomSearchableIntAttrExternal()->size() != 0)
{
room_info->roomSearchableIntAttrExternalNum = room->roomSearchableIntAttrExternal()->size();
vm::ptr<SceNpMatching2IntAttr> intattr_info(allocate(sizeof(SceNpMatching2IntAttr) * room_info->roomSearchableIntAttrExternalNum));
for (flatbuffers::uoffset_t a_index = 0; a_index < room->roomSearchableIntAttrExternal()->size(); a_index++)
{
auto int_attr = room->roomSearchableIntAttrExternal()->Get(a_index);
intattr_info[a_index].id = int_attr->id();
intattr_info[a_index].num = int_attr->num();
}
room_info->roomSearchableIntAttrExternal = intattr_info;
}
if (room->roomSearchableBinAttrExternal() && room->roomSearchableBinAttrExternal()->size() != 0)
{
room_info->roomSearchableBinAttrExternalNum = room->roomSearchableBinAttrExternal()->size();
vm::ptr<SceNpMatching2BinAttr> binattr_info(allocate(sizeof(SceNpMatching2BinAttr) * room_info->roomSearchableBinAttrExternalNum));
BinAttr_to_SceNpMatching2BinAttr(room->roomSearchableBinAttrExternal(), binattr_info);
room_info->roomSearchableBinAttrExternal = binattr_info;
}
if (room->roomBinAttrExternal() && room->roomBinAttrExternal()->size() != 0)
{
room_info->roomBinAttrExternalNum = room->roomBinAttrExternal()->size();
vm::ptr<SceNpMatching2BinAttr> binattr_info(allocate(sizeof(SceNpMatching2BinAttr) * room_info->roomBinAttrExternalNum));
BinAttr_to_SceNpMatching2BinAttr(room->roomBinAttrExternal(), binattr_info);
room_info->roomBinAttrExternal = binattr_info;
}
}
void np_handler::SearchRoomResponse_to_SceNpMatching2SearchRoomResponse(const SearchRoomResponse* resp, SceNpMatching2SearchRoomResponse* search_resp)
{
search_resp->range.size = resp->size();
search_resp->range.startIndex = resp->startIndex();
@ -66,7 +127,7 @@ void np_handler::SearchRoomReponse_to_SceNpMatching2SearchRoomResponse(const Sea
vm::addr_t previous_next = vm::cast<u32>(0);
for (flatbuffers::uoffset_t i = 0; i < resp->rooms()->size(); i++)
{
auto room = resp->rooms()->Get(i);
auto* room = resp->rooms()->Get(i);
vm::ptr<SceNpMatching2RoomDataExternal> room_info(allocate(sizeof(SceNpMatching2RoomDataExternal)));
if (i > 0)
@ -81,62 +142,7 @@ void np_handler::SearchRoomReponse_to_SceNpMatching2SearchRoomResponse(const Sea
previous_next = vm::cast(room_info.addr());
room_info->serverId = room->serverId();
room_info->worldId = room->worldId();
room_info->publicSlotNum = room->publicSlotNum();
room_info->privateSlotNum = room->privateSlotNum();
room_info->lobbyId = room->lobbyId();
room_info->roomId = room->roomId();
room_info->openPublicSlotNum = room->openPublicSlotNum();
room_info->maxSlot = room->maxSlot();
room_info->openPrivateSlotNum = room->openPrivateSlotNum();
room_info->curMemberNum = room->curMemberNum();
room_info->passwordSlotMask = room->curMemberNum();
if (auto owner = room->owner())
{
vm::ptr<SceNpUserInfo2> owner_info(allocate(sizeof(SceNpUserInfo2)));
UserInfo2_to_SceNpUserInfo2(owner, owner_info.get_ptr());
room_info->owner = owner_info;
}
if (room->roomGroup() && room->roomGroup()->size() != 0)
{
room_info->roomGroupNum = room->roomGroup()->size();
vm::ptr<SceNpMatching2RoomGroup> group_info(allocate(sizeof(SceNpMatching2RoomGroup) * room_info->roomGroupNum));
RoomGroup_to_SceNpMatching2RoomGroup(room->roomGroup(), group_info);
room_info->roomGroup = group_info;
}
room_info->flagAttr = room->flagAttr();
if (room->roomSearchableIntAttrExternal() && room->roomSearchableIntAttrExternal()->size() != 0)
{
room_info->roomSearchableIntAttrExternalNum = room->roomSearchableIntAttrExternal()->size();
vm::ptr<SceNpMatching2IntAttr> intattr_info(allocate(sizeof(SceNpMatching2IntAttr) * room_info->roomSearchableIntAttrExternalNum));
for (flatbuffers::uoffset_t a_index = 0; a_index < room->roomSearchableIntAttrExternal()->size(); a_index++)
{
auto int_attr = room->roomSearchableIntAttrExternal()->Get(a_index);
intattr_info[a_index].id = int_attr->id();
intattr_info[a_index].num = int_attr->num();
}
room_info->roomSearchableIntAttrExternal = intattr_info;
}
if (room->roomSearchableBinAttrExternal() && room->roomSearchableBinAttrExternal()->size() != 0)
{
room_info->roomSearchableBinAttrExternalNum = room->roomSearchableBinAttrExternal()->size();
vm::ptr<SceNpMatching2BinAttr> binattr_info(allocate(sizeof(SceNpMatching2BinAttr) * room_info->roomSearchableBinAttrExternalNum));
BinAttr_to_SceNpMatching2BinAttr(room->roomSearchableBinAttrExternal(), binattr_info);
room_info->roomSearchableBinAttrExternal = binattr_info;
}
if (room->roomBinAttrExternal() && room->roomBinAttrExternal()->size() != 0)
{
room_info->roomBinAttrExternalNum = room->roomBinAttrExternal()->size();
vm::ptr<SceNpMatching2BinAttr> binattr_info(allocate(sizeof(SceNpMatching2BinAttr) * room_info->roomBinAttrExternalNum));
BinAttr_to_SceNpMatching2BinAttr(room->roomBinAttrExternal(), binattr_info);
room_info->roomBinAttrExternal = binattr_info;
}
RoomDataExternal_to_SceNpMatching2RoomDataExternal(room, room_info.get_ptr());
}
}
else
@ -145,6 +151,33 @@ void np_handler::SearchRoomReponse_to_SceNpMatching2SearchRoomResponse(const Sea
}
}
void np_handler::GetRoomDataExternalListResponse_to_SceNpMatching2GetRoomDataExternalListResponse(const GetRoomDataExternalListResponse* resp, SceNpMatching2GetRoomDataExternalListResponse* get_resp)
{
get_resp->roomDataExternalNum = resp->rooms() ? resp->rooms()->size() : 0;
get_resp->roomDataExternal.set(0);
vm::addr_t previous_next = vm::cast<u32>(0);
for (std::size_t i = 0; i < get_resp->roomDataExternalNum; i++)
{
auto* room = resp->rooms()->Get(i);
vm::ptr<SceNpMatching2RoomDataExternal> room_info(allocate(sizeof(SceNpMatching2RoomDataExternal)));
if (i > 0)
{
vm::ptr<SceNpMatching2RoomDataExternal> prev_room(previous_next);
prev_room->next.set(room_info.addr());
}
else
{
get_resp->roomDataExternal = room_info;
}
previous_next = vm::cast(room_info.addr());
RoomDataExternal_to_SceNpMatching2RoomDataExternal(room, room_info.get_ptr());
}
}
u16 np_handler::RoomDataInternal_to_SceNpMatching2RoomDataInternal(const RoomDataInternal* resp, SceNpMatching2RoomDataInternal* room_info, const SceNpId& npid)
{
u16 member_id = 0;

View File

@ -167,6 +167,15 @@ table LeaveRoomRequest {
optData:PresenceOptionData;
}
table GetRoomDataExternalListRequest {
roomIds:[uint64];
attrIds:[uint16];
}
table GetRoomDataExternalListResponse {
rooms:[RoomDataExternal];
}
table SetRoomDataExternalRequest {
roomId:uint64;
roomSearchableIntAttrExternal:[IntAttr];
@ -223,3 +232,19 @@ table RoomMessageInfo {
srcMember:UserInfo2;
msg:[uint8];
}
table MessageDetails {
communicationId:string;
msgId:uint64;
mainType:uint16;
subType:uint16;
msgFeatures:uint32;
subject:string;
body:string;
data:[uint8];
}
table SendMessageRequest {
message:[uint8] (nested_flatbuffer: "MessageDetails");
npids:[string];
}

File diff suppressed because it is too large Load Diff

View File

@ -44,24 +44,25 @@ np_handler::np_handler()
{
g_fxo->need<named_thread<signaling_handler>>();
g_cfg_rpcn.load();
std::lock_guard lock(mutex_rpcn);
rpcn = rpcn::rpcn_client::get_instance();
is_connected = (g_cfg.net.net_active == np_internet_status::enabled);
is_psn_active = (g_cfg.net.psn_status >= np_psn_status::fake);
is_psn_active = (g_cfg.net.psn_status >= np_psn_status::psn_fake);
if (get_net_status() == CELL_NET_CTL_STATE_IPObtained)
{
if (!discover_ip_address())
{
nph_log.error("Failed to discover local IP!");
is_connected = false;
is_connected = false;
is_psn_active = false;
}
if (!discover_ether_address())
{
nph_log.error("Failed to discover ethernet address!");
is_connected = false;
is_connected = false;
is_psn_active = false;
}
@ -115,7 +116,7 @@ bool np_handler::discover_ip_address()
nph_log.notice("Hostname was determined to be %s", hostname.c_str());
hostent *host = gethostbyname(hostname.data());
hostent* host = gethostbyname(hostname.data());
if (!host)
{
nph_log.error("gethostbyname failed in IP discovery!");
@ -129,7 +130,7 @@ bool np_handler::discover_ip_address()
}
// 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]);
local_ip_addr = *reinterpret_cast<u32*>(host->h_addr_list[0]);
// Set public address to local discovered address for now, may be updated later;
public_ip_addr = local_ip_addr;
@ -164,10 +165,10 @@ bool np_handler::discover_ether_address()
std::vector<u8> adapter_infos(sizeof(IP_ADAPTER_INFO));
ULONG size_infos = sizeof(IP_ADAPTER_INFO);
if (GetAdaptersInfo(reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data()), &size_infos) == ERROR_BUFFER_OVERFLOW)
if (GetAdaptersInfo(reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data()), &size_infos) == ERROR_BUFFER_OVERFLOW)
adapter_infos.resize(size_infos);
if (GetAdaptersInfo(reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data()), &size_infos) == NO_ERROR && size_infos)
if (GetAdaptersInfo(reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data()), &size_infos) == NO_ERROR && size_infos)
{
PIP_ADAPTER_INFO info = reinterpret_cast<PIP_ADAPTER_INFO>(adapter_infos.data());
memcpy(ether_address.data(), info[0].Address, 6);
@ -305,6 +306,11 @@ void np_handler::string_to_avatar_url(const std::string& str, SceNpAvatarUrl* av
strcpy_trunc(avatar_url->data, str);
}
void np_handler::string_to_communication_id(const std::string& str, SceNpCommunicationId* comm_id)
{
strcpy_trunc(comm_id->data, str);
}
void np_handler::init_NP(u32 poolsize, vm::ptr<void> poolptr)
{
// Init memory pool
@ -317,7 +323,7 @@ void np_handler::init_NP(u32 poolsize, vm::ptr<void> poolptr)
memset(&online_name, 0, sizeof(online_name));
memset(&avatar_url, 0, sizeof(avatar_url));
if (g_cfg.net.psn_status >= np_psn_status::fake)
if (g_cfg.net.psn_status >= np_psn_status::psn_fake)
{
std::string s_npid = g_cfg_rpcn.get_npid();
ensure(!s_npid.empty()); // It should have been generated before this
@ -331,35 +337,39 @@ void np_handler::init_NP(u32 poolsize, vm::ptr<void> poolptr)
{
case np_psn_status::disabled:
break;
case np_psn_status::fake:
case np_psn_status::psn_fake:
{
np_handler::string_to_online_name("RPCS3's user", &online_name);
np_handler::string_to_avatar_url("https://rpcs3.net/cdn/netplay/DefaultAvatar.png", &avatar_url);
break;
}
case np_psn_status::rpcn:
case np_psn_status::psn_rpcn:
{
if (!is_psn_active)
break;
// Connect RPCN client
if (!rpcn.connect(g_cfg_rpcn.get_host()))
std::lock_guard lock(mutex_rpcn);
rpcn = rpcn::rpcn_client::get_instance();
// Make sure we're connected
if (auto state = rpcn->wait_for_connection(); state != rpcn::rpcn_state::failure_no_failure)
{
rpcn_log.error("Connection to RPCN Failed!");
is_psn_active = false;
return;
}
if (!rpcn.login(g_cfg_rpcn.get_npid(), g_cfg_rpcn.get_password(), g_cfg_rpcn.get_token()))
if (auto state = rpcn->wait_for_authentified(); state != rpcn::rpcn_state::failure_no_failure)
{
rpcn_log.error("RPCN login attempt failed!");
is_psn_active = false;
return;
}
np_handler::string_to_online_name(rpcn.get_online_name(), &online_name);
np_handler::string_to_avatar_url(rpcn.get_avatar_url(), &avatar_url);
public_ip_addr = rpcn.get_addr_sig();
np_handler::string_to_online_name(rpcn->get_online_name(), &online_name);
np_handler::string_to_avatar_url(rpcn->get_avatar_url(), &avatar_url);
public_ip_addr = rpcn->get_addr_sig();
break;
}
@ -376,10 +386,11 @@ void np_handler::terminate_NP()
mpool_avail = 0;
mpool_allocs.clear();
if (g_cfg.net.psn_status == np_psn_status::rpcn)
if (g_cfg.net.psn_status == np_psn_status::psn_rpcn)
{
rpcn_log.error("Disconnecting from RPCN!");
rpcn.disconnect();
rpcn_log.notice("Disconnecting from RPCN!");
std::lock_guard lock(mutex_rpcn);
rpcn.reset();
}
}
@ -433,12 +444,12 @@ std::vector<SceNpMatching2ServerId> np_handler::get_match2_server_list(SceNpMatc
{
std::vector<SceNpMatching2ServerId> server_list{};
if (g_cfg.net.psn_status != np_psn_status::rpcn)
if (g_cfg.net.psn_status != np_psn_status::psn_rpcn)
{
return server_list;
}
if (!rpcn.get_server_list(get_req_id(0), get_match2_context(ctx_id)->communicationId, server_list))
if (!rpcn->get_server_list(get_req_id(0), get_match2_context(ctx_id)->communicationId, server_list))
{
rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false;
@ -457,14 +468,13 @@ u32 np_handler::get_server_status(SceNpMatching2ContextId ctx_id, vm::cptr<SceNp
serv_info->server.serverId = server_id;
serv_info->server.status = SCE_NP_MATCHING2_SERVER_STATUS_AVAILABLE;
const auto cb_info = std::move(pending_requests.at(req_id));
pending_requests.erase(req_id);
const auto cb_info = take_pending_request(req_id);
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_GetServerInfo, event_key, 0, sizeof(SceNpMatching2GetServerInfoResponse), cb_info.cb_arg);
return 0;
});
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_GetServerInfo, event_key, 0, sizeof(SceNpMatching2GetServerInfoResponse), cb_info.cb_arg);
return 0;
});
return req_id;
}
@ -474,14 +484,13 @@ u32 np_handler::create_server_context(SceNpMatching2ContextId ctx_id, vm::cptr<S
u32 req_id = generate_callback_info(ctx_id, optParam);
u32 event_key = get_event_key();
const auto cb_info = std::move(pending_requests.at(req_id));
pending_requests.erase(req_id);
const auto cb_info = take_pending_request(req_id);
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_CreateServerContext, event_key, 0, 0, cb_info.cb_arg);
return 0;
});
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_CreateServerContext, event_key, 0, 0, cb_info.cb_arg);
return 0;
});
return req_id;
}
@ -490,7 +499,7 @@ u32 np_handler::get_world_list(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMat
{
u32 req_id = generate_callback_info(ctx_id, optParam);
if (!rpcn.get_world_list(req_id, get_match2_context(ctx_id)->communicationId, server_id))
if (!rpcn->get_world_list(req_id, get_match2_context(ctx_id)->communicationId, server_id))
{
rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false;
@ -503,7 +512,7 @@ u32 np_handler::create_join_room(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpM
{
u32 req_id = generate_callback_info(ctx_id, optParam);
if (!rpcn.createjoin_room(req_id, get_match2_context(ctx_id)->communicationId, req))
if (!rpcn->createjoin_room(req_id, get_match2_context(ctx_id)->communicationId, req))
{
rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false;
@ -516,7 +525,7 @@ u32 np_handler::join_room(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching
{
u32 req_id = generate_callback_info(ctx_id, optParam);
if (!rpcn.join_room(req_id, get_match2_context(ctx_id)->communicationId, req))
if (!rpcn->join_room(req_id, get_match2_context(ctx_id)->communicationId, req))
{
rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false;
@ -529,7 +538,7 @@ u32 np_handler::leave_room(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatchin
{
u32 req_id = generate_callback_info(ctx_id, optParam);
if (!rpcn.leave_room(req_id, get_match2_context(ctx_id)->communicationId, req))
if (!rpcn->leave_room(req_id, get_match2_context(ctx_id)->communicationId, req))
{
rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false;
@ -542,7 +551,20 @@ u32 np_handler::search_room(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatchi
{
u32 req_id = generate_callback_info(ctx_id, optParam);
if (!rpcn.search_room(req_id, get_match2_context(ctx_id)->communicationId, req))
if (!rpcn->search_room(req_id, get_match2_context(ctx_id)->communicationId, req))
{
rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false;
}
return req_id;
}
u32 np_handler::get_roomdata_external_list(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2GetRoomDataExternalListRequest* req)
{
u32 req_id = generate_callback_info(ctx_id, optParam);
if (!rpcn->get_roomdata_external_list(req_id, get_match2_context(ctx_id)->communicationId, req))
{
rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false;
@ -557,7 +579,7 @@ u32 np_handler::set_roomdata_external(SceNpMatching2ContextId ctx_id, vm::cptr<S
extra_nps::print_set_roomdata_ext_req(req);
if (!rpcn.set_roomdata_external(req_id, get_match2_context(ctx_id)->communicationId, req))
if (!rpcn->set_roomdata_external(req_id, get_match2_context(ctx_id)->communicationId, req))
{
rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false;
@ -570,7 +592,7 @@ u32 np_handler::get_roomdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr<S
{
u32 req_id = generate_callback_info(ctx_id, optParam);
if (!rpcn.get_roomdata_internal(req_id, get_match2_context(ctx_id)->communicationId, req))
if (!rpcn->get_roomdata_internal(req_id, get_match2_context(ctx_id)->communicationId, req))
{
rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false;
@ -585,7 +607,7 @@ u32 np_handler::set_roomdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr<S
extra_nps::print_set_roomdata_int_req(req);
if (!rpcn.set_roomdata_internal(req_id, get_match2_context(ctx_id)->communicationId, req))
if (!rpcn->set_roomdata_internal(req_id, get_match2_context(ctx_id)->communicationId, req))
{
rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false;
@ -598,7 +620,7 @@ u32 np_handler::get_ping_info(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatc
{
u32 req_id = generate_callback_info(ctx_id, optParam);
if (!rpcn.ping_room_owner(req_id, get_match2_context(ctx_id)->communicationId, req->roomId))
if (!rpcn->ping_room_owner(req_id, get_match2_context(ctx_id)->communicationId, req->roomId))
{
rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false;
@ -611,7 +633,7 @@ u32 np_handler::send_room_message(SceNpMatching2ContextId ctx_id, vm::cptr<SceNp
{
u32 req_id = generate_callback_info(ctx_id, optParam);
if (!rpcn.send_room_message(req_id, get_match2_context(ctx_id)->communicationId, req))
if (!rpcn->send_room_message(req_id, get_match2_context(ctx_id)->communicationId, req))
{
rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false;
@ -623,9 +645,12 @@ u32 np_handler::send_room_message(SceNpMatching2ContextId ctx_id, vm::cptr<SceNp
void np_handler::req_sign_infos(const std::string& npid, u32 conn_id)
{
u32 req_id = get_req_id(0x3333);
pending_sign_infos_requests[req_id] = conn_id;
{
std::lock_guard lock(mutex_pending_sign_infos_requests);
pending_sign_infos_requests[req_id] = conn_id;
}
if (!rpcn.req_sign_infos(req_id, npid))
if (!rpcn->req_sign_infos(req_id, npid))
{
rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false;
@ -640,7 +665,7 @@ void np_handler::req_ticket(u32 /*version*/, const SceNpId* /*npid*/, const char
std::string service_id_str(service_id);
if (!rpcn.req_ticket(req_id, service_id_str))
if (!rpcn->req_ticket(req_id, service_id_str))
{
rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false;
@ -649,7 +674,6 @@ void np_handler::req_ticket(u32 /*version*/, const SceNpId* /*npid*/, const char
return;
}
u32 np_handler::get_match2_event(SceNpMatching2EventKey event_key, u8* dest, u32 size)
{
std::lock_guard lock(mutex_req_results);
@ -663,67 +687,181 @@ u32 np_handler::get_match2_event(SceNpMatching2EventKey event_key, u8* dest, u32
return size_copied;
}
bool np_handler::send_basic_event(s32 event, s32 retCode, u32 reqId)
{
if (basic_handler.registered)
{
sysutil_register_cb([handler_func = this->basic_handler.handler_func, handler_arg = this->basic_handler.handler_arg, event, retCode, reqId](ppu_thread& cb_ppu) -> s32
{
handler_func(cb_ppu, event, retCode, reqId, handler_arg);
return 0;
});
return true;
}
return false;
}
void np_handler::queue_basic_event(basic_event to_queue)
{
std::lock_guard lock(mutex_queue_basic_events);
queue_basic_events.push(std::move(to_queue));
}
error_code np_handler::get_basic_event(vm::ptr<s32> event, vm::ptr<SceNpUserInfo> from, vm::ptr<s32> data, vm::ptr<u32> size)
{
basic_event cur_event;
{
std::lock_guard lock(mutex_queue_basic_events);
if (queue_basic_events.empty())
{
return not_an_error(SCE_NP_BASIC_ERROR_NO_EVENT);
}
cur_event = std::move(queue_basic_events.front());
queue_basic_events.pop();
}
const u32 size_avail = *size;
u32 res_size = std::min(static_cast<u32>(cur_event.data.size()), size_avail);
*event = cur_event.event;
memcpy(from.get_ptr(), &cur_event.from, sizeof(cur_event.from));
memcpy(data.get_ptr(), cur_event.data.data(), res_size);
*size = res_size;
if (res_size < cur_event.data.size())
{
return SCE_NP_BASIC_ERROR_DATA_LOST;
}
return CELL_OK;
}
std::optional<std::shared_ptr<std::pair<std::string, message_data>>> np_handler::get_message(u64 id)
{
return rpcn->get_message(id);
}
void np_handler::operator()()
{
if (g_cfg.net.psn_status != np_psn_status::rpcn)
if (g_cfg.net.psn_status != np_psn_status::psn_rpcn)
return;
while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped())
{
if (!rpcn.manage_connection())
bool sleep = true;
if (rpcn)
{
std::lock_guard lock(mutex_rpcn);
if (!rpcn)
{
continue;
}
auto replies = rpcn->get_replies();
for (auto& reply : replies)
{
const u16 command = reply.second.first;
const u32 req_id = reply.first;
std::vector<u8>& data = reply.second.second;
switch (command)
{
case rpcn::CommandType::GetWorldList: reply_get_world_list(req_id, data); break;
case rpcn::CommandType::CreateRoom: reply_create_join_room(req_id, data); break;
case rpcn::CommandType::JoinRoom: reply_join_room(req_id, data); break;
case rpcn::CommandType::LeaveRoom: reply_leave_room(req_id, data); break;
case rpcn::CommandType::SearchRoom: reply_search_room(req_id, data); break;
case rpcn::CommandType::GetRoomDataExternalList: reply_get_roomdata_external_list(req_id, data); break;
case rpcn::CommandType::SetRoomDataExternal: reply_set_roomdata_external(req_id, data); break;
case rpcn::CommandType::GetRoomDataInternal: reply_get_roomdata_internal(req_id, data); break;
case rpcn::CommandType::SetRoomDataInternal: reply_set_roomdata_internal(req_id, data); break;
case rpcn::CommandType::PingRoomOwner: reply_get_ping_info(req_id, data); break;
case rpcn::CommandType::SendRoomMessage: reply_send_room_message(req_id, data); break;
case rpcn::CommandType::RequestSignalingInfos: reply_req_sign_infos(req_id, data); break;
case rpcn::CommandType::RequestTicket: reply_req_ticket(req_id, data); break;
default: rpcn_log.error("Unknown reply(%d) received!", command); break;
}
}
auto notifications = rpcn->get_notifications();
for (auto& notif : notifications)
{
switch (notif.first)
{
case rpcn::NotificationType::UserJoinedRoom: notif_user_joined_room(notif.second); break;
case rpcn::NotificationType::UserLeftRoom: notif_user_left_room(notif.second); break;
case rpcn::NotificationType::RoomDestroyed: notif_room_destroyed(notif.second); break;
case rpcn::NotificationType::SignalP2PConnect: notif_p2p_connect(notif.second); break;
case rpcn::NotificationType::RoomMessageReceived: notif_room_message_received(notif.second); break;
default: rpcn_log.error("Unknown notification(%d) received!", notif.first); break;
}
}
auto messages = rpcn->get_new_messages();
if (basic_handler.registered)
{
for (const auto msg_id : messages)
{
const auto opt_msg = rpcn->get_message(msg_id);
if (!opt_msg)
{
continue;
}
const auto& msg = opt_msg.value();
if (strncmp(msg->second.commId.data, basic_handler.context.data, sizeof(basic_handler.context.data) - 1) == 0)
{
u32 event;
switch (msg->second.mainType)
{
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_DATA_ATTACHMENT:
event = SCE_NP_BASIC_EVENT_INCOMING_ATTACHMENT;
break;
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE:
event = (msg->second.msgFeatures & SCE_NP_BASIC_MESSAGE_FEATURES_BOOTABLE) ? SCE_NP_BASIC_EVENT_INCOMING_BOOTABLE_INVITATION : SCE_NP_BASIC_EVENT_INCOMING_INVITATION;
break;
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_CUSTOM_DATA:
event = (msg->second.msgFeatures & SCE_NP_BASIC_MESSAGE_FEATURES_BOOTABLE) ? SCE_NP_BASIC_EVENT_INCOMING_BOOTABLE_CUSTOM_DATA_MESSAGE : SCE_NP_BASIC_EVENT_INCOMING_CUSTOM_DATA_MESSAGE;
break;
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_GENERAL:
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_ADD_FRIEND:
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_URL_ATTACHMENT:
event = SCE_NP_BASIC_EVENT_MESSAGE;
default:
continue;
}
basic_event to_add{};
to_add.event = event;
strcpy_trunc(to_add.from.userId.handle.data, msg->first);
strcpy_trunc(to_add.from.name.data, msg->first);
queue_basic_event(std::move(to_add));
send_basic_event(event, 0, 0);
}
}
}
if (!replies.empty() || !notifications.empty())
{
sleep = false;
}
}
// TODO: replace with an appropriate semaphore
if (sleep)
{
thread_ctrl::wait_for(200'000);
continue;
}
auto replies = rpcn.get_replies();
for (auto& reply : replies)
{
const u16 command = reply.second.first;
const u32 req_id = reply.first;
std::vector<u8>& data = reply.second.second;
switch (command)
{
case CommandType::GetWorldList: reply_get_world_list(req_id, data); break;
case CommandType::CreateRoom: reply_create_join_room(req_id, data); break;
case CommandType::JoinRoom: reply_join_room(req_id, data); break;
case CommandType::LeaveRoom: reply_leave_room(req_id, data); break;
case CommandType::SearchRoom: reply_search_room(req_id, data); break;
case CommandType::SetRoomDataExternal: reply_set_roomdata_external(req_id, data); break;
case CommandType::GetRoomDataInternal: reply_get_roomdata_internal(req_id, data); break;
case CommandType::SetRoomDataInternal: reply_set_roomdata_internal(req_id, data); break;
case CommandType::PingRoomOwner: reply_get_ping_info(req_id, data); break;
case CommandType::SendRoomMessage: reply_send_room_message(req_id, data); break;
case CommandType::RequestSignalingInfos: reply_req_sign_infos(req_id, data); break;
case CommandType::RequestTicket: reply_req_ticket(req_id, data); break;
default: rpcn_log.error("Unknown reply(%d) received!", command); break;
}
}
auto notifications = rpcn.get_notifications();
for (auto& notif : notifications)
{
switch (notif.first)
{
case NotificationType::UserJoinedRoom: notif_user_joined_room(notif.second); break;
case NotificationType::UserLeftRoom: notif_user_left_room(notif.second); break;
case NotificationType::RoomDestroyed: notif_room_destroyed(notif.second); break;
case NotificationType::SignalP2PConnect: notif_p2p_connect(notif.second); break;
case NotificationType::RoomMessageReceived: notif_room_message_received(notif.second); break;
default: rpcn_log.error("Unknown notification(%d) received!", notif.first); break;
}
}
}
}
bool np_handler::reply_get_world_list(u32 req_id, std::vector<u8>& reply_data)
{
if (pending_requests.count(req_id) == 0)
return error_and_disconnect("Unexpected reply ID to GetWorldList");
const auto cb_info = std::move(pending_requests.at(req_id));
pending_requests.erase(req_id);
const auto cb_info = take_pending_request(req_id);
vec_stream reply(reply_data, 1);
@ -760,21 +898,17 @@ bool np_handler::reply_get_world_list(u32 req_id, std::vector<u8>& reply_data)
}
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_GetWorldInfoList, event_key, 0, sizeof(SceNpMatching2GetWorldInfoListResponse), cb_info.cb_arg);
return 0;
});
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_GetWorldInfoList, event_key, 0, sizeof(SceNpMatching2GetWorldInfoListResponse), cb_info.cb_arg);
return 0;
});
return true;
}
bool np_handler::reply_create_join_room(u32 req_id, std::vector<u8>& reply_data)
{
if (pending_requests.count(req_id) == 0)
return error_and_disconnect("Unexpected reply ID to CreateRoom");
const auto cb_info = std::move(pending_requests.at(req_id));
pending_requests.erase(req_id);
const auto cb_info = take_pending_request(req_id);
vec_stream reply(reply_data, 1);
auto create_room_resp = reply.get_rawdata();
@ -795,27 +929,23 @@ bool np_handler::reply_create_join_room(u32 req_id, std::vector<u8>& reply_data)
// 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(), true);
// TODO? Should this send a message to Signaling CB? Is this even necessary?
extra_nps::print_create_room_resp(room_resp);
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_CreateJoinRoom, event_key, 0, sizeof(SceNpMatching2CreateJoinRoomResponse), cb_info.cb_arg);
return 0;
});
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_CreateJoinRoom, event_key, 0, sizeof(SceNpMatching2CreateJoinRoomResponse), cb_info.cb_arg);
return 0;
});
return true;
}
bool np_handler::reply_join_room(u32 req_id, std::vector<u8>& reply_data)
{
if (pending_requests.count(req_id) == 0)
return error_and_disconnect("Unexpected reply ID to JoinRoom");
const auto cb_info = std::move(pending_requests.at(req_id));
pending_requests.erase(req_id);
const auto cb_info = take_pending_request(req_id);
vec_stream reply(reply_data, 1);
@ -839,25 +969,21 @@ bool np_handler::reply_join_room(u32 req_id, std::vector<u8>& reply_data)
// 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(), true);
// TODO? Should this send a message to Signaling CB? Is this even necessary?
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_JoinRoom, event_key, 0, sizeof(SceNpMatching2JoinRoomResponse), cb_info.cb_arg);
return 0;
});
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_JoinRoom, event_key, 0, sizeof(SceNpMatching2JoinRoomResponse), cb_info.cb_arg);
return 0;
});
return true;
}
bool np_handler::reply_leave_room(u32 req_id, std::vector<u8>& reply_data)
{
if (pending_requests.count(req_id) == 0)
return error_and_disconnect("Unexpected reply ID to LeaveRoom");
const auto cb_info = std::move(pending_requests.at(req_id));
pending_requests.erase(req_id);
const auto cb_info = take_pending_request(req_id);
vec_stream reply(reply_data, 1);
u64 room_id = reply.get<u64>();
@ -871,21 +997,17 @@ bool np_handler::reply_leave_room(u32 req_id, std::vector<u8>& reply_data)
sigh.disconnect_sig2_users(room_id);
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_LeaveRoom, event_key, 0, 0, cb_info.cb_arg);
return 0;
});
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_LeaveRoom, event_key, 0, 0, cb_info.cb_arg);
return 0;
});
return true;
}
bool np_handler::reply_search_room(u32 req_id, std::vector<u8>& reply_data)
{
if (pending_requests.count(req_id) == 0)
return error_and_disconnect("Unexpected reply ID to SearchRoom");
const auto cb_info = std::move(pending_requests.at(req_id));
pending_requests.erase(req_id);
const auto cb_info = take_pending_request(req_id);
vec_stream reply(reply_data, 1);
auto search_room_resp = reply.get_rawdata();
@ -897,43 +1019,60 @@ bool np_handler::reply_search_room(u32 req_id, std::vector<u8>& reply_data)
auto resp = flatbuffers::GetRoot<SearchRoomResponse>(search_room_resp.data());
SceNpMatching2SearchRoomResponse* search_resp = reinterpret_cast<SceNpMatching2SearchRoomResponse*>(allocate_req_result(event_key, sizeof(SceNpMatching2SearchRoomResponse)));
SearchRoomReponse_to_SceNpMatching2SearchRoomResponse(resp, search_resp);
SearchRoomResponse_to_SceNpMatching2SearchRoomResponse(resp, search_resp);
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SearchRoom, event_key, 0, sizeof(SceNpMatching2SearchRoomResponse), cb_info.cb_arg);
return 0;
});
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SearchRoom, event_key, 0, sizeof(SceNpMatching2SearchRoomResponse), cb_info.cb_arg);
return 0;
});
return true;
}
bool np_handler::reply_get_roomdata_external_list(u32 req_id, std::vector<u8>& reply_data)
{
const auto cb_info = take_pending_request(req_id);
vec_stream reply(reply_data, 1);
auto get_room_ext_resp = reply.get_rawdata();
if (reply.is_error())
return error_and_disconnect("Malformed reply to GetRoomDataExternalList command");
u32 event_key = get_event_key();
auto resp = flatbuffers::GetRoot<GetRoomDataExternalListResponse>(get_room_ext_resp.data());
SceNpMatching2GetRoomDataExternalListResponse* np_get_room_ext_resp = reinterpret_cast<SceNpMatching2GetRoomDataExternalListResponse*>(allocate_req_result(event_key, sizeof(SceNpMatching2GetRoomDataExternalListResponse)));
GetRoomDataExternalListResponse_to_SceNpMatching2GetRoomDataExternalListResponse(resp, np_get_room_ext_resp);
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SearchRoom, event_key, 0, sizeof(SceNpMatching2SearchRoomResponse), cb_info.cb_arg);
return 0;
});
return true;
}
bool np_handler::reply_set_roomdata_external(u32 req_id, std::vector<u8>& /*reply_data*/)
{
if (pending_requests.count(req_id) == 0)
return error_and_disconnect("Unexpected reply ID to SetRoomDataExternal");
const auto cb_info = std::move(pending_requests.at(req_id));
pending_requests.erase(req_id);
const auto cb_info = take_pending_request(req_id);
u32 event_key = get_event_key(); // Unsure if necessary if there is no data
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataExternal, event_key, 0, 0, cb_info.cb_arg);
return 0;
});
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataExternal, event_key, 0, 0, cb_info.cb_arg);
return 0;
});
return true;
}
bool np_handler::reply_get_roomdata_internal(u32 req_id, std::vector<u8>& reply_data)
{
if (pending_requests.count(req_id) == 0)
return error_and_disconnect("Unexpected reply ID to GetRoomDataInternal");
const auto cb_info = std::move(pending_requests.at(req_id));
pending_requests.erase(req_id);
const auto cb_info = take_pending_request(req_id);
vec_stream reply(reply_data, 1);
@ -953,40 +1092,32 @@ bool np_handler::reply_get_roomdata_internal(u32 req_id, std::vector<u8>& reply_
extra_nps::print_room_data_internal(room_resp->roomDataInternal.get_ptr());
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataInternal, event_key, 0, sizeof(SceNpMatching2GetRoomDataInternalResponse), cb_info.cb_arg);
return 0;
});
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataInternal, event_key, 0, sizeof(SceNpMatching2GetRoomDataInternalResponse), cb_info.cb_arg);
return 0;
});
return true;
}
bool np_handler::reply_set_roomdata_internal(u32 req_id, std::vector<u8>& /*reply_data*/)
{
if (pending_requests.count(req_id) == 0)
return error_and_disconnect("Unexpected reply ID to SetRoomDataInternal");
const auto cb_info = std::move(pending_requests.at(req_id));
pending_requests.erase(req_id);
const auto cb_info = take_pending_request(req_id);
u32 event_key = get_event_key(); // Unsure if necessary if there is no data
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataInternal, event_key, 0, 0, cb_info.cb_arg);
return 0;
});
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataInternal, event_key, 0, 0, cb_info.cb_arg);
return 0;
});
return true;
}
bool np_handler::reply_get_ping_info(u32 req_id, std::vector<u8>& reply_data)
{
if (pending_requests.count(req_id) == 0)
return error_and_disconnect("Unexpected reply ID to PingRoomOwner");
const auto cb_info = std::move(pending_requests.at(req_id));
pending_requests.erase(req_id);
const auto cb_info = take_pending_request(req_id);
vec_stream reply(reply_data, 1);
@ -1002,38 +1133,35 @@ bool np_handler::reply_get_ping_info(u32 req_id, std::vector<u8>& reply_data)
GetPingInfoResponse_to_SceNpMatching2SignalingGetPingInfoResponse(resp, final_ping_resp);
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SignalingGetPingInfo, event_key, 0, sizeof(SceNpMatching2SignalingGetPingInfoResponse), cb_info.cb_arg);
return 0;
});
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SignalingGetPingInfo, event_key, 0, sizeof(SceNpMatching2SignalingGetPingInfoResponse), cb_info.cb_arg);
return 0;
});
return true;
}
bool np_handler::reply_send_room_message(u32 req_id, std::vector<u8>& /*reply_data*/)
{
if (pending_requests.count(req_id) == 0)
return error_and_disconnect("Unexpected reply ID to PingRoomOwner");
const auto cb_info = std::move(pending_requests.at(req_id));
pending_requests.erase(req_id);
const auto cb_info = take_pending_request(req_id);
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SendRoomMessage, 0, 0, 0, cb_info.cb_arg);
return 0;
});
{
cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SendRoomMessage, 0, 0, 0, cb_info.cb_arg);
return 0;
});
return true;
}
bool np_handler::reply_req_sign_infos(u32 req_id, std::vector<u8>& reply_data)
{
if (!pending_sign_infos_requests.count(req_id))
return error_and_disconnect("Unexpected reply ID to req RequestSignalingInfos");
u32 conn_id = pending_sign_infos_requests.at(req_id);
pending_sign_infos_requests.erase(req_id);
u32 conn_id;
{
std::lock_guard lock(mutex_pending_sign_infos_requests);
conn_id = pending_sign_infos_requests.at(req_id);
pending_sign_infos_requests.erase(req_id);
}
vec_stream reply(reply_data, 1);
u32 addr = reply.get<u32>();
@ -1056,16 +1184,16 @@ bool np_handler::reply_req_ticket(u32 /*req_id*/, std::vector<u8>& reply_data)
if (reply.is_error())
return error_and_disconnect("Malformed reply to RequestTicket command");
current_ticket = std::move(ticket_raw);
current_ticket = std::move(ticket_raw);
auto ticket_size = static_cast<s32>(current_ticket.size());
if (manager_cb)
{
sysutil_register_cb([manager_cb = this->manager_cb, ticket_size, manager_cb_arg = this->manager_cb_arg](ppu_thread& cb_ppu) -> s32
{
manager_cb(cb_ppu, SCE_NP_MANAGER_EVENT_GOT_TICKET, ticket_size, manager_cb_arg);
return 0;
});
{
manager_cb(cb_ppu, SCE_NP_MANAGER_EVENT_GOT_TICKET, ticket_size, manager_cb_arg);
return 0;
});
}
return true;
@ -1093,10 +1221,10 @@ void np_handler::notif_user_joined_room(std::vector<u8>& data)
extra_nps::print_room_member_data_internal(notif_data->roomMemberDataInternal.get_ptr());
sysutil_register_cb([room_event_cb = this->room_event_cb, room_id, event_key, room_event_cb_ctx = this->room_event_cb_ctx, room_event_cb_arg = this->room_event_cb_arg](ppu_thread& cb_ppu) -> s32
{
room_event_cb(cb_ppu, room_event_cb_ctx, room_id, SCE_NP_MATCHING2_ROOM_EVENT_MemberJoined, event_key, 0, sizeof(SceNpMatching2RoomMemberUpdateInfo), room_event_cb_arg);
return 0;
});
{
room_event_cb(cb_ppu, room_event_cb_ctx, room_id, SCE_NP_MATCHING2_ROOM_EVENT_MemberJoined, event_key, 0, sizeof(SceNpMatching2RoomMemberUpdateInfo), room_event_cb_arg);
return 0;
});
}
void np_handler::notif_user_left_room(std::vector<u8>& data)
@ -1121,10 +1249,10 @@ void np_handler::notif_user_left_room(std::vector<u8>& data)
extra_nps::print_room_member_data_internal(notif_data->roomMemberDataInternal.get_ptr());
sysutil_register_cb([room_event_cb = this->room_event_cb, room_event_cb_ctx = this->room_event_cb_ctx, room_id, event_key, room_event_cb_arg = this->room_event_cb_arg](ppu_thread& cb_ppu) -> s32
{
room_event_cb(cb_ppu, room_event_cb_ctx, room_id, SCE_NP_MATCHING2_ROOM_EVENT_MemberLeft, event_key, 0, sizeof(SceNpMatching2RoomMemberUpdateInfo), room_event_cb_arg);
return 0;
});
{
room_event_cb(cb_ppu, room_event_cb_ctx, room_id, SCE_NP_MATCHING2_ROOM_EVENT_MemberLeft, event_key, 0, sizeof(SceNpMatching2RoomMemberUpdateInfo), room_event_cb_arg);
return 0;
});
}
void np_handler::notif_room_destroyed(std::vector<u8>& data)
@ -1151,10 +1279,10 @@ void np_handler::notif_room_destroyed(std::vector<u8>& data)
sigh.disconnect_sig2_users(room_id);
sysutil_register_cb([room_event_cb = this->room_event_cb, room_event_cb_ctx = this->room_event_cb_ctx, room_id, event_key, room_event_cb_arg = this->room_event_cb_arg](ppu_thread& cb_ppu) -> s32
{
room_event_cb(cb_ppu, room_event_cb_ctx, room_id, SCE_NP_MATCHING2_ROOM_EVENT_RoomDestroyed, event_key, 0, sizeof(SceNpMatching2RoomUpdateInfo), room_event_cb_arg);
return 0;
});
{
room_event_cb(cb_ppu, room_event_cb_ctx, room_id, SCE_NP_MATCHING2_ROOM_EVENT_RoomDestroyed, event_key, 0, sizeof(SceNpMatching2RoomUpdateInfo), room_event_cb_arg);
return 0;
});
}
void np_handler::notif_p2p_connect(std::vector<u8>& data)
@ -1165,10 +1293,10 @@ void np_handler::notif_p2p_connect(std::vector<u8>& data)
return;
}
const u64 room_id = reinterpret_cast<le_t<u64>&>(data[0]);
const u16 member_id = reinterpret_cast<le_t<u16>&>(data[8]);
const u16 port_p2p = reinterpret_cast<be_t<u16>&>(data[10]);
const u32 addr_p2p = reinterpret_cast<le_t<u32>&>(data[12]);
const u64 room_id = reinterpret_cast<le_t<u64>&>(data[0]);
const u16 member_id = reinterpret_cast<le_t<u16>&>(data[8]);
const u16 port_p2p = reinterpret_cast<be_t<u16>&>(data[10]);
const u32 addr_p2p = reinterpret_cast<le_t<u32>&>(data[12]);
rpcn_log.notice("Received notification to connect to member(%d) of room(%d): %s:%d", member_id, room_id, ip_to_string(addr_p2p), port_p2p);
@ -1193,7 +1321,7 @@ void np_handler::notif_room_message_received(std::vector<u8>& data)
u32 event_key = get_event_key();
auto message_info = flatbuffers::GetRoot<RoomMessageInfo>(message_info_raw.data());
auto message_info = flatbuffers::GetRoot<RoomMessageInfo>(message_info_raw.data());
SceNpMatching2RoomMessageInfo* notif_data = reinterpret_cast<SceNpMatching2RoomMessageInfo*>(allocate_req_result(event_key, sizeof(SceNpMatching2RoomMessageInfo)));
RoomMessageInfo_to_SceNpMatching2RoomMessageInfo(message_info, notif_data);
@ -1202,10 +1330,10 @@ void np_handler::notif_room_message_received(std::vector<u8>& data)
if (room_msg_cb)
{
sysutil_register_cb([room_msg_cb = this->room_msg_cb, room_msg_cb_ctx = this->room_msg_cb_ctx, room_id, member_id, event_key, room_msg_cb_arg = this->room_msg_cb_arg](ppu_thread& cb_ppu) -> s32
{
room_msg_cb(cb_ppu, room_msg_cb_ctx, room_id, member_id, SCE_NP_MATCHING2_ROOM_MSG_EVENT_Message, event_key, 0, sizeof(SceNpMatching2RoomUpdateInfo), room_msg_cb_arg);
return 0;
});
{
room_msg_cb(cb_ppu, room_msg_cb_ctx, room_id, member_id, SCE_NP_MATCHING2_ROOM_MSG_EVENT_Message, event_key, 0, sizeof(SceNpMatching2RoomUpdateInfo), room_msg_cb_arg);
return 0;
});
}
}
@ -1338,7 +1466,7 @@ s32 np_handler::analyze_dns_packet(s32 s, const u8* buf, u32 len)
bool np_handler::error_and_disconnect(const std::string& error_msg)
{
rpcn_log.error("%s", error_msg);
rpcn.disconnect();
rpcn.reset();
return false;
}
@ -1358,11 +1486,24 @@ u32 np_handler::generate_callback_info(SceNpMatching2ContextId ctx_id, vm::cptr<
nph_log.warning("Callback used is 0x%x", ret.cb);
pending_requests[req_id] = std::move(ret);
{
std::lock_guard lock(mutex_pending_requests);
pending_requests[req_id] = std::move(ret);
}
return req_id;
}
np_handler::callback_info np_handler::take_pending_request(u32 req_id)
{
std::lock_guard lock(mutex_pending_requests);
const auto cb_info = std::move(pending_requests.at(req_id));
pending_requests.erase(req_id);
return cb_info;
}
u8* np_handler::allocate_req_result(u32 event_key, usz size)
{
std::lock_guard lock(mutex_req_results);
@ -1385,3 +1526,13 @@ u32 np_handler::add_players_to_history(vm::cptr<SceNpId> /*npids*/, u32 /*count*
return req_id;
}
u32 np_handler::get_num_friends()
{
return rpcn->get_num_friends();
}
u32 np_handler::get_num_blocks()
{
return rpcn->get_num_blocks();
}

View File

@ -35,9 +35,10 @@ public:
static std::string ip_to_string(u32 addr);
static std::string ether_to_string(std::array<u8, 6>& ether);
// Helpers for setting various structures from string
static void string_to_npid(const std::string&, SceNpId* npid);
static void string_to_online_name(const std::string&, SceNpOnlineName* online_name);
static void string_to_avatar_url(const std::string&, SceNpAvatarUrl* avatar_url);
static void string_to_npid(const std::string& str, SceNpId* npid);
static void string_to_online_name(const std::string& str, SceNpOnlineName* online_name);
static void string_to_avatar_url(const std::string& str, SceNpAvatarUrl* avatar_url);
static void string_to_communication_id(const std::string& str, SceNpCommunicationId* comm_id);
// DNS hooking functions
void add_dns_spy(u32 sock);
@ -47,16 +48,6 @@ public:
std::vector<u8> get_dns_packet(u32 sock);
s32 analyze_dns_packet(s32 s, const u8* buf, u32 len);
enum NotificationType : u16
{
UserJoinedRoom,
UserLeftRoom,
RoomDestroyed,
SignalP2PConnect,
_SignalP2PDisconnect,
RoomMessageReceived,
};
// handles async messages from server(only needed for RPCN)
void operator()();
@ -73,12 +64,28 @@ public:
// NP Handlers/Callbacks
// Seems to be global
vm::ptr<SceNpManagerCallback> manager_cb{}; // Connection status and tickets
vm::ptr<SceNpManagerCallback> manager_cb{}; // Connection status and tickets
vm::ptr<void> manager_cb_arg{};
// Registered by SceNpCommunicationId
vm::ptr<SceNpBasicEventHandler> basic_handler;
vm::ptr<void> basic_handler_arg;
// Basic event handler;
struct
{
SceNpCommunicationId context{};
vm::ptr<SceNpBasicEventHandler> handler_func;
vm::ptr<void> handler_arg;
bool registered = false;
bool context_sensitive = false;
} basic_handler;
struct basic_event
{
s32 event = 0;
SceNpUserInfo from{};
std::vector<u8> data;
};
void queue_basic_event(basic_event to_queue);
bool send_basic_event(s32 event, s32 retCode, u32 reqId);
error_code get_basic_event(vm::ptr<s32> event, vm::ptr<SceNpUserInfo> from, vm::ptr<s32> data, vm::ptr<u32> size);
std::optional<std::shared_ptr<std::pair<std::string, message_data>>> get_message(u64 id);
// Those should probably be under match2 ctx
vm::ptr<SceNpMatching2RoomEventCallback> room_event_cb{}; // Room events
@ -98,6 +105,7 @@ public:
u32 join_room(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2JoinRoomRequest* req);
u32 leave_room(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2LeaveRoomRequest* req);
u32 search_room(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2SearchRoomRequest* req);
u32 get_roomdata_external_list(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2GetRoomDataExternalListRequest* req);
u32 set_roomdata_external(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2SetRoomDataExternalRequest* req);
u32 get_roomdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2GetRoomDataInternalRequest* req);
u32 set_roomdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2SetRoomDataInternalRequest* req);
@ -106,9 +114,16 @@ public:
u32 get_match2_event(SceNpMatching2EventKey event_key, u8* dest, u32 size);
// Friend stuff
u32 get_num_friends();
u32 get_num_blocks();
// Misc stuff
void req_ticket(u32 version, const SceNpId *npid, const char *service_id, const u8 *cookie, u32 cookie_size, const char *entitlement_id, u32 consumed_count);
const std::vector<u8>& get_ticket() const { return current_ticket; }
void req_ticket(u32 version, const SceNpId* npid, const char* service_id, const u8* cookie, u32 cookie_size, const char* entitlement_id, u32 consumed_count);
const std::vector<u8>& get_ticket() const
{
return current_ticket;
}
u32 add_players_to_history(vm::cptr<SceNpId> npids, u32 count);
// For signaling
@ -119,7 +134,7 @@ public:
static constexpr std::string_view thread_name = "NP Handler Thread";
protected:
private:
// Various generic helpers
bool discover_ip_address();
bool discover_ether_address();
@ -138,6 +153,7 @@ protected:
bool reply_join_room(u32 req_id, std::vector<u8>& reply_data);
bool reply_leave_room(u32 req_id, std::vector<u8>& reply_data);
bool reply_search_room(u32 req_id, std::vector<u8>& reply_data);
bool reply_get_roomdata_external_list(u32 req_id, std::vector<u8>& reply_data);
bool reply_set_roomdata_external(u32 req_id, std::vector<u8>& reply_data);
bool reply_get_roomdata_internal(u32 req_id, std::vector<u8>& reply_data);
bool reply_set_roomdata_internal(u32 req_id, std::vector<u8>& reply_data);
@ -150,7 +166,9 @@ protected:
void BinAttr_to_SceNpMatching2BinAttr(const flatbuffers::Vector<flatbuffers::Offset<BinAttr>>* fb_attr, vm::ptr<SceNpMatching2BinAttr> binattr_info);
void RoomGroup_to_SceNpMatching2RoomGroup(const flatbuffers::Vector<flatbuffers::Offset<RoomGroup>>* fb_group, vm::ptr<SceNpMatching2RoomGroup> group_info);
void UserInfo2_to_SceNpUserInfo2(const UserInfo2* user, SceNpUserInfo2* user_info);
void SearchRoomReponse_to_SceNpMatching2SearchRoomResponse(const SearchRoomResponse* resp, SceNpMatching2SearchRoomResponse* search_resp);
void RoomDataExternal_to_SceNpMatching2RoomDataExternal(const RoomDataExternal* room, SceNpMatching2RoomDataExternal* room_info);
void SearchRoomResponse_to_SceNpMatching2SearchRoomResponse(const SearchRoomResponse* resp, SceNpMatching2SearchRoomResponse* search_resp);
void GetRoomDataExternalListResponse_to_SceNpMatching2GetRoomDataExternalListResponse(const GetRoomDataExternalListResponse* resp, SceNpMatching2GetRoomDataExternalListResponse* get_resp);
u16 RoomDataInternal_to_SceNpMatching2RoomDataInternal(const RoomDataInternal* resp, SceNpMatching2RoomDataInternal* room_resp, const SceNpId& npid);
void RoomMemberUpdateInfo_to_SceNpMatching2RoomMemberUpdateInfo(const RoomMemberUpdateInfo* resp, SceNpMatching2RoomMemberUpdateInfo* room_info);
void RoomUpdateInfo_to_SceNpMatching2RoomUpdateInfo(const RoomUpdateInfo* update_info, SceNpMatching2RoomUpdateInfo* sce_update_info);
@ -164,11 +182,17 @@ protected:
vm::ptr<void> cb_arg;
};
u32 generate_callback_info(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam);
callback_info take_pending_request(u32 req_id);
shared_mutex mutex_pending_requests;
std::unordered_map<u32, callback_info> pending_requests;
shared_mutex mutex_pending_sign_infos_requests;
std::unordered_map<u32, u32> pending_sign_infos_requests;
protected:
shared_mutex mutex_queue_basic_events;
std::queue<basic_event> queue_basic_events;
private:
bool is_connected = false;
bool is_psn_active = false;
@ -213,5 +237,6 @@ protected:
u8* allocate_req_result(u32 event_key, usz size);
// RPCN
rpcn_client rpcn;
shared_mutex mutex_rpcn;
std::shared_ptr<rpcn::rpcn_client> rpcn;
};

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,8 @@
#include <unordered_map>
#include <chrono>
#include <thread>
#include <semaphore>
#include "Utilities/mutex.h"
#include "util/asm.hpp"
@ -26,8 +28,7 @@ class vec_stream
public:
vec_stream() = delete;
vec_stream(std::vector<u8>& _vec, usz initial_index = 0)
: vec(_vec)
, i(initial_index){}
: vec(_vec), i(initial_index) {}
bool is_error() const
{
return error;
@ -97,33 +98,70 @@ public:
protected:
std::vector<u8>& vec;
usz i = 0;
usz i = 0;
bool error = false;
};
enum CommandType : u16
namespace rpcn
{
Login,
Terminate,
Create,
SendToken,
GetServerList,
GetWorldList,
CreateRoom,
JoinRoom,
LeaveRoom,
SearchRoom,
SetRoomDataExternal,
GetRoomDataInternal,
SetRoomDataInternal,
PingRoomOwner,
SendRoomMessage,
RequestSignalingInfos,
RequestTicket,
};
enum CommandType : u16
{
Login,
Terminate,
Create,
SendToken,
AddFriend,
RemoveFriend,
AddBlock,
RemoveBlock,
GetServerList,
GetWorldList,
CreateRoom,
JoinRoom,
LeaveRoom,
SearchRoom,
GetRoomDataExternalList,
SetRoomDataExternal,
GetRoomDataInternal,
SetRoomDataInternal,
PingRoomOwner,
SendRoomMessage,
RequestSignalingInfos,
RequestTicket,
SendMessage,
};
enum NotificationType : u16
{
UserJoinedRoom,
UserLeftRoom,
RoomDestroyed,
SignalP2PConnect,
_SignalP2PDisconnect,
FriendQuery, // Other user sent a friend request
FriendNew, // Add a friend to the friendlist(either accepted a friend request or friend accepted it)
FriendLost, // Remove friend from the friendlist(user removed friend or friend removed friend)
FriendStatus, // Set status of friend to Offline or Online
RoomMessageReceived,
MessageReceived,
};
enum class rpcn_state
{
failure_no_failure,
failure_input,
failure_wolfssl,
failure_resolve,
failure_connect,
failure_id,
failure_id_already_logged_in,
failure_id_username,
failure_id_password,
failure_id_token,
failure_protocol,
failure_other,
};
class rpcn_client
{
enum PacketType : u8
{
Request,
@ -134,123 +172,245 @@ class rpcn_client
enum ErrorType : u8
{
NoError,
Malformed,
Invalid,
InvalidInput,
ErrorLogin,
ErrorCreate,
AlreadyLoggedIn,
AlreadyJoined,
DbFail,
NotFound,
NoError, // No error
Malformed, // Query was malformed, critical error that should close the connection
Invalid, // The request type is invalid(wrong stage?)
InvalidInput, // The Input doesn't fit the constraints of the request
TooSoon, // Time limited operation attempted too soon
LoginError, // An error happened related to login
LoginAlreadyLoggedIn, // Can't log in because you're already logged in
LoginInvalidUsername, // Invalid username
LoginInvalidPassword, // Invalid password
LoginInvalidToken, // Invalid token
CreationError, // An error happened related to account creation
CreationExistingUsername, // Specific
CreationBannedEmailProvider, // Specific to Account Creation: the email provider is banned
CreationExistingEmail, // Specific to Account Creation: that email is already registered to an account
AlreadyJoined, // User tried to join a room he's already part of
DbFail, // Generic failure on db side
EmailFail, // Generic failure related to email
NotFound, // Object of the query was not found(room, user, etc)
Blocked, // The operation can't complete because you've been blocked
AlreadyFriend, // Can't add friend because already friend
Unsupported,
__error_last
};
public:
rpcn_client(bool in_config = false);
~rpcn_client();
using friend_cb_func = void (*)(void* param, NotificationType ntype, const std::string& username, bool status);
using message_cb_func = void (*)(void* param, const std::shared_ptr<std::pair<std::string, message_data>> new_msg, u64 msg_id);
bool connect(const std::string& host);
bool login(const std::string& npid, const std::string& password, const std::string& token);
bool create_user(const std::string& npid, const std::string& password, const std::string& online_name, const std::string& avatar_url, const std::string& email);
void disconnect();
bool manage_connection();
std::vector<std::pair<u16, std::vector<u8>>> get_notifications();
std::unordered_map<u32, std::pair<u16, std::vector<u8>>> get_replies();
void abort();
// Synchronous requests
bool get_server_list(u32 req_id, const SceNpCommunicationId& communication_id, std::vector<u16>& server_list);
// Asynchronous requests
bool get_world_list(u32 req_id, const SceNpCommunicationId& communication_id, u16 server_id);
bool createjoin_room(u32 req_id,const SceNpCommunicationId& communication_id, const SceNpMatching2CreateJoinRoomRequest* req);
bool join_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2JoinRoomRequest* req);
bool leave_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2LeaveRoomRequest* req);
bool search_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SearchRoomRequest* req);
bool set_roomdata_external(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SetRoomDataExternalRequest* req);
bool get_roomdata_internal(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2GetRoomDataInternalRequest* req);
bool set_roomdata_internal(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SetRoomDataInternalRequest* req);
bool ping_room_owner(u32 req_id, const SceNpCommunicationId& communication_id, u64 room_id);
bool send_room_message(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SendRoomMessageRequest* req);
bool req_sign_infos(u32 req_id, const std::string& npid);
bool req_ticket(u32 req_id, const std::string& service_id);
const std::string& get_online_name() const
struct friend_data
{
return online_name;
}
const std::string& get_avatar_url() const
{
return avatar_url;
}
u32 get_addr_sig() const
{
return addr_sig.load();
}
u16 get_port_sig() const
{
return port_sig.load();
}
protected:
enum class recvn_result
{
recvn_success,
recvn_nodata,
recvn_timeout,
recvn_noconn,
recvn_fatal,
std::map<std::string, std::pair<bool, u64>> friends;
std::set<std::string> requests_sent;
std::set<std::string> requests_received;
std::set<std::string> blocked;
};
recvn_result recvn(u8* buf, usz n);
class rpcn_client
{
private:
static inline std::weak_ptr<rpcn_client> instance;
static inline shared_mutex inst_mutex;
bool get_reply(u32 expected_id, std::vector<u8>& data);
atomic_t<bool> connected = false;
atomic_t<bool> authentified = false;
atomic_t<bool> want_auth = false;
std::binary_semaphore sem_connected, sem_authentified;
std::mutex mutex_connected, mutex_authentified;
std::vector<u8> forge_request(u16 command, u32 packet_id, const std::vector<u8>& data) const;
bool send_packet(const std::vector<u8>& packet);
bool forge_send(u16 command, u32 packet_id, const std::vector<u8>& data);
bool forge_send_reply(u16 command, u32 packet_id, const std::vector<u8>& data, std::vector<u8>& reply_data);
std::binary_semaphore sem_reader, sem_writer, sem_rpcn;
std::mutex mutex_read, mutex_write;
bool is_error(ErrorType err) const;
bool error_and_disconnect(const std::string& error_mgs);
bool is_abort() const;
std::thread thread_rpcn, thread_rpcn_reader, thread_rpcn_writer;
std::string get_wolfssl_error(int error) const;
atomic_t<bool> terminate = false;
protected:
atomic_t<bool> connected = false;
atomic_t<bool> authentified = false;
atomic_t<u32> num_failures = 0;
atomic_t<rpcn_state> state = rpcn_state::failure_no_failure;
WOLFSSL_CTX* wssl_ctx = nullptr;
WOLFSSL* wssl = nullptr;
std::vector<std::vector<u8>> packets_to_send;
std::mutex mutex_packets_to_send;
bool in_config = false;
bool abort_config = false;
// Friends related
shared_mutex mutex_friends;
std::set<std::pair<friend_cb_func, void*>> friend_cbs;
friend_data friend_infos;
atomic_t<bool> server_info_received = false;
u32 received_version = 0;
void handle_friend_notification(u16 command, std::vector<u8> data);
// UDP Signaling related
steady_clock::time_point last_ping_time{}, last_pong_time{};
void handle_message(std::vector<u8> data);
sockaddr_in addr_rpcn{};
sockaddr_in addr_rpcn_udp{};
int sockfd = 0;
shared_mutex mutex_socket;
private:
rpcn_client();
shared_mutex mutex_notifs, mutex_replies, mutex_replies_sync;
std::vector<std::pair<u16, std::vector<u8>>> notifications; // notif type / data
std::unordered_map<u32, std::pair<u16, std::vector<u8>>> replies; // req id / (command / data)
std::unordered_map<u32, std::pair<u16, std::vector<u8>>> replies_sync; // same but for sync replies(Login, Create, GetServerList)
void rpcn_reader_thread();
void rpcn_writer_thread();
void rpcn_thread();
std::string online_name{};
std::string avatar_url{};
bool handle_input();
bool handle_output();
s64 user_id = 0;
void add_packet(const std::vector<u8> packet);
atomic_t<u32> addr_sig{};
atomic_t<u16> port_sig{};
};
private:
enum class recvn_result
{
recvn_success,
recvn_nodata,
recvn_timeout,
recvn_noconn,
recvn_terminate,
recvn_fatal,
};
recvn_result recvn(u8* buf, usz n);
bool send_packet(const std::vector<u8>& packet);
private:
bool connect(const std::string& host);
bool login(const std::string& npid, const std::string& password, const std::string& token);
void disconnect();
public:
~rpcn_client();
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();
rpcn_state wait_for_authentified();
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);
bool add_friend(const std::string& friend_username);
bool remove_friend(const std::string& friend_username);
u32 get_num_friends() const;
u32 get_num_blocks() const;
std::vector<std::pair<u16, std::vector<u8>>> get_notifications();
std::unordered_map<u32, std::pair<u16, std::vector<u8>>> get_replies();
std::vector<u64> get_new_messages();
std::optional<std::shared_ptr<std::pair<std::string, message_data>>> get_message(u64 id);
std::vector<std::pair<u64, std::shared_ptr<std::pair<std::string, message_data>>>> get_messages_and_register_cb(SceNpBasicMessageMainType type, bool include_bootable, message_cb_func cb_func, void* cb_param);
void remove_message_cb(message_cb_func cb_func, void* cb_param);
void discard_active_message(u64 id);
bool is_connected() const
{
return connected;
}
bool is_authentified() const
{
return authentified;
}
rpcn_state get_rpcn_state() const
{
return state;
}
void server_infos_updated();
// Synchronous requests
bool get_server_list(u32 req_id, const SceNpCommunicationId& communication_id, std::vector<u16>& server_list);
// Asynchronous requests
bool get_world_list(u32 req_id, const SceNpCommunicationId& communication_id, u16 server_id);
bool createjoin_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2CreateJoinRoomRequest* req);
bool join_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2JoinRoomRequest* req);
bool leave_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2LeaveRoomRequest* req);
bool search_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SearchRoomRequest* req);
bool get_roomdata_external_list(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2GetRoomDataExternalListRequest* req);
bool set_roomdata_external(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SetRoomDataExternalRequest* req);
bool get_roomdata_internal(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2GetRoomDataInternalRequest* req);
bool set_roomdata_internal(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SetRoomDataInternalRequest* req);
bool ping_room_owner(u32 req_id, const SceNpCommunicationId& communication_id, u64 room_id);
bool send_room_message(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SendRoomMessageRequest* req);
bool req_sign_infos(u32 req_id, const std::string& npid);
bool req_ticket(u32 req_id, const std::string& service_id);
bool sendmessage(const message_data& msg_data, const std::set<std::string>& npids);
const std::string& get_online_name() const
{
return online_name;
}
const std::string& get_avatar_url() const
{
return avatar_url;
}
u32 get_addr_sig() const
{
return addr_sig.load();
}
u16 get_port_sig() const
{
return port_sig.load();
}
private:
bool get_reply(u64 expected_id, std::vector<u8>& data);
std::vector<u8> forge_request(u16 command, u64 packet_id, const std::vector<u8>& data) const;
bool forge_send(u16 command, u64 packet_id, const std::vector<u8>& data);
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;
private:
WOLFSSL_CTX* wssl_ctx = nullptr;
WOLFSSL* read_wssl = nullptr;
WOLFSSL* write_wssl = nullptr;
atomic_t<bool> server_info_received = false;
u32 received_version = 0;
// UDP Signaling related
steady_clock::time_point last_ping_time{}, last_pong_time{};
sockaddr_in addr_rpcn{};
sockaddr_in addr_rpcn_udp{};
int sockfd = 0;
atomic_t<u64> rpcn_request_counter = 0x100000001; // Counter used for commands whose result is not forwarded to NP handler(login, create, sendmessage, etc)
shared_mutex mutex_notifs, mutex_replies, mutex_replies_sync;
std::vector<std::pair<u16, std::vector<u8>>> notifications; // notif type / data
std::unordered_map<u32, std::pair<u16, std::vector<u8>>> replies; // req id / (command / data)
std::unordered_map<u64, std::pair<u16, std::vector<u8>>> replies_sync; // same but for sync replies(Login, Create, GetServerList)
// Messages
struct message_cb_t
{
message_cb_func cb_func;
void* cb_param;
SceNpBasicMessageMainType type_filter;
bool inc_bootable;
bool operator<(const message_cb_t& other) const
{
return (cb_func < other.cb_func) || ((!(other.cb_func < cb_func)) && (cb_param < other.cb_param));
}
};
shared_mutex mutex_messages;
std::set<message_cb_t> message_cbs;
std::unordered_map<u64, std::shared_ptr<std::pair<std::string, message_data>>> messages; // msg id / (sender / message)
std::set<u64> active_messages; // msg id of messages that have not been discarded
std::vector<u64> new_messages; // list of msg_id used to inform np_handler of new messages
u64 message_counter = 3; // id counter
std::string online_name{};
std::string avatar_url{};
s64 user_id = 0;
atomic_t<u32> addr_sig{};
atomic_t<u16> port_sig{};
};
} // namespace rpcn

View File

@ -290,6 +290,7 @@ void signaling_handler::process_incoming_messages()
retire_packet(si, signal_connect);
// connection is active
update_si_addr(si, op_addr, op_port);
update_si_mapped_addr(si, sp->sent_addr, sp->sent_port);
update_si_status(si, SCE_NP_SIGNALING_CONN_STATUS_ACTIVE);
break;
case signal_confirm:
@ -299,6 +300,7 @@ void signaling_handler::process_incoming_messages()
retire_packet(si, signal_connect_ack);
// connection is active
update_si_addr(si, op_addr, op_port);
update_si_mapped_addr(si, sp->sent_addr, sp->sent_port);
update_si_status(si, SCE_NP_SIGNALING_CONN_STATUS_ACTIVE, true);
break;
case signal_finished:
@ -418,6 +420,27 @@ void signaling_handler::update_si_addr(std::shared_ptr<signaling_info>& si, u32
}
}
void signaling_handler::update_si_mapped_addr(std::shared_ptr<signaling_info>& si, u32 new_addr, u16 new_port)
{
ensure(si);
if (si->addr != new_addr || si->port != new_port)
{
in_addr addr_old, addr_new;
addr_old.s_addr = si->addr;
addr_new.s_addr = new_addr;
char ip_str_old[16];
char ip_str_new[16];
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);
si->mapped_addr = new_addr;
si->mapped_port = new_port;
}
}
void signaling_handler::update_si_status(std::shared_ptr<signaling_info>& si, s32 new_status, bool confirm_packet)
{
if (!si)
@ -485,6 +508,9 @@ 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);

View File

@ -21,6 +21,10 @@ struct signaling_info
u32 addr = 0;
u16 port = 0;
// User seen from that peer
u32 mapped_addr = 0;
u16 mapped_port = 0;
// For handler
steady_clock::time_point time_last_msg_recvd = steady_clock::now();
@ -84,6 +88,8 @@ private:
be_t<u32> signature = SIGNALING_SIGNATURE;
le_t<u32> version;
le_t<SignalingCommand> command;
le_t<u32> sent_addr;
le_t<u16> sent_port;
union {
struct
{
@ -117,6 +123,7 @@ private:
u32 create_sig_infos(const SceNpId* npid);
static void update_si_addr(std::shared_ptr<signaling_info>& si, u32 new_addr, u16 new_port);
static void update_si_mapped_addr(std::shared_ptr<signaling_info>& si, u32 new_addr, u16 new_port);
void update_si_status(std::shared_ptr<signaling_info>& si, s32 new_status, bool confirm_packet = false);
void signal_sig_callback(u32 conn_id, int event);
void signal_ext_sig_callback(u32 conn_id, int event) const;

View File

@ -74,6 +74,8 @@ struct EmuCallbacks
std::function<std::shared_ptr<class MsgDialogBase>()> get_msg_dialog;
std::function<std::shared_ptr<class OskDialogBase>()> get_osk_dialog;
std::function<std::unique_ptr<class SaveDialogBase>()> get_save_dialog;
std::function<std::shared_ptr<class SendMessageDialogBase>()> get_sendmessage_dialog;
std::function<std::shared_ptr<class RecvMessageDialogBase>()> get_recvmessage_dialog;
std::function<std::unique_ptr<class TrophyNotificationBase>()> get_trophy_notification_dialog;
std::function<std::string(localized_string_id, const char*)> get_localized_string;
std::function<std::u32string(localized_string_id, const char*)> get_localized_u32string;

View File

@ -247,8 +247,8 @@ void fmt_class_string<np_psn_status>::format(std::string& out, u64 arg)
switch (value)
{
case np_psn_status::disabled: return "Disconnected";
case np_psn_status::fake: return "Simulated";
case np_psn_status::rpcn: return "RPCN";
case np_psn_status::psn_fake: return "Simulated";
case np_psn_status::psn_rpcn: return "RPCN";
}
return unknown;

View File

@ -184,8 +184,8 @@ enum class np_internet_status
enum np_psn_status
{
disabled,
fake,
rpcn,
psn_fake,
psn_rpcn,
};
enum class shader_mode

View File

@ -303,6 +303,9 @@
<ClCompile Include="QTGeneratedFiles\Debug\moc_pkg_install_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_recvmessage_dialog_frame.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_register_editor_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -330,6 +333,9 @@
<ClCompile Include="QTGeneratedFiles\Debug\moc_screenshot_preview.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_sendmessage_dialog_frame.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_settings.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -495,6 +501,9 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_pkg_install_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_recvmessage_dialog_frame.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_register_editor_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -522,6 +531,9 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_screenshot_preview.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_sendmessage_dialog_frame.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_settings.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
@ -582,10 +594,12 @@
<ClCompile Include="rpcs3qt\patch_manager_dialog.cpp" />
<ClCompile Include="rpcs3qt\pkg_install_dialog.cpp" />
<ClCompile Include="rpcs3qt\persistent_settings.cpp" />
<ClCompile Include="rpcs3qt\recvmessage_dialog_frame.cpp" />
<ClCompile Include="rpcs3qt\render_creator.cpp" />
<ClCompile Include="rpcs3qt\rpcn_settings_dialog.cpp" />
<ClCompile Include="rpcs3qt\screenshot_manager_dialog.cpp" />
<ClCompile Include="rpcs3qt\screenshot_preview.cpp" />
<ClCompile Include="rpcs3qt\sendmessage_dialog_frame.cpp" />
<ClCompile Include="rpcs3qt\settings.cpp" />
<ClCompile Include="rpcs3qt\skylander_dialog.cpp" />
<ClCompile Include="rpcs3qt\tooltips.cpp" />
@ -1064,7 +1078,27 @@
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
</CustomBuild>
<CustomBuild Include="rpcs3qt\recvmessage_dialog_frame.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
</CustomBuild>
<ClInclude Include="rpcs3qt\richtext_item_delegate.h" />
<CustomBuild Include="rpcs3qt\sendmessage_dialog_frame.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
</CustomBuild>
<ClInclude Include="rpcs3qt\stylesheets.h" />
<CustomBuild Include="rpcs3qt\skylander_dialog.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>

View File

@ -130,6 +130,9 @@
<Filter Include="Gui\custom items">
<UniqueIdentifier>{949cff6d-9cc5-4a8a-a453-a5144da8ecf4}</UniqueIdentifier>
</Filter>
<Filter Include="Gui\rpcn">
<UniqueIdentifier>{dfd401ec-dd22-4f8a-9dea-565a03efab6a}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
@ -711,9 +714,6 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_downloader.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\rpcn_settings_dialog.cpp">
<Filter>Gui\settings</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_rpcn_settings_dialog.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
@ -756,6 +756,27 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_patch_creator_dialog.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\rpcn_settings_dialog.cpp">
<Filter>Gui\rpcn</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\sendmessage_dialog_frame.cpp">
<Filter>Gui\message dialog</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_sendmessage_dialog_frame.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_sendmessage_dialog_frame.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\recvmessage_dialog_frame.cpp">
<Filter>Gui\message dialog</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_recvmessage_dialog_frame.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_recvmessage_dialog_frame.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Input\ds4_pad_handler.h">
@ -1096,9 +1117,6 @@
<CustomBuild Include="rpcs3qt\downloader.h">
<Filter>Gui\network</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\rpcn_settings_dialog.h">
<Filter>Gui\settings</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\localized_emu.h">
<Filter>Gui\settings</Filter>
</CustomBuild>
@ -1111,6 +1129,15 @@
<CustomBuild Include="rpcs3qt\patch_creator_dialog.h">
<Filter>Gui\patch manager</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\rpcn_settings_dialog.h">
<Filter>Gui\rpcn</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\sendmessage_dialog_frame.h">
<Filter>Gui\message dialog</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\recvmessage_dialog_frame.h">
<Filter>Gui\message dialog</Filter>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<Image Include="rpcs3.ico" />

View File

@ -51,6 +51,7 @@ set(SRC_FILES
progress_dialog.cpp
qt_utils.cpp
register_editor_dialog.cpp
recvmessage_dialog_frame.cpp
render_creator.cpp
rpcn_settings_dialog.cpp
rsx_debugger.cpp
@ -60,6 +61,7 @@ set(SRC_FILES
save_manager_dialog.cpp
screenshot_manager_dialog.cpp
screenshot_preview.cpp
sendmessage_dialog_frame.cpp
settings.cpp
settings_dialog.cpp
skylander_dialog.cpp

View File

@ -1041,8 +1041,8 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_
switch (static_cast<np_psn_status>(index))
{
case np_psn_status::disabled: return tr("Disconnected", "PSN Status");
case np_psn_status::fake: return tr("Simulated", "PSN Status");
case np_psn_status::rpcn: return tr("RPCN", "PSN Status");
case np_psn_status::psn_fake: return tr("Simulated", "PSN Status");
case np_psn_status::psn_rpcn: return tr("RPCN", "PSN Status");
}
break;
case emu_settings_type::SleepTimersAccuracy:

View File

@ -163,19 +163,3 @@ private Q_SLOTS:
void handle_download_error(const QString& error);
void handle_download_finished(const QByteArray& content);
};
class compat_pixmap : public QPixmap
{
public:
compat_pixmap(const QColor& color, qreal pixel_ratio) : QPixmap(pixel_ratio * 16, pixel_ratio * 16)
{
fill(Qt::transparent);
QPainter painter(this);
setDevicePixelRatio(pixel_ratio);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::NoPen);
painter.setBrush(color);
painter.drawEllipse(0, 0, width(), height());
}
};

View File

@ -2350,7 +2350,7 @@ void game_list_frame::PopulateGameList()
compat_item->setToolTip(game->compat.tooltip);
if (!game->compat.color.isEmpty())
{
compat_item->setData(Qt::DecorationRole, compat_pixmap(game->compat.color, devicePixelRatioF() * 2));
compat_item->setData(Qt::DecorationRole, gui::utils::circle_pixmap(game->compat.color, devicePixelRatioF() * 2));
}
// Version

View File

@ -1,3 +1,8 @@
#ifdef _WIN32
// This is to avoid inclusion of winsock.h from windows.h header which creates conflicts with inclusion of winsock2.h later
#define _WINSOCKAPI_
#endif
#include "gui_application.h"
#include "qt_utils.h"
@ -23,6 +28,8 @@
#include "save_data_dialog.h"
#include "msg_dialog_frame.h"
#include "osk_dialog_frame.h"
#include "recvmessage_dialog_frame.h"
#include "sendmessage_dialog_frame.h"
#include "stylesheets.h"
#include <QScreen>
@ -346,6 +353,8 @@ void gui_application::InitializeCallbacks()
callbacks.get_msg_dialog = [this]() -> std::shared_ptr<MsgDialogBase> { return m_show_gui ? std::make_shared<msg_dialog_frame>() : nullptr; };
callbacks.get_osk_dialog = [this]() -> std::shared_ptr<OskDialogBase> { return m_show_gui ? std::make_shared<osk_dialog_frame>() : nullptr; };
callbacks.get_save_dialog = []() -> std::unique_ptr<SaveDialogBase> { return std::make_unique<save_data_dialog>(); };
callbacks.get_sendmessage_dialog = [this]() -> std::shared_ptr<SendMessageDialogBase> { return std::make_shared<sendmessage_dialog_frame>(); };
callbacks.get_recvmessage_dialog = [this]() -> std::shared_ptr<RecvMessageDialogBase> { return std::make_shared<recvmessage_dialog_frame>(); };
callbacks.get_trophy_notification_dialog = [this]() -> std::unique_ptr<TrophyNotificationBase> { return std::make_unique<trophy_notification_helper>(m_game_window); };
callbacks.on_run = [this](bool start_playtime) { OnEmulatorRun(start_playtime); };

View File

@ -10,6 +10,7 @@
#include <QTableWidget>
#include <QHeaderView>
#include <QTreeWidgetItem>
#include <QPainter>
#include <string>
@ -17,6 +18,23 @@ namespace gui
{
namespace utils
{
class circle_pixmap : public QPixmap
{
public:
circle_pixmap(const QColor& color, qreal pixel_ratio)
: QPixmap(pixel_ratio * 16, pixel_ratio * 16)
{
fill(Qt::transparent);
QPainter painter(this);
setDevicePixelRatio(pixel_ratio);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::NoPen);
painter.setBrush(color);
painter.drawEllipse(0, 0, width(), height());
}
};
template<typename T>
static QSet<T> list_to_set(const QList<T>& list)
{

View File

@ -0,0 +1,125 @@
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QMessageBox>
#include "recvmessage_dialog_frame.h"
#include "util/logs.hpp"
LOG_CHANNEL(recvmessage_dlg_log, "recvmessage dlg");
void recvmessage_callback(void* param, const std::shared_ptr<std::pair<std::string, message_data>> new_msg, u64 msg_id)
{
auto* dlg = static_cast<recvmessage_dialog_frame*>(param);
dlg->callback_handler(std::move(new_msg), msg_id);
}
recvmessage_dialog_frame::~recvmessage_dialog_frame()
{
if (m_dialog)
{
m_dialog->deleteLater();
}
}
bool recvmessage_dialog_frame::Exec(SceNpBasicMessageMainType type, SceNpBasicMessageRecvOptions options, SceNpBasicMessageRecvAction& recv_result, u64& chosen_msg_id)
{
qRegisterMetaType<recvmessage_signal_struct>();
if (m_dialog)
{
m_dialog->close();
delete m_dialog;
}
m_dialog = new custom_dialog(false);
m_dialog->setModal(true);
m_dialog->setWindowTitle(tr("Choose message:"));
m_rpcn = rpcn::rpcn_client::get_instance();
QVBoxLayout* vbox_global = new QVBoxLayout();
m_lst_messages = new QListWidget();
vbox_global->addWidget(m_lst_messages);
QHBoxLayout* hbox_btns = new QHBoxLayout();
hbox_btns->addStretch();
QPushButton* btn_accept = new QPushButton(tr("Accept"));
QPushButton* btn_deny = new QPushButton(tr("Deny"));
QPushButton* btn_cancel = new QPushButton(tr("Cancel"));
hbox_btns->addWidget(btn_accept);
hbox_btns->addWidget(btn_deny);
hbox_btns->addWidget(btn_cancel);
vbox_global->addLayout(hbox_btns);
m_dialog->setLayout(vbox_global);
bool result = false;
const bool preserve = options & SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_PRESERVE;
const bool include_bootable = options & SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_INCLUDE_BOOTABLE;
auto accept_or_deny = [preserve, this, &result, &recv_result, &chosen_msg_id](SceNpBasicMessageRecvAction result_from_action)
{
auto selected = m_lst_messages->selectedItems();
if (selected.empty())
{
QMessageBox::critical(m_dialog, tr("Error receiving a message!"), tr("You must select a message!"), QMessageBox::Ok);
return;
}
chosen_msg_id = selected[0]->data(Qt::UserRole).toULongLong();
recv_result = result_from_action;
result = true;
if (!preserve)
{
m_rpcn->discard_active_message(chosen_msg_id);
}
m_dialog->close();
};
connect(btn_accept, &QAbstractButton::clicked, this, [&accept_or_deny]()
{ accept_or_deny(SCE_NP_BASIC_MESSAGE_ACTION_ACCEPT); });
connect(btn_deny, &QAbstractButton::clicked, this, [&accept_or_deny]()
{ accept_or_deny(SCE_NP_BASIC_MESSAGE_ACTION_DENY); });
connect(this, &recvmessage_dialog_frame::signal_new_message, this, &recvmessage_dialog_frame::slot_new_message);
// Get list of messages
const auto messages = m_rpcn->get_messages_and_register_cb(type, include_bootable, recvmessage_callback, this);
for (const auto& message : messages)
{
add_message(message.second, message.first);
}
m_dialog->exec();
m_rpcn->remove_message_cb(recvmessage_callback, this);
return result;
}
void recvmessage_dialog_frame::add_message(const std::shared_ptr<std::pair<std::string, message_data>>& msg, u64 msg_id)
{
ensure(msg);
auto new_item = new QListWidgetItem(QString::fromStdString(msg->first));
new_item->setData(Qt::UserRole, static_cast<qulonglong>(msg_id));
m_lst_messages->addItem(new_item);
}
void recvmessage_dialog_frame::slot_new_message(recvmessage_signal_struct msg_and_id)
{
add_message(msg_and_id.msg, msg_and_id.msg_id);
}
void recvmessage_dialog_frame::callback_handler(std::shared_ptr<std::pair<std::string, message_data>> new_msg, u64 msg_id)
{
recvmessage_signal_struct signal_struct = {
.msg = new_msg,
.msg_id = msg_id,
};
Q_EMIT signal_new_message(signal_struct);
}

View File

@ -0,0 +1,41 @@
#pragma once
#include <QObject>
#include <QListWidget>
#include "util/types.hpp"
#include "custom_dialog.h"
#include "Emu/NP/rpcn_client.h"
struct recvmessage_signal_struct
{
std::shared_ptr<std::pair<std::string, message_data>> msg;
u64 msg_id;
};
Q_DECLARE_METATYPE(recvmessage_signal_struct);
class recvmessage_dialog_frame : public QObject, public RecvMessageDialogBase
{
Q_OBJECT
public:
recvmessage_dialog_frame() = default;
~recvmessage_dialog_frame();
bool Exec(SceNpBasicMessageMainType type, SceNpBasicMessageRecvOptions options, SceNpBasicMessageRecvAction& recv_result, u64& chosen_msg_id) override;
void callback_handler(const std::shared_ptr<std::pair<std::string, message_data>> new_msg, u64 msg_id);
private:
void add_message(const std::shared_ptr<std::pair<std::string, message_data>>& msg, u64 msg_id);
Q_SIGNALS:
void signal_new_message(const recvmessage_signal_struct msg_and_id);
private Q_SLOTS:
void slot_new_message(const recvmessage_signal_struct msg_and_id);
private:
custom_dialog* m_dialog = nullptr;
QListWidget* m_lst_messages = nullptr;
std::shared_ptr<rpcn::rpcn_client> m_rpcn;
};

View File

@ -4,20 +4,90 @@
#include <QPushButton>
#include <QRegExpValidator>
#include <QInputDialog>
#include <QGroupBox>
#include <QMenu>
#include <thread>
#include "qt_utils.h"
#include "rpcn_settings_dialog.h"
#include "Emu/NP/rpcn_config.h"
#include "Emu/NP/rpcn_client.h"
#include <wolfssl/ssl.h>
#include <wolfssl/openssl/evp.h>
rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent)
: QDialog(parent)
LOG_CHANNEL(rpcn_settings_log, "rpcn settings dlg");
const QString rpcn_state_to_qstr(rpcn::rpcn_state state)
{
setWindowTitle(tr("RPCN Configuration"));
switch (state)
{
case rpcn::rpcn_state::failure_no_failure: return QObject::tr("No Error");
case rpcn::rpcn_state::failure_input: return QObject::tr("Invalid Input");
case rpcn::rpcn_state::failure_wolfssl: return QObject::tr("WolfSSL Error");
case rpcn::rpcn_state::failure_resolve: return QObject::tr("Resolve Error");
case rpcn::rpcn_state::failure_connect: return QObject::tr("Connect Error");
case rpcn::rpcn_state::failure_id: return QObject::tr("Identification Error");
case rpcn::rpcn_state::failure_id_already_logged_in: return QObject::tr("Identification Error: User Already Logged In");
case rpcn::rpcn_state::failure_id_username: return QObject::tr("Identification Error: Invalid Username");
case rpcn::rpcn_state::failure_id_password: return QObject::tr("Identification Error: Invalid Password");
case rpcn::rpcn_state::failure_id_token: return QObject::tr("Identification Error: Invalid Token");
case rpcn::rpcn_state::failure_protocol: return QObject::tr("Protocol Version Error");
case rpcn::rpcn_state::failure_other: return QObject::tr("Unknown Error");
default: return QObject::tr("Unhandled rpcn state!");
}
}
bool validate_rpcn_username(const std::string& input)
{
if (input.length() < 3 || input.length() > 16)
return false;
return std::all_of(input.cbegin(), input.cend(), [](const char c)
{ return std::isalnum(c) || c == '-' || c == '_'; });
}
rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent)
: QDialog(parent)
{
setWindowTitle(tr("RPCN"));
setObjectName("rpcn_settings_dialog");
setMinimumSize(QSize(400, 100));
QVBoxLayout* vbox_global = new QVBoxLayout();
QGroupBox* group_btns = new QGroupBox(tr("RPCN"));
QHBoxLayout* hbox_group = new QHBoxLayout();
QPushButton* btn_account = new QPushButton(tr("Account"));
QPushButton* btn_friends = new QPushButton(tr("Friends"));
hbox_group->addWidget(btn_account);
hbox_group->addWidget(btn_friends);
group_btns->setLayout(hbox_group);
vbox_global->addWidget(group_btns);
setLayout(vbox_global);
connect(btn_account, &QPushButton::clicked, this, [this]()
{
rpcn_account_dialog dlg(this);
dlg.exec();
});
connect(btn_friends, &QPushButton::clicked, this, [this]()
{
rpcn_friends_dialog dlg(this);
if (dlg.is_ok())
dlg.exec();
});
}
rpcn_account_dialog::rpcn_account_dialog(QWidget* parent)
: QDialog(parent)
{
setWindowTitle(tr("RPCN: Configuration"));
setObjectName("rpcn_account_dialog");
setMinimumSize(QSize(400, 200));
QVBoxLayout* vbox_global = new QVBoxLayout();
@ -34,12 +104,16 @@ rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent)
m_edit_npid->setValidator(new QRegExpValidator(QRegExp("^[a-zA-Z0-9_\\-]*$"), this));
QLabel* label_pass = new QLabel(tr("Password:"));
QPushButton* btn_chg_pass = new QPushButton(tr("Set Password"));
QLabel *label_token = new QLabel(tr("Token:"));
QLabel* label_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_save = new QPushButton(tr("Save"), this);
QPushButton* btn_create = new QPushButton(tr("Create Account"), this);
QPushButton* btn_resendtoken = new QPushButton(tr("Resend Token"), this);
btn_resendtoken->setEnabled(false);
QPushButton* btn_changepass = new QPushButton(tr("Change Password"), this);
btn_changepass->setEnabled(false);
QPushButton* btn_save = new QPushButton(tr("Save"), this);
vbox_labels->addWidget(label_host);
vbox_labels->addWidget(label_npid);
@ -52,6 +126,8 @@ rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent)
vbox_edits->addWidget(m_edit_token);
hbox_buttons->addWidget(btn_create);
hbox_buttons->addWidget(btn_resendtoken);
hbox_buttons->addWidget(btn_changepass);
hbox_buttons->addStretch();
hbox_buttons->addWidget(btn_save);
@ -63,52 +139,43 @@ rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent)
setLayout(vbox_global);
connect(btn_chg_pass, &QAbstractButton::clicked, [this]()
{
QString password;
while (true)
connect(btn_chg_pass, &QAbstractButton::clicked, this, [this]()
{
bool clicked_ok = false;
password = QInputDialog::getText(this, "Set/Change Password", "Set your password", QLineEdit::Password, "", &clicked_ok);
if (!clicked_ok)
rpcn_ask_password_dialog ask_pass;
ask_pass.exec();
auto password = ask_pass.get_password();
if (!password)
return;
if (password.isEmpty())
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++)
{
QMessageBox::critical(this, tr("Wrong input"), tr("You need to enter a password!"), QMessageBox::Ok);
constexpr auto pal = "0123456789abcdef";
hash[i * 2] = pal[salted_pass[i] >> 4];
hash[1 + i * 2] = pal[salted_pass[i] & 15];
}
else
{
break;
}
}
const std::string pass_str = password.toStdString();
const std::string salt_str = "No matter where you go, everybody's connected.";
g_cfg_rpcn.set_password(hash);
g_cfg_rpcn.save();
u8 salted_pass[SHA_DIGEST_SIZE];
QMessageBox::information(this, tr("RPCN Password Saved"), tr("Your password was saved successfully!"), QMessageBox::Ok);
});
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++)
connect(btn_save, &QAbstractButton::clicked, this, [this]()
{
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.save();
});
connect(btn_save, &QAbstractButton::clicked, [this]()
{
if (this->save_config())
this->close();
});
connect(btn_create, &QAbstractButton::clicked, [this]() { this->create_account(); });
if (this->save_config())
this->close();
});
connect(btn_create, &QAbstractButton::clicked, this, [this]()
{ this->create_account(); });
g_cfg_rpcn.load();
@ -117,20 +184,12 @@ rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent)
m_edit_token->setText(QString::fromStdString(g_cfg_rpcn.get_token()));
}
bool rpcn_settings_dialog::save_config()
bool rpcn_account_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();
auto validate = [](const std::string& input) -> bool
{
if (input.length() < 3 || input.length() > 16)
return false;
return std::all_of(input.cbegin(), input.cend(), [](const char c) { return std::isalnum(c) || c == '-' || c == '_'; });
};
if (host.empty())
{
QMessageBox::critical(this, tr("Missing host"), tr("You need to enter a host for rpcn!"), QMessageBox::Ok);
@ -143,7 +202,7 @@ bool rpcn_settings_dialog::save_config()
return false;
}
if (!validate(npid))
if (!validate_rpcn_username(npid))
{
QMessageBox::critical(this, tr("Invalid character"), tr("NPID must be between 3 and 16 characters and can only contain '-', '_' or alphanumeric characters."), QMessageBox::Ok);
return false;
@ -164,7 +223,7 @@ bool rpcn_settings_dialog::save_config()
return true;
}
bool rpcn_settings_dialog::create_account()
bool rpcn_account_dialog::create_account()
{
// Validate and save
if (!save_config())
@ -176,7 +235,7 @@ bool rpcn_settings_dialog::create_account()
while (true)
{
bool clicked_ok = false;
email = QInputDialog::getText(this, "Email address", "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);
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 false;
@ -191,7 +250,7 @@ bool rpcn_settings_dialog::create_account()
}
}
const auto rpcn = std::make_shared<rpcn_client>(true);
const auto rpcn = rpcn::rpcn_client::get_instance();
const auto host = g_cfg_rpcn.get_host();
const auto npid = g_cfg_rpcn.get_npid();
@ -199,32 +258,367 @@ bool rpcn_settings_dialog::create_account()
const auto avatar_url = "https://rpcs3.net/cdn/netplay/DefaultAvatar.png";
const auto password = g_cfg_rpcn.get_password();
std::thread(
[](const std::shared_ptr<rpcn_client> rpcn)
{
while (rpcn.use_count() != 1)
rpcn->manage_connection();
rpcn->disconnect();
},
rpcn)
.detach();
if (!rpcn->connect(host))
if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure)
{
QMessageBox::critical(this, tr("Error Connecting"), tr("Failed to connect to RPCN server"), QMessageBox::Ok);
rpcn->abort();
const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(rpcn_state_to_qstr(result));
QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok);
return false;
}
if (!rpcn->create_user(npid, password, online_name, avatar_url, email.toStdString()))
if (auto error = rpcn->create_user(npid, password, online_name, avatar_url, email.toStdString()); error != rpcn::ErrorType::NoError)
{
QMessageBox::critical(this, tr("Error Creating Account"), tr("Failed to create the account"), QMessageBox::Ok);
rpcn->abort();
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 false;
}
QMessageBox::information(this, tr("Account created!"), tr("Your account has been created successfully!\nCheck your email for your token!"), QMessageBox::Ok);
rpcn->abort();
return true;
}
rpcn_ask_password_dialog::rpcn_ask_password_dialog(QWidget* parent)
: QDialog(parent)
{
QVBoxLayout* vbox_global = new QVBoxLayout();
QHBoxLayout* hbox_buttons = new QHBoxLayout();
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);
QPushButton* btn_ok = new QPushButton(tr("Ok"));
QPushButton* btn_cancel = new QPushButton(tr("Cancel"));
vbox_global->addWidget(label_pass1);
vbox_global->addWidget(m_edit_pass1);
vbox_global->addWidget(label_pass2);
vbox_global->addWidget(m_edit_pass2);
hbox_buttons->addStretch();
hbox_buttons->addWidget(btn_ok);
hbox_buttons->addWidget(btn_cancel);
vbox_global->addLayout(hbox_buttons);
setLayout(vbox_global);
connect(btn_ok, &QAbstractButton::clicked, this, [this]()
{
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("Wrong input"), tr("You need to enter a password!"), QMessageBox::Ok);
return;
}
m_password = m_edit_pass1->text().toStdString();
close();
});
connect(btn_cancel, &QAbstractButton::clicked, this, [this]()
{ this->close(); });
}
std::optional<std::string> rpcn_ask_password_dialog::get_password()
{
return m_password;
}
void friend_callback(void* param, rpcn::NotificationType ntype, const std::string& username, bool status)
{
auto* dlg = static_cast<rpcn_friends_dialog*>(param);
dlg->callback_handler(ntype, username, status);
}
rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent)
: QDialog(parent),
m_green_icon(gui::utils::circle_pixmap(QColorConstants::Svg::green, devicePixelRatioF() * 2)),
m_red_icon(gui::utils::circle_pixmap(QColorConstants::Svg::red, devicePixelRatioF() * 2)),
m_yellow_icon(gui::utils::circle_pixmap(QColorConstants::Svg::yellow, devicePixelRatioF() * 2)),
m_orange_icon(gui::utils::circle_pixmap(QColorConstants::Svg::orange, devicePixelRatioF() * 2)),
m_black_icon(gui::utils::circle_pixmap(QColorConstants::Svg::black, devicePixelRatioF() * 2))
{
setWindowTitle(tr("RPCN: Friends"));
setObjectName("rpcn_friends_dialog");
setMinimumSize(QSize(400, 100));
QVBoxLayout* vbox_global = new QVBoxLayout();
QHBoxLayout* hbox_groupboxes = new QHBoxLayout();
QGroupBox* grp_list_friends = new QGroupBox(tr("Friends"));
QVBoxLayout* vbox_lst_friends = new QVBoxLayout();
m_lst_friends = new QListWidget(this);
m_lst_friends->setContextMenuPolicy(Qt::CustomContextMenu);
vbox_lst_friends->addWidget(m_lst_friends);
QPushButton* btn_addfriend = new QPushButton(tr("Add Friend"));
vbox_lst_friends->addWidget(btn_addfriend);
grp_list_friends->setLayout(vbox_lst_friends);
hbox_groupboxes->addWidget(grp_list_friends);
QGroupBox* grp_list_requests = new QGroupBox(tr("Friend Requests"));
QVBoxLayout* vbox_lst_requests = new QVBoxLayout();
m_lst_requests = new QListWidget(this);
m_lst_requests->setContextMenuPolicy(Qt::CustomContextMenu);
vbox_lst_requests->addWidget(m_lst_requests);
QHBoxLayout* hbox_request_btns = new QHBoxLayout();
vbox_lst_requests->addLayout(hbox_request_btns);
grp_list_requests->setLayout(vbox_lst_requests);
hbox_groupboxes->addWidget(grp_list_requests);
QGroupBox* grp_list_blocks = new QGroupBox(tr("Blocked Users"));
QVBoxLayout* vbox_lst_blocks = new QVBoxLayout();
m_lst_blocks = new QListWidget(this);
vbox_lst_blocks->addWidget(m_lst_blocks);
grp_list_blocks->setLayout(vbox_lst_blocks);
hbox_groupboxes->addWidget(grp_list_blocks);
vbox_global->addLayout(hbox_groupboxes);
setLayout(vbox_global);
// Tries to connect to RPCN
m_rpcn = rpcn::rpcn_client::get_instance();
if (auto res = m_rpcn->wait_for_connection(); res != rpcn::rpcn_state::failure_no_failure)
{
const QString error_msg = tr("Failed to connect to RPCN:\n%0").arg(rpcn_state_to_qstr(res));
QMessageBox::warning(parent, tr("Error connecting to RPCN!"), error_msg, QMessageBox::Ok);
return;
}
if (auto res = m_rpcn->wait_for_authentified(); res != rpcn::rpcn_state::failure_no_failure)
{
const QString error_msg = tr("Failed to authentify to RPCN:\n%0").arg(rpcn_state_to_qstr(res));
QMessageBox::warning(parent, tr("Error authentifying to RPCN!"), error_msg, QMessageBox::Ok);
return;
}
// Get friends, setup callback and setup comboboxes
rpcn::friend_data data;
m_rpcn->get_friends_and_register_cb(data, friend_callback, this);
for (const auto& fr : data.friends)
{
add_update_list(m_lst_friends, QString::fromStdString(fr.first), fr.second.first ? m_green_icon : m_red_icon, fr.second.first);
}
for (const auto& fr_req : data.requests_sent)
{
add_update_list(m_lst_requests, QString::fromStdString(fr_req), m_orange_icon, QVariant(false));
}
for (const auto& fr_req : data.requests_received)
{
add_update_list(m_lst_requests, QString::fromStdString(fr_req), m_yellow_icon, QVariant(true));
}
for (const auto& blck : data.blocked)
{
add_update_list(m_lst_blocks, QString::fromStdString(blck), m_red_icon, QVariant(false));
}
connect(this, &rpcn_friends_dialog::signal_add_update_friend, this, &rpcn_friends_dialog::add_update_friend);
connect(this, &rpcn_friends_dialog::signal_remove_friend, this, &rpcn_friends_dialog::remove_friend);
connect(this, &rpcn_friends_dialog::signal_add_query, this, &rpcn_friends_dialog::add_query);
connect(m_lst_friends, &QListWidget::customContextMenuRequested, this, [this](const QPoint& pos)
{
if (!m_lst_friends->itemAt(pos) || m_lst_friends->selectedItems().count() != 1)
{
return;
}
QListWidgetItem* selected_item = m_lst_friends->selectedItems().first();
std::string str_sel_friend = selected_item->text().toStdString();
QMenu* context_menu = new QMenu();
QAction* remove_friend_action = context_menu->addAction(tr("&Remove Friend"));
connect(remove_friend_action, &QAction::triggered, this, [this, str_sel_friend]()
{
if (!m_rpcn->remove_friend(str_sel_friend))
{
QMessageBox::critical(this, tr("Error removing a friend!"), tr("An error occured trying to remove a friend!"), QMessageBox::Ok);
}
else
{
QMessageBox::information(this, tr("Friend removed!"), tr("You've successfully removed a friend!"), QMessageBox::Ok);
}
});
context_menu->exec(m_lst_friends->viewport()->mapToGlobal(pos));
context_menu->deleteLater();
});
connect(m_lst_requests, &QListWidget::customContextMenuRequested, this, [this](const QPoint& pos)
{
if (!m_lst_requests->itemAt(pos) || m_lst_requests->selectedItems().count() != 1)
{
return;
}
QListWidgetItem* selected_item = m_lst_requests->selectedItems().first();
// Only create context menu for incoming requests
if (selected_item->data(Qt::UserRole) == false)
{
return;
}
std::string str_sel_friend = selected_item->text().toStdString();
QMenu* context_menu = new QMenu();
QAction* remove_friend_action = context_menu->addAction(tr("&Accept Request"));
connect(remove_friend_action, &QAction::triggered, this, [this, str_sel_friend]()
{
if (!m_rpcn->add_friend(str_sel_friend))
{
QMessageBox::critical(this, tr("Error adding a friend!"), tr("An error occured trying to add a friend!"), QMessageBox::Ok);
}
else
{
QMessageBox::information(this, tr("Friend added!"), tr("You've successfully added a friend!"), QMessageBox::Ok);
}
});
context_menu->exec(m_lst_requests->viewport()->mapToGlobal(pos));
context_menu->deleteLater();
});
connect(btn_addfriend, &QAbstractButton::clicked, this, [this]()
{
std::string str_friend_username;
while (true)
{
bool ok = false;
QString friend_username = QInputDialog::getText(this, tr("Add a friend"), tr("Friend's username:"), QLineEdit::Normal, "", &ok);
if (!ok)
{
return;
}
str_friend_username = friend_username.toStdString();
if (validate_rpcn_username(str_friend_username))
{
break;
}
QMessageBox::critical(this, tr("Error validating username"), tr("The username you entered is invalid"), QMessageBox::Ok);
}
if (!m_rpcn->add_friend(str_friend_username))
{
QMessageBox::critical(this, tr("Error adding friend"), tr("An error occured adding friend"), QMessageBox::Ok);
}
else
{
add_update_list(m_lst_requests, QString::fromStdString(str_friend_username), m_orange_icon, QVariant(false));
QMessageBox::information(this, tr("Friend added"), tr("Friend was successfully added!"), QMessageBox::Ok);
}
});
m_rpcn_ok = true;
}
rpcn_friends_dialog::~rpcn_friends_dialog()
{
m_rpcn->remove_friend_cb(friend_callback, this);
}
bool rpcn_friends_dialog::is_ok() const
{
return m_rpcn_ok;
}
void rpcn_friends_dialog::add_update_list(QListWidget* list, const QString& name, const QIcon& icon, const QVariant& data)
{
QListWidgetItem* item = nullptr;
if (auto found = list->findItems(name, Qt::MatchExactly); !found.empty())
{
item = found[0];
item->setData(Qt::UserRole, data);
item->setIcon(icon);
}
else
{
item = new QListWidgetItem(name);
item->setIcon(icon);
item->setData(Qt::UserRole, data);
list->addItem(item);
}
}
void rpcn_friends_dialog::remove_list(QListWidget* list, const QString& name)
{
if (auto found = list->findItems(name, Qt::MatchExactly); !found.empty())
{
delete list->takeItem(list->row(found[0]));
}
}
void rpcn_friends_dialog::add_update_friend(QString name, bool status)
{
add_update_list(m_lst_friends, name, status ? m_green_icon : m_red_icon, status);
remove_list(m_lst_requests, name);
}
void rpcn_friends_dialog::remove_friend(QString name)
{
remove_list(m_lst_friends, name);
remove_list(m_lst_requests, name);
}
void rpcn_friends_dialog::add_query(QString name)
{
add_update_list(m_lst_requests, name, m_yellow_icon, QVariant(true));
}
void rpcn_friends_dialog::callback_handler(rpcn::NotificationType ntype, std::string username, bool status)
{
QString qtr_username = QString::fromStdString(username);
switch (ntype)
{
case rpcn::NotificationType::FriendQuery: // Other user sent a friend request
{
Q_EMIT signal_add_query(qtr_username);
break;
}
case rpcn::NotificationType::FriendNew: // Add a friend to the friendlist(either accepted a friend request or friend accepted it)
{
Q_EMIT signal_add_update_friend(qtr_username, status);
break;
}
case rpcn::NotificationType::FriendLost: // Remove friend from the friendlist(user removed friend or friend removed friend)
{
Q_EMIT signal_remove_friend(qtr_username);
break;
}
case rpcn::NotificationType::FriendStatus: // Set status of friend to Offline or Online
{
Q_EMIT signal_add_update_friend(qtr_username, status);
break;
}
default:
{
rpcn_settings_log.fatal("An unhandled notification type was received by the rpcn friends dialog callback!");
break;
}
}
}

View File

@ -1,14 +1,25 @@
#pragma once
#include <optional>
#include <QDialog>
#include <QLineEdit>
#include <QListWidget>
#include "Emu/NP/rpcn_client.h"
class rpcn_settings_dialog : public QDialog
{
Q_OBJECT
public:
rpcn_settings_dialog(QWidget* parent = nullptr);
};
class rpcn_account_dialog : public QDialog
{
Q_OBJECT
public:
rpcn_account_dialog(QWidget* parent = nullptr);
bool save_config();
bool create_account();
@ -16,3 +27,51 @@ public:
protected:
QLineEdit *m_edit_host, *m_edit_npid, *m_edit_token;
};
class rpcn_ask_password_dialog : public QDialog
{
Q_OBJECT
public:
rpcn_ask_password_dialog(QWidget* parent = nullptr);
std::optional<std::string> get_password();
private:
QLineEdit *m_edit_pass1, *m_edit_pass2;
std::optional<std::string> m_password;
};
class rpcn_friends_dialog : public QDialog
{
Q_OBJECT
public:
rpcn_friends_dialog(QWidget* parent = nullptr);
~rpcn_friends_dialog();
void callback_handler(rpcn::NotificationType ntype, std::string username, bool status);
bool is_ok() const;
private:
void add_update_list(QListWidget* list, const QString& name, const QIcon& icon, const QVariant& data);
void remove_list(QListWidget* list, const QString& name);
private Q_SLOTS:
void add_update_friend(QString name, bool status);
void remove_friend(QString name);
void add_query(QString name);
Q_SIGNALS:
void signal_add_update_friend(QString name, bool status);
void signal_remove_friend(QString name);
void signal_add_query(QString name);
private:
const QIcon m_green_icon, m_red_icon, m_yellow_icon, m_orange_icon, m_black_icon;
// list of friends
QListWidget* m_lst_friends = nullptr;
// list of friend requests sent by/to the current user
QListWidget* m_lst_requests = nullptr;
// list of people blocked by the user
QListWidget* m_lst_blocks = nullptr;
std::shared_ptr<rpcn::rpcn_client> m_rpcn;
bool m_rpcn_ok = false;
};

View File

@ -0,0 +1,164 @@
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QMessageBox>
#include "sendmessage_dialog_frame.h"
#include "util/logs.hpp"
LOG_CHANNEL(sendmessage_dlg_log, "sendmessage dlg");
void sendmessage_friend_callback(void* param, rpcn::NotificationType ntype, const std::string& username, bool status)
{
auto* dlg = static_cast<sendmessage_dialog_frame*>(param);
dlg->callback_handler(ntype, username, status);
}
sendmessage_dialog_frame::~sendmessage_dialog_frame()
{
if (m_dialog)
{
m_dialog->deleteLater();
}
}
bool sendmessage_dialog_frame::Exec(message_data& msg_data, std::set<std::string>& npids)
{
if (m_dialog)
{
m_dialog->close();
delete m_dialog;
}
m_dialog = new custom_dialog(false);
m_dialog->setModal(true);
m_dialog->setWindowTitle(tr("Choose friend to message:"));
m_rpcn = rpcn::rpcn_client::get_instance();
QVBoxLayout* vbox_global = new QVBoxLayout();
m_lst_friends = new QListWidget();
vbox_global->addWidget(m_lst_friends);
QHBoxLayout* hbox_btns = new QHBoxLayout();
hbox_btns->addStretch();
QPushButton* btn_ok = new QPushButton(tr("Ok"));
QPushButton* btn_cancel = new QPushButton(tr("Cancel"));
hbox_btns->addWidget(btn_ok);
hbox_btns->addWidget(btn_cancel);
vbox_global->addLayout(hbox_btns);
m_dialog->setLayout(vbox_global);
connect(this, &sendmessage_dialog_frame::signal_add_friend, this, &sendmessage_dialog_frame::slot_add_friend);
connect(this, &sendmessage_dialog_frame::signal_remove_friend, this, &sendmessage_dialog_frame::slot_remove_friend);
bool result = false;
connect(btn_ok, &QAbstractButton::clicked, this, [this, &msg_data, &npids, &result]()
{
// Check one target is selected
auto selected = m_lst_friends->selectedItems();
if (selected.empty())
{
QMessageBox::critical(m_dialog, tr("Error sending a message!"), tr("You must select a friend!"), QMessageBox::Ok);
return;
}
npids.insert(selected[0]->text().toStdString());
// Send the message
result = m_rpcn->sendmessage(msg_data, npids);
m_dialog->close();
});
connect(btn_cancel, &QAbstractButton::clicked, m_dialog, &custom_dialog::close);
rpcn::friend_data data;
m_rpcn->get_friends_and_register_cb(data, sendmessage_friend_callback, this);
for (const auto& fr : data.friends)
{
// Only add online friends to the list
if (fr.second.first)
{
add_friend(m_lst_friends, QString::fromStdString(fr.first));
}
}
m_dialog->exec();
m_rpcn->remove_friend_cb(sendmessage_friend_callback, this);
return result;
}
void sendmessage_dialog_frame::add_friend(QListWidget* list, const QString& name)
{
if (auto found = list->findItems(name, Qt::MatchExactly); !found.empty())
{
return;
}
list->addItem(new QListWidgetItem(name));
}
void sendmessage_dialog_frame::remove_friend(QListWidget* list, const QString& name)
{
if (auto found = list->findItems(name, Qt::MatchExactly); !found.empty())
{
delete list->takeItem(list->row(found[0]));
}
}
void sendmessage_dialog_frame::slot_add_friend(QString name)
{
add_friend(m_lst_friends, name);
}
void sendmessage_dialog_frame::slot_remove_friend(QString name)
{
remove_friend(m_lst_friends, name);
}
void sendmessage_dialog_frame::callback_handler(rpcn::NotificationType ntype, const std::string& username, bool status)
{
QString qtr_username = QString::fromStdString(username);
switch (ntype)
{
case rpcn::NotificationType::FriendQuery: // Other user sent a friend request
break;
case rpcn::NotificationType::FriendNew: // Add a friend to the friendlist(either accepted a friend request or friend accepted it)
{
if (status)
{
Q_EMIT signal_add_friend(qtr_username);
}
break;
}
case rpcn::NotificationType::FriendLost: // Remove friend from the friendlist(user removed friend or friend removed friend)
{
Q_EMIT signal_remove_friend(qtr_username);
break;
}
case rpcn::NotificationType::FriendStatus: // Set status of friend to Offline or Online
{
if (status)
{
Q_EMIT signal_add_friend(qtr_username);
}
else
{
Q_EMIT signal_remove_friend(qtr_username);
}
break;
}
default:
{
sendmessage_dlg_log.fatal("An unhandled notification type was received by the sendmessage dialog callback!");
break;
}
}
}

View File

@ -0,0 +1,37 @@
#pragma once
#include <QObject>
#include <QListWidget>
#include "util/types.hpp"
#include "custom_dialog.h"
#include "Emu/NP/rpcn_client.h"
class sendmessage_dialog_frame : public QObject, public SendMessageDialogBase
{
Q_OBJECT
public:
sendmessage_dialog_frame() = default;
~sendmessage_dialog_frame();
bool Exec(message_data& msg_data, std::set<std::string>& npids) override;
void callback_handler(rpcn::NotificationType ntype, const std::string& username, bool status);
private:
void add_friend(QListWidget* list, const QString& name);
void remove_friend(QListWidget* list, const QString& name);
private:
custom_dialog* m_dialog = nullptr;
QListWidget* m_lst_friends = nullptr;
std::shared_ptr<rpcn::rpcn_client> m_rpcn;
Q_SIGNALS:
void signal_add_friend(QString name);
void signal_remove_friend(QString name);
private Q_SLOTS:
void slot_add_friend(QString name);
void slot_remove_friend(QString name);
};