1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-25 20:22:30 +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) # TODO(cjj19970505@live.cn)
# OPENSSL_EXTRA, WOLFSSL_DES_ECB and HAVE_SNI are unconfigurable from CMake cache. # 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) # 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_TLS13 "no" CACHE INTERNAL "")
set(WOLFSSL_SHA224 "yes" CACHE INTERNAL "") set(WOLFSSL_SHA224 "yes" CACHE INTERNAL "")

View File

@ -48,7 +48,7 @@
<ClCompile> <ClCompile>
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>./wolfssl;./wolfssl/IDE/WIN;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <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> <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<PrecompiledHeader> <PrecompiledHeader>
</PrecompiledHeader> </PrecompiledHeader>
@ -62,7 +62,7 @@
<Optimization>MaxSpeed</Optimization> <Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>./wolfssl;./wolfssl/IDE/WIN;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <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> <FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader> <PrecompiledHeader>
</PrecompiledHeader> </PrecompiledHeader>

View File

@ -110,6 +110,15 @@ error_code cellSysutilAvc2StartVoiceDetection()
error_code cellSysutilAvc2UnloadAsync() error_code cellSysutilAvc2UnloadAsync()
{ {
cellSysutilAvc2.todo("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; return CELL_OK;
} }

View File

@ -3,6 +3,8 @@
#include "Emu/system_utils.hpp" #include "Emu/system_utils.hpp"
#include "Emu/VFS.h" #include "Emu/VFS.h"
#include "Emu/Cell/PPUModule.h" #include "Emu/Cell/PPUModule.h"
#include "Emu/Io/interception.h"
#include "Utilities/StrUtil.h"
#include "sysPrxForUser.h" #include "sysPrxForUser.h"
#include "Emu/IdManager.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) error_code sceNpInit(u32 poolsize, vm::ptr<void> poolptr)
{ {
sceNp.warning("sceNpInit(poolsize=0x%x, poolptr=*0x%x)", poolsize, 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) 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>>(); 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; return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
} }
if (nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
}
if (!context || !handler) if (!context || !handler)
{ {
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT; return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
} }
nph.basic_handler = handler; memcpy(&nph.basic_handler.context, context.get_ptr(), sizeof(nph.basic_handler.context));
nph.basic_handler_arg = arg; 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; return CELL_OK;
} }
@ -660,11 +675,22 @@ error_code sceNpBasicRegisterContextSensitiveHandler(vm::cptr<SceNpCommunication
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED; return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
} }
if (nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
}
if (!context || !handler) if (!context || !handler)
{ {
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT; 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; return CELL_OK;
} }
@ -679,6 +705,13 @@ error_code sceNpBasicUnregisterHandler()
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED; 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; 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) 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>>(); 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; 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; 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); 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; return CELL_OK;
} }
@ -845,9 +971,9 @@ error_code sceNpBasicRecvMessageAttachment(sys_memory_container_t containerId)
return CELL_OK; 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>>(); 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; return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
} }
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!buffer || !size) if (!buffer || !size)
{ {
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT; 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; 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; return CELL_OK;
} }
error_code sceNpBasicRecvMessageCustom(u16 mainType, u32 recvOptions, sys_memory_container_t containerId) 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>>(); 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; 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; 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; return CELL_OK;
} }
@ -972,8 +1172,7 @@ error_code sceNpBasicGetFriendListEntryCount(vm::ptr<u32> count)
return SCE_NP_ERROR_ID_NOT_FOUND; return SCE_NP_ERROR_ID_NOT_FOUND;
} }
// TODO: Check if there are any friends *count = nph.get_num_friends();
*count = 0;
return CELL_OK; return CELL_OK;
} }
@ -1258,7 +1457,7 @@ error_code sceNpBasicGetBlockListEntryCount(vm::ptr<u32> count)
} }
// TODO: Check if there are block lists // TODO: Check if there are block lists
*count = 0; *count = nph.get_num_blocks();
return CELL_OK; 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; return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
} }
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!event || !from || !data || !size) if (!event || !from || !data || !size)
{ {
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT; 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 //*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) 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(); const auto& ticket = nph.get_ticket();
*bufferSize = static_cast<u32>(ticket.size());
if (!buffer) if (!buffer)
{ {
*bufferSize = static_cast<u32>(ticket.size());
return CELL_OK; return CELL_OK;
} }

View File

@ -3,6 +3,8 @@
#include "cellRtc.h" #include "cellRtc.h"
#include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/ErrorCodes.h"
#include <set>
error_code sceNpInit(u32 poolsize, vm::ptr<void> poolptr); error_code sceNpInit(u32 poolsize, vm::ptr<void> poolptr);
error_code sceNpTerm(); error_code sceNpTerm();
@ -1022,11 +1024,11 @@ struct SceNpBasicMessageDetails
be_t<u16> mainType; be_t<u16> mainType;
be_t<u16> subType; be_t<u16> subType;
be_t<u32> msgFeatures; be_t<u32> msgFeatures;
const SceNpId npids; vm::bptr<SceNpId> npids;
be_t<u32> count; be_t<u32> count;
const s8 subject; vm::bptr<char> subject;
const s8 body; vm::bptr<char> body;
const be_t<u32> data; vm::bptr<u8> data;
be_t<u32> size; 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 SceNpProfileResultHandler = s32(s32 result, vm::ptr<void> arg);
using SceNpManagerSubSigninCallback = void(s32 result, vm::ptr<SceNpId> npId, vm::ptr<void> cb_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) switch (code)
{ {
case 1: case SCE_NP_SIGNALING_CONN_INFO_RTT:
{ {
connInfo->rtt = 20000; // HACK connInfo->rtt = 20000; // HACK
break; break;
} }
case 2: case SCE_NP_SIGNALING_CONN_INFO_BANDWIDTH:
{ {
connInfo->bandwidth = 10'000'000; // 10 MBPS HACK connInfo->bandwidth = 10'000'000; // 10 MBPS HACK
break; 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>>(); auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
const auto si = sigh.get_sig2_infos(roomId, memberId); const auto si = sigh.get_sig2_infos(roomId, memberId);
@ -631,7 +637,15 @@ error_code sceNpMatching2SignalingGetConnectionInfo(
connInfo->address.addr.np_s_addr = si.addr; connInfo->address.addr.np_s_addr = si.addr;
break; 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 connInfo->packet_loss = 1; // HACK
break; break;
@ -1274,6 +1288,8 @@ error_code sceNpMatching2GetRoomDataExternalList(
return res; return res;
} }
*assignedReqId = nph.get_roomdata_external_list(ctxId, optParam, reqParam.get_ptr());
return CELL_OK; return CELL_OK;
} }

View File

@ -978,7 +978,7 @@ struct network_thread
WSADATA wsa_data; WSADATA wsa_data;
WSAStartup(MAKEWORD(2, 2), &wsa_data); WSAStartup(MAKEWORD(2, 2), &wsa_data);
#endif #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)); list_p2p_ports.emplace(std::piecewise_construct, std::forward_as_tuple(3658), std::forward_as_tuple(3658));
} }

View File

@ -55,32 +55,8 @@ 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)
{ {
search_resp->range.size = resp->size();
search_resp->range.startIndex = resp->startIndex();
search_resp->range.total = resp->total();
if (resp->rooms() && resp->rooms()->size() != 0)
{
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);
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
{
search_resp->roomDataExternal = room_info;
}
previous_next = vm::cast(room_info.addr());
room_info->serverId = room->serverId(); room_info->serverId = room->serverId();
room_info->worldId = room->worldId(); room_info->worldId = room->worldId();
room_info->publicSlotNum = room->publicSlotNum(); room_info->publicSlotNum = room->publicSlotNum();
@ -91,7 +67,8 @@ void np_handler::SearchRoomReponse_to_SceNpMatching2SearchRoomResponse(const Sea
room_info->maxSlot = room->maxSlot(); room_info->maxSlot = room->maxSlot();
room_info->openPrivateSlotNum = room->openPrivateSlotNum(); room_info->openPrivateSlotNum = room->openPrivateSlotNum();
room_info->curMemberNum = room->curMemberNum(); room_info->curMemberNum = room->curMemberNum();
room_info->passwordSlotMask = room->curMemberNum(); room_info->passwordSlotMask = room->passwordSlotMask();
if (auto owner = room->owner()) if (auto owner = room->owner())
{ {
vm::ptr<SceNpUserInfo2> owner_info(allocate(sizeof(SceNpUserInfo2))); vm::ptr<SceNpUserInfo2> owner_info(allocate(sizeof(SceNpUserInfo2)));
@ -137,6 +114,35 @@ void np_handler::SearchRoomReponse_to_SceNpMatching2SearchRoomResponse(const Sea
BinAttr_to_SceNpMatching2BinAttr(room->roomBinAttrExternal(), binattr_info); BinAttr_to_SceNpMatching2BinAttr(room->roomBinAttrExternal(), binattr_info);
room_info->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();
search_resp->range.total = resp->total();
if (resp->rooms() && resp->rooms()->size() != 0)
{
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);
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
{
search_resp->roomDataExternal = room_info;
}
previous_next = vm::cast(room_info.addr());
RoomDataExternal_to_SceNpMatching2RoomDataExternal(room, room_info.get_ptr());
} }
} }
else 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 np_handler::RoomDataInternal_to_SceNpMatching2RoomDataInternal(const RoomDataInternal* resp, SceNpMatching2RoomDataInternal* room_info, const SceNpId& npid)
{ {
u16 member_id = 0; u16 member_id = 0;

View File

@ -167,6 +167,15 @@ table LeaveRoomRequest {
optData:PresenceOptionData; optData:PresenceOptionData;
} }
table GetRoomDataExternalListRequest {
roomIds:[uint64];
attrIds:[uint16];
}
table GetRoomDataExternalListResponse {
rooms:[RoomDataExternal];
}
table SetRoomDataExternalRequest { table SetRoomDataExternalRequest {
roomId:uint64; roomId:uint64;
roomSearchableIntAttrExternal:[IntAttr]; roomSearchableIntAttrExternal:[IntAttr];
@ -223,3 +232,19 @@ table RoomMessageInfo {
srcMember:UserInfo2; srcMember:UserInfo2;
msg:[uint8]; 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,10 +44,11 @@ np_handler::np_handler()
{ {
g_fxo->need<named_thread<signaling_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_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 (get_net_status() == CELL_NET_CTL_STATE_IPObtained)
{ {
@ -115,7 +116,7 @@ bool np_handler::discover_ip_address()
nph_log.notice("Hostname was determined to be %s", hostname.c_str()); nph_log.notice("Hostname was determined to be %s", hostname.c_str());
hostent *host = gethostbyname(hostname.data()); hostent* host = gethostbyname(hostname.data());
if (!host) if (!host)
{ {
nph_log.error("gethostbyname failed in IP discovery!"); 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?) // 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; // Set public address to local discovered address for now, may be updated later;
public_ip_addr = local_ip_addr; public_ip_addr = local_ip_addr;
@ -305,6 +306,11 @@ void np_handler::string_to_avatar_url(const std::string& str, SceNpAvatarUrl* av
strcpy_trunc(avatar_url->data, str); 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) void np_handler::init_NP(u32 poolsize, vm::ptr<void> poolptr)
{ {
// Init memory pool // 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(&online_name, 0, sizeof(online_name));
memset(&avatar_url, 0, sizeof(avatar_url)); 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(); std::string s_npid = g_cfg_rpcn.get_npid();
ensure(!s_npid.empty()); // It should have been generated before this 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: case np_psn_status::disabled:
break; 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_online_name("RPCS3's user", &online_name);
np_handler::string_to_avatar_url("https://rpcs3.net/cdn/netplay/DefaultAvatar.png", &avatar_url); np_handler::string_to_avatar_url("https://rpcs3.net/cdn/netplay/DefaultAvatar.png", &avatar_url);
break; break;
} }
case np_psn_status::rpcn: case np_psn_status::psn_rpcn:
{ {
if (!is_psn_active) if (!is_psn_active)
break; break;
// Connect RPCN client std::lock_guard lock(mutex_rpcn);
if (!rpcn.connect(g_cfg_rpcn.get_host())) 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!"); rpcn_log.error("Connection to RPCN Failed!");
is_psn_active = false; is_psn_active = false;
return; 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!"); rpcn_log.error("RPCN login attempt failed!");
is_psn_active = false; is_psn_active = false;
return; return;
} }
np_handler::string_to_online_name(rpcn.get_online_name(), &online_name); np_handler::string_to_online_name(rpcn->get_online_name(), &online_name);
np_handler::string_to_avatar_url(rpcn.get_avatar_url(), &avatar_url); np_handler::string_to_avatar_url(rpcn->get_avatar_url(), &avatar_url);
public_ip_addr = rpcn.get_addr_sig(); public_ip_addr = rpcn->get_addr_sig();
break; break;
} }
@ -376,10 +386,11 @@ void np_handler::terminate_NP()
mpool_avail = 0; mpool_avail = 0;
mpool_allocs.clear(); 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_log.notice("Disconnecting from RPCN!");
rpcn.disconnect(); 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{}; 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; 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!"); rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false; is_psn_active = false;
@ -457,8 +468,7 @@ u32 np_handler::get_server_status(SceNpMatching2ContextId ctx_id, vm::cptr<SceNp
serv_info->server.serverId = server_id; serv_info->server.serverId = server_id;
serv_info->server.status = SCE_NP_MATCHING2_SERVER_STATUS_AVAILABLE; serv_info->server.status = SCE_NP_MATCHING2_SERVER_STATUS_AVAILABLE;
const auto cb_info = std::move(pending_requests.at(req_id)); const auto cb_info = take_pending_request(req_id);
pending_requests.erase(req_id);
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{ {
@ -474,8 +484,7 @@ u32 np_handler::create_server_context(SceNpMatching2ContextId ctx_id, vm::cptr<S
u32 req_id = generate_callback_info(ctx_id, optParam); u32 req_id = generate_callback_info(ctx_id, optParam);
u32 event_key = get_event_key(); u32 event_key = get_event_key();
const auto cb_info = std::move(pending_requests.at(req_id)); const auto cb_info = take_pending_request(req_id);
pending_requests.erase(req_id);
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{ {
@ -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); 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!"); rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false; 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); 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!"); rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false; 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); 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!"); rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false; 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); 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!"); rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false; 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); 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!"); rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false; 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); 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!"); rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false; 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); 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!"); rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false; 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); 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!"); rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false; 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); 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!"); rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false; 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); 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!"); rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false; 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) void np_handler::req_sign_infos(const std::string& npid, u32 conn_id)
{ {
u32 req_id = get_req_id(0x3333); u32 req_id = get_req_id(0x3333);
{
std::lock_guard lock(mutex_pending_sign_infos_requests);
pending_sign_infos_requests[req_id] = conn_id; 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!"); rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false; 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); 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!"); rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false; is_psn_active = false;
@ -649,7 +674,6 @@ void np_handler::req_ticket(u32 /*version*/, const SceNpId* /*npid*/, const char
return; return;
} }
u32 np_handler::get_match2_event(SceNpMatching2EventKey event_key, u8* dest, u32 size) u32 np_handler::get_match2_event(SceNpMatching2EventKey event_key, u8* dest, u32 size)
{ {
std::lock_guard lock(mutex_req_results); std::lock_guard lock(mutex_req_results);
@ -663,20 +687,80 @@ u32 np_handler::get_match2_event(SceNpMatching2EventKey event_key, u8* dest, u32
return size_copied; 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()() 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; return;
while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped()) 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)
{ {
thread_ctrl::wait_for(200'000);
continue; continue;
} }
auto replies = rpcn.get_replies(); auto replies = rpcn->get_replies();
for (auto& reply : replies) for (auto& reply : replies)
{ {
const u16 command = reply.second.first; const u16 command = reply.second.first;
@ -685,45 +769,99 @@ void np_handler::operator()()
switch (command) switch (command)
{ {
case CommandType::GetWorldList: reply_get_world_list(req_id, data); break; case rpcn::CommandType::GetWorldList: reply_get_world_list(req_id, data); break;
case CommandType::CreateRoom: reply_create_join_room(req_id, data); break; case rpcn::CommandType::CreateRoom: reply_create_join_room(req_id, data); break;
case CommandType::JoinRoom: reply_join_room(req_id, data); break; case rpcn::CommandType::JoinRoom: reply_join_room(req_id, data); break;
case CommandType::LeaveRoom: reply_leave_room(req_id, data); break; case rpcn::CommandType::LeaveRoom: reply_leave_room(req_id, data); break;
case CommandType::SearchRoom: reply_search_room(req_id, data); break; case rpcn::CommandType::SearchRoom: reply_search_room(req_id, data); break;
case CommandType::SetRoomDataExternal: reply_set_roomdata_external(req_id, data); break; case rpcn::CommandType::GetRoomDataExternalList: reply_get_roomdata_external_list(req_id, data); break;
case CommandType::GetRoomDataInternal: reply_get_roomdata_internal(req_id, data); break; case rpcn::CommandType::SetRoomDataExternal: reply_set_roomdata_external(req_id, data); break;
case CommandType::SetRoomDataInternal: reply_set_roomdata_internal(req_id, data); break; case rpcn::CommandType::GetRoomDataInternal: reply_get_roomdata_internal(req_id, data); break;
case CommandType::PingRoomOwner: reply_get_ping_info(req_id, data); break; case rpcn::CommandType::SetRoomDataInternal: reply_set_roomdata_internal(req_id, data); break;
case CommandType::SendRoomMessage: reply_send_room_message(req_id, data); break; case rpcn::CommandType::PingRoomOwner: reply_get_ping_info(req_id, data); break;
case CommandType::RequestSignalingInfos: reply_req_sign_infos(req_id, data); break; case rpcn::CommandType::SendRoomMessage: reply_send_room_message(req_id, data); break;
case CommandType::RequestTicket: reply_req_ticket(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; default: rpcn_log.error("Unknown reply(%d) received!", command); break;
} }
} }
auto notifications = rpcn.get_notifications(); auto notifications = rpcn->get_notifications();
for (auto& notif : notifications) for (auto& notif : notifications)
{ {
switch (notif.first) switch (notif.first)
{ {
case NotificationType::UserJoinedRoom: notif_user_joined_room(notif.second); break; case rpcn::NotificationType::UserJoinedRoom: notif_user_joined_room(notif.second); break;
case NotificationType::UserLeftRoom: notif_user_left_room(notif.second); break; case rpcn::NotificationType::UserLeftRoom: notif_user_left_room(notif.second); break;
case NotificationType::RoomDestroyed: notif_room_destroyed(notif.second); break; case rpcn::NotificationType::RoomDestroyed: notif_room_destroyed(notif.second); break;
case NotificationType::SignalP2PConnect: notif_p2p_connect(notif.second); break; case rpcn::NotificationType::SignalP2PConnect: notif_p2p_connect(notif.second); break;
case NotificationType::RoomMessageReceived: notif_room_message_received(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; 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;
}
} }
} }
bool np_handler::reply_get_world_list(u32 req_id, std::vector<u8>& reply_data) bool np_handler::reply_get_world_list(u32 req_id, std::vector<u8>& reply_data)
{ {
if (pending_requests.count(req_id) == 0) const auto cb_info = take_pending_request(req_id);
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);
vec_stream reply(reply_data, 1); vec_stream reply(reply_data, 1);
@ -770,11 +908,7 @@ bool np_handler::reply_get_world_list(u32 req_id, std::vector<u8>& reply_data)
bool np_handler::reply_create_join_room(u32 req_id, std::vector<u8>& reply_data) bool np_handler::reply_create_join_room(u32 req_id, std::vector<u8>& reply_data)
{ {
if (pending_requests.count(req_id) == 0) const auto cb_info = take_pending_request(req_id);
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);
vec_stream reply(reply_data, 1); vec_stream reply(reply_data, 1);
auto create_room_resp = reply.get_rawdata(); auto create_room_resp = reply.get_rawdata();
@ -795,7 +929,7 @@ bool np_handler::reply_create_join_room(u32 req_id, std::vector<u8>& reply_data)
// Establish Matching2 self signaling info // Establish Matching2 self signaling info
auto& sigh = g_fxo->get<named_thread<signaling_handler>>(); auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
sigh.set_self_sig2_info(room_info->roomId, 1); 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? // TODO? Should this send a message to Signaling CB? Is this even necessary?
extra_nps::print_create_room_resp(room_resp); extra_nps::print_create_room_resp(room_resp);
@ -811,11 +945,7 @@ bool np_handler::reply_create_join_room(u32 req_id, std::vector<u8>& reply_data)
bool np_handler::reply_join_room(u32 req_id, std::vector<u8>& reply_data) bool np_handler::reply_join_room(u32 req_id, std::vector<u8>& reply_data)
{ {
if (pending_requests.count(req_id) == 0) const auto cb_info = take_pending_request(req_id);
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);
vec_stream reply(reply_data, 1); vec_stream reply(reply_data, 1);
@ -839,7 +969,7 @@ bool np_handler::reply_join_room(u32 req_id, std::vector<u8>& reply_data)
// Establish Matching2 self signaling info // Establish Matching2 self signaling info
auto& sigh = g_fxo->get<named_thread<signaling_handler>>(); auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
sigh.set_self_sig2_info(room_info->roomId, member_id); 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? // TODO? Should this send a message to Signaling CB? Is this even necessary?
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
@ -853,11 +983,7 @@ bool np_handler::reply_join_room(u32 req_id, std::vector<u8>& reply_data)
bool np_handler::reply_leave_room(u32 req_id, std::vector<u8>& reply_data) bool np_handler::reply_leave_room(u32 req_id, std::vector<u8>& reply_data)
{ {
if (pending_requests.count(req_id) == 0) const auto cb_info = take_pending_request(req_id);
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);
vec_stream reply(reply_data, 1); vec_stream reply(reply_data, 1);
u64 room_id = reply.get<u64>(); u64 room_id = reply.get<u64>();
@ -881,11 +1007,7 @@ bool np_handler::reply_leave_room(u32 req_id, std::vector<u8>& reply_data)
bool np_handler::reply_search_room(u32 req_id, std::vector<u8>& reply_data) bool np_handler::reply_search_room(u32 req_id, std::vector<u8>& reply_data)
{ {
if (pending_requests.count(req_id) == 0) const auto cb_info = take_pending_request(req_id);
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);
vec_stream reply(reply_data, 1); vec_stream reply(reply_data, 1);
auto search_room_resp = reply.get_rawdata(); auto search_room_resp = reply.get_rawdata();
@ -897,7 +1019,32 @@ bool np_handler::reply_search_room(u32 req_id, std::vector<u8>& reply_data)
auto resp = flatbuffers::GetRoot<SearchRoomResponse>(search_room_resp.data()); auto resp = flatbuffers::GetRoot<SearchRoomResponse>(search_room_resp.data());
SceNpMatching2SearchRoomResponse* search_resp = reinterpret_cast<SceNpMatching2SearchRoomResponse*>(allocate_req_result(event_key, sizeof(SceNpMatching2SearchRoomResponse))); 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;
});
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 sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{ {
@ -910,11 +1057,7 @@ bool np_handler::reply_search_room(u32 req_id, std::vector<u8>& reply_data)
bool np_handler::reply_set_roomdata_external(u32 req_id, std::vector<u8>& /*reply_data*/) bool np_handler::reply_set_roomdata_external(u32 req_id, std::vector<u8>& /*reply_data*/)
{ {
if (pending_requests.count(req_id) == 0) const auto cb_info = take_pending_request(req_id);
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);
u32 event_key = get_event_key(); // Unsure if necessary if there is no data u32 event_key = get_event_key(); // Unsure if necessary if there is no data
@ -929,11 +1072,7 @@ bool np_handler::reply_set_roomdata_external(u32 req_id, std::vector<u8>& /*repl
bool np_handler::reply_get_roomdata_internal(u32 req_id, std::vector<u8>& reply_data) bool np_handler::reply_get_roomdata_internal(u32 req_id, std::vector<u8>& reply_data)
{ {
if (pending_requests.count(req_id) == 0) const auto cb_info = take_pending_request(req_id);
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);
vec_stream reply(reply_data, 1); vec_stream reply(reply_data, 1);
@ -963,11 +1102,7 @@ bool np_handler::reply_get_roomdata_internal(u32 req_id, std::vector<u8>& reply_
bool np_handler::reply_set_roomdata_internal(u32 req_id, std::vector<u8>& /*reply_data*/) bool np_handler::reply_set_roomdata_internal(u32 req_id, std::vector<u8>& /*reply_data*/)
{ {
if (pending_requests.count(req_id) == 0) const auto cb_info = take_pending_request(req_id);
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);
u32 event_key = get_event_key(); // Unsure if necessary if there is no data u32 event_key = get_event_key(); // Unsure if necessary if there is no data
@ -982,11 +1117,7 @@ bool np_handler::reply_set_roomdata_internal(u32 req_id, std::vector<u8>& /*repl
bool np_handler::reply_get_ping_info(u32 req_id, std::vector<u8>& reply_data) bool np_handler::reply_get_ping_info(u32 req_id, std::vector<u8>& reply_data)
{ {
if (pending_requests.count(req_id) == 0) const auto cb_info = take_pending_request(req_id);
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);
vec_stream reply(reply_data, 1); vec_stream reply(reply_data, 1);
@ -1012,11 +1143,7 @@ bool np_handler::reply_get_ping_info(u32 req_id, std::vector<u8>& reply_data)
bool np_handler::reply_send_room_message(u32 req_id, std::vector<u8>& /*reply_data*/) bool np_handler::reply_send_room_message(u32 req_id, std::vector<u8>& /*reply_data*/)
{ {
if (pending_requests.count(req_id) == 0) const auto cb_info = take_pending_request(req_id);
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);
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{ {
@ -1029,11 +1156,12 @@ bool np_handler::reply_send_room_message(u32 req_id, std::vector<u8>& /*reply_da
bool np_handler::reply_req_sign_infos(u32 req_id, std::vector<u8>& reply_data) bool np_handler::reply_req_sign_infos(u32 req_id, std::vector<u8>& reply_data)
{ {
if (!pending_sign_infos_requests.count(req_id)) u32 conn_id;
return error_and_disconnect("Unexpected reply ID to req RequestSignalingInfos"); {
std::lock_guard lock(mutex_pending_sign_infos_requests);
u32 conn_id = pending_sign_infos_requests.at(req_id); conn_id = pending_sign_infos_requests.at(req_id);
pending_sign_infos_requests.erase(req_id); pending_sign_infos_requests.erase(req_id);
}
vec_stream reply(reply_data, 1); vec_stream reply(reply_data, 1);
u32 addr = reply.get<u32>(); u32 addr = reply.get<u32>();
@ -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) bool np_handler::error_and_disconnect(const std::string& error_msg)
{ {
rpcn_log.error("%s", error_msg); rpcn_log.error("%s", error_msg);
rpcn.disconnect(); rpcn.reset();
return false; 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); nph_log.warning("Callback used is 0x%x", ret.cb);
{
std::lock_guard lock(mutex_pending_requests);
pending_requests[req_id] = std::move(ret); pending_requests[req_id] = std::move(ret);
}
return req_id; 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) u8* np_handler::allocate_req_result(u32 event_key, usz size)
{ {
std::lock_guard lock(mutex_req_results); 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; 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 ip_to_string(u32 addr);
static std::string ether_to_string(std::array<u8, 6>& ether); static std::string ether_to_string(std::array<u8, 6>& ether);
// Helpers for setting various structures from string // Helpers for setting various structures from string
static void string_to_npid(const std::string&, SceNpId* npid); static void string_to_npid(const std::string& str, SceNpId* npid);
static void string_to_online_name(const std::string&, SceNpOnlineName* online_name); static void string_to_online_name(const std::string& str, SceNpOnlineName* online_name);
static void string_to_avatar_url(const std::string&, SceNpAvatarUrl* avatar_url); 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 // DNS hooking functions
void add_dns_spy(u32 sock); void add_dns_spy(u32 sock);
@ -47,16 +48,6 @@ public:
std::vector<u8> get_dns_packet(u32 sock); std::vector<u8> get_dns_packet(u32 sock);
s32 analyze_dns_packet(s32 s, const u8* buf, u32 len); 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) // handles async messages from server(only needed for RPCN)
void operator()(); void operator()();
@ -76,9 +67,25 @@ public:
vm::ptr<SceNpManagerCallback> manager_cb{}; // Connection status and tickets vm::ptr<SceNpManagerCallback> manager_cb{}; // Connection status and tickets
vm::ptr<void> manager_cb_arg{}; vm::ptr<void> manager_cb_arg{};
// Registered by SceNpCommunicationId // Basic event handler;
vm::ptr<SceNpBasicEventHandler> basic_handler; struct
vm::ptr<void> basic_handler_arg; {
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 // Those should probably be under match2 ctx
vm::ptr<SceNpMatching2RoomEventCallback> room_event_cb{}; // Room events 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 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 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 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 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 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); 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); u32 get_match2_event(SceNpMatching2EventKey event_key, u8* dest, u32 size);
// Friend stuff
u32 get_num_friends();
u32 get_num_blocks();
// Misc stuff // 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); 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; } const std::vector<u8>& get_ticket() const
{
return current_ticket;
}
u32 add_players_to_history(vm::cptr<SceNpId> npids, u32 count); u32 add_players_to_history(vm::cptr<SceNpId> npids, u32 count);
// For signaling // For signaling
@ -119,7 +134,7 @@ public:
static constexpr std::string_view thread_name = "NP Handler Thread"; static constexpr std::string_view thread_name = "NP Handler Thread";
protected: private:
// Various generic helpers // Various generic helpers
bool discover_ip_address(); bool discover_ip_address();
bool discover_ether_address(); bool discover_ether_address();
@ -138,6 +153,7 @@ protected:
bool reply_join_room(u32 req_id, std::vector<u8>& reply_data); bool reply_join_room(u32 req_id, std::vector<u8>& reply_data);
bool reply_leave_room(u32 req_id, std::vector<u8>& reply_data); bool reply_leave_room(u32 req_id, std::vector<u8>& reply_data);
bool reply_search_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_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_get_roomdata_internal(u32 req_id, std::vector<u8>& reply_data);
bool reply_set_roomdata_internal(u32 req_id, std::vector<u8>& reply_data); bool reply_set_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 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 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 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); u16 RoomDataInternal_to_SceNpMatching2RoomDataInternal(const RoomDataInternal* resp, SceNpMatching2RoomDataInternal* room_resp, const SceNpId& npid);
void RoomMemberUpdateInfo_to_SceNpMatching2RoomMemberUpdateInfo(const RoomMemberUpdateInfo* resp, SceNpMatching2RoomMemberUpdateInfo* room_info); void RoomMemberUpdateInfo_to_SceNpMatching2RoomMemberUpdateInfo(const RoomMemberUpdateInfo* resp, SceNpMatching2RoomMemberUpdateInfo* room_info);
void RoomUpdateInfo_to_SceNpMatching2RoomUpdateInfo(const RoomUpdateInfo* update_info, SceNpMatching2RoomUpdateInfo* sce_update_info); void RoomUpdateInfo_to_SceNpMatching2RoomUpdateInfo(const RoomUpdateInfo* update_info, SceNpMatching2RoomUpdateInfo* sce_update_info);
@ -164,11 +182,17 @@ protected:
vm::ptr<void> cb_arg; vm::ptr<void> cb_arg;
}; };
u32 generate_callback_info(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam); 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; std::unordered_map<u32, callback_info> pending_requests;
shared_mutex mutex_pending_sign_infos_requests;
std::unordered_map<u32, u32> 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_connected = false;
bool is_psn_active = false; bool is_psn_active = false;
@ -213,5 +237,6 @@ protected:
u8* allocate_req_result(u32 event_key, usz size); u8* allocate_req_result(u32 event_key, usz size);
// RPCN // 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 <unordered_map>
#include <chrono> #include <chrono>
#include <thread>
#include <semaphore>
#include "Utilities/mutex.h" #include "Utilities/mutex.h"
#include "util/asm.hpp" #include "util/asm.hpp"
@ -26,8 +28,7 @@ class vec_stream
public: public:
vec_stream() = delete; vec_stream() = delete;
vec_stream(std::vector<u8>& _vec, usz initial_index = 0) vec_stream(std::vector<u8>& _vec, usz initial_index = 0)
: vec(_vec) : vec(_vec), i(initial_index) {}
, i(initial_index){}
bool is_error() const bool is_error() const
{ {
return error; return error;
@ -101,18 +102,25 @@ protected:
bool error = false; bool error = false;
}; };
enum CommandType : u16 namespace rpcn
{ {
enum CommandType : u16
{
Login, Login,
Terminate, Terminate,
Create, Create,
SendToken, SendToken,
AddFriend,
RemoveFriend,
AddBlock,
RemoveBlock,
GetServerList, GetServerList,
GetWorldList, GetWorldList,
CreateRoom, CreateRoom,
JoinRoom, JoinRoom,
LeaveRoom, LeaveRoom,
SearchRoom, SearchRoom,
GetRoomDataExternalList,
SetRoomDataExternal, SetRoomDataExternal,
GetRoomDataInternal, GetRoomDataInternal,
SetRoomDataInternal, SetRoomDataInternal,
@ -120,10 +128,40 @@ enum CommandType : u16
SendRoomMessage, SendRoomMessage,
RequestSignalingInfos, RequestSignalingInfos,
RequestTicket, 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 enum PacketType : u8
{ {
Request, Request,
@ -134,41 +172,157 @@ class rpcn_client
enum ErrorType : u8 enum ErrorType : u8
{ {
NoError, NoError, // No error
Malformed, Malformed, // Query was malformed, critical error that should close the connection
Invalid, Invalid, // The request type is invalid(wrong stage?)
InvalidInput, InvalidInput, // The Input doesn't fit the constraints of the request
ErrorLogin, TooSoon, // Time limited operation attempted too soon
ErrorCreate, LoginError, // An error happened related to login
AlreadyLoggedIn, LoginAlreadyLoggedIn, // Can't log in because you're already logged in
AlreadyJoined, LoginInvalidUsername, // Invalid username
DbFail, LoginInvalidPassword, // Invalid password
NotFound, 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, Unsupported,
__error_last __error_last
}; };
public: using friend_cb_func = void (*)(void* param, NotificationType ntype, const std::string& username, bool status);
rpcn_client(bool in_config = false); using message_cb_func = void (*)(void* param, const std::shared_ptr<std::pair<std::string, message_data>> new_msg, u64 msg_id);
~rpcn_client();
struct friend_data
{
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;
};
class rpcn_client
{
private:
static inline std::weak_ptr<rpcn_client> instance;
static inline shared_mutex inst_mutex;
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::binary_semaphore sem_reader, sem_writer, sem_rpcn;
std::mutex mutex_read, mutex_write;
std::thread thread_rpcn, thread_rpcn_reader, thread_rpcn_writer;
atomic_t<bool> terminate = false;
atomic_t<u32> num_failures = 0;
atomic_t<rpcn_state> state = rpcn_state::failure_no_failure;
std::vector<std::vector<u8>> packets_to_send;
std::mutex mutex_packets_to_send;
// Friends related
shared_mutex mutex_friends;
std::set<std::pair<friend_cb_func, void*>> friend_cbs;
friend_data friend_infos;
void handle_friend_notification(u16 command, std::vector<u8> data);
void handle_message(std::vector<u8> data);
private:
rpcn_client();
void rpcn_reader_thread();
void rpcn_writer_thread();
void rpcn_thread();
bool handle_input();
bool handle_output();
void add_packet(const std::vector<u8> packet);
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 connect(const std::string& host);
bool login(const std::string& npid, const std::string& password, const std::string& token); 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(); void disconnect();
bool manage_connection();
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::vector<std::pair<u16, std::vector<u8>>> get_notifications();
std::unordered_map<u32, std::pair<u16, std::vector<u8>>> get_replies(); std::unordered_map<u32, std::pair<u16, std::vector<u8>>> get_replies();
void abort();
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 // Synchronous requests
bool get_server_list(u32 req_id, const SceNpCommunicationId& communication_id, std::vector<u16>& server_list); bool get_server_list(u32 req_id, const SceNpCommunicationId& communication_id, std::vector<u16>& server_list);
// Asynchronous requests // Asynchronous requests
bool get_world_list(u32 req_id, const SceNpCommunicationId& communication_id, u16 server_id); 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 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 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 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 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 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 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 set_roomdata_internal(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SetRoomDataInternalRequest* req);
@ -176,6 +330,7 @@ public:
bool send_room_message(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SendRoomMessageRequest* req); 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_sign_infos(u32 req_id, const std::string& npid);
bool req_ticket(u32 req_id, const std::string& service_id); 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 const std::string& get_online_name() const
{ {
@ -195,40 +350,22 @@ public:
return port_sig.load(); return port_sig.load();
} }
protected: private:
enum class recvn_result bool get_reply(u64 expected_id, std::vector<u8>& data);
{
recvn_success,
recvn_nodata,
recvn_timeout,
recvn_noconn,
recvn_fatal,
};
recvn_result recvn(u8* buf, usz n); 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 get_reply(u32 expected_id, std::vector<u8>& data); bool forge_send_reply(u16 command, u64 packet_id, const std::vector<u8>& data, std::vector<u8>& reply_data);
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);
bool is_error(ErrorType err) const; bool is_error(ErrorType err) const;
bool error_and_disconnect(const std::string& error_mgs); bool error_and_disconnect(const std::string& error_mgs);
bool is_abort() const;
std::string get_wolfssl_error(int error) const; std::string get_wolfssl_error(WOLFSSL* wssl, int error) const;
protected:
atomic_t<bool> connected = false;
atomic_t<bool> authentified = false;
private:
WOLFSSL_CTX* wssl_ctx = nullptr; WOLFSSL_CTX* wssl_ctx = nullptr;
WOLFSSL* wssl = nullptr; WOLFSSL* read_wssl = nullptr;
WOLFSSL* write_wssl = nullptr;
bool in_config = false;
bool abort_config = false;
atomic_t<bool> server_info_received = false; atomic_t<bool> server_info_received = false;
u32 received_version = 0; u32 received_version = 0;
@ -239,12 +376,33 @@ protected:
sockaddr_in addr_rpcn{}; sockaddr_in addr_rpcn{};
sockaddr_in addr_rpcn_udp{}; sockaddr_in addr_rpcn_udp{};
int sockfd = 0; int sockfd = 0;
shared_mutex mutex_socket;
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; shared_mutex mutex_notifs, mutex_replies, mutex_replies_sync;
std::vector<std::pair<u16, std::vector<u8>>> notifications; // notif type / data 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; // req id / (command / data)
std::unordered_map<u32, std::pair<u16, std::vector<u8>>> replies_sync; // same but for sync replies(Login, Create, GetServerList) 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 online_name{};
std::string avatar_url{}; std::string avatar_url{};
@ -253,4 +411,6 @@ protected:
atomic_t<u32> addr_sig{}; atomic_t<u32> addr_sig{};
atomic_t<u16> port_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); retire_packet(si, signal_connect);
// connection is active // connection is active
update_si_addr(si, op_addr, op_port); 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); update_si_status(si, SCE_NP_SIGNALING_CONN_STATUS_ACTIVE);
break; break;
case signal_confirm: case signal_confirm:
@ -299,6 +300,7 @@ void signaling_handler::process_incoming_messages()
retire_packet(si, signal_connect_ack); retire_packet(si, signal_connect_ack);
// connection is active // connection is active
update_si_addr(si, op_addr, op_port); 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); update_si_status(si, SCE_NP_SIGNALING_CONN_STATUS_ACTIVE, true);
break; break;
case signal_finished: 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) void signaling_handler::update_si_status(std::shared_ptr<signaling_info>& si, s32 new_status, bool confirm_packet)
{ {
if (!si) 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); 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) if (send_packet_from_p2p_port(packet, dest) == -1)
{ {
sign_log.error("Failed to send signaling packet to %s:%d", ip_str, port); 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; u32 addr = 0;
u16 port = 0; u16 port = 0;
// User seen from that peer
u32 mapped_addr = 0;
u16 mapped_port = 0;
// For handler // For handler
steady_clock::time_point time_last_msg_recvd = steady_clock::now(); steady_clock::time_point time_last_msg_recvd = steady_clock::now();
@ -84,6 +88,8 @@ private:
be_t<u32> signature = SIGNALING_SIGNATURE; be_t<u32> signature = SIGNALING_SIGNATURE;
le_t<u32> version; le_t<u32> version;
le_t<SignalingCommand> command; le_t<SignalingCommand> command;
le_t<u32> sent_addr;
le_t<u16> sent_port;
union { union {
struct struct
{ {
@ -117,6 +123,7 @@ private:
u32 create_sig_infos(const SceNpId* npid); 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_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 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_sig_callback(u32 conn_id, int event);
void signal_ext_sig_callback(u32 conn_id, int event) const; 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 MsgDialogBase>()> get_msg_dialog;
std::function<std::shared_ptr<class OskDialogBase>()> get_osk_dialog; std::function<std::shared_ptr<class OskDialogBase>()> get_osk_dialog;
std::function<std::unique_ptr<class SaveDialogBase>()> get_save_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::unique_ptr<class TrophyNotificationBase>()> get_trophy_notification_dialog;
std::function<std::string(localized_string_id, const char*)> get_localized_string; std::function<std::string(localized_string_id, const char*)> get_localized_string;
std::function<std::u32string(localized_string_id, const char*)> get_localized_u32string; 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) switch (value)
{ {
case np_psn_status::disabled: return "Disconnected"; case np_psn_status::disabled: return "Disconnected";
case np_psn_status::fake: return "Simulated"; case np_psn_status::psn_fake: return "Simulated";
case np_psn_status::rpcn: return "RPCN"; case np_psn_status::psn_rpcn: return "RPCN";
} }
return unknown; return unknown;

View File

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

View File

@ -303,6 +303,9 @@
<ClCompile Include="QTGeneratedFiles\Debug\moc_pkg_install_dialog.cpp"> <ClCompile Include="QTGeneratedFiles\Debug\moc_pkg_install_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </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"> <ClCompile Include="QTGeneratedFiles\Debug\moc_register_editor_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
@ -330,6 +333,9 @@
<ClCompile Include="QTGeneratedFiles\Debug\moc_screenshot_preview.cpp"> <ClCompile Include="QTGeneratedFiles\Debug\moc_screenshot_preview.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </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"> <ClCompile Include="QTGeneratedFiles\Debug\moc_settings.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
@ -495,6 +501,9 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_pkg_install_dialog.cpp"> <ClCompile Include="QTGeneratedFiles\Release\moc_pkg_install_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile> </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"> <ClCompile Include="QTGeneratedFiles\Release\moc_register_editor_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
@ -522,6 +531,9 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_screenshot_preview.cpp"> <ClCompile Include="QTGeneratedFiles\Release\moc_screenshot_preview.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile> </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"> <ClCompile Include="QTGeneratedFiles\Release\moc_settings.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
@ -582,10 +594,12 @@
<ClCompile Include="rpcs3qt\patch_manager_dialog.cpp" /> <ClCompile Include="rpcs3qt\patch_manager_dialog.cpp" />
<ClCompile Include="rpcs3qt\pkg_install_dialog.cpp" /> <ClCompile Include="rpcs3qt\pkg_install_dialog.cpp" />
<ClCompile Include="rpcs3qt\persistent_settings.cpp" /> <ClCompile Include="rpcs3qt\persistent_settings.cpp" />
<ClCompile Include="rpcs3qt\recvmessage_dialog_frame.cpp" />
<ClCompile Include="rpcs3qt\render_creator.cpp" /> <ClCompile Include="rpcs3qt\render_creator.cpp" />
<ClCompile Include="rpcs3qt\rpcn_settings_dialog.cpp" /> <ClCompile Include="rpcs3qt\rpcn_settings_dialog.cpp" />
<ClCompile Include="rpcs3qt\screenshot_manager_dialog.cpp" /> <ClCompile Include="rpcs3qt\screenshot_manager_dialog.cpp" />
<ClCompile Include="rpcs3qt\screenshot_preview.cpp" /> <ClCompile Include="rpcs3qt\screenshot_preview.cpp" />
<ClCompile Include="rpcs3qt\sendmessage_dialog_frame.cpp" />
<ClCompile Include="rpcs3qt\settings.cpp" /> <ClCompile Include="rpcs3qt\settings.cpp" />
<ClCompile Include="rpcs3qt\skylander_dialog.cpp" /> <ClCompile Include="rpcs3qt\skylander_dialog.cpp" />
<ClCompile Include="rpcs3qt\tooltips.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)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs> <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
</CustomBuild> </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" /> <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" /> <ClInclude Include="rpcs3qt\stylesheets.h" />
<CustomBuild Include="rpcs3qt\skylander_dialog.h"> <CustomBuild Include="rpcs3qt\skylander_dialog.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs> <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>

View File

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

View File

@ -51,6 +51,7 @@ set(SRC_FILES
progress_dialog.cpp progress_dialog.cpp
qt_utils.cpp qt_utils.cpp
register_editor_dialog.cpp register_editor_dialog.cpp
recvmessage_dialog_frame.cpp
render_creator.cpp render_creator.cpp
rpcn_settings_dialog.cpp rpcn_settings_dialog.cpp
rsx_debugger.cpp rsx_debugger.cpp
@ -60,6 +61,7 @@ set(SRC_FILES
save_manager_dialog.cpp save_manager_dialog.cpp
screenshot_manager_dialog.cpp screenshot_manager_dialog.cpp
screenshot_preview.cpp screenshot_preview.cpp
sendmessage_dialog_frame.cpp
settings.cpp settings.cpp
settings_dialog.cpp settings_dialog.cpp
skylander_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)) switch (static_cast<np_psn_status>(index))
{ {
case np_psn_status::disabled: return tr("Disconnected", "PSN Status"); case np_psn_status::disabled: return tr("Disconnected", "PSN Status");
case np_psn_status::fake: return tr("Simulated", "PSN Status"); case np_psn_status::psn_fake: return tr("Simulated", "PSN Status");
case np_psn_status::rpcn: return tr("RPCN", "PSN Status"); case np_psn_status::psn_rpcn: return tr("RPCN", "PSN Status");
} }
break; break;
case emu_settings_type::SleepTimersAccuracy: case emu_settings_type::SleepTimersAccuracy:

View File

@ -163,19 +163,3 @@ private Q_SLOTS:
void handle_download_error(const QString& error); void handle_download_error(const QString& error);
void handle_download_finished(const QByteArray& content); 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); compat_item->setToolTip(game->compat.tooltip);
if (!game->compat.color.isEmpty()) 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 // 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 "gui_application.h"
#include "qt_utils.h" #include "qt_utils.h"
@ -23,6 +28,8 @@
#include "save_data_dialog.h" #include "save_data_dialog.h"
#include "msg_dialog_frame.h" #include "msg_dialog_frame.h"
#include "osk_dialog_frame.h" #include "osk_dialog_frame.h"
#include "recvmessage_dialog_frame.h"
#include "sendmessage_dialog_frame.h"
#include "stylesheets.h" #include "stylesheets.h"
#include <QScreen> #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_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_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_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.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); }; callbacks.on_run = [this](bool start_playtime) { OnEmulatorRun(start_playtime); };

View File

@ -10,6 +10,7 @@
#include <QTableWidget> #include <QTableWidget>
#include <QHeaderView> #include <QHeaderView>
#include <QTreeWidgetItem> #include <QTreeWidgetItem>
#include <QPainter>
#include <string> #include <string>
@ -17,6 +18,23 @@ namespace gui
{ {
namespace utils 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> template<typename T>
static QSet<T> list_to_set(const QList<T>& list) 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 <QPushButton>
#include <QRegExpValidator> #include <QRegExpValidator>
#include <QInputDialog> #include <QInputDialog>
#include <QGroupBox>
#include <QMenu>
#include <thread> #include <thread>
#include "qt_utils.h"
#include "rpcn_settings_dialog.h" #include "rpcn_settings_dialog.h"
#include "Emu/NP/rpcn_config.h" #include "Emu/NP/rpcn_config.h"
#include "Emu/NP/rpcn_client.h"
#include <wolfssl/ssl.h> #include <wolfssl/ssl.h>
#include <wolfssl/openssl/evp.h> #include <wolfssl/openssl/evp.h>
LOG_CHANNEL(rpcn_settings_log, "rpcn settings dlg");
const QString rpcn_state_to_qstr(rpcn::rpcn_state state)
{
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) rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent)
: QDialog(parent) : QDialog(parent)
{ {
setWindowTitle(tr("RPCN Configuration")); setWindowTitle(tr("RPCN"));
setObjectName("rpcn_settings_dialog"); 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)); setMinimumSize(QSize(400, 200));
QVBoxLayout* vbox_global = new QVBoxLayout(); QVBoxLayout* vbox_global = new QVBoxLayout();
@ -34,11 +104,15 @@ rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent)
m_edit_npid->setValidator(new QRegExpValidator(QRegExp("^[a-zA-Z0-9_\\-]*$"), this)); m_edit_npid->setValidator(new QRegExpValidator(QRegExp("^[a-zA-Z0-9_\\-]*$"), this));
QLabel* label_pass = new QLabel(tr("Password:")); QLabel* label_pass = new QLabel(tr("Password:"));
QPushButton* btn_chg_pass = new QPushButton(tr("Set 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 = new QLineEdit();
m_edit_token->setMaxLength(16); m_edit_token->setMaxLength(16);
QPushButton* btn_create = new QPushButton(tr("Create Account"), 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); QPushButton* btn_save = new QPushButton(tr("Save"), this);
vbox_labels->addWidget(label_host); vbox_labels->addWidget(label_host);
@ -52,6 +126,8 @@ rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent)
vbox_edits->addWidget(m_edit_token); vbox_edits->addWidget(m_edit_token);
hbox_buttons->addWidget(btn_create); hbox_buttons->addWidget(btn_create);
hbox_buttons->addWidget(btn_resendtoken);
hbox_buttons->addWidget(btn_changepass);
hbox_buttons->addStretch(); hbox_buttons->addStretch();
hbox_buttons->addWidget(btn_save); hbox_buttons->addWidget(btn_save);
@ -63,28 +139,16 @@ rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent)
setLayout(vbox_global); setLayout(vbox_global);
connect(btn_chg_pass, &QAbstractButton::clicked, [this]() connect(btn_chg_pass, &QAbstractButton::clicked, this, [this]()
{ {
QString password; rpcn_ask_password_dialog ask_pass;
ask_pass.exec();
while (true) auto password = ask_pass.get_password();
{ if (!password)
bool clicked_ok = false;
password = QInputDialog::getText(this, "Set/Change Password", "Set your password", QLineEdit::Password, "", &clicked_ok);
if (!clicked_ok)
return; return;
if (password.isEmpty()) const std::string pass_str = password.value();
{
QMessageBox::critical(this, tr("Wrong input"), tr("You need to enter a password!"), QMessageBox::Ok);
}
else
{
break;
}
}
const std::string pass_str = password.toStdString();
const std::string salt_str = "No matter where you go, everybody's connected."; const std::string salt_str = "No matter where you go, everybody's connected.";
u8 salted_pass[SHA_DIGEST_SIZE]; u8 salted_pass[SHA_DIGEST_SIZE];
@ -101,14 +165,17 @@ rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent)
g_cfg_rpcn.set_password(hash); g_cfg_rpcn.set_password(hash);
g_cfg_rpcn.save(); g_cfg_rpcn.save();
QMessageBox::information(this, tr("RPCN Password Saved"), tr("Your password was saved successfully!"), QMessageBox::Ok);
}); });
connect(btn_save, &QAbstractButton::clicked, [this]() connect(btn_save, &QAbstractButton::clicked, this, [this]()
{ {
if (this->save_config()) if (this->save_config())
this->close(); this->close();
}); });
connect(btn_create, &QAbstractButton::clicked, [this]() { this->create_account(); }); connect(btn_create, &QAbstractButton::clicked, this, [this]()
{ this->create_account(); });
g_cfg_rpcn.load(); 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())); 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 host = m_edit_host->text().toStdString();
const auto npid = m_edit_npid->text().toStdString(); const auto npid = m_edit_npid->text().toStdString();
const auto token = m_edit_token->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()) if (host.empty())
{ {
QMessageBox::critical(this, tr("Missing host"), tr("You need to enter a host for rpcn!"), QMessageBox::Ok); 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; 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); 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; return false;
@ -164,7 +223,7 @@ bool rpcn_settings_dialog::save_config()
return true; return true;
} }
bool rpcn_settings_dialog::create_account() bool rpcn_account_dialog::create_account()
{ {
// Validate and save // Validate and save
if (!save_config()) if (!save_config())
@ -176,7 +235,7 @@ bool rpcn_settings_dialog::create_account()
while (true) while (true)
{ {
bool clicked_ok = false; 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) if (!clicked_ok)
return false; 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 host = g_cfg_rpcn.get_host();
const auto npid = g_cfg_rpcn.get_npid(); 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 avatar_url = "https://rpcs3.net/cdn/netplay/DefaultAvatar.png";
const auto password = g_cfg_rpcn.get_password(); const auto password = g_cfg_rpcn.get_password();
std::thread( if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure)
[](const std::shared_ptr<rpcn_client> rpcn)
{ {
while (rpcn.use_count() != 1) const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(rpcn_state_to_qstr(result));
rpcn->manage_connection(); QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok);
rpcn->disconnect();
},
rpcn)
.detach();
if (!rpcn->connect(host))
{
QMessageBox::critical(this, tr("Error Connecting"), tr("Failed to connect to RPCN server"), QMessageBox::Ok);
rpcn->abort();
return false; 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); QString error_message;
rpcn->abort(); 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; return false;
} }
QMessageBox::information(this, tr("Account created!"), tr("Your account has been created successfully!\nCheck your email for your token!"), QMessageBox::Ok); 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; 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 #pragma once
#include <optional>
#include <QDialog> #include <QDialog>
#include <QLineEdit> #include <QLineEdit>
#include <QListWidget>
#include "Emu/NP/rpcn_client.h"
class rpcn_settings_dialog : public QDialog class rpcn_settings_dialog : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
rpcn_settings_dialog(QWidget* parent = nullptr); 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 save_config();
bool create_account(); bool create_account();
@ -16,3 +27,51 @@ public:
protected: protected:
QLineEdit *m_edit_host, *m_edit_npid, *m_edit_token; 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);
};