diff --git a/.gitignore b/.gitignore index f576495fd3..40fb58d238 100644 --- a/.gitignore +++ b/.gitignore @@ -122,6 +122,9 @@ yaml-cpp.pc /3rdparty/libusb_cmake/config.h /3rdparty/libusb_cmake/libusb-1.0.pc +# miniupnp +/3rdparty/miniupnp/x64/* + # ssl certificate cacert.pem diff --git a/.gitmodules b/.gitmodules index 0d6c707eb6..8c19376012 100644 --- a/.gitmodules +++ b/.gitmodules @@ -80,3 +80,6 @@ path = 3rdparty/libsdl-org/SDL url = https://github.com/libsdl-org/SDL.git ignore = dirty +[submodule "3rdparty/miniupnp/miniupnp"] + path = 3rdparty/miniupnp/miniupnp + url = https://github.com/miniupnp/miniupnp.git diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 2ad07551f2..60e006e811 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -358,6 +358,9 @@ if(USE_SDL) endif() endif() +# MINIUPNP +add_subdirectory(miniupnp EXCLUDE_FROM_ALL) + # add nice ALIAS targets for ease of use if(USE_SYSTEM_LIBUSB) add_library(3rdparty::libusb ALIAS usb-1.0-shared) @@ -385,3 +388,4 @@ add_library(3rdparty::wolfssl ALIAS wolfssl) add_library(3rdparty::libcurl ALIAS libcurl) add_library(3rdparty::soundtouch ALIAS soundtouch) add_library(3rdparty::sdl2 ALIAS ${SDL2_TARGET}) +add_library(3rdparty::miniupnpc ALIAS libminiupnpc-static) diff --git a/3rdparty/miniupnp/CMakeLists.txt b/3rdparty/miniupnp/CMakeLists.txt new file mode 100644 index 0000000000..93c3dc55cd --- /dev/null +++ b/3rdparty/miniupnp/CMakeLists.txt @@ -0,0 +1,8 @@ +option (UPNPC_BUILD_STATIC "Build static library" TRUE) +option (UPNPC_BUILD_SHARED "Build shared library" FALSE) +option (UPNPC_BUILD_TESTS "Build test executables" FALSE) +option (UPNPC_BUILD_SAMPLE "Build sample executables" FALSE) +option (NO_GETADDRINFO "Define NO_GETADDRINFO" FALSE) +option (UPNPC_NO_INSTALL "Disable installation" TRUE) + +add_subdirectory(miniupnp/miniupnpc EXCLUDE_FROM_ALL) diff --git a/3rdparty/miniupnp/miniupnp b/3rdparty/miniupnp/miniupnp new file mode 160000 index 0000000000..f4a739d730 --- /dev/null +++ b/3rdparty/miniupnp/miniupnp @@ -0,0 +1 @@ +Subproject commit f4a739d73083bee207af30b8aa3e668383ee070e diff --git a/3rdparty/miniupnp/miniupnpc_static.vcxproj b/3rdparty/miniupnp/miniupnpc_static.vcxproj new file mode 100644 index 0000000000..f8b876794b --- /dev/null +++ b/3rdparty/miniupnp/miniupnpc_static.vcxproj @@ -0,0 +1,138 @@ + + + + + Debug + x64 + + + Release + x64 + + + + Win32Proj + {5228f863-e0dd-4de7-aa7b-5c52b14cd4d0} + miniupnpcstatic + + + + + + StaticLibrary + true + Unicode + + + StaticLibrary + false + true + Unicode + + + + + + + + + + + + + + + true + $(SolutionDir)lib\$(Configuration)-$(Platform)\ + + + false + $(SolutionDir)lib\$(Configuration)-$(Platform)\ + + + + Level3 + true + _WINSOCK_DEPRECATED_NO_WARNINGS; _CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + generated;miniupnp\miniupnpc;miniupnp\miniupnpc\include + MultiThreadedDebug + + + true + Windows + + + cd $(ProjectDir)miniupnp\miniupnpc\msvc +cscript genminiupnpcstrings.vbs + + + + + Level3 + true + true + true + _WINSOCK_DEPRECATED_NO_WARNINGS; _CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + generated;miniupnp\miniupnpc;miniupnp\miniupnpc\include + MultiThreaded + + + + + true + true + true + + + cd $(ProjectDir)miniupnp\miniupnpc\msvc +cscript genminiupnpcstrings.vbs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rpcs3.sln b/rpcs3.sln index 2f88bdf168..5f0efc1b92 100644 --- a/rpcs3.sln +++ b/rpcs3.sln @@ -74,7 +74,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcurl", "3rdparty\curl\li {73973223-5EE8-41CA-8E88-1D60E89A237B} = {73973223-5EE8-41CA-8E88-1D60E89A237B} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SPIRV", "3rdparty\SPIRV\spirv.vcxproj", "{4CBD3DDD-5555-49A4-A44D-DD3D8CB516A1}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spirv", "3rdparty\SPIRV\spirv.vcxproj", "{4CBD3DDD-5555-49A4-A44D-DD3D8CB516A1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3rdParty", "3rdParty", "{6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}" EndProject @@ -93,6 +93,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pine", "pine", "{A55DA1B5-C 3rdparty\pine\pine_server.h = 3rdparty\pine\pine_server.h EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniupnpc_static", "3rdparty\miniupnp\miniupnpc_static.vcxproj", "{5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -187,6 +189,10 @@ Global {8DC244EE-A0BD-4038-BAF7-CFAFA5EB2BAA}.Debug|x64.Build.0 = Debug|x64 {8DC244EE-A0BD-4038-BAF7-CFAFA5EB2BAA}.Release|x64.ActiveCfg = Release|x64 {8DC244EE-A0BD-4038-BAF7-CFAFA5EB2BAA}.Release|x64.Build.0 = Release|x64 + {5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}.Debug|x64.ActiveCfg = Debug|x64 + {5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}.Debug|x64.Build.0 = Debug|x64 + {5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}.Release|x64.ActiveCfg = Release|x64 + {5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -216,6 +222,7 @@ Global {508C291A-3D18-49F5-B25D-F7C8DB92CB21} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8} {8DC244EE-A0BD-4038-BAF7-CFAFA5EB2BAA} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8} {A55DA1B5-CC17-4525-BE7F-1659CE17BB56} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8} + {5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {06CC7920-E085-4B81-9582-8DE8AAD42510} diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 4c06529b4c..4c6b604acf 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -159,6 +159,10 @@ target_link_libraries(rpcs3_emu PUBLIC 3rdparty::soundtouch) +target_link_libraries(rpcs3_emu + PUBLIC + 3rdparty::miniupnpc) + # Cell target_sources(rpcs3_emu PRIVATE Cell/MFC.cpp @@ -411,6 +415,8 @@ target_sources(rpcs3_emu PRIVATE NP/np_structs_extra.cpp NP/rpcn_client.cpp NP/rpcn_config.cpp + NP/upnp_config.cpp + NP/upnp_handler.cpp ) # Memory diff --git a/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp b/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp index 96b803e15a..f8957e8556 100644 --- a/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp +++ b/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp @@ -221,16 +221,15 @@ error_code cellNetCtlGetInfo(s32 code, vm::ptr info) switch (code) { case CELL_NET_CTL_INFO_DEVICE: info->device = CELL_NET_CTL_DEVICE_WIRED; break; - // case CELL_NET_CTL_INFO_ETHER_ADDR: std::memset(info->ether_addr.data, 0xFF, sizeof(info->ether_addr.data)); break; case CELL_NET_CTL_INFO_MTU: info->mtu = 1500; break; case CELL_NET_CTL_INFO_LINK: info->link = CELL_NET_CTL_LINK_CONNECTED; break; - case CELL_NET_CTL_INFO_LINK_TYPE: info->link_type = CELL_NET_CTL_LINK_TYPE_10BASE_FULL; break; + case CELL_NET_CTL_INFO_LINK_TYPE: info->link_type = CELL_NET_CTL_LINK_TYPE_100BASE_FULL; break; case CELL_NET_CTL_INFO_IP_CONFIG: info->ip_config = CELL_NET_CTL_IP_STATIC; break; case CELL_NET_CTL_INFO_DEFAULT_ROUTE: strcpy_trunc(info->default_route, "192.168.1.1"); break; case CELL_NET_CTL_INFO_PRIMARY_DNS: strcpy_trunc(info->primary_dns, np::ip_to_string(nph.get_dns_ip())); break; case CELL_NET_CTL_INFO_SECONDARY_DNS: strcpy_trunc(info->secondary_dns, np::ip_to_string(nph.get_dns_ip())); break; - case CELL_NET_CTL_INFO_IP_ADDRESS: strcpy_trunc(info->ip_address, np::ip_to_string(nph.get_public_ip_addr())); break; - case CELL_NET_CTL_INFO_NETMASK: strcpy_trunc(info->netmask, "255.255.255.255"); break; + case CELL_NET_CTL_INFO_IP_ADDRESS: strcpy_trunc(info->ip_address, np::ip_to_string(nph.get_local_ip_addr())); break; // verified on HW + case CELL_NET_CTL_INFO_NETMASK: strcpy_trunc(info->netmask, "255.255.255.0"); break; case CELL_NET_CTL_INFO_HTTP_PROXY_CONFIG: info->http_proxy_config = 0; break; case CELL_NET_CTL_INFO_DHCP_HOSTNAME: strcpy_trunc(info->dhcp_hostname, nph.get_hostname()); break; default: cellNetCtl.error("Unsupported request: %s", InfoCodeToName(code)); break; @@ -348,7 +347,7 @@ error_code cellNetCtlGetNatInfo(vm::ptr natInfo) natInfo->nat_type = CELL_NET_CTL_NATINFO_NAT_TYPE_2; natInfo->stun_status = CELL_NET_CTL_NATINFO_STUN_OK; - natInfo->upnp_status = CELL_NET_CTL_NATINFO_UPNP_NO; + natInfo->upnp_status = nph.get_upnp_status(); return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/sceNp.cpp b/rpcs3/Emu/Cell/Modules/sceNp.cpp index 4e9b509489..9ed16d56ac 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp.cpp @@ -18,6 +18,7 @@ #include "Emu/Cell/lv2/sys_fs.h" #include "Emu/NP/np_handler.h" #include "Emu/NP/np_contexts.h" +#include "Emu/NP/np_helpers.h" #include "Emu/system_config.h" LOG_CHANNEL(sceNp); @@ -5237,11 +5238,11 @@ error_code sceNpSignalingActivateConnection(u32 ctx_id, vm::ptr npId, v return SCE_NP_SIGNALING_ERROR_INVALID_ARGUMENT; } - if (strncmp(nph.get_npid().handle.data, npId->handle.data, 16) == 0) + if (np::is_same_npid(nph.get_npid(), *npId)) return SCE_NP_SIGNALING_ERROR_OWN_NP_ID; auto& sigh = g_fxo->get>(); - *conn_id = sigh.init_sig_infos(npId.get_ptr()); + *conn_id = sigh.init_sig1(*npId); return CELL_OK; } @@ -5298,19 +5299,15 @@ error_code sceNpSignalingGetConnectionStatus(u32 ctx_id, u32 conn_id, vm::ptrconn_status; if (peer_addr) - (*peer_addr).np_s_addr = si.addr; // infos.addr is already BE + (*peer_addr).np_s_addr = si->addr; // infos.addr is already BE if (peer_port) - *peer_port = si.port; + *peer_port = si->port; return CELL_OK; } @@ -5334,33 +5331,36 @@ error_code sceNpSignalingGetConnectionInfo(u32 ctx_id, u32 conn_id, s32 code, vm auto& sigh = g_fxo->get>(); const auto si = sigh.get_sig_infos(conn_id); + if (!si) + return SCE_NP_SIGNALING_ERROR_CONN_NOT_FOUND; + switch (code) { case SCE_NP_SIGNALING_CONN_INFO_RTT: { - info->rtt = si.rtt; + info->rtt = si->rtt; break; } case SCE_NP_SIGNALING_CONN_INFO_BANDWIDTH: { - info->bandwidth = 10'000'000; // 10 MBPS HACK + info->bandwidth = 100'000'000; // 100 MBPS HACK break; } case SCE_NP_SIGNALING_CONN_INFO_PEER_NPID: { - info->npId = si.npid; + info->npId = si->npid; break; } case SCE_NP_SIGNALING_CONN_INFO_PEER_ADDRESS: { - info->address.port = std::bit_cast>(si.port); - info->address.addr.np_s_addr = si.addr; + info->address.port = std::bit_cast>(si->port); + info->address.addr.np_s_addr = si->addr; break; } case SCE_NP_SIGNALING_CONN_INFO_MAPPED_ADDRESS: { - info->address.port = std::bit_cast>(si.mapped_port); - info->address.addr.np_s_addr = si.mapped_addr; + info->address.port = std::bit_cast>(si->mapped_port); + info->address.addr.np_s_addr = si->mapped_addr; break; } case SCE_NP_SIGNALING_CONN_INFO_PACKET_LOSS: @@ -5393,8 +5393,13 @@ error_code sceNpSignalingGetConnectionFromNpId(u32 ctx_id, vm::ptr npId return SCE_NP_SIGNALING_ERROR_INVALID_ARGUMENT; } + if (np::is_same_npid(*npId, nph.get_npid())) + { + return SCE_NP_SIGNALING_ERROR_OWN_NP_ID; + } + auto& sigh = g_fxo->get>(); - const auto found_conn_id = sigh.get_conn_id_from_npid(npId.get_ptr()); + const auto found_conn_id = sigh.get_conn_id_from_npid(*npId); if (!found_conn_id) { @@ -5455,9 +5460,9 @@ error_code sceNpSignalingGetLocalNetInfo(u32 ctx_id, vm::ptrmapped_addr = nph.get_public_ip_addr(); // Pure speculation below - info->nat_status = 0; - info->upnp_status = 0; - info->npport_status = 0; + info->nat_status = SCE_NP_SIGNALING_NETINFO_NAT_STATUS_TYPE2; + info->upnp_status = nph.get_upnp_status(); + info->npport_status = SCE_NP_SIGNALING_NETINFO_NPPORT_STATUS_OPEN; info->npport = SCE_NP_PORT; return CELL_OK; @@ -5537,25 +5542,8 @@ error_code sceNpUtilCmpNpId(vm::ptr id1, vm::ptr id2) return SCE_NP_UTIL_ERROR_INVALID_ARGUMENT; } - // Unknown what this constant means - // if (id1->reserved[0] != 1 || id2->reserved[0] != 1) - // { - // return SCE_NP_UTIL_ERROR_INVALID_NP_ID; - // } - - if (strncmp(id1->handle.data, id2->handle.data, 16))// || id1->unk1[0] != id2->unk1[0]) - { + if (!np::is_same_npid(*id1, *id2)) return not_an_error(SCE_NP_UTIL_ERROR_NOT_MATCH); - } - - // if (id1->unk1[1] != id2->unk1[1]) - // { - // // If either is zero they match - // if (id1->opt[4] && id2->opt[4]) - // { - // return SCE_NP_UTIL_ERROR_NOT_MATCH; - // } - // } return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/sceNp2.cpp b/rpcs3/Emu/Cell/Modules/sceNp2.cpp index b4f9923bbe..91c62e7290 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp2.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp2.cpp @@ -6,6 +6,7 @@ #include "sceNp2.h" #include "Emu/NP/np_handler.h" #include "Emu/NP/np_contexts.h" +#include "Emu/NP/np_helpers.h" #include "cellSysutil.h" LOG_CHANNEL(sceNp2); @@ -444,20 +445,35 @@ error_code sceNpMatching2SignalingGetConnectionStatus( return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + auto [res, npid] = nph.local_get_npid(roomId, memberId); + + if (res) + return res; + + if (np::is_same_npid(nph.get_npid(), *npid)) + return SCE_NP_SIGNALING_ERROR_OWN_NP_ID; + auto& sigh = g_fxo->get>(); - const auto si = sigh.get_sig2_infos(roomId, memberId); + auto conn_id = sigh.get_conn_id_from_npid(*npid); + if (!conn_id) + return SCE_NP_SIGNALING_ERROR_CONN_NOT_FOUND; - *connStatus = si.connStatus; + const auto si = sigh.get_sig_infos(*conn_id); + + if (!si) + return SCE_NP_SIGNALING_ERROR_CONN_NOT_FOUND; + + *connStatus = si->conn_status; if (peerAddr) { - (*peerAddr).np_s_addr = si.addr; // infos.addr is already BE + (*peerAddr).np_s_addr = si->addr; // infos.addr is already BE } if (peerPort) { - *peerPort = si.port; + *peerPort = si->port; } return CELL_OK; @@ -616,42 +632,58 @@ error_code sceNpMatching2SignalingGetConnectionInfo( return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + auto [res, npid] = nph.local_get_npid(roomId, memberId); + + if (res) + return res; + + if (np::is_same_npid(nph.get_npid(), *npid)) + return SCE_NP_SIGNALING_ERROR_OWN_NP_ID; + auto& sigh = g_fxo->get>(); - const auto si = sigh.get_sig2_infos(roomId, memberId); + + auto conn_id = sigh.get_conn_id_from_npid(*npid); + if (!conn_id) + return SCE_NP_SIGNALING_ERROR_CONN_NOT_FOUND; + + const auto si = sigh.get_sig_infos(*conn_id); + + if (!si) + return SCE_NP_SIGNALING_ERROR_CONN_NOT_FOUND; switch (code) { case SCE_NP_SIGNALING_CONN_INFO_RTT: { - connInfo->rtt = si.rtt; + connInfo->rtt = si->rtt; sceNp2.warning("Returning a RTT of %d microseconds", connInfo->rtt); break; } case SCE_NP_SIGNALING_CONN_INFO_BANDWIDTH: { - connInfo->bandwidth = 10'000'000; // 10 MBPS HACK + connInfo->bandwidth = 100'000'000; // 100 MBPS HACK break; } case SCE_NP_SIGNALING_CONN_INFO_PEER_NPID: { - connInfo->npId = si.npid; + connInfo->npId = si->npid; break; } case SCE_NP_SIGNALING_CONN_INFO_PEER_ADDRESS: { - connInfo->address.port = std::bit_cast>(si.port); - connInfo->address.addr.np_s_addr = si.addr; + connInfo->address.port = std::bit_cast>(si->port); + connInfo->address.addr.np_s_addr = si->addr; break; } case SCE_NP_SIGNALING_CONN_INFO_MAPPED_ADDRESS: { - connInfo->address.port = std::bit_cast>(si.mapped_port); - connInfo->address.addr.np_s_addr = si.mapped_addr; + connInfo->address.port = std::bit_cast>(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 = 0; // HACK break; } default: @@ -709,7 +741,7 @@ error_code sceNpMatching2GetRoomMemberDataExternalList(SceNpMatching2ContextId c error_code sceNpMatching2AbortRequest(SceNpMatching2ContextId ctxId, SceNpMatching2RequestId reqId) { - sceNp2.todo("sceNpMatching2AbortRequest(ctxId=%d, reqId=%d)", ctxId, reqId); + sceNp2.warning("sceNpMatching2AbortRequest(ctxId=%d, reqId=%d)", ctxId, reqId); auto& nph = g_fxo->get>(); @@ -728,6 +760,9 @@ error_code sceNpMatching2AbortRequest(SceNpMatching2ContextId ctxId, SceNpMatchi return SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND; } + if (!nph.abort_request(reqId)) + return SCE_NP_MATCHING2_ERROR_REQUEST_NOT_FOUND; + return CELL_OK; } @@ -1591,7 +1626,7 @@ error_code sceNpMatching2SignalingCancelPeerNetInfo(SceNpMatching2ContextId ctxI error_code sceNpMatching2SignalingGetLocalNetInfo(vm::ptr netinfo) { - sceNp2.todo("sceNpMatching2SignalingGetLocalNetInfo(netinfo=*0x%x)", netinfo); + sceNp2.warning("sceNpMatching2SignalingGetLocalNetInfo(netinfo=*0x%x)", netinfo); auto& nph = g_fxo->get>(); @@ -1600,12 +1635,17 @@ error_code sceNpMatching2SignalingGetLocalNetInfo(vm::ptrsize != sizeof(SceNpMatching2SignalingNetInfo)) { - // TODO: check netinfo->size return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + netinfo->localAddr = nph.get_local_ip_addr(); + netinfo->mappedAddr = nph.get_public_ip_addr(); + + // Pure speculation below + netinfo->natStatus = SCE_NP_SIGNALING_NETINFO_NAT_STATUS_TYPE2; + return CELL_OK; } @@ -1639,9 +1679,8 @@ error_code sceNpMatching2SignalingGetPeerNetInfoResult(SceNpMatching2ContextId c return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!netinfo) + if (!netinfo || netinfo->size != sizeof(SceNpMatching2SignalingNetInfo)) { - // TODO: check netinfo->size return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } diff --git a/rpcs3/Emu/Cell/Modules/sceNp2.h b/rpcs3/Emu/Cell/Modules/sceNp2.h index 73c91827e2..711f050274 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp2.h +++ b/rpcs3/Emu/Cell/Modules/sceNp2.h @@ -1555,9 +1555,9 @@ struct SceNpMatching2CbQueueInfo u8 reserved[12]; }; -union SceNpMatching2SignalingNetInfo // TODO check values +struct SceNpMatching2SignalingNetInfo { - be_t size; + be_t size; be_t localAddr; be_t mappedAddr; be_t natStatus; diff --git a/rpcs3/Emu/Cell/Modules/sceNpUtil.cpp b/rpcs3/Emu/Cell/Modules/sceNpUtil.cpp index e4ace7fbbe..b26d604a25 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpUtil.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpUtil.cpp @@ -39,8 +39,8 @@ struct bandwidth_test } } - test_result.upload_bps = 16000.0; - test_result.download_bps = 16000.0; + test_result.upload_bps = 100'000'000.0; + test_result.download_bps = 100'000'000.0; test_result.result = CELL_OK; status = SCE_NP_UTIL_BANDWIDTH_TEST_STATUS_FINISHED; finished = true; diff --git a/rpcs3/Emu/Cell/lv2/sys_net.cpp b/rpcs3/Emu/Cell/lv2/sys_net.cpp index 653f2bee81..641bd5c1da 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net.cpp @@ -1769,7 +1769,7 @@ error_code sys_net_abort(ppu_thread& ppu, s32 type, u64 arg, s32 flags) { ppu.state += cpu_flag::wait; - sys_net.todo("sys_net_abort(type=%d, arg=0x%x, flags=0x%x)", type, arg, flags); + sys_net.warning("sys_net_abort(type=%d, arg=0x%x, flags=0x%x)", type, arg, flags); enum abort_type : s32 { diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp index 03ace40252..cc800a1633 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp @@ -50,6 +50,13 @@ lv2_socket_native::~lv2_socket_native() ::close(socket); #endif } + + if (bound_port) + { + auto& nph = g_fxo->get>(); + nph.upnp_remove_port_mapping(bound_port, type == SYS_NET_SOCK_STREAM ? "TCP" : "UDP"); + bound_port = 0; + } } s32 lv2_socket_native::create_socket() @@ -143,10 +150,36 @@ s32 lv2_socket_native::bind(const sys_net_sockaddr& addr) native_addr.sin_addr.s_addr = saddr; ::socklen_t native_addr_len = sizeof(native_addr); + // Note that this is a hack(TODO) + // ATM we don't support binding 3658 udp because we use it for the p2ps main socket + // Only Fat Princess is known to do this to my knowledge + if (psa_in->sin_port == 3658 && type == SYS_NET_SOCK_DGRAM) + { + native_addr.sin_port = std::bit_cast>(3659); + } + sys_net.warning("[Native] Trying to bind %s:%d", native_addr.sin_addr, std::bit_cast, u16>(native_addr.sin_port)); if (::bind(socket, reinterpret_cast(&native_addr), native_addr_len) == 0) { + // Only UPNP port forward binds to 0.0.0.0 + if (saddr == 0) + { + if (native_addr.sin_port == 0) + { + sockaddr_in client_addr; + socklen_t client_addr_size = sizeof(client_addr); + ensure(::getsockname(socket, reinterpret_cast(&client_addr), &client_addr_size) == 0); + bound_port = std::bit_cast>(client_addr.sin_port); + } + else + { + bound_port = std::bit_cast>(native_addr.sin_port); + } + + nph.upnp_add_port_mapping(bound_port, type == SYS_NET_SOCK_STREAM ? "TCP" : "UDP"); + } + last_bound_addr = addr; return CELL_OK; } @@ -1011,6 +1044,13 @@ void lv2_socket_native::close() auto& dnshook = g_fxo->get(); dnshook.remove_dns_spy(lv2_id); + + if (bound_port) + { + auto& nph = g_fxo->get>(); + nph.upnp_remove_port_mapping(bound_port, type == SYS_NET_SOCK_STREAM ? "TCP" : "UDP"); + bound_port = 0; + } } s32 lv2_socket_native::shutdown(s32 how) diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h index 0fce833bea..f6b221b97d 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h @@ -68,4 +68,5 @@ private: s32 so_reuseaddr = 0; s32 so_reuseport = 0; #endif + u16 bound_port = 0; }; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp b/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp index cf04c3216f..984ac20dfb 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp @@ -83,14 +83,10 @@ void need_network() initialize_tcp_timeout_monitor(); } -network_thread::network_thread() noexcept -{ - if (g_cfg.net.psn_status == np_psn_status::psn_rpcn) - list_p2p_ports.emplace(std::piecewise_construct, std::forward_as_tuple(SCE_NP_PORT), std::forward_as_tuple(SCE_NP_PORT)); -} - -network_thread::~network_thread() +void network_thread::bind_sce_np_port() { + std::lock_guard list_lock(list_p2p_ports_mutex); + list_p2p_ports.emplace(std::piecewise_construct, std::forward_as_tuple(SCE_NP_PORT), std::forward_as_tuple(SCE_NP_PORT)); } void network_thread::operator()() diff --git a/rpcs3/Emu/Cell/lv2/sys_net/network_context.h b/rpcs3/Emu/Cell/lv2/sys_net/network_context.h index 75897ed52e..accdcee027 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/network_context.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/network_context.h @@ -17,10 +17,7 @@ struct network_thread static constexpr auto thread_name = "Network Thread"; - network_thread() noexcept; - - ~network_thread(); - + void bind_sce_np_port(); void operator()(); }; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp index d63d01fbab..1bcc4ca1a2 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp @@ -10,6 +10,7 @@ #include "Emu/NP/signaling_handler.h" #include "sys_net_helpers.h" #include "Emu/NP/vport0.h" +#include "Emu/NP/np_handler.h" LOG_CHANNEL(sys_net); @@ -69,6 +70,9 @@ nt_p2p_port::nt_p2p_port(u16 port) if (ret_bind == -1) fmt::throw_exception("Failed to bind DGRAM socket to %d for P2P: %s!", port, get_last_error(true)); + auto& nph = g_fxo->get>(); + nph.upnp_add_port_mapping(port, "UDP"); + sys_net.notice("P2P port %d was bound!", port); } @@ -82,6 +86,9 @@ nt_p2p_port::~nt_p2p_port() ::close(p2p_socket); #endif } + + auto& nph = g_fxo->get>(); + nph.upnp_remove_port_mapping(port, "UDP"); } void nt_p2p_port::dump_packet(p2ps_encapsulated_tcp* tcph) diff --git a/rpcs3/Emu/NP/np_cache.cpp b/rpcs3/Emu/NP/np_cache.cpp index accbbc118f..743147feb6 100644 --- a/rpcs3/Emu/NP/np_cache.cpp +++ b/rpcs3/Emu/NP/np_cache.cpp @@ -3,6 +3,9 @@ #include "Emu/NP/np_allocator.h" #include "Emu/NP/np_cache.h" +#include "Emu/NP/np_helpers.h" + +LOG_CHANNEL(np_cache); namespace np { @@ -80,20 +83,32 @@ namespace np rooms[sce_roomdata->roomId].update(sce_roomdata); } - void cache_manager::add_member(SceNpMatching2RoomId room_id, const SceNpMatching2RoomMemberDataInternal* sce_roommemberdata) + bool cache_manager::add_member(SceNpMatching2RoomId room_id, const SceNpMatching2RoomMemberDataInternal* sce_roommemberdata) { std::lock_guard lock(mutex); - ensure(rooms.contains(room_id), "cache_manager::add_member: Room not cached!"); + if (!rooms.contains(room_id)) + { + np_cache.error("np_cache::add_member cache miss: room_id(%d)", room_id); + return false; + } + rooms[room_id].members.insert_or_assign(sce_roommemberdata->memberId, member_cache(sce_roommemberdata)); + return true; } - void cache_manager::del_member(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id) + bool cache_manager::del_member(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id) { std::lock_guard lock(mutex); - ensure(rooms.contains(room_id), "cache_manager::del_member: Room not cached!"); + if (!rooms.contains(room_id)) + { + np_cache.error("np_cache::del_member cache miss: room_id(%d)/member_id(%d)", room_id, member_id); + return false; + } + rooms.erase(member_id); + return true; } void cache_manager::update_password(SceNpMatching2RoomId room_id, const std::optional& password) @@ -121,7 +136,7 @@ namespace np SceNpMatching2RoomJoinedSlotMask join_mask = 0; for (const auto& member : room.members) { - join_mask |= (1 << (member.first - 1)); + join_mask |= (1 << ((member.first >> 4) - 1)); } slots.joinedSlotMask = join_mask; slots.passwordSlotMask = room.mask_password; @@ -307,14 +322,45 @@ namespace np return needed_data_size; } - SceNpId cache_manager::get_npid(u64 room_id, u16 member_id) + std::pair> cache_manager::get_npid(u64 room_id, u16 member_id) { std::lock_guard lock(mutex); - ensure(rooms.contains(room_id), "cache_manager::get_npid: Room not cached!"); - ensure(::at32(rooms, room_id).members.contains(member_id), "cache_manager::get_npid: Member not cached!"); + if (!rooms.contains(room_id)) + { + np_cache.error("np_cache::get_npid cache miss room_id: room_id(%d)/member_id(%d)", room_id, member_id); + return {SCE_NP_MATCHING2_ERROR_INVALID_ROOM_ID, std::nullopt}; + } - return ::at32(::at32(rooms, room_id).members, member_id).userInfo.npId; + if (!::at32(rooms, room_id).members.contains(member_id)) + { + np_cache.error("np_cache::get_npid cache miss member_id: room_id(%d)/member_id(%d)", room_id, member_id); + return {SCE_NP_MATCHING2_ERROR_INVALID_MEMBER_ID, std::nullopt}; + } + + return {CELL_OK, ::at32(::at32(rooms, room_id).members, member_id).userInfo.npId}; + } + + std::optional cache_manager::get_memberid(u64 room_id, const SceNpId& npid) + { + std::lock_guard lock(mutex); + + if (!rooms.contains(room_id)) + { + np_cache.error("np_cache::get_memberid cache miss room_id: room_id(%d)/npid(%s)", room_id, static_cast(npid.handle.data)); + return std::nullopt; + } + + const auto& members = ::at32(rooms, room_id).members; + + for (const auto& [id, member_cache] : members) + { + if (np::is_same_npid(member_cache.userInfo.npId, npid)) + return id; + } + + np_cache.error("np_cache::get_memberid cache miss member_id: room_id(%d)/npid(%s)", room_id, static_cast(npid.handle.data)); + return std::nullopt; } } // namespace np diff --git a/rpcs3/Emu/NP/np_cache.h b/rpcs3/Emu/NP/np_cache.h index 7d66bf3067..976c3b5838 100644 --- a/rpcs3/Emu/NP/np_cache.h +++ b/rpcs3/Emu/NP/np_cache.h @@ -67,15 +67,16 @@ namespace np cache_manager() = default; void insert_room(const SceNpMatching2RoomDataInternal* sce_roomdata); - void add_member(SceNpMatching2RoomId room_id, const SceNpMatching2RoomMemberDataInternal* sce_roommemberdata); - void del_member(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id); + bool add_member(SceNpMatching2RoomId room_id, const SceNpMatching2RoomMemberDataInternal* sce_roommemberdata); + bool del_member(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id); void update_password(SceNpMatching2RoomId room_id, const std::optional& password); std::pair> get_slots(SceNpMatching2RoomId room_id); std::pair> get_memberids(u64 room_id, s32 sort_method); std::pair> get_password(SceNpMatching2RoomId room_id); error_code get_member_and_attrs(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id, const std::vector& binattrs_list, SceNpMatching2RoomMemberDataInternal* ptr_member, u32 addr_data, u32 size_data); - SceNpId get_npid(u64 room_id, u16 member_id); + std::pair> get_npid(u64 room_id, u16 member_id); + std::optional get_memberid(u64 room_id, const SceNpId& npid); private: shared_mutex mutex; diff --git a/rpcs3/Emu/NP/np_handler.cpp b/rpcs3/Emu/NP/np_handler.cpp index 8d2353c31a..900899ece1 100644 --- a/rpcs3/Emu/NP/np_handler.cpp +++ b/rpcs3/Emu/NP/np_handler.cpp @@ -6,7 +6,6 @@ #include "Emu/Cell/Modules/sceNp2.h" #include "Emu/Cell/Modules/cellNetCtl.h" #include "Utilities/StrUtil.h" -#include "Emu/Cell/Modules/cellSysutil.h" #include "Emu/IdManager.h" #include "Emu/NP/np_structs_extra.h" #include "Emu/System.h" @@ -14,6 +13,8 @@ #include "Emu/NP/np_contexts.h" #include "Emu/NP/np_helpers.h" #include "Emu/RSX/Overlays/overlay_message.h" +#include "Emu/Cell/lv2/sys_net/network_context.h" +#include "Emu/Cell/lv2/sys_net/sys_net_helpers.h" #ifdef _WIN32 #include @@ -342,21 +343,18 @@ namespace np { g_fxo->need>(); - std::lock_guard lock(mutex_rpcn); - rpcn = rpcn::rpcn_client::get_instance(); - is_connected = (g_cfg.net.net_active == np_internet_status::enabled); - is_psn_active = (g_cfg.net.psn_status >= np_psn_status::psn_fake); + is_psn_active = (g_cfg.net.psn_status >= np_psn_status::psn_fake) && is_connected; if (get_net_status() == CELL_NET_CTL_STATE_IPObtained) { - discover_ip_address(); - if (!discover_ether_address()) + if (!discover_ether_address() || !discover_ip_address()) { - nph_log.error("Failed to discover ethernet address!"); + nph_log.error("Failed to discover ethernet or ip address!"); is_connected = false; is_psn_active = false; + return; } // Convert dns address @@ -383,6 +381,19 @@ namespace np { bind_ip = conv.s_addr; } + + if (g_cfg.net.upnp_enabled) + upnp.upnp_enable(); + } + + if (is_psn_active && g_cfg.net.psn_status == np_psn_status::psn_rpcn) + { + g_fxo->need(); + auto& nc = g_fxo->get(); + nc.bind_sce_np_port(); + + std::lock_guard lock(mutex_rpcn); + rpcn = rpcn::rpcn_client::get_instance(); } } @@ -449,46 +460,45 @@ namespace np ar(m_pool, m_size, m_allocs, m_avail); } - void np_handler::discover_ip_address() + bool np_handler::discover_ip_address() { - hostname.clear(); - hostname.resize(1024); + auto sockfd = socket(AF_INET, SOCK_DGRAM, 0); - const auto use_default_ip_addr = [this](const std::string_view error_msg) + auto close_socket = [&]() { - nph_log.error("discover_ip_address: %s", error_msg); - nph_log.error("discover_ip_address: Defaulting to 127.0.0.1!"); - local_ip_addr = 0x0100007f; - public_ip_addr = local_ip_addr; +#ifdef _WIN32 + closesocket(sockfd); +#else + close(sockfd); +#endif }; - if (gethostname(hostname.data(), hostname.size()) == -1) + ::sockaddr_in addr; + std::memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = 53; + addr.sin_addr.s_addr = 0x08080808; + if (connect(sockfd, reinterpret_cast(&addr), sizeof(addr)) != 0) { - use_default_ip_addr("gethostname failed!"); - return; + // If connect fails a route to the internet is not available + nph_log.error("connect to discover local ip failed: %d", get_native_error()); + close_socket(); + return false; // offline } - // nph_log.notice("discover_ip_address: Hostname was determined to be %s", hostname.c_str()); - - hostent* host = gethostbyname(hostname.data()); - if (!host) + sockaddr_in client_addr; + socklen_t client_addr_size = sizeof(client_addr); + if (getsockname(sockfd, reinterpret_cast(&client_addr), &client_addr_size) != 0) { - use_default_ip_addr("gethostbyname failed!"); - return; + rpcn_log.error("getsockname to discover local ip failed: %d", get_native_error()); + close_socket(); + return true; // still assume online } - if (host->h_addrtype != AF_INET) - { - use_default_ip_addr("Could only find IPv6 addresses for current host!"); - return; - } - - local_ip_addr = read_from_ptr(host->h_addr_list[0]); - - // Set public address to local discovered address for now, may be updated later from RPCN socket - public_ip_addr = local_ip_addr; - - // nph_log.notice("discover_ip_address: IP was determined to be %s", ip_to_string(local_ip_addr)); + local_ip_addr = client_addr.sin_addr.s_addr; + nph_log.trace("discover_ip_address: IP was determined to be %s", ip_to_string(local_ip_addr)); + close_socket(); + return true; } bool np_handler::discover_ether_address() @@ -612,6 +622,14 @@ namespace np return is_psn_active ? SCE_NP_MANAGER_STATUS_ONLINE : SCE_NP_MANAGER_STATUS_OFFLINE; } + s32 np_handler::get_upnp_status() const + { + if (upnp.is_active()) + return SCE_NP_SIGNALING_NETINFO_UPNP_STATUS_VALID; + + return SCE_NP_SIGNALING_NETINFO_UPNP_STATUS_INVALID; + } + const SceNpId& np_handler::get_npid() const { return npid; @@ -649,7 +667,7 @@ namespace np std::string s_npid = g_cfg_rpcn.get_npid(); ensure(!s_npid.empty()); // It should have been generated before this - string_to_npid(s_npid, &npid); + string_to_npid(s_npid, npid); auto& sigh = g_fxo->get>(); sigh.set_self_sig_info(npid); } @@ -660,8 +678,8 @@ namespace np break; case np_psn_status::psn_fake: { - string_to_online_name("RPCS3's user", &online_name); - string_to_avatar_url("https://rpcs3.net/cdn/netplay/DefaultAvatar.png", &avatar_url); + string_to_online_name("RPCS3's user", online_name); + string_to_avatar_url("https://rpcs3.net/cdn/netplay/DefaultAvatar.png", avatar_url); break; } case np_psn_status::psn_rpcn: @@ -692,8 +710,8 @@ namespace np rsx::overlays::queue_message(localized_string_id::RPCN_SUCCESS_LOGGED_ON); - string_to_online_name(rpcn->get_online_name(), &online_name); - string_to_avatar_url(rpcn->get_avatar_url(), &avatar_url); + string_to_online_name(rpcn->get_online_name(), online_name); + string_to_avatar_url(rpcn->get_avatar_url(), avatar_url); public_ip_addr = rpcn->get_addr_sig(); local_ip_addr = std::bit_cast>(rpcn->get_addr_local()); @@ -930,7 +948,7 @@ namespace np return false; } - u32 np_handler::generate_callback_info(SceNpMatching2ContextId ctx_id, vm::cptr optParam) + u32 np_handler::generate_callback_info(SceNpMatching2ContextId ctx_id, vm::cptr optParam, SceNpMatching2Event event_type) { callback_info ret; @@ -942,6 +960,7 @@ namespace np ret.ctx_id = ctx_id; ret.cb_arg = (optParam && optParam->cbFuncArg) ? optParam->cbFuncArg : ctx->default_match2_optparam.cbFuncArg; ret.cb = (optParam && optParam->cbFunc) ? optParam->cbFunc : ctx->default_match2_optparam.cbFunc; + ret.event_type = event_type; nph_log.warning("Callback used is 0x%x", ret.cb); @@ -953,16 +972,31 @@ namespace np return req_id; } - np_handler::callback_info np_handler::take_pending_request(u32 req_id) + std::optional np_handler::take_pending_request(u32 req_id) { std::lock_guard lock(mutex_pending_requests); + if (!pending_requests.contains(req_id)) + return std::nullopt; + const auto cb_info = std::move(::at32(pending_requests, req_id)); pending_requests.erase(req_id); return cb_info; } + bool np_handler::abort_request(u32 req_id) + { + auto cb_info_opt = take_pending_request(req_id); + + if (!cb_info_opt) + return false; + + cb_info_opt->queue_callback(req_id, 0, SCE_NP_MATCHING2_ERROR_ABORTED, 0); + + return true; + } + event_data& np_handler::allocate_req_result(u32 event_key, u32 max_size, u32 initial_size) { std::lock_guard lock(mutex_match2_req_results); @@ -974,14 +1008,14 @@ namespace np { const u32 req_id = get_req_id(0); - // if (basic_handler) - // { - // sysutil_register_cb([basic_handler = this->basic_handler, req_id, basic_handler_arg = this->basic_handler_arg](ppu_thread& cb_ppu) -> s32 - // { - // basic_handler(cb_ppu, SCE_NP_BASIC_EVENT_ADD_PLAYERS_HISTORY_RESULT, 0, req_id, basic_handler_arg); - // return 0; - // }); - // } + if (basic_handler.handler_func) + { + sysutil_register_cb([req_id, cb = basic_handler.handler_func, cb_arg = basic_handler.handler_arg](ppu_thread& cb_ppu) -> s32 + { + cb(cb_ppu, SCE_NP_BASIC_EVENT_ADD_PLAYERS_HISTORY_RESULT, 0, req_id, cb_arg); + return 0; + }); + } return req_id; } @@ -1006,11 +1040,16 @@ namespace np } SceNpId npid_friend; - string_to_npid(str_friend.value(), &npid_friend); + string_to_npid(str_friend.value(), npid_friend); return {CELL_OK, npid_friend}; } + std::pair> np_handler::local_get_npid(u64 room_id, u16 member_id) + { + return np_cache.get_npid(room_id, member_id); + } + std::pair> np_handler::local_get_room_password(SceNpMatching2RoomId room_id) { return np_cache.get_password(room_id); @@ -1030,4 +1069,14 @@ namespace np { return np_cache.get_member_and_attrs(room_id, member_id, binattrs_list, ptr_member, addr_data, size_data); } + + void np_handler::upnp_add_port_mapping(u16 internal_port, std::string_view protocol) + { + upnp.add_port_redir(np::ip_to_string(get_local_ip_addr()), internal_port, protocol); + } + + void np_handler::upnp_remove_port_mapping(u16 internal_port, std::string_view protocol) + { + upnp.remove_port_redir(internal_port, protocol); + } } // namespace np diff --git a/rpcs3/Emu/NP/np_handler.h b/rpcs3/Emu/NP/np_handler.h index c26125e607..98456b8cf4 100644 --- a/rpcs3/Emu/NP/np_handler.h +++ b/rpcs3/Emu/NP/np_handler.h @@ -7,6 +7,7 @@ #include "Emu/Memory/vm_ptr.h" #include "Emu/Cell/Modules/sceNp.h" #include "Emu/Cell/Modules/sceNp2.h" +#include "Emu/Cell/Modules/cellSysutil.h" #include "Emu/NP/rpcn_client.h" #include "Emu/NP/generated/np2_structs_generated.h" @@ -15,6 +16,7 @@ #include "Emu/NP/np_cache.h" #include "Emu/NP/np_event_data.h" #include "Emu/NP/np_contexts.h" +#include "Emu/NP/upnp_handler.h" namespace np { @@ -83,6 +85,7 @@ namespace np s32 get_psn_status() const; s32 get_net_status() const; + s32 get_upnp_status() const; const SceNpId& get_npid() const; const SceNpOnlineId& get_online_id() const; @@ -165,6 +168,7 @@ namespace np void get_score_friend(std::shared_ptr& trans_ctx, SceNpScoreBoardId boardId, bool include_self, vm::ptr rankArray, u32 rankArraySize, vm::ptr commentArray, u32 commentArraySize, vm::ptr infoArray, u32 infoArraySize, u32 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, bool async); // Local functions + std::pair> local_get_npid(u64 room_id, u16 member_id); std::pair> local_get_room_slots(SceNpMatching2RoomId room_id); std::pair> local_get_room_password(SceNpMatching2RoomId room_id); std::pair> local_get_room_memberids(SceNpMatching2RoomId room_id, s32 sort_method); @@ -179,10 +183,15 @@ namespace np 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 ticket& get_ticket() const; u32 add_players_to_history(vm::cptr npids, u32 count); + bool abort_request(u32 req_id); // For signaling void req_sign_infos(const std::string& npid, u32 conn_id); + // For UPNP + void upnp_add_port_mapping(u16 internal_port, std::string_view protocol); + void upnp_remove_port_mapping(u16 internal_port, std::string_view protocol); + // For custom menu struct custom_menu_action { @@ -205,7 +214,7 @@ namespace np private: // Various generic helpers - void discover_ip_address(); + bool discover_ip_address(); bool discover_ether_address(); bool error_and_disconnect(const std::string& error_msg); @@ -267,9 +276,23 @@ namespace np SceNpMatching2ContextId ctx_id; vm::ptr cb; vm::ptr cb_arg; + SceNpMatching2Event event_type; + + void queue_callback(u32 req_id, u32 event_key, s32 error_code, u32 data_size) const + { + if (cb) + { + sysutil_register_cb([=, *this](ppu_thread& cb_ppu) -> s32 + { + cb(cb_ppu, ctx_id, req_id, event_type, event_key, error_code, data_size, cb_arg); + return 0; + }); + } + } }; - u32 generate_callback_info(SceNpMatching2ContextId ctx_id, vm::cptr optParam); - callback_info take_pending_request(u32 req_id); + + u32 generate_callback_info(SceNpMatching2ContextId ctx_id, vm::cptr optParam, SceNpMatching2Event event_type); + std::optional take_pending_request(u32 req_id); shared_mutex mutex_pending_requests; std::unordered_map pending_requests; @@ -327,5 +350,8 @@ namespace np // RPCN shared_mutex mutex_rpcn; std::shared_ptr rpcn; + + // UPNP + upnp_handler upnp; }; } // namespace np diff --git a/rpcs3/Emu/NP/np_helpers.cpp b/rpcs3/Emu/NP/np_helpers.cpp index dc06bdb09a..3bd3af7c38 100644 --- a/rpcs3/Emu/NP/np_helpers.cpp +++ b/rpcs3/Emu/NP/np_helpers.cpp @@ -22,25 +22,65 @@ namespace np return fmt::format("%02X:%02X:%02X:%02X:%02X:%02X", ether[0], ether[1], ether[2], ether[3], ether[4], ether[5]); } - void string_to_npid(std::string_view str, SceNpId* npid) + void string_to_npid(std::string_view str, SceNpId& npid) { - memset(npid, 0, sizeof(SceNpId)); - strcpy_trunc(npid->handle.data, str); + memset(&npid, 0, sizeof(npid)); + strcpy_trunc(npid.handle.data, str); // npid->reserved[0] = 1; } - void string_to_online_name(std::string_view str, SceNpOnlineName* online_name) + void string_to_online_name(std::string_view str, SceNpOnlineName& online_name) { - strcpy_trunc(online_name->data, str); + memset(&online_name, 0, sizeof(online_name)); + strcpy_trunc(online_name.data, str); } - void string_to_avatar_url(std::string_view str, SceNpAvatarUrl* avatar_url) + void string_to_avatar_url(std::string_view str, SceNpAvatarUrl& avatar_url) { - strcpy_trunc(avatar_url->data, str); + memset(&avatar_url, 0, sizeof(avatar_url)); + strcpy_trunc(avatar_url.data, str); } - void string_to_communication_id(std::string_view str, SceNpCommunicationId* comm_id) + void string_to_communication_id(std::string_view str, SceNpCommunicationId& comm_id) { - strcpy_trunc(comm_id->data, str); + memset(&comm_id, 0, sizeof(comm_id)); + strcpy_trunc(comm_id.data, str); + } + + bool is_valid_npid(const SceNpId& npid) + { + if (!std::all_of(npid.handle.data, npid.handle.data + 16, [](char c) { return std::isalnum(c) || c == '-' || c == '_' || c == 0; } ) + || npid.handle.data[16] != 0 + || !std::all_of(npid.handle.dummy, npid.handle.dummy + 3, [](char val) { return val == 0; }) ) + { + return false; + } + + return true; + } + + bool is_same_npid(const SceNpId& npid_1, const SceNpId& npid_2) + { + // Unknown what this constant means + // if (id1->reserved[0] != 1 || id2->reserved[0] != 1) + // { + // return SCE_NP_UTIL_ERROR_INVALID_NP_ID; + // } + + if (strncmp(npid_1.handle.data, npid_2.handle.data, 16) == 0) // || id1->unk1[0] != id2->unk1[0]) + { + return true; + } + + // if (id1->unk1[1] != id2->unk1[1]) + // { + // // If either is zero they match + // if (id1->opt[4] && id2->opt[4]) + // { + // return SCE_NP_UTIL_ERROR_NOT_MATCH; + // } + // } + + return false; } } diff --git a/rpcs3/Emu/NP/np_helpers.h b/rpcs3/Emu/NP/np_helpers.h index f80f1c9def..a0cd17f822 100644 --- a/rpcs3/Emu/NP/np_helpers.h +++ b/rpcs3/Emu/NP/np_helpers.h @@ -1,3 +1,5 @@ +#pragma once + #include "util/types.hpp" #include "Emu/Cell/Modules/sceNp.h" #include "Emu/Cell/Modules/sceNp2.h" @@ -7,8 +9,11 @@ namespace np std::string ip_to_string(u32 addr); std::string ether_to_string(std::array& ether); - void string_to_npid(std::string_view str, SceNpId* npid); - void string_to_online_name(std::string_view str, SceNpOnlineName* online_name); - void string_to_avatar_url(std::string_view str, SceNpAvatarUrl* avatar_url); - void string_to_communication_id(std::string_view str, SceNpCommunicationId* comm_id); + void string_to_npid(std::string_view str, SceNpId& npid); + void string_to_online_name(std::string_view str, SceNpOnlineName& online_name); + void string_to_avatar_url(std::string_view str, SceNpAvatarUrl& avatar_url); + void string_to_communication_id(std::string_view str, SceNpCommunicationId& comm_id); + + bool is_valid_npid(const SceNpId& npid); + bool is_same_npid(const SceNpId& npid_1, const SceNpId& npid_2); } diff --git a/rpcs3/Emu/NP/np_notifications.cpp b/rpcs3/Emu/NP/np_notifications.cpp index 532c1454f9..dda857ae9c 100644 --- a/rpcs3/Emu/NP/np_notifications.cpp +++ b/rpcs3/Emu/NP/np_notifications.cpp @@ -29,7 +29,8 @@ namespace np RoomMemberUpdateInfo_to_SceNpMatching2RoomMemberUpdateInfo(edata, update_info, notif_data); np_memory.shrink_allocation(edata.addr(), edata.size()); - np_cache.add_member(room_id, notif_data->roomMemberDataInternal.get_ptr()); + if (!np_cache.add_member(room_id, notif_data->roomMemberDataInternal.get_ptr())) + return; rpcn_log.notice("Received notification that user %s(%d) joined the room(%d)", notif_data->roomMemberDataInternal->userInfo.npId.handle.data, notif_data->roomMemberDataInternal->memberId, room_id); extra_nps::print_room_member_data_internal(notif_data->roomMemberDataInternal.get_ptr()); @@ -60,7 +61,8 @@ namespace np RoomMemberUpdateInfo_to_SceNpMatching2RoomMemberUpdateInfo(edata, update_info, notif_data); np_memory.shrink_allocation(edata.addr(), edata.size()); - np_cache.del_member(room_id, notif_data->roomMemberDataInternal->memberId); + if (!np_cache.del_member(room_id, notif_data->roomMemberDataInternal->memberId)) + return; rpcn_log.notice("Received notification that user %s(%d) left the room(%d)", notif_data->roomMemberDataInternal->userInfo.npId.handle.data, notif_data->roomMemberDataInternal->memberId, room_id); extra_nps::print_room_member_data_internal(notif_data->roomMemberDataInternal.get_ptr()); @@ -154,7 +156,8 @@ namespace np RoomMemberDataInternalUpdateInfo_to_SceNpMatching2RoomMemberDataInternalUpdateInfo(edata, update_info, notif_data); np_memory.shrink_allocation(edata.addr(), edata.size()); - np_cache.add_member(room_id, notif_data->newRoomMemberDataInternal.get_ptr()); + if (!np_cache.add_member(room_id, notif_data->newRoomMemberDataInternal.get_ptr())) + return; rpcn_log.notice("Received notification that user's %s(%d) room (%d) data was updated", notif_data->newRoomMemberDataInternal->userInfo.npId.handle.data, notif_data->newRoomMemberDataInternal->memberId, room_id); extra_nps::print_room_member_data_internal(notif_data->newRoomMemberDataInternal.get_ptr()); @@ -200,22 +203,27 @@ namespace np void np_handler::notif_p2p_connect(std::vector& data) { - if (data.size() != 16) + vec_stream noti(data); + const u64 room_id = noti.get(); + const u16 member_id = noti.get(); + const u16 port_p2p = noti.get(); + const u32 addr_p2p = noti.get(); + + if (noti.is_error()) { - rpcn_log.error("Notification data for SignalP2PConnect != 14"); + rpcn_log.error("Received faulty SignalP2PConnect notification"); return; } - const u64 room_id = read_from_ptr>(data); - const u16 member_id = read_from_ptr>(data, 8); - const u16 port_p2p = read_from_ptr>(data, 10); - const u32 addr_p2p = read_from_ptr>(data, 12); + auto [res, npid] = np_cache.get_npid(room_id, member_id); + if (!npid) + return; - rpcn_log.notice("Received notification to connect to member(%d) of room(%d): %s:%d", member_id, room_id, ip_to_string(addr_p2p), port_p2p); + rpcn_log.notice("Received notification to connect to member(%d=%s) of room(%d): %s:%d", member_id, reinterpret_cast((*npid).handle.data), room_id, ip_to_string(addr_p2p), port_p2p); // Attempt Signaling auto& sigh = g_fxo->get>(); - sigh.set_sig2_infos(room_id, member_id, SCE_NP_SIGNALING_CONN_STATUS_PENDING, addr_p2p, port_p2p, np_cache.get_npid(room_id, member_id)); - sigh.start_sig2(room_id, member_id); + const u32 conn_id = sigh.init_sig2(*npid, room_id, member_id); + sigh.start_sig(conn_id, addr_p2p, port_p2p); } } // namespace np diff --git a/rpcs3/Emu/NP/np_requests.cpp b/rpcs3/Emu/NP/np_requests.cpp index f0bc99066a..9fcdaa08a8 100644 --- a/rpcs3/Emu/NP/np_requests.cpp +++ b/rpcs3/Emu/NP/np_requests.cpp @@ -35,70 +35,47 @@ namespace np u32 np_handler::get_server_status(SceNpMatching2ContextId ctx_id, vm::cptr optParam, u16 server_id) { // TODO: actually implement interaction with server for this? - u32 req_id = generate_callback_info(ctx_id, optParam); + u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetServerInfo); u32 event_key = get_event_key(); - auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetServerInfo, sizeof(SceNpMatching2GetServerInfoResponse)); + auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetServerInfo, sizeof(SceNpMatching2GetServerInfoResponse)); SceNpMatching2GetServerInfoResponse* serv_info = reinterpret_cast(edata.data()); - serv_info->server.serverId = server_id; - serv_info->server.status = SCE_NP_MATCHING2_SERVER_STATUS_AVAILABLE; + serv_info->server.serverId = server_id; + serv_info->server.status = SCE_NP_MATCHING2_SERVER_STATUS_AVAILABLE; np_memory.shrink_allocation(edata.addr(), edata.size()); - const auto cb_info = take_pending_request(req_id); - - if (cb_info.cb) - { - sysutil_register_cb([=, size = edata.size()](ppu_thread& cb_ppu) -> s32 - { - cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_GetServerInfo, event_key, 0, size, cb_info.cb_arg); - return 0; - }); - } + const auto cb_info_opt = take_pending_request(req_id); + ensure(cb_info_opt); + cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); return req_id; } u32 np_handler::create_server_context(SceNpMatching2ContextId ctx_id, vm::cptr optParam, u16 /*server_id*/) { - u32 req_id = generate_callback_info(ctx_id, optParam); - u32 event_key = get_event_key(); + u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_CreateServerContext); - const auto cb_info = take_pending_request(req_id); - - if (cb_info.cb) - { - sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 - { - cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_CreateServerContext, event_key, 0, 0, cb_info.cb_arg); - return 0; - }); - } + const auto cb_info_opt = take_pending_request(req_id); + ensure(cb_info_opt); + cb_info_opt->queue_callback(req_id, 0, 0, 0); return req_id; } u32 np_handler::delete_server_context(SceNpMatching2ContextId ctx_id, vm::cptr optParam, u16 /*server_id*/) { - u32 req_id = generate_callback_info(ctx_id, optParam); - u32 event_key = get_event_key(); + u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_DeleteServerContext); - const auto cb_info = take_pending_request(req_id); - - if (cb_info.cb) - { - sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 - { - cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_DeleteServerContext, event_key, 0, 0, cb_info.cb_arg); - return 0; - }); - } + const auto cb_info_opt = take_pending_request(req_id); + ensure(cb_info_opt); + cb_info_opt->queue_callback(req_id, 0, 0, 0); return req_id; } u32 np_handler::get_world_list(SceNpMatching2ContextId ctx_id, vm::cptr optParam, u16 server_id) { - u32 req_id = generate_callback_info(ctx_id, optParam); + u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetWorldInfoList); if (!rpcn->get_world_list(req_id, get_match2_context(ctx_id)->communicationId, server_id)) { @@ -111,7 +88,10 @@ namespace np bool np_handler::reply_get_world_list(u32 req_id, std::vector& reply_data) { - const auto cb_info = take_pending_request(req_id); + auto cb_info_opt = take_pending_request(req_id); + + if (!cb_info_opt) + return true; vec_stream reply(reply_data, 1); @@ -129,8 +109,8 @@ namespace np u32 event_key = get_event_key(); - auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetWorldInfoList, sizeof(SceNpMatching2GetWorldInfoListResponse)); - auto* world_info = reinterpret_cast(edata.data()); + auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetWorldInfoList, sizeof(SceNpMatching2GetWorldInfoListResponse)); + auto* world_info = reinterpret_cast(edata.data()); world_info->worldNum = world_list.size(); if (!world_list.empty()) @@ -138,32 +118,19 @@ namespace np auto* worlds = edata.allocate(sizeof(SceNpMatching2World) * world_list.size(), world_info->world); for (usz i = 0; i < world_list.size(); i++) { - worlds[i].worldId = world_list[i]; - worlds[i].numOfLobby = 1; // TODO - worlds[i].maxNumOfTotalLobbyMember = 10000; - worlds[i].curNumOfTotalLobbyMember = 1; - worlds[i].curNumOfRoom = 1; - worlds[i].curNumOfTotalRoomMember = 1; + worlds[i].worldId = world_list[i]; } } np_memory.shrink_allocation(edata.addr(), edata.size()); - - if (cb_info.cb) - { - sysutil_register_cb([=, size = edata.size()](ppu_thread& cb_ppu) -> s32 - { - cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_GetWorldInfoList, event_key, 0, size, cb_info.cb_arg); - return 0; - }); - } + cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); return true; } u32 np_handler::create_join_room(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2CreateJoinRoomRequest* req) { - u32 req_id = generate_callback_info(ctx_id, optParam); + u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_CreateJoinRoom); extra_nps::print_createjoinroom(req); @@ -181,7 +148,10 @@ namespace np bool np_handler::reply_create_join_room(u32 req_id, std::vector& reply_data) { - const auto cb_info = take_pending_request(req_id); + auto cb_info_opt = take_pending_request(req_id); + + if (!cb_info_opt) + return true; vec_stream reply(reply_data, 1); auto* resp = reply.get_flatbuffer(); @@ -191,7 +161,7 @@ namespace np u32 event_key = get_event_key(); - auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_CreateJoinRoom, sizeof(SceNpMatching2CreateJoinRoomResponse)); + auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_CreateJoinRoom, sizeof(SceNpMatching2CreateJoinRoomResponse)); auto* room_resp = reinterpret_cast(edata.data()); auto* room_info = edata.allocate(sizeof(SceNpMatching2RoomDataInternal), room_resp->roomDataInternal); RoomDataInternal_to_SceNpMatching2RoomDataInternal(edata, resp, room_info, npid); @@ -200,29 +170,16 @@ namespace np np_cache.insert_room(room_info); np_cache.update_password(room_resp->roomDataInternal->roomId, cached_cj_password); - // Establish Matching2 self signaling info - auto& sigh = g_fxo->get>(); - 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(), get_npid(), true); - // TODO? Should this send a message to Signaling CB? Is this even necessary? - extra_nps::print_create_room_resp(room_resp); - if (cb_info.cb) - { - sysutil_register_cb([=, size = edata.size()](ppu_thread& cb_ppu) -> s32 - { - cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_CreateJoinRoom, event_key, 0, size, cb_info.cb_arg); - return 0; - }); - } + cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); return true; } u32 np_handler::join_room(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2JoinRoomRequest* req) { - u32 req_id = generate_callback_info(ctx_id, optParam); + u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_JoinRoom); extra_nps::print_joinroom(req); @@ -237,6 +194,11 @@ namespace np bool np_handler::reply_join_room(u32 req_id, std::vector& reply_data) { + auto cb_info_opt = take_pending_request(req_id); + + if (!cb_info_opt) + return true; + s32 error_code = 0; if (rpcn::is_error(static_cast(reply_data[0]))) @@ -250,19 +212,9 @@ namespace np } } - const auto cb_info = take_pending_request(req_id); - if (error_code != 0) { - if (cb_info.cb) - { - sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 - { - cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_JoinRoom, 0, error_code, 0, cb_info.cb_arg); - return 0; - }); - } - + cb_info_opt->queue_callback(req_id, 0, error_code, 0); return true; } @@ -275,37 +227,24 @@ namespace np u32 event_key = get_event_key(); - auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_JoinRoom, sizeof(SceNpMatching2JoinRoomResponse)); + auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_JoinRoom, sizeof(SceNpMatching2JoinRoomResponse)); auto* room_resp = reinterpret_cast(edata.data()); auto* room_info = edata.allocate(sizeof(SceNpMatching2RoomDataInternal), room_resp->roomDataInternal); - u16 member_id = RoomDataInternal_to_SceNpMatching2RoomDataInternal(edata, resp, room_info, npid); + RoomDataInternal_to_SceNpMatching2RoomDataInternal(edata, resp, room_info, npid); np_memory.shrink_allocation(edata.addr(), edata.size()); np_cache.insert_room(room_info); extra_nps::print_room_data_internal(room_info); - // Establish Matching2 self signaling info - auto& sigh = g_fxo->get>(); - 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(), get_npid(), true); - // TODO? Should this send a message to Signaling CB? Is this even necessary? - - if (cb_info.cb) - { - sysutil_register_cb([=, size = edata.size()](ppu_thread& cb_ppu) -> s32 - { - cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_JoinRoom, event_key, 0, size, cb_info.cb_arg); - return 0; - }); - } + cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); return true; } u32 np_handler::leave_room(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2LeaveRoomRequest* req) { - u32 req_id = generate_callback_info(ctx_id, optParam); + u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_LeaveRoom); if (!rpcn->leave_room(req_id, get_match2_context(ctx_id)->communicationId, req)) { @@ -318,34 +257,27 @@ namespace np bool np_handler::reply_leave_room(u32 req_id, std::vector& reply_data) { - const auto cb_info = take_pending_request(req_id); + auto cb_info_opt = take_pending_request(req_id); + + if (!cb_info_opt) + return true; vec_stream reply(reply_data, 1); u64 room_id = reply.get(); if (reply.is_error()) return error_and_disconnect("Malformed reply to LeaveRoom command"); - u32 event_key = get_event_key(); // Unsure if necessary if there is no data - // Disconnect all users from that room auto& sigh = g_fxo->get>(); sigh.disconnect_sig2_users(room_id); - - if (cb_info.cb) - { - sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 - { - cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_LeaveRoom, event_key, 0, 0, cb_info.cb_arg); - return 0; - }); - } + cb_info_opt->queue_callback(req_id, 0, 0, 0); return true; } u32 np_handler::search_room(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SearchRoomRequest* req) { - u32 req_id = generate_callback_info(ctx_id, optParam); + u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SearchRoom); extra_nps::print_search_room(req); @@ -360,7 +292,10 @@ namespace np bool np_handler::reply_search_room(u32 req_id, std::vector& reply_data) { - const auto cb_info = take_pending_request(req_id); + auto cb_info_opt = take_pending_request(req_id); + + if (!cb_info_opt) + return true; vec_stream reply(reply_data, 1); auto* resp = reply.get_flatbuffer(); @@ -370,28 +305,20 @@ namespace np u32 event_key = get_event_key(); - auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_SearchRoom, sizeof(SceNpMatching2SearchRoomResponse)); + auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_SearchRoom, sizeof(SceNpMatching2SearchRoomResponse)); auto* search_resp = reinterpret_cast(edata.data()); SearchRoomResponse_to_SceNpMatching2SearchRoomResponse(edata, resp, search_resp); np_memory.shrink_allocation(edata.addr(), edata.size()); extra_nps::print_search_room_resp(search_resp); - - if (cb_info.cb) - { - sysutil_register_cb([=, size = edata.size()](ppu_thread& cb_ppu) -> s32 - { - cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SearchRoom, event_key, 0, size, cb_info.cb_arg); - return 0; - }); - } + cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); return true; } u32 np_handler::get_roomdata_external_list(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2GetRoomDataExternalListRequest* req) { - u32 req_id = generate_callback_info(ctx_id, optParam); + u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataExternalList); extra_nps::print_get_roomdata_external_list_req(req); @@ -406,7 +333,10 @@ namespace np bool np_handler::reply_get_roomdata_external_list(u32 req_id, std::vector& reply_data) { - const auto cb_info = take_pending_request(req_id); + auto cb_info_opt = take_pending_request(req_id); + + if (!cb_info_opt) + return true; vec_stream reply(reply_data, 1); auto* resp = reply.get_flatbuffer(); @@ -416,28 +346,21 @@ namespace np u32 event_key = get_event_key(); - auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetRoomDataExternalList, sizeof(SceNpMatching2GetRoomDataExternalListResponse)); + auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetRoomDataExternalList, sizeof(SceNpMatching2GetRoomDataExternalListResponse)); auto* sce_get_room_ext_resp = reinterpret_cast(edata.data()); GetRoomDataExternalListResponse_to_SceNpMatching2GetRoomDataExternalListResponse(edata, resp, sce_get_room_ext_resp); np_memory.shrink_allocation(edata.addr(), edata.size()); extra_nps::print_get_roomdata_external_list_resp(sce_get_room_ext_resp); - if (cb_info.cb) - { - sysutil_register_cb([=, size = edata.size()](ppu_thread& cb_ppu) -> s32 - { - cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataExternalList, event_key, 0, size, cb_info.cb_arg); - return 0; - }); - } + cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); return true; } u32 np_handler::set_roomdata_external(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SetRoomDataExternalRequest* req) { - u32 req_id = generate_callback_info(ctx_id, optParam); + u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataExternal); extra_nps::print_set_roomdata_ext_req(req); @@ -452,25 +375,19 @@ namespace np bool np_handler::reply_set_roomdata_external(u32 req_id, std::vector& /*reply_data*/) { - const auto cb_info = take_pending_request(req_id); + auto cb_info_opt = take_pending_request(req_id); - u32 event_key = get_event_key(); // Unsure if necessary if there is no data + if (!cb_info_opt) + return true; - if (cb_info.cb) - { - sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 - { - cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataExternal, event_key, 0, 0, cb_info.cb_arg); - return 0; - }); - } + cb_info_opt->queue_callback(req_id, 0, 0, 0); return true; } u32 np_handler::get_roomdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2GetRoomDataInternalRequest* req) { - u32 req_id = generate_callback_info(ctx_id, optParam); + u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataInternal); if (!rpcn->get_roomdata_internal(req_id, get_match2_context(ctx_id)->communicationId, req)) { @@ -483,7 +400,10 @@ namespace np bool np_handler::reply_get_roomdata_internal(u32 req_id, std::vector& reply_data) { - const auto cb_info = take_pending_request(req_id); + auto cb_info_opt = take_pending_request(req_id); + + if (!cb_info_opt) + return true; vec_stream reply(reply_data, 1); @@ -494,7 +414,7 @@ namespace np u32 event_key = get_event_key(); - auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetRoomDataInternal, sizeof(SceNpMatching2GetRoomDataInternalResponse)); + auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetRoomDataInternal, sizeof(SceNpMatching2GetRoomDataInternalResponse)); auto* room_resp = reinterpret_cast(edata.data()); auto* room_info = edata.allocate(sizeof(SceNpMatching2RoomDataInternal), room_resp->roomDataInternal); RoomDataInternal_to_SceNpMatching2RoomDataInternal(edata, resp, room_info, npid); @@ -504,21 +424,14 @@ namespace np extra_nps::print_room_data_internal(room_info); - if (cb_info.cb) - { - sysutil_register_cb([=, size = edata.size()](ppu_thread& cb_ppu) -> s32 - { - cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataInternal, event_key, 0, size, cb_info.cb_arg); - return 0; - }); - } + cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); return true; } u32 np_handler::set_roomdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SetRoomDataInternalRequest* req) { - u32 req_id = generate_callback_info(ctx_id, optParam); + u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataInternal); // TODO: extra_nps::print_set_roomdata_req(req); @@ -535,25 +448,19 @@ namespace np bool np_handler::reply_set_roomdata_internal(u32 req_id, std::vector& /*reply_data*/) { - const auto cb_info = take_pending_request(req_id); + auto cb_info_opt = take_pending_request(req_id); - u32 event_key = get_event_key(); // Unsure if necessary if there is no data + if (!cb_info_opt) + return true; - if (cb_info.cb) - { - sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 - { - cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataInternal, event_key, 0, 0, cb_info.cb_arg); - return 0; - }); - } + cb_info_opt->queue_callback(req_id, 0, 0, 0); return true; } u32 np_handler::set_roommemberdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SetRoomMemberDataInternalRequest* req) { - u32 req_id = generate_callback_info(ctx_id, optParam); + u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomMemberDataInternal); extra_nps::print_set_roommemberdata_int_req(req); @@ -568,25 +475,19 @@ namespace np bool np_handler::reply_set_roommemberdata_internal(u32 req_id, std::vector& /*reply_data*/) { - const auto cb_info = take_pending_request(req_id); + auto cb_info_opt = take_pending_request(req_id); - u32 event_key = get_event_key(); // Unsure if necessary if there is no data + if (!cb_info_opt) + return true; - if (cb_info.cb) - { - sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 - { - cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomMemberDataInternal, event_key, 0, 0, cb_info.cb_arg); - return 0; - }); - } + cb_info_opt->queue_callback(req_id, 0, 0, 0); return true; } u32 np_handler::get_ping_info(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SignalingGetPingInfoRequest* req) { - u32 req_id = generate_callback_info(ctx_id, optParam); + u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SignalingGetPingInfo); if (!rpcn->ping_room_owner(req_id, get_match2_context(ctx_id)->communicationId, req->roomId)) { @@ -599,7 +500,10 @@ namespace np bool np_handler::reply_get_ping_info(u32 req_id, std::vector& reply_data) { - const auto cb_info = take_pending_request(req_id); + auto cb_info_opt = take_pending_request(req_id); + + if (!cb_info_opt) + return true; vec_stream reply(reply_data, 1); @@ -610,26 +514,18 @@ namespace np u32 event_key = get_event_key(); - auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_SignalingGetPingInfo, sizeof(SceNpMatching2SignalingGetPingInfoResponse)); + auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_SignalingGetPingInfo, sizeof(SceNpMatching2SignalingGetPingInfoResponse)); auto* final_ping_resp = reinterpret_cast(edata.data()); GetPingInfoResponse_to_SceNpMatching2SignalingGetPingInfoResponse(resp, final_ping_resp); np_memory.shrink_allocation(edata.addr(), edata.size()); - - if (cb_info.cb) - { - sysutil_register_cb([=, size = edata.size()](ppu_thread& cb_ppu) -> s32 - { - cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SignalingGetPingInfo, event_key, 0, size, cb_info.cb_arg); - return 0; - }); - } + cb_info_opt->queue_callback(req_id, event_key, 0, edata.size()); return true; } u32 np_handler::send_room_message(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SendRoomMessageRequest* req) { - u32 req_id = generate_callback_info(ctx_id, optParam); + u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SendRoomMessage); if (!rpcn->send_room_message(req_id, get_match2_context(ctx_id)->communicationId, req)) { @@ -642,16 +538,12 @@ namespace np bool np_handler::reply_send_room_message(u32 req_id, std::vector& /*reply_data*/) { - const auto cb_info = take_pending_request(req_id); + auto cb_info_opt = take_pending_request(req_id); - if (cb_info.cb) - { - sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 - { - cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SendRoomMessage, 0, 0, 0, cb_info.cb_arg); - return 0; - }); - } + if (!cb_info_opt) + return true; + + cb_info_opt->queue_callback(req_id, 0, 0, 0); return true; } @@ -727,7 +619,7 @@ namespace np if (reply.is_error()) return error_and_disconnect("Malformed reply to RequestTicket command"); - current_ticket = ticket(std::move(ticket_raw)); + current_ticket = ticket(std::move(ticket_raw)); auto ticket_size = static_cast(current_ticket.size()); if (manager_cb) @@ -785,7 +677,7 @@ namespace np { std::unique_lock lock(trans_ctx->mutex); - u32 req_id = get_req_id(0x3334); + u32 req_id = get_req_id(0x3334); trans_ctx->tdata = tdata_get_board_infos{.boardInfo = boardInfo}; rpcn->get_board_infos(req_id, trans_ctx->communicationId, boardId); @@ -803,10 +695,10 @@ namespace np SceNpScoreBoardInfo board_info; - board_info.rankLimit = resp->rankLimit(); - board_info.updateMode = resp->updateMode(); - board_info.sortMode = resp->sortMode(); - board_info.uploadNumLimit = resp->uploadNumLimit(); + board_info.rankLimit = resp->rankLimit(); + board_info.updateMode = resp->updateMode(); + board_info.sortMode = resp->sortMode(); + board_info.uploadNumLimit = resp->uploadNumLimit(); board_info.uploadSizeLimit = resp->uploadSizeLimit(); std::lock_guard lock_trans(mutex_score_transactions); @@ -832,8 +724,8 @@ namespace np void np_handler::record_score(std::shared_ptr& trans_ctx, SceNpScoreBoardId boardId, SceNpScoreValue score, vm::cptr scoreComment, const u8* data, u32 data_size, vm::ptr tmpRank, bool async) { std::unique_lock lock(trans_ctx->mutex); - u32 req_id = get_req_id(0x3334); - std::optional str_comment = scoreComment ? std::optional(std::string(reinterpret_cast(scoreComment->data))) : std::nullopt; + u32 req_id = get_req_id(0x3334); + std::optional str_comment = scoreComment ? std::optional(std::string(reinterpret_cast(scoreComment->data))) : std::nullopt; std::optional> vec_data; if (data) @@ -904,7 +796,7 @@ namespace np if (!tdata) { trans_ctx->tdata = tdata_record_score_data{.game_data_size = totalSize}; - tdata = std::get_if(&trans_ctx->tdata); + tdata = std::get_if(&trans_ctx->tdata); tdata->game_data.reserve(totalSize); } @@ -913,7 +805,7 @@ namespace np if (tdata->game_data.size() == tdata->game_data_size) { trans_ctx->result = std::nullopt; - u32 req_id = get_req_id(0x3334); + u32 req_id = get_req_id(0x3334); rpcn->record_score_data(req_id, trans_ctx->communicationId, trans_ctx->pcId, boardId, score, tdata->game_data); score_async_handler(std::move(lock), trans_ctx, req_id, async); } @@ -984,7 +876,7 @@ namespace np usz to_copy = std::min(tdata->game_data.size(), static_cast(recvSize)); std::memcpy(score_data.get_ptr(), tdata->game_data.data(), to_copy); tdata->game_data.erase(tdata->game_data.begin(), tdata->game_data.begin() + to_copy); - *totalSize = tdata->game_data_size; + *totalSize = tdata->game_data_size; trans_ctx->result = not_an_error(to_copy); } @@ -1038,14 +930,14 @@ namespace np u32 req_id = get_req_id(0x3334); trans_ctx->tdata = tdata_get_score_generic{ - .rankArray = rankArray, + .rankArray = rankArray, .rankArraySize = rankArraySize, - .commentArray = commentArray, - .infoArray = infoArray, + .commentArray = commentArray, + .infoArray = infoArray, .infoArraySize = infoArraySize, - .arrayNum = arrayNum, - .lastSortDate = lastSortDate, - .totalRecord = totalRecord, + .arrayNum = arrayNum, + .lastSortDate = lastSortDate, + .totalRecord = totalRecord, }; bool with_comments = !!commentArray; @@ -1088,9 +980,9 @@ namespace np memset(tdata->rankArray.get_ptr(), 0, tdata->rankArraySize); auto* fb_rankarray = resp->rankArray(); - u32 target_index = 0; + u32 target_index = 0; - vm::ptr rankArray = vm::static_ptr_cast(tdata->rankArray); + vm::ptr rankArray = vm::static_ptr_cast(tdata->rankArray); vm::ptr rankPlayerArray = vm::static_ptr_cast(tdata->rankArray); for (flatbuffers::uoffset_t i = 0; i < fb_rankarray->size(); i++) @@ -1109,18 +1001,18 @@ namespace np else { rankPlayerArray[i].hasData = 1; - cur_rank = &rankPlayerArray[i].rankData; + cur_rank = &rankPlayerArray[i].rankData; } - string_to_npid(fb_rankdata->npId()->string_view(), &cur_rank->npId); - string_to_online_name(fb_rankdata->onlineName()->string_view(), &cur_rank->onlineName); + string_to_npid(fb_rankdata->npId()->string_view(), cur_rank->npId); + string_to_online_name(fb_rankdata->onlineName()->string_view(), cur_rank->onlineName); - cur_rank->pcId = fb_rankdata->pcId(); - cur_rank->serialRank = fb_rankdata->rank(); - cur_rank->rank = fb_rankdata->rank(); - cur_rank->highestRank = fb_rankdata->rank(); - cur_rank->scoreValue = fb_rankdata->score(); - cur_rank->hasGameData = fb_rankdata->hasGameData(); + cur_rank->pcId = fb_rankdata->pcId(); + cur_rank->serialRank = fb_rankdata->rank(); + cur_rank->rank = fb_rankdata->rank(); + cur_rank->highestRank = fb_rankdata->rank(); + cur_rank->scoreValue = fb_rankdata->score(); + cur_rank->hasGameData = fb_rankdata->hasGameData(); cur_rank->recordDate.tick = fb_rankdata->recordDate(); target_index++; @@ -1170,7 +1062,7 @@ namespace np } tdata->lastSortDate->tick = resp->lastSortDate(); - *tdata->totalRecord = resp->totalRecord(); + *tdata->totalRecord = resp->totalRecord(); if (fb_rankarray->size()) trans_ctx->result = not_an_error(fb_rankarray->size()); @@ -1188,16 +1080,16 @@ namespace np void np_handler::get_score_friend(std::shared_ptr& trans_ctx, SceNpScoreBoardId boardId, bool include_self, vm::ptr rankArray, u32 rankArraySize, vm::ptr commentArray, [[maybe_unused]] u32 commentArraySize, vm::ptr infoArray, u32 infoArraySize, u32 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, bool async) { std::unique_lock lock(trans_ctx->mutex); - u32 req_id = get_req_id(0x3334); + u32 req_id = get_req_id(0x3334); trans_ctx->tdata = tdata_get_score_generic{ - .rankArray = rankArray, + .rankArray = rankArray, .rankArraySize = rankArraySize, - .commentArray = commentArray, - .infoArray = infoArray, + .commentArray = commentArray, + .infoArray = infoArray, .infoArraySize = infoArraySize, - .arrayNum = arrayNum, - .lastSortDate = lastSortDate, - .totalRecord = totalRecord, + .arrayNum = arrayNum, + .lastSortDate = lastSortDate, + .totalRecord = totalRecord, }; bool with_comments = !!commentArray; @@ -1215,16 +1107,16 @@ namespace np void np_handler::get_score_npid(std::shared_ptr& trans_ctx, SceNpScoreBoardId boardId, const std::vector>& npid_vec, vm::ptr rankArray, u32 rankArraySize, vm::ptr commentArray, [[maybe_unused]] u32 commentArraySize, vm::ptr infoArray, u32 infoArraySize, u32 arrayNum, vm::ptr lastSortDate, vm::ptr totalRecord, bool async) { std::unique_lock lock(trans_ctx->mutex); - u32 req_id = get_req_id(0x3334); + u32 req_id = get_req_id(0x3334); trans_ctx->tdata = tdata_get_score_generic{ - .rankArray = rankArray, + .rankArray = rankArray, .rankArraySize = rankArraySize, - .commentArray = commentArray, - .infoArray = infoArray, + .commentArray = commentArray, + .infoArray = infoArray, .infoArraySize = infoArraySize, - .arrayNum = arrayNum, - .lastSortDate = lastSortDate, - .totalRecord = totalRecord, + .arrayNum = arrayNum, + .lastSortDate = lastSortDate, + .totalRecord = totalRecord, }; bool with_comments = !!commentArray; diff --git a/rpcs3/Emu/NP/np_structs_extra.cpp b/rpcs3/Emu/NP/np_structs_extra.cpp index aad69fa80a..eb533886f1 100644 --- a/rpcs3/Emu/NP/np_structs_extra.cpp +++ b/rpcs3/Emu/NP/np_structs_extra.cpp @@ -18,6 +18,7 @@ namespace extra_nps void print_sigoptparam(const SceNpMatching2SignalingOptParam* opt) { + sceNp2.warning("SceNpMatching2SignalingOptParam:"); sceNp2.warning("type: %d", opt->type); sceNp2.warning("flag: %d", opt->flag); sceNp2.warning("hubMemberId: %d", opt->hubMemberId); @@ -227,7 +228,10 @@ namespace extra_nps sceNp2.warning("curMemberNum: %d", room->curMemberNum); sceNp2.warning("SceNpMatching2RoomPasswordSlotMask: 0x%x", room->passwordSlotMask); sceNp2.warning("owner: *0x%x", room->owner); - print_userinfo2(room->owner.get_ptr()); + + if (room->owner) + print_userinfo2(room->owner.get_ptr()); + sceNp2.warning("roomGroup: *0x%x", room->roomGroup); // TODO: print roomGroup sceNp2.warning("roomGroupNum: %d", room->roomGroupNum); diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp index bd98c196c6..fc65514943 100644 --- a/rpcs3/Emu/NP/rpcn_client.cpp +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -71,7 +71,7 @@ namespace rpcn return get_localized_string(rpcn_state_to_localized_string_id(state)); } - constexpr u32 RPCN_PROTOCOL_VERSION = 18; + constexpr u32 RPCN_PROTOCOL_VERSION = 19; constexpr usz RPCN_HEADER_SIZE = 15; constexpr usz COMMUNICATION_ID_SIZE = 9; diff --git a/rpcs3/Emu/NP/rpcn_config.cpp b/rpcs3/Emu/NP/rpcn_config.cpp index d2c8fb3f1a..decdbb54e2 100644 --- a/rpcs3/Emu/NP/rpcn_config.cpp +++ b/rpcs3/Emu/NP/rpcn_config.cpp @@ -1,6 +1,6 @@ #include "stdafx.h" #include "rpcn_config.h" -#include "Emu/System.h" +#include "Utilities/File.h" cfg_rpcn g_cfg_rpcn; diff --git a/rpcs3/Emu/NP/signaling_handler.cpp b/rpcs3/Emu/NP/signaling_handler.cpp index 51cb18dc2f..5eb415569c 100644 --- a/rpcs3/Emu/NP/signaling_handler.cpp +++ b/rpcs3/Emu/NP/signaling_handler.cpp @@ -5,6 +5,7 @@ #include "Emu/Cell/Modules/cellSysutil.h" #include "np_handler.h" #include "Emu/NP/vport0.h" +#include "Emu/NP/np_helpers.h" #ifdef _WIN32 #include @@ -51,7 +52,7 @@ void signaling_handler::set_sig_cb(u32 sig_cb_ctx, vm::ptrsig_cb_ctx = sig_cb_ctx; - this->sig_cb = sig_cb; + this->sig_cb = sig_cb; this->sig_cb_arg = sig_cb_arg; } @@ -59,7 +60,7 @@ void signaling_handler::set_ext_sig_cb(u32 sig_ext_cb_ctx, vm::ptrsig_ext_cb_ctx = sig_ext_cb_ctx; - this->sig_ext_cb = sig_ext_cb; + this->sig_ext_cb = sig_ext_cb; this->sig_ext_cb_arg = sig_ext_cb_arg; } @@ -67,7 +68,7 @@ void signaling_handler::set_sig2_cb(u16 sig2_cb_ctx, vm::ptrsig2_cb_ctx = sig2_cb_ctx; - this->sig2_cb = sig2_cb; + this->sig2_cb = sig2_cb; this->sig2_cb_arg = sig2_cb_arg; } @@ -102,17 +103,15 @@ void signaling_handler::signal_ext_sig_callback(u32 conn_id, int event) const void signaling_handler::signal_sig2_callback(u64 room_id, u16 member_id, SceNpMatching2Event event) const { - // Signal the callback - if (sig2_cb) + if (room_id && sig2_cb) { sysutil_register_cb([sig2_cb = this->sig2_cb, sig2_cb_ctx = this->sig2_cb_ctx, room_id, member_id, event, sig2_cb_arg = this->sig2_cb_arg](ppu_thread& cb_ppu) -> s32 { sig2_cb(cb_ppu, sig2_cb_ctx, room_id, member_id, event, 0, sig2_cb_arg); return 0; }); + sign_log.notice("Called sig2 CB: 0x%x (room_id: %d, member_id: %d)", event, room_id, member_id); } - - sign_log.notice("Called sig2 CB: 0x%x (room_id: %d, member_id: %d)", event, room_id, member_id); } /////////////////////////////////// @@ -125,7 +124,7 @@ void signaling_handler::reschedule_packet(std::shared_ptr& si, S { if (it->second.packet.command == cmd && it->second.sig_info == si) { - auto new_queue = qpackets.extract(it); + auto new_queue = qpackets.extract(it); new_queue.key() = new_timepoint; qpackets.insert(std::move(new_queue)); return; @@ -164,9 +163,21 @@ bool signaling_handler::validate_signaling_packet(const signaling_packet* sp) return false; } - if (sp->version != 1u && sp->version != 2u) + if (sp->version != SIGNALING_VERSION) { - sign_log.error("Invalid version in signaling packet"); + sign_log.error("Invalid version in signaling packet: %d, expected: %d", sp->version, SIGNALING_VERSION); + + if (sp->version > SIGNALING_VERSION) + sign_log.error("You are most likely using an outdated version of RPCS3"); + else + sign_log.error("The other user is most likely using an outdated version of RPCS3"); + + return false; + } + + if (!np::is_valid_npid(sp->npid)) + { + sign_log.error("Invalid npid in signaling packet"); return false; } @@ -187,57 +198,36 @@ void signaling_handler::process_incoming_messages() auto op_addr = msg.src_addr; auto op_port = msg.src_port; - auto* sp = reinterpret_cast(msg.data.data()); + const auto* sp = reinterpret_cast(msg.data.data()); if (!validate_signaling_packet(sp)) continue; if (sign_log.trace) { - in_addr addr; + in_addr addr{}; addr.s_addr = op_addr; + char ip_str[16]; + inet_ntop(AF_INET, &addr, ip_str, sizeof(ip_str)); + std::string_view npid(sp->npid.handle.data); - if (sp->version == 1u) - { - char npid_buf[17]{}; - memcpy(npid_buf, sp->V1.npid.handle.data, 16); - std::string npid(npid_buf); - - char ip_str[16]; - inet_ntop(AF_INET, &addr, ip_str, sizeof(ip_str)); - - sign_log.trace("sig1 %s from %s:%d(%s)", sp->command, ip_str, op_port, npid); - } - else - { - char inet_addr[16]; - const char* inet_addr_string = inet_ntop(AF_INET, &addr, inet_addr, sizeof(inet_addr)); - - sign_log.trace("sig2 %s from %s:%d(%d:%d)", sp->command, inet_addr_string, op_port, sp->V2.room_id, sp->V2.member_id); - } + sign_log.trace("SP %s from %s:%d(npid: %s)", sp->command, ip_str, op_port, npid); } bool reply = false, schedule_repeat = false; - auto& sent_packet = sp->version == 1u ? sig1_packet : sig2_packet; + auto& sent_packet = sig_packet; // Get signaling info for user to know if we should even bother looking further auto si = get_signaling_ptr(sp); - if (!si && sp->version == 1u && sp->command == signal_connect) + if (!si && sp->command == signal_connect) { // Connection can be remotely established and not mutual - const u32 conn_id = create_sig_infos(&sp->V1.npid); - si = ::at32(sig1_peers, conn_id); - // Activate the connection without triggering the main CB - si->connStatus = SCE_NP_SIGNALING_CONN_STATUS_ACTIVE; - si->addr = op_addr; - si->port = op_port; - si->ext_status = ext_sign_peer; - // Notify extended callback that peer activated - signal_ext_sig_callback(conn_id, SCE_NP_SIGNALING_EVENT_EXT_PEER_ACTIVATED); + const u32 conn_id = get_always_conn_id(sp->npid); + si = ::at32(sig_peers, conn_id); } - if (!si || (si->connStatus == SCE_NP_SIGNALING_CONN_STATUS_INACTIVE && sp->command != signal_finished)) + if (!si && sp->command != signal_finished) { // User is unknown to us or the connection is inactive // Ignore packet unless it's a finished packet in case the finished_ack wasn't received by opponent @@ -258,7 +248,7 @@ void signaling_handler::process_incoming_messages() } } - sent_packet.command = signal_ping; + sent_packet.command = signal_ping; sent_packet.timestamp_sender = now.time_since_epoch().count(); send_signaling_packet(sent_packet, si->addr, si->port); queue_signaling_packet(sent_packet, si, now + REPEAT_PING_DELAY); @@ -266,12 +256,12 @@ void signaling_handler::process_incoming_messages() const auto update_rtt = [&](u64 rtt_timestamp) { - u32 rtt = now.time_since_epoch().count() - rtt_timestamp; + u32 rtt = now.time_since_epoch().count() - rtt_timestamp; si->last_rtts[(si->rtt_counters % 6)] = rtt; si->rtt_counters++; std::size_t num_rtts = std::min(static_cast(6), si->rtt_counters); - u64 sum = 0; + u64 sum = 0; for (std::size_t index = 0; index < num_rtts; index++) { sum += si->last_rtts[index]; @@ -283,59 +273,57 @@ void signaling_handler::process_incoming_messages() switch (sp->command) { case signal_ping: - reply = true; - schedule_repeat = false; - sent_packet.command = signal_pong; + reply = true; + schedule_repeat = false; + sent_packet.command = signal_pong; sent_packet.timestamp_sender = sp->timestamp_sender; break; case signal_pong: update_rtt(sp->timestamp_sender); - reply = false; + reply = false; schedule_repeat = false; reschedule_packet(si, signal_ping, now + 10s); break; case signal_connect: - reply = true; - schedule_repeat = true; - sent_packet.command = signal_connect_ack; + reply = true; + schedule_repeat = true; + sent_packet.command = signal_connect_ack; sent_packet.timestamp_sender = sp->timestamp_sender; sent_packet.timestamp_receiver = now.time_since_epoch().count(); - // connection is established + update_si_addr(si, op_addr, op_port); break; case signal_connect_ack: update_rtt(sp->timestamp_sender); - reply = true; + reply = true; schedule_repeat = false; setup_ping(); sent_packet.command = signal_confirm; sent_packet.timestamp_receiver = now.time_since_epoch().count(); retire_packet(si, signal_connect); - // connection is active update_si_addr(si, op_addr, op_port); update_si_mapped_addr(si, sp->sent_addr, sp->sent_port); update_si_status(si, SCE_NP_SIGNALING_CONN_STATUS_ACTIVE); break; case signal_confirm: update_rtt(sp->timestamp_receiver); - reply = false; + reply = false; schedule_repeat = false; setup_ping(); retire_packet(si, signal_connect_ack); - // connection is active update_si_addr(si, op_addr, op_port); update_si_mapped_addr(si, sp->sent_addr, sp->sent_port); - update_si_status(si, SCE_NP_SIGNALING_CONN_STATUS_ACTIVE, true); + update_ext_si_status(si, true); break; case signal_finished: - reply = true; - schedule_repeat = false; + reply = true; + schedule_repeat = false; sent_packet.command = signal_finished_ack; - // terminate connection - update_si_status(si, SCE_NP_SIGNALING_CONN_STATUS_INACTIVE); + update_ext_si_status(si, false); break; case signal_finished_ack: - reply = false; + reply = false; schedule_repeat = false; + update_si_status(si, SCE_NP_SIGNALING_CONN_STATUS_INACTIVE); retire_packet(si, signal_finished); break; default: sign_log.error("Invalid signaling command received"); continue; @@ -369,37 +357,39 @@ void signaling_handler::operator()() for (auto it = qpackets.begin(); it != qpackets.end();) { - if (it->first > now) + auto& [timestamp, sig] = *it; + + if (timestamp > now) break; - if (it->second.sig_info->time_last_msg_recvd < now - 60s) + if (sig.sig_info->time_last_msg_recvd < now - 60s) { // We had no connection to opponent for 60 seconds, consider the connection dead - sign_log.trace("Timeout disconnection"); - update_si_status(it->second.sig_info, SCE_NP_SIGNALING_CONN_STATUS_INACTIVE); - break; // qpackets will be emptied of all packets from this user so we're requeuing + sign_log.notice("Timeout disconnection"); + update_si_status(sig.sig_info, SCE_NP_SIGNALING_CONN_STATUS_INACTIVE); + break; // qpackets will be emptied of all packets for this user so we're requeuing } // Update the timestamp if necessary - switch (it->second.packet.command) + switch (sig.packet.command) { case signal_connect: case signal_ping: - it->second.packet.timestamp_sender = now.time_since_epoch().count(); + sig.packet.timestamp_sender = now.time_since_epoch().count(); break; case signal_connect_ack: - it->second.packet.timestamp_receiver = now.time_since_epoch().count(); + sig.packet.timestamp_receiver = now.time_since_epoch().count(); break; default: break; } // Resend the packet - send_signaling_packet(it->second.packet, it->second.sig_info->addr, it->second.sig_info->port); + send_signaling_packet(sig.packet, sig.sig_info->addr, sig.sig_info->port); // Reschedule another packet - SignalingCommand cmd = it->second.packet.command; - auto& si = it->second.sig_info; + SignalingCommand cmd = sig.packet.command; + auto& si = sig.sig_info; std::chrono::milliseconds delay(500); switch (cmd) @@ -442,16 +432,20 @@ void signaling_handler::update_si_addr(std::shared_ptr& si, u32 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; + if (sign_log.trace) + { + 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)); + 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 Address from %s:%d to %s:%d", ip_str_old, si->port, ip_str_new, new_port); + } - sign_log.trace("Updated Address from %s:%d to %s:%d", ip_str_old, si->port, ip_str_new, new_port); si->addr = new_addr; si->port = new_port; } @@ -461,87 +455,90 @@ void signaling_handler::update_si_mapped_addr(std::shared_ptr& s { ensure(si); - if (si->addr != new_addr || si->port != new_port) + if (si->mapped_addr != new_addr || si->mapped_port != new_port) { - in_addr addr_old, addr_new; - addr_old.s_addr = si->mapped_addr; - addr_new.s_addr = new_addr; + if (sign_log.trace) + { + in_addr addr_old, addr_new; + addr_old.s_addr = si->mapped_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)); + 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->mapped_port, ip_str_new, new_port); + } - sign_log.trace("Updated Mapped Address from %s:%d to %s:%d", ip_str_old, si->mapped_port, ip_str_new, new_port); si->mapped_addr = new_addr; si->mapped_port = new_port; } } -void signaling_handler::update_si_status(std::shared_ptr& si, s32 new_status, bool confirm_packet) +void signaling_handler::update_si_status(std::shared_ptr& si, s32 new_status) { if (!si) return; - if (si->connStatus == SCE_NP_SIGNALING_CONN_STATUS_PENDING && new_status == SCE_NP_SIGNALING_CONN_STATUS_ACTIVE) + if (si->conn_status == SCE_NP_SIGNALING_CONN_STATUS_PENDING && new_status == SCE_NP_SIGNALING_CONN_STATUS_ACTIVE) { - si->connStatus = SCE_NP_SIGNALING_CONN_STATUS_ACTIVE; - ensure(si->version == 1u || si->version == 2u); - if (si->version == 1u) - signal_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_ESTABLISHED); - else - signal_sig2_callback(si->room_id, si->member_id, SCE_NP_MATCHING2_SIGNALING_EVENT_Established); + si->conn_status = SCE_NP_SIGNALING_CONN_STATUS_ACTIVE; - return; + signal_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_ESTABLISHED); + signal_sig2_callback(si->room_id, si->member_id, SCE_NP_MATCHING2_SIGNALING_EVENT_Established); + + if (si->op_activated) + signal_ext_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_EXT_MUTUAL_ACTIVATED); } - - if ((si->connStatus == SCE_NP_SIGNALING_CONN_STATUS_PENDING || si->connStatus == SCE_NP_SIGNALING_CONN_STATUS_ACTIVE) && new_status == SCE_NP_SIGNALING_CONN_STATUS_INACTIVE) + else if ((si->conn_status == SCE_NP_SIGNALING_CONN_STATUS_PENDING || si->conn_status == SCE_NP_SIGNALING_CONN_STATUS_ACTIVE) && new_status == SCE_NP_SIGNALING_CONN_STATUS_INACTIVE) { - si->connStatus = SCE_NP_SIGNALING_CONN_STATUS_INACTIVE; - ensure(si->version == 1u || si->version == 2u); - if (si->version == 1u) - signal_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_DEAD); - else - signal_sig2_callback(si->room_id, si->member_id, SCE_NP_MATCHING2_SIGNALING_EVENT_Dead); - + si->conn_status = SCE_NP_SIGNALING_CONN_STATUS_INACTIVE; + signal_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_DEAD); + signal_sig2_callback(si->room_id, si->member_id, SCE_NP_MATCHING2_SIGNALING_EVENT_Dead); retire_all_packets(si); - return; } +} - if (confirm_packet && si->version == 1u && si->ext_status == ext_sign_none) +void signaling_handler::update_ext_si_status(std::shared_ptr& si, bool op_activated) +{ + if (op_activated && !si->op_activated) { - si->ext_status = ext_sign_mutual; - signal_ext_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_EXT_MUTUAL_ACTIVATED); + si->op_activated = true; + + if (si->conn_status != SCE_NP_SIGNALING_CONN_STATUS_ACTIVE) + signal_ext_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_EXT_PEER_ACTIVATED); + else + signal_ext_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_EXT_MUTUAL_ACTIVATED); + } + else if (!op_activated && si->op_activated) + { + si->op_activated = false; + + signal_ext_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_EXT_PEER_DEACTIVATED); } } void signaling_handler::set_self_sig_info(SceNpId& npid) { std::lock_guard lock(data_mutex); - sig1_packet.V1.npid = npid; -} - -void signaling_handler::set_self_sig2_info(u64 room_id, u16 member_id) -{ - std::lock_guard lock(data_mutex); - sig2_packet.V2.room_id = room_id; - sig2_packet.V2.member_id = member_id; + sig_packet.npid = npid; } void signaling_handler::send_signaling_packet(signaling_packet& sp, u32 addr, u16 port) const { std::vector packet(sizeof(signaling_packet) + VPORT_0_HEADER_SIZE); reinterpret_cast&>(packet[0]) = 0; // VPort 0 - packet[2] = SUBSET_SIGNALING; - sp.sent_addr = addr; - sp.sent_port = port; + packet[2] = SUBSET_SIGNALING; + sp.sent_addr = addr; + sp.sent_port = port; memcpy(packet.data() + VPORT_0_HEADER_SIZE, &sp, sizeof(signaling_packet)); sockaddr_in dest; memset(&dest, 0, sizeof(sockaddr_in)); - dest.sin_family = AF_INET; + dest.sin_family = AF_INET; dest.sin_addr.s_addr = addr; - dest.sin_port = std::bit_cast>(port); + dest.sin_port = std::bit_cast>(port); char ip_str[16]; inet_ntop(AF_INET, &dest.sin_addr, ip_str, sizeof(ip_str)); @@ -558,55 +555,41 @@ void signaling_handler::queue_signaling_packet(signaling_packet& sp, std::shared { queued_packet qp; qp.sig_info = std::move(si); - qp.packet = sp; + qp.packet = sp; qpackets.emplace(wakeup_time, std::move(qp)); } std::shared_ptr signaling_handler::get_signaling_ptr(const signaling_packet* sp) { - // V1 - if (sp->version == 1u) - { - char npid_buf[17]{}; - memcpy(npid_buf, sp->V1.npid.handle.data, 16); - std::string npid(npid_buf); + u32 conn_id; - if (!npid_to_conn_id.contains(npid)) - return nullptr; + char npid_buf[17]{}; + memcpy(npid_buf, sp->npid.handle.data, 16); + std::string npid(npid_buf); - const u32 conn_id = ::at32(npid_to_conn_id, npid); - if (!sig1_peers.contains(conn_id)) - { - sign_log.error("Discrepancy in signaling 1 data"); - return nullptr; - } - - return ::at32(sig1_peers, conn_id); - } - - // V2 - auto room_id = sp->V2.room_id; - auto member_id = sp->V2.member_id; - if (!sig2_peers.contains(room_id) || !::at32(sig2_peers, room_id).contains(member_id)) + if (!npid_to_conn_id.contains(npid)) return nullptr; - return ::at32(::at32(sig2_peers, room_id), member_id); + conn_id = ::at32(npid_to_conn_id, npid); + + if (!sig_peers.contains(conn_id)) + { + sign_log.error("Discrepancy in signaling data"); + return nullptr; + } + + return ::at32(sig_peers, conn_id); } void signaling_handler::start_sig(u32 conn_id, u32 addr, u16 port) { std::lock_guard lock(data_mutex); - start_sig_nl(conn_id, addr, port); -} - -void signaling_handler::start_sig_nl(u32 conn_id, u32 addr, u16 port) -{ - auto& sent_packet = sig1_packet; + auto& sent_packet = sig_packet; sent_packet.command = signal_connect; sent_packet.timestamp_sender = steady_clock::now().time_since_epoch().count(); - ensure(sig1_peers.contains(conn_id)); - std::shared_ptr si = ::at32(sig1_peers, conn_id); + ensure(sig_peers.contains(conn_id)); + std::shared_ptr si = ::at32(sig_peers, conn_id); si->addr = addr; si->port = port; @@ -616,133 +599,113 @@ void signaling_handler::start_sig_nl(u32 conn_id, u32 addr, u16 port) wake_up(); } +void signaling_handler::stop_sig_nl(u32 conn_id) +{ + if (!sig_peers.contains(conn_id)) + return; + + auto& sent_packet = sig_packet; + sent_packet.command = signal_finished; + + std::shared_ptr si = ::at32(sig_peers, conn_id); + + send_signaling_packet(sent_packet, si->addr, si->port); + queue_signaling_packet(sent_packet, std::move(si), steady_clock::now() + REPEAT_FINISHED_DELAY); +} + void signaling_handler::stop_sig(u32 conn_id) { std::lock_guard lock(data_mutex); - if (!sig1_peers.contains(conn_id)) - return; - - auto& sent_packet = sig1_packet; - sent_packet.command = signal_finished; - - std::shared_ptr si = ::at32(sig1_peers, conn_id); - - send_signaling_packet(sent_packet, si->addr, si->port); - queue_signaling_packet(sent_packet, si, steady_clock::now() + REPEAT_FINISHED_DELAY); -} - -void signaling_handler::start_sig2(u64 room_id, u16 member_id) -{ - std::lock_guard lock(data_mutex); - - auto& sent_packet = sig2_packet; - sent_packet.command = signal_connect; - sent_packet.timestamp_sender = steady_clock::now().time_since_epoch().count(); - - ensure(sig2_peers.contains(room_id)); - const auto& sp = ::at32(sig2_peers, room_id); - - ensure(sp.contains(member_id)); - std::shared_ptr si = ::at32(sp, member_id); - - send_signaling_packet(sent_packet, si->addr, si->port); - queue_signaling_packet(sent_packet, si, steady_clock::now() + REPEAT_CONNECT_DELAY); - wake_up(); + stop_sig_nl(conn_id); } void signaling_handler::disconnect_sig2_users(u64 room_id) { std::lock_guard lock(data_mutex); - if (!sig2_peers.contains(room_id)) - return; - - auto& sent_packet = sig2_packet; - - sent_packet.command = signal_finished; - - for (const auto& member : ::at32(sig2_peers, room_id)) + for (auto& [conn_id, si] : sig_peers) { - auto& si = member.second; - if (si->connStatus != SCE_NP_SIGNALING_CONN_STATUS_INACTIVE && !si->self) + if (si->room_id == room_id) { - send_signaling_packet(sent_packet, si->addr, si->port); - queue_signaling_packet(sent_packet, si, steady_clock::now() + REPEAT_FINISHED_DELAY); + stop_sig_nl(conn_id); } } } -u32 signaling_handler::create_sig_infos(const SceNpId* npid) +u32 signaling_handler::get_always_conn_id(const SceNpId& npid) { - ensure(npid->handle.data[16] == 0); - std::string npid_str(reinterpret_cast(npid->handle.data)); - + std::string npid_str(reinterpret_cast(npid.handle.data)); if (npid_to_conn_id.contains(npid_str)) - { return ::at32(npid_to_conn_id, npid_str); - } const u32 conn_id = cur_conn_id++; - npid_to_conn_id.emplace(npid_str, conn_id); - sig1_peers.emplace(conn_id, std::make_shared()); - ::at32(sig1_peers, conn_id)->version = 1; - ::at32(sig1_peers, conn_id)->conn_id = conn_id; - ::at32(sig1_peers, conn_id)->npid = *npid; + npid_to_conn_id.emplace(std::move(npid_str), conn_id); + sig_peers.emplace(conn_id, std::make_shared()); + auto& si = ::at32(sig_peers, conn_id); + si->conn_id = conn_id; + si->npid = npid; return conn_id; } -u32 signaling_handler::init_sig_infos(const SceNpId* npid) +u32 signaling_handler::init_sig1(const SceNpId& npid) { std::lock_guard lock(data_mutex); - const u32 conn_id = create_sig_infos(npid); + const u32 conn_id = get_always_conn_id(npid); - if (sig1_peers[conn_id]->connStatus == SCE_NP_SIGNALING_CONN_STATUS_INACTIVE) + if (sig_peers[conn_id]->conn_status == SCE_NP_SIGNALING_CONN_STATUS_INACTIVE) { sign_log.trace("Creating new sig1 connection and requesting infos from RPCN"); - sig1_peers[conn_id]->connStatus = SCE_NP_SIGNALING_CONN_STATUS_PENDING; + sig_peers[conn_id]->conn_status = SCE_NP_SIGNALING_CONN_STATUS_PENDING; // Request peer infos from RPCN - std::string npid_str(reinterpret_cast(npid->handle.data)); + std::string npid_str(reinterpret_cast(npid.handle.data)); auto& nph = g_fxo->get>(); nph.req_sign_infos(npid_str, conn_id); } - else - { - // Connection already exists(from passive connection) - if (sig1_peers[conn_id]->connStatus == SCE_NP_SIGNALING_CONN_STATUS_ACTIVE && sig1_peers[conn_id]->ext_status == ext_sign_peer) - { - sign_log.trace("Activating already peer activated connection"); - sig1_peers[conn_id]->ext_status = ext_sign_mutual; - start_sig_nl(conn_id, sig1_peers[conn_id]->addr, sig1_peers[conn_id]->port); - signal_sig_callback(conn_id, SCE_NP_SIGNALING_EVENT_ESTABLISHED); - signal_ext_sig_callback(conn_id, SCE_NP_SIGNALING_EVENT_EXT_MUTUAL_ACTIVATED); - } - } return conn_id; } -signaling_info signaling_handler::get_sig_infos(u32 conn_id) +u32 signaling_handler::init_sig2(const SceNpId& npid, u64 room_id, u16 member_id) { std::lock_guard lock(data_mutex); - return *sig1_peers[conn_id]; + u32 conn_id = get_always_conn_id(npid); + auto& si = ::at32(sig_peers, conn_id); + si->room_id = room_id; + si->member_id = member_id; + si->conn_status = SCE_NP_SIGNALING_CONN_STATUS_PENDING; + + return conn_id; } -std::optional signaling_handler::get_conn_id_from_npid(const SceNpId* npid) +std::optional signaling_handler::get_conn_id_from_npid(const SceNpId& npid) { std::lock_guard lock(data_mutex); - // Diff behaviour here depending on SDK version, 420+ always succeeds - return create_sig_infos(npid); + + std::string npid_str(reinterpret_cast(npid.handle.data)); + if (npid_to_conn_id.contains(npid_str)) + return ::at32(npid_to_conn_id, npid_str); + + return std::nullopt; +} + +std::optional signaling_handler::get_sig_infos(u32 conn_id) +{ + std::lock_guard lock(data_mutex); + if (sig_peers.contains(conn_id)) + return *::at32(sig_peers, conn_id); + + return std::nullopt; } std::optional signaling_handler::get_conn_id_from_addr(u32 addr, u16 port) { std::lock_guard lock(data_mutex); - for (const auto& [conn_id, conn_info] : sig1_peers) + for (const auto& [conn_id, conn_info] : sig_peers) { if (conn_info && std::bit_cast>(conn_info->addr) == addr && conn_info->port == port) { @@ -752,36 +715,3 @@ std::optional signaling_handler::get_conn_id_from_addr(u32 addr, u16 port) return std::nullopt; } - -void signaling_handler::set_sig2_infos(u64 room_id, u16 member_id, s32 status, u32 addr, u16 port, const SceNpId& npid, bool self) -{ - std::lock_guard lock(data_mutex); - if (!sig2_peers[room_id][member_id]) - sig2_peers[room_id][member_id] = std::make_shared(); - - auto& peer = sig2_peers[room_id][member_id]; - peer->connStatus = status; - peer->addr = addr; - peer->port = port; - peer->self = self; - peer->version = 2; - peer->room_id = room_id; - peer->member_id = member_id; - peer->npid = npid; -} - -signaling_info signaling_handler::get_sig2_infos(u64 room_id, u16 member_id) -{ - std::lock_guard lock(data_mutex); - - if (!sig2_peers[room_id][member_id]) - { - sig2_peers[room_id][member_id] = std::make_shared(); - auto& peer = sig2_peers[room_id][member_id]; - peer->room_id = room_id; - peer->member_id = member_id; - peer->version = 2; - } - - return *sig2_peers[room_id][member_id]; -} diff --git a/rpcs3/Emu/NP/signaling_handler.h b/rpcs3/Emu/NP/signaling_handler.h index 0525a58a8b..bbfe78b04a 100644 --- a/rpcs3/Emu/NP/signaling_handler.h +++ b/rpcs3/Emu/NP/signaling_handler.h @@ -9,18 +9,11 @@ #include #include -enum ext_signaling_status : u8 -{ - ext_sign_none = 0, - ext_sign_peer = 1, - ext_sign_mutual = 2, -}; - struct signaling_info { - s32 connStatus = SCE_NP_SIGNALING_CONN_STATUS_INACTIVE; - u32 addr = 0; - u16 port = 0; + s32 conn_status = SCE_NP_SIGNALING_CONN_STATUS_INACTIVE; + u32 addr = 0; + u16 port = 0; // User seen from that peer u32 mapped_addr = 0; @@ -28,16 +21,15 @@ struct signaling_info // For handler steady_clock::time_point time_last_msg_recvd = steady_clock::now(); - bool self = false; - u32 version = 0; + bool self = false; SceNpId npid{}; // Signaling - u32 conn_id = 0; - ext_signaling_status ext_status = ext_sign_none; + u32 conn_id = 0; + bool op_activated = false; // Matching2 - u64 room_id = 0; + u64 room_id = 0; u16 member_id = 0; // Stats @@ -68,16 +60,13 @@ public: signaling_handler& operator=(thread_state); void set_self_sig_info(SceNpId& npid); - void set_self_sig2_info(u64 room_id, u16 member_id); - u32 init_sig_infos(const SceNpId* npid); - signaling_info get_sig_infos(u32 conn_id); - std::optional get_conn_id_from_npid(const SceNpId* npid); + u32 init_sig1(const SceNpId& npid); + u32 init_sig2(const SceNpId& npid, u64 room_id, u16 member_id); + std::optional get_sig_infos(u32 conn_id); + std::optional get_conn_id_from_npid(const SceNpId& npid); std::optional get_conn_id_from_addr(u32 addr, u16 port); - void set_sig2_infos(u64 room_id, u16 member_id, s32 status, u32 addr, u16 port, const SceNpId& npid, bool self = false); - signaling_info get_sig2_infos(u64 room_id, u16 member_id); - void set_sig_cb(u32 sig_cb_ctx, vm::ptr sig_cb, vm::ptr sig_cb_arg); void set_ext_sig_cb(u32 sig_ext_cb_ctx, vm::ptr sig_ext_cb, vm::ptr sig_ext_cb_arg); void set_sig2_cb(u16 sig2_cb_ctx, vm::ptr sig2_cb, vm::ptr sig2_cb_arg); @@ -91,32 +80,22 @@ public: static constexpr auto thread_name = "Signaling Manager Thread"sv; private: - static constexpr auto REPEAT_CONNECT_DELAY = std::chrono::milliseconds(200); - static constexpr auto REPEAT_PING_DELAY = std::chrono::milliseconds(500); - static constexpr auto REPEAT_FINISHED_DELAY = std::chrono::milliseconds(500); + static constexpr auto REPEAT_CONNECT_DELAY = std::chrono::milliseconds(200); + static constexpr auto REPEAT_PING_DELAY = std::chrono::milliseconds(500); + static constexpr auto REPEAT_FINISHED_DELAY = std::chrono::milliseconds(500); static constexpr be_t SIGNALING_SIGNATURE = (static_cast('S') << 24 | static_cast('I') << 16 | static_cast('G') << 8 | static_cast('N')); + static constexpr le_t SIGNALING_VERSION = 3; struct signaling_packet { be_t signature = SIGNALING_SIGNATURE; - le_t version; + le_t version = SIGNALING_VERSION; le_t timestamp_sender; le_t timestamp_receiver; le_t command; le_t sent_addr; le_t sent_port; - union - { - struct - { - SceNpId npid; - } V1; - struct - { - le_t room_id; - le_t member_id; - } V2; - }; + SceNpId npid; }; struct queued_packet @@ -137,33 +116,32 @@ private: vm::ptr sig2_cb{}; vm::ptr sig2_cb_arg{}; - u32 create_sig_infos(const SceNpId* npid); + u32 get_always_conn_id(const SceNpId& npid); static void update_si_addr(std::shared_ptr& si, u32 new_addr, u16 new_port); static void update_si_mapped_addr(std::shared_ptr& si, u32 new_addr, u16 new_port); - void update_si_status(std::shared_ptr& si, s32 new_status, bool confirm_packet = false); + static void update_room_info(std::shared_ptr& si, u64 room_id, u16 member_id); + void update_si_status(std::shared_ptr& si, s32 new_status); + void update_ext_si_status(std::shared_ptr& si, bool op_activated); void signal_sig_callback(u32 conn_id, int event); void signal_ext_sig_callback(u32 conn_id, int event) const; void signal_sig2_callback(u64 room_id, u16 member_id, SceNpMatching2Event event) const; - void start_sig_nl(u32 conn_id, u32 addr, u16 port); - static bool validate_signaling_packet(const signaling_packet* sp); void reschedule_packet(std::shared_ptr& si, SignalingCommand cmd, steady_clock::time_point new_timepoint); void retire_packet(std::shared_ptr& si, SignalingCommand cmd); void retire_all_packets(std::shared_ptr& si); + void stop_sig_nl(u32 conn_id); std::mutex data_mutex; std::condition_variable wakey; - signaling_packet sig1_packet{.version = 1u}; - signaling_packet sig2_packet{.version = 2u}; + signaling_packet sig_packet{}; std::map qpackets; // (wakeup time, packet) u32 cur_conn_id = 1; - std::unordered_map npid_to_conn_id; // (npid, conn_id) - std::unordered_map> sig1_peers; // (conn_id, sig_info) - std::unordered_map>> sig2_peers; // (room (member_id, sig_info)) + std::unordered_map npid_to_conn_id; // (npid, conn_id) + std::unordered_map> sig_peers; // (conn_id, sig_info) void process_incoming_messages(); std::shared_ptr get_signaling_ptr(const signaling_packet* sp); diff --git a/rpcs3/Emu/NP/upnp_config.cpp b/rpcs3/Emu/NP/upnp_config.cpp new file mode 100644 index 0000000000..aae1b92e51 --- /dev/null +++ b/rpcs3/Emu/NP/upnp_config.cpp @@ -0,0 +1,59 @@ +#include "stdafx.h" +#include "upnp_config.h" +#include "Utilities/File.h" + +LOG_CHANNEL(upnp_cfg_log, "UPNP_CFG"); + +void cfg_upnp::load() +{ + const std::string path = cfg_upnp::get_path(); + + fs::file cfg_file(path, fs::read); + if (cfg_file) + { + upnp_cfg_log.notice("Loading UPNP config. Path: %s", path); + from_string(cfg_file.to_string()); + } + else + { + upnp_cfg_log.notice("UPNP config missing. Using default settings. Path: %s", path); + from_default(); + } +} + +void cfg_upnp::save() const +{ +#ifdef _WIN32 + const std::string path_to_cfg = fs::get_config_dir() + "config/"; + if (!fs::create_path(path_to_cfg)) + { + upnp_cfg_log.error("Could not create path: %s", path_to_cfg); + } +#endif + + fs::pending_file cfg_file(cfg_upnp::get_path()); + + if (!cfg_file.file || (cfg_file.file.write(to_string()), !cfg_file.commit())) + { + upnp_cfg_log.error("Could not save config: %s (error=%s)", cfg_upnp::get_path(), fs::g_tls_error); + } +} + +std::string cfg_upnp::get_device_url() const +{ + return device_url.to_string(); +} + +void cfg_upnp::set_device_url(std::string_view url) +{ + device_url.from_string(url); +} + +std::string cfg_upnp::get_path() +{ +#ifdef _WIN32 + return fs::get_config_dir() + "config/upnp.yml"; +#else + return fs::get_config_dir() + "upnp.yml"; +#endif +} diff --git a/rpcs3/Emu/NP/upnp_config.h b/rpcs3/Emu/NP/upnp_config.h new file mode 100644 index 0000000000..f1068c07c0 --- /dev/null +++ b/rpcs3/Emu/NP/upnp_config.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Utilities/Config.h" + +struct cfg_upnp : cfg::node +{ + cfg::string device_url{this, "DeviceUrl", ""}; + + void load(); + void save() const; + + std::string get_device_url() const; + + void set_device_url(std::string_view url); + +private: + static std::string get_path(); +}; diff --git a/rpcs3/Emu/NP/upnp_handler.cpp b/rpcs3/Emu/NP/upnp_handler.cpp new file mode 100644 index 0000000000..dca00de986 --- /dev/null +++ b/rpcs3/Emu/NP/upnp_handler.cpp @@ -0,0 +1,186 @@ +#include "stdafx.h" +#include "upnp_handler.h" +#include "util/logs.hpp" + +#include +#include + +LOG_CHANNEL(upnp_log, "UPNP"); + +upnp_handler::~upnp_handler() +{ + std::lock_guard lock(m_mutex); + + for (const auto& [protocol, prot_bindings] : m_bindings) + { + for (const auto& [internal_port, external_port] : prot_bindings) + { + remove_port_redir_external(external_port, protocol); + } + } + + m_active = false; +} + +void upnp_handler::upnp_enable() +{ + std::lock_guard lock(m_mutex); + + m_cfg.load(); + + auto check_igd = [&](const char* url) -> bool + { + int desc_xml_size = 0; + int status_code = 0; + + m_igd_data = {}; + m_igd_urls = {}; + + char* desc_xml = static_cast(miniwget(url, &desc_xml_size, 1, &status_code)); + + if (!desc_xml) + return false; + + parserootdesc(desc_xml, desc_xml_size, &m_igd_data); + free(desc_xml); + desc_xml = nullptr; + GetUPNPUrls(&m_igd_urls, &m_igd_data, url, 1); + + return true; + }; + + std::string dev_url = m_cfg.get_device_url(); + + if (!dev_url.empty()) + { + if (check_igd(dev_url.c_str())) + { + upnp_log.notice("Saved UPNP(%s) enabled", dev_url); + m_active = true; + return; + } + + upnp_log.error("Saved UPNP(%s) isn't available anymore", dev_url); + } + + upnp_log.notice("Starting UPNP search"); + + int upnperror = 0; + UPNPDev* devlist = upnpDiscover(2000, nullptr, nullptr, 0, 0, 2, &upnperror); + + if (!devlist) + { + upnp_log.error("No UPNP device was found"); + return; + } + + const UPNPDev* dev = devlist; + for (; dev; dev = dev->pNext) + { + if (strstr(dev->st, "InternetGatewayDevice")) + break; + } + + if (dev) + { + int desc_xml_size = 0; + int status_code = 0; + char* desc_xml = static_cast(miniwget(dev->descURL, &desc_xml_size, 1, &status_code)); + + if (desc_xml) + { + IGDdatas igd_data{}; + UPNPUrls igd_urls{}; + parserootdesc(desc_xml, desc_xml_size, &igd_data); + free(desc_xml); + desc_xml = nullptr; + GetUPNPUrls(&igd_urls, &igd_data, dev->descURL, 1); + + upnp_log.notice("Found UPnP device type:%s at %s", dev->st, dev->descURL); + + m_cfg.set_device_url(dev->descURL); + m_cfg.save(); + + m_active = true; + } + else + { + upnp_log.error("Failed to retrieve UPNP xml for %s", dev->descURL); + } + } + else + { + upnp_log.error("No UPNP IGD device was found"); + } + + freeUPNPDevlist(devlist); +} + +void upnp_handler::add_port_redir(std::string_view addr, u16 internal_port, std::string_view protocol) +{ + if (!m_active) + return; + + std::lock_guard lock(m_mutex); + + u16 external_port = internal_port; + std::string internal_port_str = fmt::format("%d", internal_port); + int res = 0; + + for (u16 external_port = internal_port; external_port < internal_port + 100; external_port++) + { + std::string external_port_str = fmt::format("%d", external_port); + res = UPNP_AddPortMapping(m_igd_urls.controlURL, m_igd_data.first.servicetype, external_port_str.c_str(), internal_port_str.c_str(), addr.data(), "RPCS3", protocol.data(), nullptr, nullptr); + if (res == UPNPCOMMAND_SUCCESS) + { + m_bindings[std::string(protocol)][internal_port] = external_port; + upnp_log.notice("Successfully bound %s:%d(%s) to IGD:%d", addr, internal_port, protocol, external_port); + return; + } + + // need more testing, may vary per router, etc, for now assume port conflict silently + // else if (res != 718) // ConflictInMappingEntry + // { + // upnp_log.error("Failed to bind %s:%d(%s) to IGD:%d: %d", addr, internal_port, protocol, external_port, res); + // return; + // } + } + + upnp_log.error("Failed to bind %s:%d(%s) to IGD:(%d=>%d): %d", addr, internal_port, protocol, internal_port, external_port, res); +} + +void upnp_handler::remove_port_redir(u16 internal_port, std::string_view protocol) +{ + if (!m_active) + return; + + std::lock_guard lock(m_mutex); + + const std::string str_protocol(protocol); + + if (!m_bindings.contains(str_protocol) || !::at32(m_bindings, str_protocol).contains(internal_port)) + { + upnp_log.error("tried to unbind port mapping %d to IGD(%s) but it isn't bound", internal_port, protocol); + return; + } + + const u16 external_port = ::at32(::at32(m_bindings, str_protocol), internal_port); + + remove_port_redir_external(external_port, protocol); + + ensure(::at32(m_bindings, str_protocol).erase(internal_port)); + upnp_log.notice("Successfully deleted port mapping %d to IGD:%d(%s)", internal_port, external_port, protocol); +} + +void upnp_handler::remove_port_redir_external(u16 external_port, std::string_view protocol, bool verbose) +{ + const std::string str_ext_port = fmt::format("%d", external_port); + + if (int res = UPNP_DeletePortMapping(m_igd_urls.controlURL, m_igd_data.first.servicetype, str_ext_port.c_str(), protocol.data(), nullptr); res != 0 && verbose) + upnp_log.error("Failed to delete port mapping IGD:%s(%s): %d", str_ext_port, protocol, res); +} + +bool upnp_handler::is_active() const +{ + return m_active; +} diff --git a/rpcs3/Emu/NP/upnp_handler.h b/rpcs3/Emu/NP/upnp_handler.h new file mode 100644 index 0000000000..40892f7443 --- /dev/null +++ b/rpcs3/Emu/NP/upnp_handler.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include + +#include "upnp_config.h" +#include "Utilities/mutex.h" + +class upnp_handler +{ +public: + ~upnp_handler(); + + void upnp_enable(); + void add_port_redir(std::string_view addr, u16 internal_port, std::string_view protocol); + void remove_port_redir(u16 internal_port, std::string_view protocol); + + bool is_active() const; + +private: + void remove_port_redir_external(u16 external_port, std::string_view protocol, bool verbose = true); + +private: + atomic_t m_active = false; + + shared_mutex m_mutex; + cfg_upnp m_cfg; + IGDdatas m_igd_data{}; + UPNPUrls m_igd_urls{}; + std::unordered_map> m_bindings; +}; diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index 9ee57bd99c..eed37fba87 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -298,6 +298,7 @@ struct cfg_root : cfg::node cfg::string bind_address{this, "Bind address", "0.0.0.0"}; cfg::string dns{this, "DNS address", "8.8.8.8"}; cfg::string swap_list{this, "IP swap list", ""}; + cfg::_bool upnp_enabled{this, "UPNP Enabled", false}; cfg::_enum psn_status{this, "PSN status", np_psn_status::disabled}; } net{this}; diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 6ca290d2be..f3aa78a029 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -40,10 +40,10 @@ Use - ..\3rdparty\wolfssl\wolfssl;..\3rdparty\flatbuffers\include;..\3rdparty\libusb\libusb\libusb;..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\zlib\zlib;..\llvm\include;..\llvm_build\include;$(VULKAN_SDK)\Include + ..\3rdparty\miniupnp\miniupnp\miniupnpc\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\flatbuffers\include;..\3rdparty\libusb\libusb\libusb;..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\zlib\zlib;..\llvm\include;..\llvm_build\include;$(VULKAN_SDK)\Include MaxSpeed - HAVE_VULKAN;HAVE_SDL2;%(PreprocessorDefinitions) - HAVE_VULKAN;HAVE_SDL2;%(PreprocessorDefinitions) + MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL2;%(PreprocessorDefinitions) + MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL2;%(PreprocessorDefinitions) cmd.exe /c "$(SolutionDir)\Utilities\git-version-gen.cmd" @@ -82,6 +82,8 @@ + + @@ -516,6 +518,8 @@ + + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 9f58492206..ee3c6e8de8 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1138,6 +1138,12 @@ Emu\GPU\RSX\Overlays\HomeMenu + + Emu\NP + + + Emu\NP + @@ -2290,6 +2296,12 @@ Emu\GPU\RSX\Overlays\HomeMenu + + Emu\NP + + + Emu\NP + diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index c990a14684..c87940b4cc 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -79,7 +79,7 @@ 4577;4467;%(DisableSpecificWarnings) $(IntDir) MaxSpeed - _WINDOWS;UNICODE;WIN32;WIN64;WIN32_LEAN_AND_MEAN;HAVE_VULKAN;HAVE_SDL2;WITH_DISCORD_RPC;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;NDEBUG;QT_WINEXTRAS_LIB;QT_CONCURRENT_LIB;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB;QT_SVG_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;WIN32;WIN64;WIN32_LEAN_AND_MEAN;HAVE_VULKAN;MINIUPNP_STATICLIB;HAVE_SDL2;WITH_DISCORD_RPC;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;NDEBUG;QT_WINEXTRAS_LIB;QT_CONCURRENT_LIB;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB;QT_SVG_LIB;%(PreprocessorDefinitions) false $(IntDir)vc$(PlatformToolsetVersion).pdb true @@ -88,7 +88,7 @@ Level3 - DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmain.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgets.lib;$(QTDIR)\lib\Qt5Gui.lib;$(QTDIR)\lib\Qt5Core.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5WinExtras.lib;Qt5Concurrent.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimedia.lib;Qt5MultimediaWidgets.lib;Qt5Svg.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;%(AdditionalDependencies) + DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmain.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgets.lib;$(QTDIR)\lib\Qt5Gui.lib;$(QTDIR)\lib\Qt5Core.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5WinExtras.lib;Qt5Concurrent.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimedia.lib;Qt5MultimediaWidgets.lib;Qt5Svg.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;%(AdditionalDependencies) ..\3rdparty\OpenAL\libs\Win64;..\3rdparty\glslang\build\hlsl\Release;..\3rdparty\glslang\build\SPIRV\Release;..\3rdparty\glslang\build\OGLCompilersDLL\Release;..\3rdparty\glslang\build\glslang\OSDependent\Windows\Release;..\3rdparty\glslang\build\glslang\Release;..\3rdparty\SPIRV\build\source\Release;..\3rdparty\SPIRV\build\source\opt\Release;..\lib\$(CONFIGURATION)-$(PLATFORM);..\3rdparty\XAudio2Redist\libs;..\3rdparty\discord-rpc\lib;$(QTDIR)\lib;%(AdditionalLibraryDirectories);$(VULKAN_SDK)\Lib "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) true @@ -130,7 +130,7 @@ 4577;4467;%(DisableSpecificWarnings) $(IntDir) Disabled - _WINDOWS;UNICODE;WIN32;WIN64;WIN32_LEAN_AND_MEAN;HAVE_VULKAN;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;QT_WINEXTRAS_LIB;QT_CONCURRENT_LIB;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB;QT_SVG_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;WIN32;WIN64;WIN32_LEAN_AND_MEAN;HAVE_VULKAN;MINIUPNP_STATICLIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;QT_WINEXTRAS_LIB;QT_CONCURRENT_LIB;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB;QT_SVG_LIB;%(PreprocessorDefinitions) false true true @@ -139,7 +139,7 @@ $(IntDir)vc$(PlatformToolsetVersion).pdb - DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;ksuser.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;OGLCompilerd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmaind.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgetsd.lib;$(QTDIR)\lib\Qt5Guid.lib;$(QTDIR)\lib\Qt5Cored.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5WinExtrasd.lib;Qt5Concurrentd.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimediad.lib;Qt5MultimediaWidgetsd.lib;Qt5Svgd.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;%(AdditionalDependencies) + DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;ksuser.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;OGLCompilerd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmaind.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgetsd.lib;$(QTDIR)\lib\Qt5Guid.lib;$(QTDIR)\lib\Qt5Cored.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5WinExtrasd.lib;Qt5Concurrentd.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimediad.lib;Qt5MultimediaWidgetsd.lib;Qt5Svgd.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;%(AdditionalDependencies) ..\3rdparty\OpenAL\libs\Win64;..\3rdparty\glslang\build\hlsl\Debug;..\3rdparty\glslang\build\SPIRV\Debug;..\3rdparty\glslang\build\OGLCompilersDLL\Debug;..\3rdparty\glslang\build\glslang\OSDependent\Windows\Debug;..\3rdparty\glslang\build\glslang\Debug;..\3rdparty\SPIRV\build\source\opt\Debug;..\3rdparty\XAudio2Redist\libs;..\3rdparty\discord-rpc\lib;..\lib\$(CONFIGURATION)-$(PLATFORM);$(QTDIR)\lib;%(AdditionalLibraryDirectories);$(VULKAN_SDK)\Lib "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" /VERBOSE %(AdditionalOptions) true diff --git a/rpcs3/rpcs3qt/downloader.cpp b/rpcs3/rpcs3qt/downloader.cpp index a4d19a5155..e9f4af0996 100644 --- a/rpcs3/rpcs3qt/downloader.cpp +++ b/rpcs3/rpcs3qt/downloader.cpp @@ -173,7 +173,7 @@ usz downloader::update_buffer(char* data, usz size) if (m_actual_download_size < 0) { - if (curl_easy_getinfo(m_curl->get_curl(), CURLINFO_CONTENT_LENGTH_DOWNLOAD, &m_actual_download_size) == CURLE_OK && m_actual_download_size > 0) + if (curl_easy_getinfo(m_curl->get_curl(), CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &m_actual_download_size) == CURLE_OK && m_actual_download_size > 0) { max = static_cast(m_actual_download_size); } diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index 3ef5bfc674..5806dfa8a1 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -173,6 +173,7 @@ enum class emu_settings_type IpSwapList, PSNStatus, BindAddress, + EnableUpnp, // System LicenseArea, @@ -353,6 +354,7 @@ inline static const QMap settings_location = { emu_settings_type::IpSwapList, { "Net", "IP swap list"}}, { emu_settings_type::PSNStatus, { "Net", "PSN status"}}, { emu_settings_type::BindAddress, { "Net", "Bind address"}}, + { emu_settings_type::EnableUpnp, { "Net", "UPNP Enabled"}}, // System { emu_settings_type::LicenseArea, { "System", "License Area"}}, diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index ccfd443f52..f3f89f68bd 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1304,6 +1304,9 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceLineEdit(ui->edit_swaps, emu_settings_type::IpSwapList); SubscribeTooltip(ui->gb_edit_swaps, tooltips.settings.dns_swap); + m_emu_settings->EnhanceCheckBox(ui->enable_upnp, emu_settings_type::EnableUpnp); + SubscribeTooltip(ui->enable_upnp, tooltips.settings.enable_upnp); + // Comboboxes m_emu_settings->EnhanceComboBox(ui->netStatusBox, emu_settings_type::InternetStatus); @@ -1312,8 +1315,10 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std connect(ui->netStatusBox, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { ui->edit_dns->setEnabled(index > 0); + ui->enable_upnp->setEnabled(index > 0); }); ui->edit_dns->setEnabled(ui->netStatusBox->currentIndex() > 0); + ui->enable_upnp->setEnabled(ui->netStatusBox->currentIndex() > 0); m_emu_settings->EnhanceComboBox(ui->psnStatusBox, emu_settings_type::PSNStatus); SubscribeTooltip(ui->gb_psnStatusBox, tooltips.settings.psn_status); diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index 83feb3757e..a4c7fa4ff5 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -2077,7 +2077,7 @@ Network Configuration - + @@ -2147,6 +2147,13 @@ + + + + Enable UPNP + + + diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 62d048f5eb..497803e138 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -230,6 +230,7 @@ public: const QString dns = tr("DNS used to resolve hostnames by applications."); const QString dns_swap = tr("DNS Swap List."); const QString bind = tr("Interface IP Address to bind to."); + const QString enable_upnp = tr("Enable UPNP.\nThis will automatically forward ports bound on 0.0.0.0 if your router has UPNP enabled."); // system