From 190822c2b28d75f23c8447b2f81dc0589da37765 Mon Sep 17 00:00:00 2001 From: RipleyTom Date: Thu, 27 Aug 2020 21:47:04 +0200 Subject: [PATCH] RPCN Client (#8663) --- .gitmodules | 5 +- 3rdparty/CMakeLists.txt | 9 +- 3rdparty/flatbuffers | 1 + 3rdparty/wolfssl | 2 +- Utilities/Config.h | 2 +- rpcs3/CMakeLists.txt | 2 +- rpcs3/Emu/CMakeLists.txt | 8 +- rpcs3/Emu/Cell/Modules/cellNetCtl.cpp | 10 +- rpcs3/Emu/Cell/Modules/cellSysutil.cpp | 4 +- rpcs3/Emu/Cell/Modules/cellSysutilAvc2.cpp | 43 +- rpcs3/Emu/Cell/Modules/cellSysutilAvc2.h | 12 + rpcs3/Emu/Cell/Modules/sceNp.cpp | 106 +- rpcs3/Emu/Cell/Modules/sceNp2.cpp | 199 +- rpcs3/Emu/Cell/Modules/sceNp2.h | 12 +- rpcs3/Emu/Cell/Modules/sceNpCommerce2.cpp | 61 +- rpcs3/Emu/Cell/Modules/sceNpCommerce2.h | 102 +- rpcs3/Emu/Cell/lv2/sys_net.cpp | 1286 +++++++- rpcs3/Emu/Cell/lv2/sys_net.h | 64 + rpcs3/Emu/NP/fb_helpers.cpp | 406 +++ rpcs3/Emu/NP/generated/np2_structs.fbs | 225 ++ .../Emu/NP/generated/np2_structs_generated.h | 2779 +++++++++++++++++ rpcs3/Emu/NP/np_handler.cpp | 1022 +++++- rpcs3/Emu/NP/np_handler.h | 244 +- rpcs3/Emu/NP/np_structs_extra.cpp | 212 ++ rpcs3/Emu/NP/np_structs_extra.h | 23 + rpcs3/Emu/NP/rpcn_client.cpp | 1125 +++++++ rpcs3/Emu/NP/rpcn_client.h | 253 ++ rpcs3/Emu/NP/rpcn_config.cpp | 110 + rpcs3/Emu/NP/rpcn_config.h | 32 + rpcs3/Emu/system_config.h | 1 - rpcs3/Emu/system_config_types.cpp | 1 + rpcs3/Emu/system_config_types.h | 1 + rpcs3/emucore.vcxproj | 10 +- rpcs3/emucore.vcxproj.filters | 24 + rpcs3/rpcs3.vcxproj | 45 +- rpcs3/rpcs3.vcxproj.filters | 18 + rpcs3/rpcs3qt/CMakeLists.txt | 1 + rpcs3/rpcs3qt/emu_settings_type.h | 2 - rpcs3/rpcs3qt/main_window.cpp | 7 + rpcs3/rpcs3qt/main_window.ui | 11 +- rpcs3/rpcs3qt/rpcn_settings_dialog.cpp | 229 ++ rpcs3/rpcs3qt/rpcn_settings_dialog.h | 20 + rpcs3/rpcs3qt/settings_dialog.cpp | 25 - rpcs3/rpcs3qt/settings_dialog.ui | 33 - rpcs3/rpcs3qt/tooltips.h | 9 +- 45 files changed, 8365 insertions(+), 431 deletions(-) create mode 160000 3rdparty/flatbuffers create mode 100644 rpcs3/Emu/NP/fb_helpers.cpp create mode 100644 rpcs3/Emu/NP/generated/np2_structs.fbs create mode 100644 rpcs3/Emu/NP/generated/np2_structs_generated.h create mode 100644 rpcs3/Emu/NP/np_structs_extra.cpp create mode 100644 rpcs3/Emu/NP/np_structs_extra.h create mode 100644 rpcs3/Emu/NP/rpcn_client.cpp create mode 100644 rpcs3/Emu/NP/rpcn_client.h create mode 100644 rpcs3/Emu/NP/rpcn_config.cpp create mode 100644 rpcs3/Emu/NP/rpcn_config.h create mode 100644 rpcs3/rpcs3qt/rpcn_settings_dialog.cpp create mode 100644 rpcs3/rpcs3qt/rpcn_settings_dialog.h diff --git a/.gitmodules b/.gitmodules index 26df596f1d..54bb75f3ef 100644 --- a/.gitmodules +++ b/.gitmodules @@ -71,4 +71,7 @@ path = 3rdparty/wolfssl url = https://github.com/RipleyTom/wolfssl.git ignore = dirty - +[submodule "3rdparty/flatbuffers"] + path = 3rdparty/flatbuffers + url = https://github.com/google/flatbuffers.git + ignore = dirty diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 8dfc95e688..504366a459 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -74,6 +74,9 @@ else() add_library(3rdparty_7z INTERFACE) endif() +add_library(3rdparty_flatbuffers INTERFACE) +target_include_directories(3rdparty_flatbuffers INTERFACE flatbuffers/include) + # libPNG # Select the version of libpng to use, default is builtin if (NOT USE_SYSTEM_LIBPNG) @@ -413,16 +416,17 @@ endif() # LLVM include(llvm.cmake) +# WOLFSSL +add_subdirectory(wolfssl EXCLUDE_FROM_ALL) + # CURL if(USE_SYSTEM_CURL) message("-- RPCS3: using shared libcurl") find_package(CURL REQUIRED) - add_library(wolfssl-3-static INTERFACE) add_library(libcurl INTERFACE) target_link_libraries(libcurl INTERFACE CURL::libcurl) else() message("-- RPCS3: building libcurl + wolfssl submodules") - add_subdirectory(wolfssl EXCLUDE_FROM_ALL) add_subdirectory(curl EXCLUDE_FROM_ALL) endif() @@ -430,6 +434,7 @@ endif() add_library(3rdparty::libusb ALIAS usb-1.0-static) add_library(3rdparty::zlib ALIAS 3rdparty_zlib) add_library(3rdparty::7z ALIAS 3rdparty_7z) +add_library(3rdparty::flatbuffers ALIAS 3rdparty_flatbuffers) add_library(3rdparty::pugixml ALIAS pugixml) add_library(3rdparty::yaml-cpp ALIAS yaml-cpp) add_library(3rdparty::xxhash ALIAS xxhash) diff --git a/3rdparty/flatbuffers b/3rdparty/flatbuffers new file mode 160000 index 0000000000..9e7e8cbe9f --- /dev/null +++ b/3rdparty/flatbuffers @@ -0,0 +1 @@ +Subproject commit 9e7e8cbe9f675123dd41b7c62868acad39188cae diff --git a/3rdparty/wolfssl b/3rdparty/wolfssl index f7130a4e43..d0749c6549 160000 --- a/3rdparty/wolfssl +++ b/3rdparty/wolfssl @@ -1 +1 @@ -Subproject commit f7130a4e43170ac2bb1046a65d10c01e6ec1d698 +Subproject commit d0749c65498672462b88fc8be5ea066cf65067f1 diff --git a/Utilities/Config.h b/Utilities/Config.h index 3c81685d20..81fbb630fa 100644 --- a/Utilities/Config.h +++ b/Utilities/Config.h @@ -384,7 +384,7 @@ namespace cfg using uint64 = uint<0, UINT64_MAX>; // Simple string entry with mutex - class string final : public _base + class string : public _base { const std::string m_name; diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index 30360cee06..b772e39779 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -83,7 +83,7 @@ set_target_properties(rpcs3 AUTOUIC ON) target_link_libraries(rpcs3 rpcs3_emu rpcs3_ui) -target_link_libraries(rpcs3 3rdparty::discord-rpc 3rdparty::qt5 3rdparty::hidapi 3rdparty::libusb) +target_link_libraries(rpcs3 3rdparty::discord-rpc 3rdparty::qt5 3rdparty::hidapi 3rdparty::libusb 3rdparty::wolfssl) target_link_libraries(rpcs3 ${ADDITIONAL_LIBS}) # Win resource file diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index c022b509a7..023cd854f8 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -12,7 +12,7 @@ target_link_libraries(rpcs3_emu PRIVATE 3rdparty::zlib 3rdparty::yaml-cpp PUBLIC - 3rdparty::libevdev) + 3rdparty::libevdev 3rdparty::flatbuffers) find_package(Threads REQUIRED) @@ -362,7 +362,11 @@ target_sources(rpcs3_emu PRIVATE # Np target_sources(rpcs3_emu PRIVATE + NP/fb_helpers.cpp NP/np_handler.cpp + NP/np_structs_extra.cpp + NP/rpcn_client.cpp + NP/rpcn_config.cpp ) # Memory @@ -452,7 +456,7 @@ target_link_libraries(rpcs3_emu 3rdparty::ffmpeg 3rdparty::cereal 3rdparty::opengl 3rdparty::stblib 3rdparty::vulkan 3rdparty::glew - 3rdparty::libusb + 3rdparty::libusb 3rdparty::wolfssl PRIVATE 3rdparty::span 3rdparty::xxhash ) diff --git a/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp b/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp index a52d8ccb04..e3274c7738 100644 --- a/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp +++ b/rpcs3/Emu/Cell/Modules/cellNetCtl.cpp @@ -177,7 +177,7 @@ error_code cellNetCtlDelHandler(s32 hid) error_code cellNetCtlGetInfo(s32 code, vm::ptr info) { - cellNetCtl.todo("cellNetCtlGetInfo(code=0x%x (%s), info=*0x%x)", code, InfoCodeToName(code), info); + cellNetCtl.warning("cellNetCtlGetInfo(code=0x%x (%s), info=*0x%x)", code, InfoCodeToName(code), info); const auto nph = g_fxo->get>(); @@ -213,7 +213,7 @@ error_code cellNetCtlGetInfo(s32 code, vm::ptr info) } else if (code == CELL_NET_CTL_INFO_IP_ADDRESS) { - strcpy_trunc(info->ip_address, nph->get_ip()); + strcpy_trunc(info->ip_address, np_handler::ip_to_string(nph->get_local_ip_addr())); } else if (code == CELL_NET_CTL_INFO_NETMASK) { @@ -306,7 +306,6 @@ error_code cellNetCtlNetStartDialogUnloadAsync(vm::ptrresult = nph->get_net_status() == CELL_NET_CTL_STATE_IPObtained ? 0 : CELL_NET_CTL_ERROR_DIALOG_CANCELED; sysutil_send_system_cmd(CELL_SYSUTIL_NET_CTL_NETSTART_UNLOADED, 0); - return CELL_OK; } @@ -331,6 +330,10 @@ error_code cellNetCtlGetNatInfo(vm::ptr natInfo) return CELL_NET_CTL_ERROR_INVALID_SIZE; } + 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; + return CELL_OK; } @@ -467,7 +470,6 @@ error_code cellGameUpdateCheckStartWithoutDialogAsyncEx(vm::ptr status) { - cellSysutil.warning("cellSysutilGetBgmPlaybackStatus(status=*0x%x)", status); + cellSysutil.trace("cellSysutilGetBgmPlaybackStatus(status=*0x%x)", status); // TODO status->playerState = CELL_SYSUTIL_BGMPLAYBACK_STATUS_STOP; @@ -473,7 +473,7 @@ error_code cellSysutilGetBgmPlaybackStatus(vm::ptr error_code cellSysutilGetBgmPlaybackStatus2(vm::ptr status2) { - cellSysutil.warning("cellSysutilGetBgmPlaybackStatus2(status2=*0x%x)", status2); + cellSysutil.trace("cellSysutilGetBgmPlaybackStatus2(status2=*0x%x)", status2); // TODO status2->playerState = CELL_SYSUTIL_BGMPLAYBACK_STATUS_STOP; diff --git a/rpcs3/Emu/Cell/Modules/cellSysutilAvc2.cpp b/rpcs3/Emu/Cell/Modules/cellSysutilAvc2.cpp index 6d63c04c3a..421dc9592d 100644 --- a/rpcs3/Emu/Cell/Modules/cellSysutilAvc2.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSysutilAvc2.cpp @@ -4,6 +4,7 @@ #include "sceNp.h" #include "sceNp2.h" #include "cellSysutilAvc2.h" +#include "cellSysutil.h" LOG_CHANNEL(cellSysutilAvc2); @@ -34,9 +35,18 @@ void fmt_class_string::format(std::string& out, u64 arg) }); } +vm::ptr avc2_cb{}; +vm::ptr avc2_cb_arg{}; + error_code cellSysutilAvc2GetPlayerInfo(vm::cptr player_id, vm::ptr player_info) { cellSysutilAvc2.todo("cellSysutilAvc2GetPlayerInfo(player_id=*0x%x, player_info=*0x%x)", player_id, player_info); + + player_info->connected = 1; + player_info->joined = 1; + player_info->mic_attached = 0; + player_info->member_id = *player_id; + return CELL_OK; } @@ -117,7 +127,19 @@ error_code cellSysutilAvc2GetAttribute(vm::ptr attr) error_code cellSysutilAvc2LoadAsync(SceNpMatching2ContextId ctx_id, u32 container, vm::ptr callback_func, vm::ptr user_data, vm::cptr init_param) { - cellSysutilAvc2.todo("cellSysutilAvc2LoadAsync(ctx_id=0x%x, container=0x%x, callback_func=*0x%x, user_data=*0x%x, init_param=*0x%x)", ctx_id, container, callback_func, user_data, init_param); + cellSysutilAvc2.warning("cellSysutilAvc2LoadAsync(ctx_id=0x%x, container=0x%x, callback_func=*0x%x, user_data=*0x%x, init_param=*0x%x)", ctx_id, container, callback_func, user_data, init_param); + + avc2_cb = callback_func; + avc2_cb_arg = user_data; + + if (avc2_cb) + { + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + avc2_cb(cb_ppu, CELL_AVC2_EVENT_LOAD_SUCCEEDED, 0, avc2_cb_arg); + return 0; + }); + } + return CELL_OK; } @@ -204,7 +226,16 @@ error_code cellSysutilAvc2GetPlayerVoiceMuting(SceNpMatching2RoomMemberId member error_code cellSysutilAvc2JoinChatRequest(vm::cptr room_id) { - cellSysutilAvc2.todo("cellSysutilAvc2JoinChatRequest(room_id=*0x%x)", room_id); + cellSysutilAvc2.warning("cellSysutilAvc2JoinChatRequest(room_id=*0x%x)", room_id); + + if (avc2_cb) + { + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + avc2_cb(cb_ppu, CELL_AVC2_EVENT_JOIN_SUCCEEDED, 0, avc2_cb_arg); + return 0; + }); + } + return CELL_OK; } @@ -309,6 +340,7 @@ error_code cellSysutilAvc2CreateWindow(SceNpMatching2RoomMemberId member_id) error_code cellSysutilAvc2GetSpeakerMuting(vm::ptr muting) { cellSysutilAvc2.todo("cellSysutilAvc2GetSpeakerMuting(muting=*0x%x)", muting); + *muting = 1; return CELL_OK; } @@ -327,6 +359,13 @@ error_code cellSysutilAvc2SetWindowSize(SceNpMatching2RoomMemberId member_id, f3 error_code cellSysutilAvc2EnumPlayers(vm::ptr players_num, vm::ptr players_id) { cellSysutilAvc2.todo("cellSysutilAvc2EnumPlayers(players_num=*0x%x, players_id=*0x%x)", players_num, players_id); + + if (players_num) + *players_num = 1; + + if (players_id) + *players_id = 1; + return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/cellSysutilAvc2.h b/rpcs3/Emu/Cell/Modules/cellSysutilAvc2.h index 99ff01f191..911b45f19d 100644 --- a/rpcs3/Emu/Cell/Modules/cellSysutilAvc2.h +++ b/rpcs3/Emu/Cell/Modules/cellSysutilAvc2.h @@ -123,6 +123,18 @@ enum CELL_AVC2_MIC_STATUS_UNKNOWN = 3, }; +enum +{ + CELL_AVC2_EVENT_LOAD_SUCCEEDED = 0x00000001, + CELL_AVC2_EVENT_LOAD_FAILED = 0x00000002, + CELL_AVC2_EVENT_UNLOAD_SUCCEEDED = 0x00000003, + CELL_AVC2_EVENT_UNLOAD_FAILED = 0x00000004, + CELL_AVC2_EVENT_JOIN_SUCCEEDED = 0x00000005, + CELL_AVC2_EVENT_JOIN_FAILED = 0x00000006, + CELL_AVC2_EVENT_LEAVE_SUCCEEDED = 0x00000007, + CELL_AVC2_EVENT_LEAVE_FAILED = 0x00000008, +}; + typedef u32 CellSysutilAvc2AttributeId; typedef u32 CellSysutilAvc2WindowAttributeId; diff --git a/rpcs3/Emu/Cell/Modules/sceNp.cpp b/rpcs3/Emu/Cell/Modules/sceNp.cpp index 3ff4815339..a518d2f4f8 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp.cpp @@ -15,7 +15,7 @@ LOG_CHANNEL(sceNp); -template<> +template <> void fmt_class_string::format(std::string& out, u64 arg) { format_enum(out, arg, [](auto error) @@ -639,6 +639,9 @@ error_code sceNpBasicRegisterHandler(vm::cptr context, vm: return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT; } + nph->basic_handler = handler; + nph->basic_handler_arg = arg; + return CELL_OK; } @@ -1140,6 +1143,13 @@ error_code sceNpBasicAddPlayersHistoryAsync(vm::cptr npids, u32 count, return SCE_NP_BASIC_ERROR_EXCEEDS_MAX; } + auto req_id = nph->add_players_to_history(npids, count); + + if (reqId) + { + *reqId = req_id; + } + return CELL_OK; } @@ -1888,7 +1898,7 @@ error_code sceNpFriendlistAbortGui() error_code sceNpLookupInit() { - sceNp.todo("sceNpLookupInit()"); + sceNp.warning("sceNpLookupInit()"); const auto nph = g_fxo->get>(); @@ -1930,7 +1940,7 @@ error_code sceNpLookupTerm() error_code sceNpLookupCreateTitleCtx(vm::cptr communicationId, vm::cptr selfNpId) { - sceNp.todo("sceNpLookupCreateTitleCtx(communicationId=*0x%x, selfNpId=0x%x)", communicationId, selfNpId); + sceNp.warning("sceNpLookupCreateTitleCtx(communicationId=*0x%x, selfNpId=0x%x)", communicationId, selfNpId); const auto nph = g_fxo->get>(); @@ -1944,12 +1954,12 @@ error_code sceNpLookupCreateTitleCtx(vm::cptr communicatio return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT; } - return not_an_error(nph->create_lookup_context(communicationId)); + return not_an_error(nph->create_lookup_title_context(communicationId)); } error_code sceNpLookupDestroyTitleCtx(s32 titleCtxId) { - sceNp.todo("sceNpLookupDestroyTitleCtx(titleCtxId=%d)", titleCtxId); + sceNp.warning("sceNpLookupDestroyTitleCtx(titleCtxId=%d)", titleCtxId); const auto nph = g_fxo->get>(); @@ -1958,7 +1968,7 @@ error_code sceNpLookupDestroyTitleCtx(s32 titleCtxId) return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; } - if (!nph->destroy_lookup_context(titleCtxId)) + if (!nph->destroy_lookup_title_context(titleCtxId)) return SCE_NP_COMMUNITY_ERROR_INVALID_ID; return CELL_OK; @@ -1966,7 +1976,7 @@ error_code sceNpLookupDestroyTitleCtx(s32 titleCtxId) error_code sceNpLookupCreateTransactionCtx(s32 titleCtxId) { - sceNp.todo("sceNpLookupCreateTransactionCtx(titleCtxId=%d)", titleCtxId); + sceNp.warning("sceNpLookupCreateTransactionCtx(titleCtxId=%d)", titleCtxId); const auto nph = g_fxo->get>(); @@ -1980,12 +1990,12 @@ error_code sceNpLookupCreateTransactionCtx(s32 titleCtxId) return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID; } - return CELL_OK; + return not_an_error(nph->create_lookup_transaction_context(titleCtxId)); } error_code sceNpLookupDestroyTransactionCtx(s32 transId) { - sceNp.todo("sceNpLookupDestroyTransactionCtx(transId=%d)", transId); + sceNp.warning("sceNpLookupDestroyTransactionCtx(transId=%d)", transId); const auto nph = g_fxo->get>(); @@ -1994,6 +2004,11 @@ error_code sceNpLookupDestroyTransactionCtx(s32 transId) return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; } + if (!nph->destroy_lookup_transaction_context(transId)) + { + return SCE_NP_COMMUNITY_ERROR_INVALID_ID; + } + return CELL_OK; } @@ -2008,7 +2023,7 @@ error_code sceNpLookupSetTimeout(s32 ctxId, usecond_t timeout) return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; } - if (timeout > 10000000) // 10 seconds + if (timeout < 10000000) // 10 seconds { return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT; } @@ -2046,7 +2061,7 @@ error_code sceNpLookupWaitAsync(s32 transId, vm::ptr result) error_code sceNpLookupPollAsync(s32 transId, vm::ptr result) { - sceNp.todo("sceNpLookupPollAsync(transId=%d, result=%d)", transId, result); + sceNp.todo("sceNpLookupPollAsync(transId=%d, result=*0x%x)", transId, result); const auto nph = g_fxo->get>(); @@ -2055,6 +2070,8 @@ error_code sceNpLookupPollAsync(s32 transId, vm::ptr result) return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED; } + *result = 0; + return CELL_OK; } @@ -2394,6 +2411,8 @@ error_code sceNpLookupTitleSmallStorageAsync(s32 transId, vm::ptr data, u6 return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID; } + std::memset(data.get_ptr(), 0, maxSize); + return CELL_OK; } @@ -2413,6 +2432,9 @@ error_code sceNpManagerRegisterCallback(vm::ptr callback, return SCE_NP_ERROR_INVALID_ARGUMENT; } + nph->manager_cb = callback; + nph->manager_cb_arg = arg; + return CELL_OK; } @@ -2427,6 +2449,8 @@ error_code sceNpManagerUnregisterCallback() return SCE_NP_ERROR_NOT_INITIALIZED; } + nph->manager_cb.set(0); + return CELL_OK; } @@ -2438,7 +2462,7 @@ error_code sceNpManagerGetStatus(vm::ptr status) if (!nph->is_NP_init) { - return SCE_NP_ERROR_NOT_INITIALIZED; + //return SCE_NP_ERROR_NOT_INITIALIZED; } if (!status) @@ -2459,7 +2483,7 @@ error_code sceNpManagerGetNetworkTime(vm::ptr pTick) if (!nph->is_NP_init) { - return SCE_NP_ERROR_NOT_INITIALIZED; + //return SCE_NP_ERROR_NOT_INITIALIZED; } if (!pTick) @@ -2486,13 +2510,13 @@ error_code sceNpManagerGetNetworkTime(vm::ptr pTick) error_code sceNpManagerGetOnlineId(vm::ptr onlineId) { - sceNp.todo("sceNpManagerGetOnlineId(onlineId=*0x%x)", onlineId); + sceNp.warning("sceNpManagerGetOnlineId(onlineId=*0x%x)", onlineId); const auto nph = g_fxo->get>(); if (!nph->is_NP_init) { - return SCE_NP_ERROR_NOT_INITIALIZED; + //return SCE_NP_ERROR_NOT_INITIALIZED; } if (!onlineId) @@ -2517,14 +2541,14 @@ error_code sceNpManagerGetOnlineId(vm::ptr onlineId) error_code sceNpManagerGetNpId(ppu_thread& ppu, vm::ptr npId) { - sceNp.todo("sceNpManagerGetNpId(npId=*0x%x)", npId); + sceNp.warning("sceNpManagerGetNpId(npId=*0x%x)", npId); const auto nph = g_fxo->get>(); - if (!nph->is_NP_init) - { - return SCE_NP_ERROR_NOT_INITIALIZED; - } + // if (!nph->is_NP_init) + // { + // return SCE_NP_ERROR_NOT_INITIALIZED; + // } if (!npId) { @@ -4320,6 +4344,8 @@ error_code sceNpSignalingCreateCtx(vm::ptr npId, vm::ptrcreate_signaling_context(npId, handler, arg); + return CELL_OK; } @@ -4334,6 +4360,11 @@ error_code sceNpSignalingDestroyCtx(u32 ctx_id) return SCE_NP_SIGNALING_ERROR_NOT_INITIALIZED; } + if (!nph->destroy_signaling_context(ctx_id)) + { + return SCE_NP_SIGNALING_ERROR_CTX_NOT_FOUND; + } + return CELL_OK; } @@ -4529,6 +4560,15 @@ error_code sceNpSignalingGetLocalNetInfo(u32 ctx_id, vm::ptrlocal_addr = nph->get_local_ip_addr(); + info->mapped_addr = nph->get_public_ip_addr(); + + // Pure speculation below + info->nat_status = 0; + info->upnp_status = 0; + info->npport_status = 0; + info->npport = 3658; + return CELL_OK; } @@ -4607,24 +4647,24 @@ error_code sceNpUtilCmpNpId(vm::ptr id1, vm::ptr id2) } // Unknown what this constant means - if (id1->reserved[0] != 1 || id2->reserved[0] != 1) - { - return SCE_NP_UTIL_ERROR_INVALID_NP_ID; - } + // 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 (strncmp(id1->handle.data, id2->handle.data, 16))// || id1->unk1[0] != id2->unk1[0]) { return 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; - } - } + // 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 f9e3061723..abb4eafa7c 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp2.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp2.cpp @@ -311,7 +311,7 @@ error_code sceNpMatching2LeaveLobby( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -350,6 +350,8 @@ error_code sceNpMatching2GetWorldInfoList( return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + *assignedReqId = nph->get_world_list(ctxId, optParam, reqParam->serverId); + return CELL_OK; } @@ -379,7 +381,7 @@ error_code sceNpMatching2GetLobbyMemberDataInternalList(SceNpMatching2ContextId return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -404,13 +406,15 @@ error_code sceNpMatching2SearchRoom( return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + *assignedReqId = nph->search_room(ctxId, optParam, reqParam.get_ptr()); + return CELL_OK; } error_code sceNpMatching2SignalingGetConnectionStatus( SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId memberId, vm::ptr connStatus, vm::ptr peerAddr, vm::ptr peerPort) { - sceNp2.todo("sceNpMatching2SignalingGetConnectionStatus(ctxId=%d, roomId=%d, memberId=%d, connStatus=*0x%x, peerAddr=*0x%x, peerPort=*0x%x)", ctxId, roomId, memberId, connStatus, peerAddr, peerPort); + sceNp2.warning("sceNpMatching2SignalingGetConnectionStatus(ctxId=%d, roomId=%d, memberId=%d, connStatus=*0x%x, peerAddr=*0x%x, peerPort=*0x%x)", ctxId, roomId, memberId, connStatus, peerAddr, peerPort); const auto nph = g_fxo->get>(); @@ -419,6 +423,12 @@ error_code sceNpMatching2SignalingGetConnectionStatus( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } + const auto& infos = nph->get_peer_infos(ctxId, roomId, memberId); + + *connStatus = infos.connStatus; + (*peerAddr).np_s_addr = infos.addr; // infos.addr is already BE + *peerPort = std::bit_cast>(infos.port); // infos.port is already BE + return CELL_OK; } @@ -434,7 +444,7 @@ error_code sceNpMatching2SetUserInfo( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -468,7 +478,7 @@ error_code sceNpMatching2GetLobbyMemberDataInternal( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -478,7 +488,7 @@ error_code sceNpMatching2GetLobbyMemberDataInternal( error_code sceNpMatching2ContextStart(SceNpMatching2ContextId ctxId) { - sceNp2.todo("sceNpMatching2ContextStart(ctxId=%d)", ctxId); + sceNp2.warning("sceNpMatching2ContextStart(ctxId=%d)", ctxId); const auto nph = g_fxo->get>(); @@ -487,13 +497,26 @@ error_code sceNpMatching2ContextStart(SceNpMatching2ContextId ctxId) return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } + const auto ctx = nph->get_match2_context(ctxId); + if (!ctx) + return SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID; + + if (ctx->context_callback) + { + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + ctx->context_callback(cb_ppu, ctxId, SCE_NP_MATCHING2_CONTEXT_EVENT_Start, SCE_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ACTION, 0, ctx->context_callback_param); + return 0; + }); + } + + return CELL_OK; } error_code sceNpMatching2CreateServerContext( SceNpMatching2ContextId ctxId, vm::cptr reqParam, vm::cptr optParam, vm::ptr assignedReqId) { - sceNp2.todo("sceNpMatching2CreateServerContext(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId); + sceNp2.warning("sceNpMatching2CreateServerContext(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId); const auto nph = g_fxo->get>(); @@ -502,11 +525,13 @@ error_code sceNpMatching2CreateServerContext( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + *assignedReqId = nph->create_server_context(ctxId, optParam, reqParam->serverId); + return CELL_OK; } @@ -527,7 +552,7 @@ error_code sceNpMatching2GetMemoryInfo(vm::ptr memInfo error_code sceNpMatching2LeaveRoom( SceNpMatching2ContextId ctxId, vm::cptr reqParam, vm::cptr optParam, vm::ptr assignedReqId) { - sceNp2.todo("sceNpMatching2LeaveRoom(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId); + sceNp2.warning("sceNpMatching2LeaveRoom(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId); const auto nph = g_fxo->get>(); @@ -541,6 +566,8 @@ error_code sceNpMatching2LeaveRoom( return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + *assignedReqId = nph->leave_room(ctxId, optParam, reqParam.get_ptr()); + return CELL_OK; } @@ -561,6 +588,8 @@ error_code sceNpMatching2SetRoomDataExternal( return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + *assignedReqId = nph->set_roomdata_external(ctxId, optParam, reqParam.get_ptr()); + return CELL_OK; } @@ -576,6 +605,37 @@ error_code sceNpMatching2SignalingGetConnectionInfo( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } + switch (code) + { + case 1: + { + connInfo->rtt = 20000; // HACK + break; + } + case 2: + { + connInfo->bandwidth = 10'000'000; // 10 MBPS HACK + break; + } + case 5: + { + const auto& infos = nph->get_peer_infos(ctxId, roomId, memberId); + connInfo->address.port = infos.port; + connInfo->address.addr.np_s_addr = infos.addr; + break; + } + case 6: + { + connInfo->packet_loss = 1; // HACK + break; + } + default: + { + sceNp2.fatal("sceNpMatching2SignalingGetConnectionInfo Unimplemented code: %d", code); + return CELL_OK; + } + } + return CELL_OK; } @@ -591,11 +651,13 @@ error_code sceNpMatching2SendRoomMessage( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + *assignedReqId = nph->send_room_message(ctxId, optParam, reqParam.get_ptr()); + return CELL_OK; } @@ -611,7 +673,7 @@ error_code sceNpMatching2JoinLobby( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -631,7 +693,7 @@ error_code sceNpMatching2GetRoomMemberDataExternalList(SceNpMatching2ContextId c return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -670,6 +732,8 @@ error_code sceNpMatching2GetServerInfo( return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + *assignedReqId = nph->get_server_status(ctxId, optParam, reqParam->serverId); + return CELL_OK; } @@ -689,7 +753,7 @@ error_code sceNpMatching2GetEventData(SceNpMatching2ContextId ctxId, SceNpMatchi return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } - return CELL_OK; + return not_an_error(nph->get_match2_event(eventKey, static_cast(buf.get_ptr()), bufLen)); } error_code sceNpMatching2GetRoomSlotInfoLocal(SceNpMatching2ContextId ctxId, const SceNpMatching2RoomId roomId, vm::ptr roomSlotInfo) @@ -718,7 +782,7 @@ error_code sceNpMatching2SendLobbyChatMessage( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -771,6 +835,8 @@ error_code sceNpMatching2JoinRoom( return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + *assignedReqId = nph->join_room(ctxId, optParam, reqParam.get_ptr()); + return CELL_OK; } @@ -816,7 +882,7 @@ error_code sceNpMatching2KickoutRoomMember( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -826,7 +892,7 @@ error_code sceNpMatching2KickoutRoomMember( error_code sceNpMatching2ContextStartAsync(SceNpMatching2ContextId ctxId, u32 timeout) { - sceNp2.todo("sceNpMatching2ContextStartAsync(ctxId=%d, timeout=%d)", ctxId, timeout); + sceNp2.warning("sceNpMatching2ContextStartAsync(ctxId=%d, timeout=%d)", ctxId, timeout); const auto nph = g_fxo->get>(); @@ -835,6 +901,18 @@ error_code sceNpMatching2ContextStartAsync(SceNpMatching2ContextId ctxId, u32 ti return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } + const auto ctx = nph->get_match2_context(ctxId); + if (!ctx) + return SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID; + + if (ctx->context_callback) + { + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + ctx->context_callback(cb_ppu, ctxId, SCE_NP_MATCHING2_CONTEXT_EVENT_Start, SCE_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ACTION, 0, ctx->context_callback_param); + return 0; + }); + } + return CELL_OK; } @@ -850,7 +928,7 @@ error_code sceNpMatching2SetSignalingOptParam( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -860,7 +938,7 @@ error_code sceNpMatching2SetSignalingOptParam( error_code sceNpMatching2RegisterContextCallback(SceNpMatching2ContextId ctxId, vm::ptr cbFunc, vm::ptr cbFuncArg) { - sceNp2.todo("sceNpMatching2RegisterContextCallback(ctxId=%d, cbFunc=*0x%x, cbFuncArg=*0x%x)", ctxId, cbFunc, cbFuncArg); + sceNp2.warning("sceNpMatching2RegisterContextCallback(ctxId=%d, cbFunc=*0x%x, cbFuncArg=*0x%x)", ctxId, cbFunc, cbFuncArg); const auto nph = g_fxo->get>(); @@ -869,6 +947,13 @@ error_code sceNpMatching2RegisterContextCallback(SceNpMatching2ContextId ctxId, return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } + const auto ctx = nph->get_match2_context(ctxId); + if (!ctx) + return SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID; + + ctx->context_callback = cbFunc; + ctx->context_callback_param = cbFuncArg; + return CELL_OK; } @@ -884,7 +969,7 @@ error_code sceNpMatching2SendRoomChatMessage( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -909,6 +994,8 @@ error_code sceNpMatching2SetRoomDataInternal( return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + *assignedReqId = nph->set_roomdata_internal(ctxId, optParam, reqParam.get_ptr()); + return CELL_OK; } @@ -929,6 +1016,8 @@ error_code sceNpMatching2GetRoomDataInternal( return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + *assignedReqId = nph->get_roomdata_internal(ctxId, optParam, reqParam.get_ptr()); + return CELL_OK; } @@ -949,6 +1038,8 @@ error_code sceNpMatching2SignalingGetPingInfo( return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + *assignedReqId = nph->get_ping_info(ctxId, optParam, reqParam.get_ptr()); + return CELL_OK; } @@ -963,7 +1054,16 @@ error_code sceNpMatching2GetServerIdListLocal(SceNpMatching2ContextId ctxId, vm: return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - return not_an_error(0); // Number of servers + const auto slist = nph->get_match2_server_list(ctxId); + + u32 num_servs = std::min(static_cast(slist.size()), serverIdNum); + + for (u32 i = 0; i < num_servs; i++) + { + serverId[i] = slist[i]; + } + + return not_an_error(static_cast(num_servs)); } error_code sceNpUtilBuildCdnUrl(vm::cptr url, vm::ptr buf, u64 bufSize, vm::ptr required, vm::ptr option) @@ -996,7 +1096,7 @@ error_code sceNpMatching2GrantRoomOwner( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -1021,6 +1121,8 @@ error_code sceNpMatching2CreateContext( return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + *ctxId = nph->create_match2_context(commId, passPhrase); + return CELL_OK; } @@ -1049,6 +1151,10 @@ error_code sceNpMatching2RegisterSignalingCallback(SceNpMatching2ContextId ctxId return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } + nph->signal_event_cb = cbFunc; + nph->signal_event_cb_ctx = ctxId; + nph->signal_event_cb_arg = cbFuncArg; + return CELL_OK; } @@ -1078,7 +1184,7 @@ error_code sceNpMatching2GetUserInfoList( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -1098,7 +1204,7 @@ error_code sceNpMatching2GetRoomMemberDataInternal( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -1118,7 +1224,7 @@ error_code sceNpMatching2SetRoomMemberDataInternal( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -1138,7 +1244,7 @@ error_code sceNpMatching2JoinProhibitiveRoom( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -1172,7 +1278,7 @@ error_code sceNpMatching2DeleteServerContext( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -1182,7 +1288,7 @@ error_code sceNpMatching2DeleteServerContext( error_code sceNpMatching2SetDefaultRequestOptParam(SceNpMatching2ContextId ctxId, vm::cptr optParam) { - sceNp2.todo("sceNpMatching2SetDefaultRequestOptParam(ctxId=%d, optParam=*0x%x)", ctxId, optParam); + sceNp2.warning("sceNpMatching2SetDefaultRequestOptParam(ctxId=%d, optParam=*0x%x)", ctxId, optParam); const auto nph = g_fxo->get>(); @@ -1196,6 +1302,12 @@ error_code sceNpMatching2SetDefaultRequestOptParam(SceNpMatching2ContextId ctxId return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + const auto ctx = nph->get_match2_context(ctxId); + if (!ctx) + return SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID; + + memcpy(&ctx->default_match2_optparam, optParam.get_ptr(), sizeof(SceNpMatching2RequestOptParam)); + return CELL_OK; } @@ -1210,6 +1322,10 @@ error_code sceNpMatching2RegisterRoomEventCallback(SceNpMatching2ContextId ctxId return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } + nph->room_event_cb = cbFunc; + nph->room_event_cb_ctx = ctxId; + nph->room_event_cb_arg = cbFuncArg; + return CELL_OK; } @@ -1239,7 +1355,7 @@ error_code sceNpMatching2GetRoomDataExternalList( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -1264,6 +1380,8 @@ error_code sceNpMatching2CreateJoinRoom( return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } + *assignedReqId = nph->create_join_room(ctxId, optParam, reqParam.get_ptr()); + return CELL_OK; } @@ -1293,7 +1411,7 @@ error_code sceNpMatching2GetLobbyInfoList( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -1328,7 +1446,7 @@ error_code sceNpMatching2SendLobbyInvitation( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -1347,6 +1465,21 @@ error_code sceNpMatching2ContextStop(SceNpMatching2ContextId ctxId) return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } + const auto ctx = nph->get_match2_context(ctxId); + + if (!ctx) + { + return SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID; + } + + if (ctx->context_callback) + { + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + ctx->context_callback(cb_ppu, ctxId, SCE_NP_MATCHING2_CONTEXT_EVENT_Stop, SCE_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ACTION, 0, ctx->context_callback_param); + return 0; + }); + } + return CELL_OK; } @@ -1362,7 +1495,7 @@ error_code sceNpMatching2SetLobbyMemberDataInternal( return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } - if (!reqParam || !optParam || !assignedReqId) + if (!reqParam || !assignedReqId) { return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT; } @@ -1381,6 +1514,10 @@ error_code sceNpMatching2RegisterRoomMessageCallback(SceNpMatching2ContextId ctx return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED; } + nph->room_msg_cb = cbFunc; + nph->room_msg_cb_ctx = ctxId; + nph->room_msg_cb_arg = cbFuncArg; + return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/sceNp2.h b/rpcs3/Emu/Cell/Modules/sceNp2.h index 87009370fd..4f0eb26473 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp2.h +++ b/rpcs3/Emu/Cell/Modules/sceNp2.h @@ -850,7 +850,7 @@ union SceNpMatching2RoomMessageDestination { vm::bptr memberId; be_t memberIdNum; - }; + } multicastTarget; SceNpMatching2TeamId multicastTargetTeamId; }; @@ -1117,7 +1117,7 @@ struct SceNpMatching2SendRoomMessageRequest SceNpMatching2CastType castType; u8 padding[3]; SceNpMatching2RoomMessageDestination dst; - vm::cptr msg; + vm::bcptr msg; be_t msgLen; be_t option; }; @@ -1129,7 +1129,7 @@ struct SceNpMatching2SendRoomChatMessageRequest SceNpMatching2CastType castType; u8 padding[3]; SceNpMatching2RoomMessageDestination dst; - vm::cptr msg; + vm::bcptr msg; be_t msgLen; be_t option; }; @@ -1255,7 +1255,7 @@ struct SceNpMatching2SendLobbyChatMessageRequest SceNpMatching2CastType castType; u8 padding[3]; SceNpMatching2LobbyMessageDestination dst; - vm::cptr msg; + vm::bcptr msg; be_t msgLen; be_t option; }; @@ -1415,7 +1415,7 @@ struct SceNpMatching2RoomMessageInfo u8 padding[2]; vm::bptr dst; vm::bptr srcMember; - vm::cptr msg; + vm::bcptr msg; be_t msgLen; }; @@ -1458,7 +1458,7 @@ struct SceNpMatching2LobbyMessageInfo u8 padding[2]; vm::bptr dst; vm::bptr srcMember; - vm::cptr msg; + vm::bcptr msg; be_t msgLen; }; diff --git a/rpcs3/Emu/Cell/Modules/sceNpCommerce2.cpp b/rpcs3/Emu/Cell/Modules/sceNpCommerce2.cpp index 6997093904..1ffd1fabad 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpCommerce2.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNpCommerce2.cpp @@ -1,8 +1,12 @@ #include "stdafx.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/IdManager.h" #include "sceNpCommerce2.h" #include "sceNp.h" +#include "cellSysutil.h" + +#include "Emu/NP/np_handler.h" LOG_CHANNEL(sceNpCommerce2); @@ -91,19 +95,40 @@ error_code sceNpCommerce2Term() error_code sceNpCommerce2CreateCtx(u32 version, vm::cptr npId, vm::ptr handler, vm::ptr arg, vm::ptr ctx_id) { - sceNpCommerce2.todo("sceNpCommerce2CreateCtx(version=%d, npId=*0x%x, handler=*0x%x, arg=*0x%x, ctx_id=*0x%x)", version, npId, handler, arg, ctx_id); + sceNpCommerce2.warning("sceNpCommerce2CreateCtx(version=%d, npId=*0x%x, handler=*0x%x, arg=*0x%x, ctx_id=*0x%x)", version, npId, handler, arg, ctx_id); + const auto nph = g_fxo->get>(); + + *ctx_id = nph->create_commerce2_context(version, npId, handler, arg); + return CELL_OK; } -error_code sceNpCommerce2DestroyCtx(u32 ctx_id) +s32 sceNpCommerce2DestroyCtx(u32 ctx_id) { - sceNpCommerce2.todo("sceNpCommerce2DestroyCtx(ctx_id=%d)", ctx_id); + sceNpCommerce2.warning("sceNpCommerce2DestroyCtx(ctx_id=%d)", ctx_id); + const auto nph = g_fxo->get>(); + + nph->destroy_commerce2_context(ctx_id); + return CELL_OK; } -error_code sceNpCommerce2EmptyStoreCheckStart(u32 ctx_id, s32 store_check_type, vm::cptr target_id) +s32 sceNpCommerce2EmptyStoreCheckStart(u32 ctx_id, s32 store_check_type, vm::cptr target_id) { - sceNpCommerce2.todo("sceNpCommerce2EmptyStoreCheckStart(ctx_id=%d, store_check_type=%d, target_id=%s)", ctx_id, store_check_type, target_id); + sceNpCommerce2.warning("sceNpCommerce2EmptyStoreCheckStart(ctx_id=%d, store_check_type=%d, target_id=*0x%x(%s))", ctx_id, store_check_type, target_id, target_id); + + const auto nph = g_fxo->get>(); + + const auto ctx = nph->get_commerce2_context(ctx_id); + + if (ctx->context_callback) + { + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + ctx->context_callback(cb_ppu, ctx_id, 0, SCE_NP_COMMERCE2_EVENT_EMPTY_STORE_CHECK_DONE, 0, ctx->context_callback_param); + return 0; + }); + } + return CELL_OK; } @@ -113,15 +138,28 @@ error_code sceNpCommerce2EmptyStoreCheckAbort(u32 ctx_id) return CELL_OK; } -error_code sceNpCommerce2EmptyStoreCheckFinish(u32 ctx_id, vm::ptr is_empty) +s32 sceNpCommerce2EmptyStoreCheckFinish(u32 ctx_id, vm::ptr is_empty) { - sceNpCommerce2.todo("sceNpCommerce2EmptyStoreCheckFinish(ctx_id=%d, is_empty=*0x%x)", ctx_id, is_empty); + sceNpCommerce2.warning("sceNpCommerce2EmptyStoreCheckFinish(ctx_id=%d, is_empty=*0x%x)", ctx_id, is_empty); + *is_empty = SCE_NP_COMMERCE2_STORE_IS_NOT_EMPTY; return CELL_OK; } -error_code sceNpCommerce2CreateSessionStart(u32 ctx_id) +s32 sceNpCommerce2CreateSessionStart(u32 ctx_id) { - sceNpCommerce2.todo("sceNpCommerce2CreateSessionStart(ctx_id=%d)", ctx_id); + sceNpCommerce2.warning("sceNpCommerce2CreateSessionStart(ctx_id=%d)", ctx_id); + const auto nph = g_fxo->get>(); + + const auto ctx = nph->get_commerce2_context(ctx_id); + + if (ctx->context_callback) + { + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + ctx->context_callback(cb_ppu, ctx_id, 0, SCE_NP_COMMERCE2_EVENT_CREATE_SESSION_DONE, 0, ctx->context_callback_param); + return 0; + }); + } + return CELL_OK; } @@ -131,9 +169,10 @@ error_code sceNpCommerce2CreateSessionAbort(u32 ctx_id) return CELL_OK; } -error_code sceNpCommerce2CreateSessionFinish(u32 ctx_id, vm::ptr sessionInfo) +s32 sceNpCommerce2CreateSessionFinish(u32 ctx_id, vm::ptr sessionInfo) { - sceNpCommerce2.todo("sceNpCommerce2CreateSessionFinish(ctx_id=%d, sessionInfo=*0x%x)", ctx_id, sessionInfo); + sceNpCommerce2.warning("sceNpCommerce2CreateSessionFinish(ctx_id=%d, sessionInfo=*0x%x)", ctx_id, sessionInfo); + memset(sessionInfo.get_ptr(), 0, sizeof(sessionInfo)); return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/sceNpCommerce2.h b/rpcs3/Emu/Cell/Modules/sceNpCommerce2.h index c35fa15798..fb2d0f97b4 100644 --- a/rpcs3/Emu/Cell/Modules/sceNpCommerce2.h +++ b/rpcs3/Emu/Cell/Modules/sceNpCommerce2.h @@ -77,6 +77,10 @@ enum SCE_NP_COMMERCE2_EVENT_DO_PRODUCT_CODE_FINISHED = 0x0054, SCE_NP_COMMERCE2_EVENT_EMPTY_STORE_CHECK_DONE = 0x0061, SCE_NP_COMMERCE2_EVENT_EMPTY_STORE_CHECK_ABORT = 0x0062, + SCE_NP_COMMERCE2_EVENT_RESERVED01_STARTED = 0x0071, + SCE_NP_COMMERCE2_EVENT_RESERVED01_SUCCESS = 0x0072, + SCE_NP_COMMERCE2_EVENT_RESERVED01_BACK = 0x0073, + SCE_NP_COMMERCE2_EVENT_RESERVED01_FINISHED = 0x0074, }; // Category data type @@ -103,33 +107,81 @@ enum SceNpCommerce2GameSkuDataType SCE_NP_COMMERCE2_GAME_SKU_DATA_TYPE_MAX }; -// Constanc for commerce functions and structures +// Store stuff enum { - SCE_NP_COMMERCE2_CURRENCY_CODE_LEN = 3, - SCE_NP_COMMERCE2_CURRENCY_SYMBOL_LEN = 3, - SCE_NP_COMMERCE2_THOUSAND_SEPARATOR_LEN = 4, - SCE_NP_COMMERCE2_DECIMAL_LETTER_LEN = 4, - SCE_NP_COMMERCE2_SP_NAME_LEN = 256, - SCE_NP_COMMERCE2_CATEGORY_ID_LEN = 56, - SCE_NP_COMMERCE2_CATEGORY_NAME_LEN = 256, - SCE_NP_COMMERCE2_CATEGORY_DESCRIPTION_LEN = 1024, - SCE_NP_COMMERCE2_PRODUCT_ID_LEN = 48, - SCE_NP_COMMERCE2_PRODUCT_NAME_LEN = 256, - SCE_NP_COMMERCE2_PRODUCT_SHORT_DESCRIPTION_LEN = 1024, - SCE_NP_COMMERCE2_PRODUCT_LONG_DESCRIPTION_LEN = 4000, - SCE_NP_COMMERCE2_SKU_ID_LEN = 56, - SCE_NP_COMMERCE2_SKU_NAME_LEN = 180, - SCE_NP_COMMERCE2_URL_LEN = 256, - SCE_NP_COMMERCE2_RATING_SYSTEM_ID_LEN = 16, - SCE_NP_COMMERCE2_RATING_DESCRIPTION_LEN = 60, - SCE_NP_COMMERCE2_RECV_BUF_SIZE = 262144, - SCE_NP_COMMERCE2_PRODUCT_CODE_BLOCK_LEN = 4, - SCE_NP_COMMERCE2_GETCAT_MAX_COUNT = 60, - SCE_NP_COMMERCE2_DO_CHECKOUT_MEMORY_CONTAINER_SIZE = 10485760, - SCE_NP_COMMERCE2_DO_PROD_BROWSE_MEMORY_CONTAINER_SIZE = 16777216, - SCE_NP_COMMERCE2_DO_DL_LIST_MEMORY_CONTAINER_SIZE = 10485760, - SCE_NP_COMMERCE2_DO_PRODUCT_CODE_MEMORY_CONTAINER_SIZE = 16777216, + SCE_NP_COMMERCE2_STORE_IS_NOT_EMPTY = 0, + SCE_NP_COMMERCE2_STORE_IS_EMPTY = 1, + + SCE_NP_COMMERCE2_STORE_CHECK_TYPE_CATEGORY = 1, + + SCE_NP_COMMERCE2_STORE_BROWSE_TYPE_CATEGORY = 1, + SCE_NP_COMMERCE2_STORE_BROWSE_TYPE_PRODUCT = 2, + SCE_NP_COMMERCE2_STORE_BROWSE_TYPE_PRODUCT_CODE = 3, +}; + +// Content Stuff +enum +{ + SCE_NP_COMMERCE2_CONTENT_TYPE_CATEGORY = 1, + SCE_NP_COMMERCE2_CONTENT_TYPE_PRODUCT = 2, + + SCE_NP_COMMERCE2_CONTENT_RATING_DESC_TYPE_ICON = 1, + SCE_NP_COMMERCE2_CONTENT_RATING_DESC_TYPE_TEXT = 2, +}; + +// Game SKU +enum +{ + SCE_NP_COMMERCE2_SKU_CHECKOUT_MAX = 16, + SCE_NP_COMMERCE2_SKU_DL_LIST_MAX = 16, + + SCE_NP_COMMERCE2_SKU_PURCHASABILITY_FLAG_ON = 1, + SCE_NP_COMMERCE2_SKU_PURCHASABILITY_FLAG_OFF = 0, + + SCE_NP_COMMERCE2_SKU_ANN_PURCHASED_CANNOT_PURCHASE_AGAIN = 0x80000000, + SCE_NP_COMMERCE2_SKU_ANN_PURCHASED_CAN_PURCHASE_AGAIN = 0x40000000, + SCE_NP_COMMERCE2_SKU_ANN_IN_THE_CART = 0x20000000, + SCE_NP_COMMERCE2_SKU_ANN_CONTENTLINK_SKU = 0x10000000, + SCE_NP_COMMERCE2_SKU_ANN_CREDIT_CARD_REQUIRED = 0x08000000, + SCE_NP_COMMERCE2_SKU_ANN_CHARGE_IMMEDIATELY = 0x04000000, +}; + +// Constants for commerce functions and structures +enum +{ + SCE_NP_COMMERCE2_VERSION = 2, + SCE_NP_COMMERCE2_CTX_MAX = 1, + SCE_NP_COMMERCE2_REQ_MAX = 1, + SCE_NP_COMMERCE2_CURRENCY_CODE_LEN = 3, + SCE_NP_COMMERCE2_CURRENCY_SYMBOL_LEN = 3, + SCE_NP_COMMERCE2_THOUSAND_SEPARATOR_LEN = 4, + SCE_NP_COMMERCE2_DECIMAL_LETTER_LEN = 4, + SCE_NP_COMMERCE2_SP_NAME_LEN = 256, + SCE_NP_COMMERCE2_CATEGORY_ID_LEN = 56, + SCE_NP_COMMERCE2_CATEGORY_NAME_LEN = 256, + SCE_NP_COMMERCE2_CATEGORY_DESCRIPTION_LEN = 1024, + SCE_NP_COMMERCE2_PRODUCT_ID_LEN = 48, + SCE_NP_COMMERCE2_PRODUCT_NAME_LEN = 256, + SCE_NP_COMMERCE2_PRODUCT_SHORT_DESCRIPTION_LEN = 1024, + SCE_NP_COMMERCE2_PRODUCT_LONG_DESCRIPTION_LEN = 4000, + SCE_NP_COMMERCE2_SKU_ID_LEN = 56, + SCE_NP_COMMERCE2_SKU_NAME_LEN = 180, + SCE_NP_COMMERCE2_URL_LEN = 256, + SCE_NP_COMMERCE2_RATING_SYSTEM_ID_LEN = 16, + SCE_NP_COMMERCE2_RATING_DESCRIPTION_LEN = 60, + SCE_NP_COMMERCE2_RECV_BUF_SIZE = 262144, + SCE_NP_COMMERCE2_PRODUCT_CODE_BLOCK_LEN = 4, + SCE_NP_COMMERCE2_PRODUCT_CODE_INPUT_MODE_USER_INPUT = 0, + SCE_NP_COMMERCE2_PRODUCT_CODE_INPUT_MODE_CODE_SPECIFIED = 1, + SCE_NP_COMMERCE2_GETCAT_MAX_COUNT = 60, + SCE_NP_COMMERCE2_GETPRODLIST_MAX_COUNT = 60, + SCE_NP_COMMERCE2_DO_CHECKOUT_MEMORY_CONTAINER_SIZE = 10485760, + SCE_NP_COMMERCE2_DO_PROD_BROWSE_MEMORY_CONTAINER_SIZE = 16777216, + SCE_NP_COMMERCE2_DO_DL_LIST_MEMORY_CONTAINER_SIZE = 10485760, + SCE_NP_COMMERCE2_DO_PRODUCT_CODE_MEMORY_CONTAINER_SIZE = 16777216, + SCE_NP_COMMERCE2_SYM_POS_PRE = 0, + SCE_NP_COMMERCE2_SYM_POS_POST = 1, }; // Common structure used when receiving data diff --git a/rpcs3/Emu/Cell/lv2/sys_net.cpp b/rpcs3/Emu/Cell/lv2/sys_net.cpp index a9e42b3ad2..3f5b2f710b 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net.cpp @@ -25,6 +25,10 @@ #include "Emu/NP/np_handler.h" +#include +#include +#include + LOG_CHANNEL(sys_net); template<> @@ -248,12 +252,572 @@ static void network_clear_queue(ppu_thread& ppu) }); } +// Object in charge of retransmiting packets for STREAM_P2P sockets +class tcp_timeout_monitor +{ + public: + void add_message(s32 sock_id, const sockaddr_in *dst, std::vector data, u64 seq) + { + { + std::lock_guard lock(data_mutex); + + const auto now = std::chrono::system_clock::now(); + + message msg; + msg.dst_addr = *dst; + msg.sock_id = sock_id; + msg.data = std::move(data); + msg.seq = seq; + msg.initial_sendtime = now; + + rtt_info rtt = rtts[sock_id]; + + const auto expected_time = now + rtt.rtt_time; + + msgs.insert(std::make_pair(expected_time, std::move(msg))); + } + wakey.notify_one(); // TODO: Should be improved to only wake if new timeout < old timeout + } + + void confirm_data_received(s32 sock_id, u64 ack) + { + std::lock_guard lock(data_mutex); + rtts[sock_id].num_retries = 0; + // TODO: reduce RTT? + for (auto it = msgs.begin(); it != msgs.end();) + { + auto& msg = it->second; + if (msg.sock_id == sock_id && msg.seq < ack) + { + it = msgs.erase(it); + continue; + } + it++; + } + } + + void operator()() + { + while (thread_ctrl::state() != thread_state::aborting) + { + std::unique_lock lock(data_mutex); + if (msgs.size()) + wakey.wait_until(lock, msgs.begin()->first); + else + wakey.wait(lock); + + const auto now = std::chrono::system_clock::now(); + // Check for messages that haven't been acked + std::set rtt_increased; + for (auto it = msgs.begin(); it != msgs.end();) + { + if (it->first >= now) + { + // reply is late, increases rtt + auto& msg = it->second; + const auto addr = msg.dst_addr.sin_addr.s_addr; + rtt_info rtt = rtts[msg.sock_id]; + // Only increases rtt once per loop(in case a big number of packets are sent at once) + if (!rtt_increased.count(msg.sock_id)) + { + rtt.num_retries += 1; + // Increases current rtt by 10% + rtt.rtt_time *= 1.1; + rtts[addr] = rtt; + + rtt_increased.emplace(msg.sock_id); + } + + if (rtt.num_retries >= 10) + { + // Too many retries, need to notify the socket that the connection is dead + idm::check(msg.sock_id, [&](lv2_socket& sock) + { + sock.p2ps.status = lv2_socket::p2ps_i::stream_status::stream_closed; + }); + it = msgs.erase(it); + continue; + } + + // resend the message + const auto res = idm::check(msg.sock_id, [&](lv2_socket& sock) -> bool + { + if (sendto(sock.socket, reinterpret_cast(msg.data.data()), msg.data.size(), 0, reinterpret_cast(&msg.dst_addr), sizeof(msg.dst_addr)) == -1) + { + sock.p2ps.status = lv2_socket::p2ps_i::stream_status::stream_closed; + return false; + } + return true; + }); + + if (!res || !res.ret) + { + it = msgs.erase(it); + continue; + } + + // Update key timeout + msgs.insert(std::make_pair(now + rtt.rtt_time, std::move(msg))); + it = msgs.erase(it); + } + else + { + break; + } + } + } + } + + public: + std::condition_variable wakey; + static constexpr auto thread_name = "Tcp Over Udp Timeout Manager Thread"sv; + + private: + std::mutex data_mutex; + // List of outgoing messages + struct message + { + s32 sock_id; + ::sockaddr_in dst_addr; + std::vector data; + u64 seq; + std::chrono::time_point initial_sendtime; + }; + std::map, message> msgs; // (wakeup time, msg) + // List of rtts + struct rtt_info + { + unsigned long num_retries = 0; + std::chrono::milliseconds rtt_time = 50ms; + }; + std::unordered_map rtts; // (sock_id, rtt) +}; + +struct nt_p2p_port +{ + // Real socket where P2P packets are received/sent + lv2_socket::socket_type p2p_socket = 0; + u16 port; + + shared_mutex bound_p2p_vports_mutex; + // For DGRAM_P2P sockets(vport, sock_id) + std::map bound_p2p_vports{}; + // For STREAM_P2P sockets(key, sock_id) + // key is ( (src_vport) << 48 | (dst_vport) << 32 | addr ) with src_vport and addr being 0 for listening sockets + std::map bound_p2p_streams{}; + + // Queued messages from RPCN + shared_mutex s_rpcn_mutex; + std::queue> rpcn_msgs{}; + + std::array p2p_recv_data{}; + + std::mt19937 randgen; + + nt_p2p_port(u16 port) : port(port) + { + // Creates and bind P2P Socket + p2p_socket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + + if (p2p_socket == -1) + sys_net.fatal("Failed to create DGRAM socket for P2P socket!"); + +#ifdef _WIN32 + u_long _true = 1; + ::ioctlsocket(p2p_socket, FIONBIO, &_true); +#else + ::fcntl(p2p_socket, F_SETFL, ::fcntl(p2p_socket, F_GETFL, 0) | O_NONBLOCK); +#endif + + u32 optval = 131072; + if (setsockopt(p2p_socket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast(&optval), sizeof(optval)) != 0) + sys_net.fatal("Error setsockopt SO_RCVBUF on P2P socket"); + + ::sockaddr_in p2p_saddr{}; + p2p_saddr.sin_family = AF_INET; + p2p_saddr.sin_port = std::bit_cast>(port); // htons(port); + p2p_saddr.sin_addr.s_addr = 0; // binds to 0.0.0.0 + const auto ret_bind = ::bind(p2p_socket, reinterpret_cast(&p2p_saddr), sizeof(p2p_saddr)); + + if (ret_bind == -1) + sys_net.fatal("Failed to bind DGRAM socket to %d for P2P!", port); + + sys_net.notice("P2P port %d was bound!", port); + + // Initializes random generator + std::random_device rd; + randgen.seed(rd()); + } + + ~nt_p2p_port() + { + if (p2p_socket) + { +#ifdef _WIN32 + ::closesocket(p2p_socket); +#else + ::close(p2p_socket); +#endif + } + } + + static u16 tcp_checksum(const u16* buffer, std::size_t size) + { + u32 cksum = 0; + while (size > 1) + { + cksum += *buffer++; + size -= sizeof(u16); + } + if (size) + cksum += *reinterpret_cast(buffer); + + cksum = (cksum >> 16) + (cksum & 0xffff); + cksum += (cksum >> 16); + return static_cast(~cksum); + } + + static std::vector generate_u2s_packet(const lv2_socket::p2ps_i::encapsulated_tcp& header, const u8 *data, const u32 datasize) + { + const u32 packet_size = (sizeof(u16) + sizeof(lv2_socket::p2ps_i::encapsulated_tcp) + datasize); + ASSERT(packet_size < 65535); // packet size shouldn't be bigger than possible UDP payload + std::vector packet(packet_size); + u8 *packet_data = packet.data(); + + *reinterpret_cast *>(packet_data) = header.dst_port; + memcpy(packet_data+sizeof(u16), &header, sizeof(lv2_socket::p2ps_i::encapsulated_tcp)); + if(datasize) + memcpy(packet_data+sizeof(u16)+sizeof(lv2_socket::p2ps_i::encapsulated_tcp), data, datasize); + + return packet; + } + + static void send_u2s_packet(lv2_socket &sock, s32 sock_id, std::vector data, const ::sockaddr_in* dst, u32 seq = 0, bool require_ack = true) + { + if (sendto(sock.socket, reinterpret_cast(data.data()), data.size(), 0, reinterpret_cast(&dst), sizeof(sockaddr)) == -1) + { + sys_net.warning("Attempting to send a u2s packet failed, closing socket!"); + sock.p2ps.status = lv2_socket::p2ps_i::stream_status::stream_closed; + return; + } + + // Adds to tcp timeout monitor to resend the message until an ack is received + if (require_ack) + { + auto tcpm = g_fxo->get>(); + tcpm->add_message(sock_id, dst, std::move(data), seq); + } + } + + bool handle_connected(s32 sock_id, lv2_socket::p2ps_i::encapsulated_tcp* tcp_header, u8* data, ::sockaddr_storage* op_addr) + { + const auto sock = idm::check(sock_id, [&](lv2_socket& sock) -> bool + { + std::lock_guard lock(sock.mutex); + + if (sock.p2ps.status != lv2_socket::p2ps_i::stream_status::stream_connected && sock.p2ps.status != lv2_socket::p2ps_i::stream_status::stream_handshaking) + return false; + + if (tcp_header->flags == lv2_socket::p2ps_i::ACK) + { + auto tcpm = g_fxo->get>(); + tcpm->confirm_data_received(sock_id, tcp_header->ack); + } + + auto send_ack = [&]() + { + auto final_ack = sock.p2ps.data_beg_seq; + while (sock.p2ps.data_mapping.count(final_ack)) + { + final_ack += sock.p2ps.data_mapping[final_ack]; + } + sock.p2ps.data_available = final_ack - sock.p2ps.data_beg_seq; + + lv2_socket::p2ps_i::encapsulated_tcp send_hdr; + send_hdr.src_port = tcp_header->dst_port; + send_hdr.dst_port = tcp_header->src_port; + send_hdr.flags = lv2_socket::p2ps_i::ACK; + send_hdr.ack = final_ack; + send_hdr.length = 0; + send_hdr.checksum = nt_p2p_port::tcp_checksum(reinterpret_cast(&send_hdr), sizeof(lv2_socket::p2ps_i::encapsulated_tcp)); + auto packet = generate_u2s_packet(send_hdr, nullptr, 0); + send_u2s_packet(sock, sock_id, std::move(packet), reinterpret_cast<::sockaddr_in*>(op_addr), 0, false); + }; + + if (tcp_header->seq < sock.p2ps.data_beg_seq) + { + // Data has already been processed + if (tcp_header->flags != lv2_socket::p2ps_i::ACK && tcp_header->flags != lv2_socket::p2ps_i::RST) + send_ack(); + return true; + } + + if (sock.p2ps.status == lv2_socket::p2ps_i::stream_status::stream_handshaking) + { + // Only expect SYN|ACK + if (tcp_header->flags == (lv2_socket::p2ps_i::SYN | lv2_socket::p2ps_i::ACK)) + { + sock.p2ps.data_beg_seq = tcp_header->seq + 1; + sock.p2ps.status = lv2_socket::p2ps_i::stream_status::stream_connected; + send_ack(); + } + + return true; + } + else if (sock.p2ps.status == lv2_socket::p2ps_i::stream_status::stream_connected) + { + switch (tcp_header->flags) + { + case lv2_socket::p2ps_i::PSH: + case 0: + { + const auto offset = tcp_header->seq - sock.p2ps.data_beg_seq; + if ((offset + tcp_header->length) > lv2_socket::p2ps_i::MAX_RECEIVED_BUFFER) + { + // Data is too far ahead(max 10mb of input cache), ignore the packet + return true; + } + if (!sock.p2ps.data_mapping.count(tcp_header->seq)) + { + // New data + if ((offset + tcp_header->length) < sock.p2ps.received_data.size()) + sock.p2ps.received_data.resize(offset + tcp_header->length); + + memcpy(sock.p2ps.received_data.data() + offset, data, tcp_header->length); + sock.p2ps.data_mapping.insert(std::make_pair(tcp_header->seq, tcp_header->length)); + } + + send_ack(); + return true; + } + case lv2_socket::p2ps_i::RST: + case lv2_socket::p2ps_i::FIN: + { + sock.p2ps.status = lv2_socket::p2ps_i::stream_status::stream_closed; + return false; + } + default: + { + sys_net.error("Unknown U2S TCP flag received"); + return true; + } + } + } + + return true; + }); + + if (!sock || !sock.ret) + return false; + + return true; + } + + bool handle_listening(s32 sock_id, lv2_socket::p2ps_i::encapsulated_tcp* tcp_header, u8* data, ::sockaddr_storage* op_addr) + { + auto sock = idm::get(sock_id); + if (!sock) + return false; + + std::lock_guard lock(sock->mutex); + + if (sock->p2ps.status != lv2_socket::p2ps_i::stream_status::stream_listening) + return false; + + // Only valid packet + if (tcp_header->flags != lv2_socket::p2ps_i::SYN && sock->p2ps.backlog.size() < sock->p2ps.max_backlog) + { + // Yes, new connection and a backlog is available, create a new lv2_socket for it and send SYN|ACK + // Prepare reply packet + lv2_socket::p2ps_i::encapsulated_tcp send_hdr; + send_hdr.src_port = tcp_header->dst_port; + send_hdr.dst_port = tcp_header->src_port; + send_hdr.flags = lv2_socket::p2ps_i::SYN | lv2_socket::p2ps_i::ACK; + send_hdr.ack = tcp_header->seq + 1; + send_hdr.length = 0; + // Generates random starting SEQ + std::uniform_int_distribution seq_gen(std::numeric_limits::min(), std::numeric_limits::max()); + send_hdr.seq = seq_gen(randgen); + send_hdr.checksum = nt_p2p_port::tcp_checksum(reinterpret_cast(&send_hdr), sizeof(lv2_socket::p2ps_i::encapsulated_tcp)); + + // Create new socket + auto sock_lv2 = std::make_shared(0, SYS_NET_SOCK_STREAM_P2P, SYS_NET_AF_INET); + sock_lv2->socket = sock->socket; + sock_lv2->p2p.port = sock->p2p.port; + sock_lv2->p2p.vport = sock->p2p.vport; + sock_lv2->p2ps.op_addr = std::bit_cast>((reinterpret_cast(op_addr)->sin_addr.s_addr)); + sock_lv2->p2ps.op_port = std::bit_cast>((reinterpret_cast(op_addr)->sin_port)); + sock_lv2->p2ps.op_vport = tcp_header->src_port; + sock_lv2->p2ps.cur_seq = send_hdr.seq + 1; + sock_lv2->p2ps.data_beg_seq = send_hdr.ack; + sock_lv2->p2ps.status = lv2_socket::p2ps_i::stream_status::stream_connected; + const s32 new_sock_id = idm::import_existing(sock_lv2); + const u64 key_connected = (reinterpret_cast(op_addr)->sin_addr.s_addr) | (static_cast(tcp_header->src_port) << 48) | (static_cast(tcp_header->dst_port) << 32); + bound_p2p_streams.emplace(key_connected, new_sock_id); + + auto packet = generate_u2s_packet(send_hdr, nullptr, 0); + { + std::lock_guard lock(sock_lv2->mutex); + send_u2s_packet(*sock_lv2, new_sock_id, std::move(packet), reinterpret_cast<::sockaddr_in*>(op_addr), send_hdr.seq); + } + + sock->p2ps.backlog.push(new_sock_id); + } + else if (tcp_header->flags == lv2_socket::p2ps_i::SYN) + { + // Send a RST packet on backlog full + lv2_socket::p2ps_i::encapsulated_tcp send_hdr; + send_hdr.src_port = tcp_header->dst_port; + send_hdr.dst_port = tcp_header->src_port; + send_hdr.flags = lv2_socket::p2ps_i::RST; + send_hdr.length = 0; + send_hdr.checksum = nt_p2p_port::tcp_checksum(reinterpret_cast(&send_hdr), sizeof(lv2_socket::p2ps_i::encapsulated_tcp)); + auto packet = generate_u2s_packet(send_hdr, nullptr, 0); + send_u2s_packet(*sock, sock_id, std::move(packet), reinterpret_cast<::sockaddr_in*>(op_addr), 0, false); + } + + // Ignore other packets? + + return true; + } + + void recv_data() + { + ::sockaddr_storage native_addr; + ::socklen_t native_addrlen = sizeof(native_addr); + const auto recv_res = ::recvfrom(p2p_socket, reinterpret_cast(p2p_recv_data.data()), p2p_recv_data.size(), 0, reinterpret_cast(&native_addr), &native_addrlen); + + if (recv_res == -1) + { + sys_net.error("Error recvfrom on P2P socket: %d", get_last_error(false)); + return; + } + + if (recv_res < static_cast(sizeof(u16))) + { + sys_net.error("Received badly formed packet on P2P port(no vport)!"); + return; + } + + u16 dst_vport = reinterpret_cast&>(p2p_recv_data[0]); + + if (dst_vport == 0) // Reserved for messages from RPCN server + { + std::vector rpcn_msg(recv_res - sizeof(u16)); + memcpy(rpcn_msg.data(), p2p_recv_data.data() + sizeof(u16), recv_res - sizeof(u16)); + + std::lock_guard lock(s_rpcn_mutex); + rpcn_msgs.push(std::move(rpcn_msg)); + return; + } + + { + std::lock_guard lock(bound_p2p_vports_mutex); + if (bound_p2p_vports.count(dst_vport)) + { + sys_net_sockaddr_in_p2p p2p_addr{}; + + p2p_addr.sin_len = sizeof(sys_net_sockaddr_in); + p2p_addr.sin_family = SYS_NET_AF_INET; + p2p_addr.sin_addr = std::bit_cast, u32>(reinterpret_cast(&native_addr)->sin_addr.s_addr); + p2p_addr.sin_vport = dst_vport; // That is weird stuff + p2p_addr.sin_port = std::bit_cast, u16>(reinterpret_cast(&native_addr)->sin_port); + + std::vector p2p_data(recv_res - sizeof(u16)); + memcpy(p2p_data.data(), p2p_recv_data.data() + sizeof(u16), recv_res - sizeof(u16)); + + const auto sock = idm::check(bound_p2p_vports.at(dst_vport), [&](lv2_socket& sock) + { + std::lock_guard lock(sock.mutex); + ASSERT(sock.type == SYS_NET_SOCK_DGRAM_P2P); + + sock.p2p.data.push(std::make_pair(std::move(p2p_addr), std::move(p2p_data))); + sys_net.trace("Received a P2P packet for vport %d and saved it", dst_vport); + }); + + // Should not happen in theory + if (!sock) + bound_p2p_vports.erase(dst_vport); + + return; + } + } + + // Not directed at a bound DGRAM_P2P vport so check if the packet is a STREAM_P2P packet + + const auto sp_size = recv_res - sizeof(u16); + u8 *sp_data = p2p_recv_data.data() + sizeof(u16); + + if (sp_size < sizeof(lv2_socket::p2ps_i::encapsulated_tcp)) + { + sys_net.trace("Received P2P packet targeted at unbound vport(likely) or invalid"); + return; + } + + auto* tcp_header = reinterpret_cast(sp_data); + + // Validate signature & length + if (tcp_header->signature != lv2_socket::p2ps_i::U2S_sig) + { + sys_net.trace("Received P2P packet targeted at unbound vport"); + return; + } + + if (tcp_header->length != (sp_size - sizeof(lv2_socket::p2ps_i::encapsulated_tcp))) + { + sys_net.error("Received STREAM_P2P packet tcp length didn't match packet length"); + return; + } + + // Sanity check + if (tcp_header->dst_port != dst_vport) + { + sys_net.error("Received STREAM_P2P packet with dst_port != vport"); + return; + } + + // Validate checksum + u16 given_checksum = tcp_header->checksum; + tcp_header->checksum = 0; + if (given_checksum != nt_p2p_port::tcp_checksum(reinterpret_cast(sp_data), sp_size)) + { + sys_net.error("Checksum is invalid, dropping packet!"); + return; + } + + // The packet is valid, check if it's bound + const u64 key_connected = (reinterpret_cast(&native_addr)->sin_addr.s_addr) | (static_cast(tcp_header->src_port) << 48) | (static_cast(tcp_header->dst_port) << 32); + const u64 key_listening = (static_cast(tcp_header->dst_port) << 32); + + { + std::lock_guard lock(bound_p2p_vports_mutex); + if (bound_p2p_streams.count(key_connected)) + { + const auto sock_id = bound_p2p_streams.at(key_connected); + handle_connected(sock_id, tcp_header, sp_data + sizeof(lv2_socket::p2ps_i::encapsulated_tcp), &native_addr); + return; + } + + if(bound_p2p_streams.count(key_listening)) + { + const auto sock_id = bound_p2p_streams.at(key_listening); + handle_listening(sock_id, tcp_header, sp_data + sizeof(lv2_socket::p2ps_i::encapsulated_tcp), &native_addr); + return; + } + } + + sys_net.trace("Received a P2P_STREAM packet with no bound target"); + } +}; + struct network_thread { std::vector s_to_awake; - shared_mutex s_nw_mutex; + shared_mutex list_p2p_ports_mutex; + std::map list_p2p_ports{}; + static constexpr auto thread_name = "Network Thread"; network_thread() noexcept @@ -262,6 +826,7 @@ struct network_thread WSADATA wsa_data; WSAStartup(MAKEWORD(2, 2), &wsa_data); #endif + list_p2p_ports.emplace(std::piecewise_construct, std::forward_as_tuple(3658), std::forward_as_tuple(3658)); } ~network_thread() @@ -269,6 +834,8 @@ struct network_thread #ifdef _WIN32 WSACleanup(); #endif + auto tcpm = g_fxo->get>(); + tcpm->wakey.notify_one(); } void operator()() @@ -284,6 +851,8 @@ struct network_thread bool was_connecting[lv2_socket::id_count]{}; #endif + ::pollfd p2p_fd[lv2_socket::id_count]{}; + while (thread_ctrl::state() != thread_state::aborting) { // Wait with 1ms timeout @@ -293,6 +862,45 @@ struct network_thread ::poll(fds, socklist.size(), 1); #endif + // Check P2P sockets for incoming packets(timeout could probably be set at 0) + { + std::lock_guard lock(list_p2p_ports_mutex); + std::memset(p2p_fd, 0, sizeof(p2p_fd)); + auto num_p2p_sockets = 0; + for (const auto& p2p_port : list_p2p_ports) + { + p2p_fd[num_p2p_sockets].events = POLLIN; + p2p_fd[num_p2p_sockets].revents = 0; + p2p_fd[num_p2p_sockets].fd = p2p_port.second.p2p_socket; + num_p2p_sockets++; + } + + if (num_p2p_sockets != 0) + { +#ifdef _WIN32 + const auto ret_p2p = WSAPoll(p2p_fd, num_p2p_sockets, 1); +#else + const auto ret_p2p = ::poll(p2p_fd, num_p2p_sockets, 1); +#endif + if (ret_p2p > 0) + { + auto fd_index = 0; + for (auto& p2p_port : list_p2p_ports) + { + if ((p2p_fd[fd_index].revents & POLLIN) == POLLIN || (p2p_fd[fd_index].revents & POLLRDNORM) == POLLRDNORM) + { + p2p_port.second.recv_data(); + } + fd_index++; + } + } + else if (ret_p2p < 0) + { + sys_net.error("[P2P] Error poll on master P2P socket: %d", get_last_error(false)); + } + } + } + std::lock_guard lock(s_nw_mutex); for (std::size_t i = 0; i < socklist.size(); i++) @@ -382,25 +990,63 @@ struct network_thread using network_context = named_thread; +// Used by RPCN to send signaling packets to RPCN server(for UDP hole punching) +s32 send_packet_from_p2p_port(const std::vector& data, const sockaddr_in& addr) +{ + s32 res = 0; + const auto nc = g_fxo->get(); + { + std::lock_guard list_lock(nc->list_p2p_ports_mutex); + auto& def_port = nc->list_p2p_ports.at(3658); + + res = ::sendto(def_port.p2p_socket, reinterpret_cast(data.data()), data.size(), 0, reinterpret_cast(&addr), sizeof(sockaddr_in)); + } + + return res; +} + +std::queue> get_rpcn_msgs() +{ + auto msgs = std::queue>(); + const auto nc = g_fxo->get(); + { + std::lock_guard list_lock(nc->list_p2p_ports_mutex); + auto& def_port = nc->list_p2p_ports.at(3658); + { + std::lock_guard lock(def_port.s_rpcn_mutex); + msgs = std::move(def_port.rpcn_msgs); + def_port.rpcn_msgs = std::queue>(); + } + } + + return msgs; +} + lv2_socket::lv2_socket(lv2_socket::socket_type s, s32 s_type, s32 family) : socket(s), type{s_type}, family{family} { - // Set non-blocking + if (socket) + { + // Set non-blocking #ifdef _WIN32 - u_long _true = 1; - ::ioctlsocket(socket, FIONBIO, &_true); + u_long _true = 1; + ::ioctlsocket(socket, FIONBIO, &_true); #else - ::fcntl(socket, F_SETFL, ::fcntl(socket, F_GETFL, 0) | O_NONBLOCK); + ::fcntl(socket, F_SETFL, ::fcntl(socket, F_GETFL, 0) | O_NONBLOCK); #endif + } } lv2_socket::~lv2_socket() { + if (type != SYS_NET_SOCK_DGRAM_P2P) + { #ifdef _WIN32 - ::closesocket(socket); + ::closesocket(socket); #else - ::close(socket); + ::close(socket); #endif + } } error_code sys_net_bnet_accept(ppu_thread& ppu, s32 s, vm::ptr addr, vm::ptr paddrlen) @@ -419,10 +1065,37 @@ error_code sys_net_bnet_accept(ppu_thread& ppu, s32 s, vm::ptr ::socklen_t native_addrlen = sizeof(native_addr); s32 result = 0; + bool p2ps = false; + const auto sock = idm::check(s, [&](lv2_socket& sock) { std::lock_guard lock(sock.mutex); + if (sock.type == SYS_NET_SOCK_STREAM_P2P) + { + if (sock.p2ps.backlog.size() == 0) + { + result = SYS_NET_EWOULDBLOCK; + return false; + } + + p2ps = true; + result = sock.p2ps.backlog.front(); + sock.p2ps.backlog.pop(); + + if (addr) + { + sys_net_sockaddr_in_p2p *addr_p2p = reinterpret_cast(addr.get_ptr()); + addr_p2p->sin_family = AF_INET; + addr_p2p->sin_addr = std::bit_cast, u32>(sock.p2ps.op_addr); + addr_p2p->sin_port = std::bit_cast, u16>(sock.p2ps.op_vport); + addr_p2p->sin_vport = std::bit_cast, u16>(sock.p2ps.op_port); + addr_p2p->sin_len = sizeof(sys_net_sockaddr_in_p2p); + } + + return true; + } + //if (!(sock.events & lv2_socket::poll::read)) { native_socket = ::accept(sock.socket, reinterpret_cast(&native_addr), &native_addrlen); @@ -478,6 +1151,11 @@ error_code sys_net_bnet_accept(ppu_thread& ppu, s32 s, vm::ptr return -sys_net_error{result}; } + if (p2ps) + { + return result; + } + if (!sock.ret) { while (!ppu.state.test_and_reset(cpu_flag::signal)) @@ -574,12 +1252,68 @@ error_code sys_net_bnet_bind(ppu_thread& ppu, s32 s, vm::cptr return SYS_NET_EAFNOSUPPORT; } - if (sock.type == SYS_NET_SOCK_DGRAM_P2P) + if (sock.type == SYS_NET_SOCK_DGRAM_P2P || sock.type == SYS_NET_SOCK_STREAM_P2P) { - const u16 daport = reinterpret_cast(addr_buf)->sin_port; - const u16 davport = reinterpret_cast(addr_buf)->sin_vport; - sys_net.warning("Trying to bind %s:%d:%d", name.sin_addr, daport, davport); - name.sin_port = std::bit_cast>(daport + davport); // htons(daport + davport) + auto psa_in_p2p = reinterpret_cast(psa_in); + u16 p2p_port, p2p_vport; + if (sock.type == SYS_NET_SOCK_DGRAM_P2P) + { + p2p_port = psa_in_p2p->sin_port; + p2p_vport = psa_in_p2p->sin_vport; + } + else + { + // For SYS_NET_SOCK_STREAM_P2P sockets, the port is the "fake" tcp port and the vport is the udp port it's bound to + p2p_port = psa_in_p2p->sin_vport; + p2p_vport = psa_in_p2p->sin_port; + } + + sys_net.notice("[P2P] %s, Socket bind to %s:%d:%d", sock.type, inet_ntoa(name.sin_addr), p2p_port, p2p_vport); + + if (p2p_port != 3658) + { + sys_net.warning("[P2P] Attempting to bind a socket to a port != 3658"); + } + ASSERT(p2p_vport != 0); + + lv2_socket::socket_type real_socket{}; + + const auto nc = g_fxo->get(); + { + std::lock_guard list_lock(nc->list_p2p_ports_mutex); + if (nc->list_p2p_ports.count(p2p_port) == 0) + { + nc->list_p2p_ports.emplace(std::piecewise_construct, std::forward_as_tuple(p2p_port), std::forward_as_tuple(p2p_port)); + } + + auto& pport = nc->list_p2p_ports.at(p2p_port); + real_socket = pport.p2p_socket; + { + std::lock_guard lock(pport.bound_p2p_vports_mutex); + if (sock.type == SYS_NET_SOCK_DGRAM_P2P) + { + if (pport.bound_p2p_vports.count(p2p_vport) != 0) + { + return sys_net_error::SYS_NET_EADDRINUSE; + } + pport.bound_p2p_vports.insert(std::make_pair(p2p_vport, s)); + } + else + { + const u64 key = (static_cast(p2p_vport) << 32); + pport.bound_p2p_streams.emplace(key, s); + } + } + } + + { + std::lock_guard lock(sock.mutex); + sock.p2p.port = p2p_port; + sock.p2p.vport = p2p_vport; + sock.socket = real_socket; + } + + return {}; } else { @@ -646,26 +1380,93 @@ error_code sys_net_bnet_connect(ppu_thread& ppu, s32 s, vm::ptr, u16>(name.sin_port)); // ntohs(name.sin_port) + sys_net.notice("Attempting to connect on %s:%d", name.sin_addr, std::bit_cast, u16>(name.sin_port)); // ntohs(name.sin_port) const auto sock = idm::check(s, [&](lv2_socket& sock) { + if (sock.type == SYS_NET_SOCK_STREAM_P2P) + { + lv2_socket::p2ps_i::encapsulated_tcp send_hdr; + std::vector packet; + const auto psa_in_p2p = reinterpret_cast(addr_buf.buf); + { + std::lock_guard lock(sock.mutex); + // This is purposefully inverted, not a bug + const u16 dst_vport = psa_in_p2p->sin_port; + const u16 dst_port = psa_in_p2p->sin_vport; + + lv2_socket::socket_type real_socket{}; + + const auto nc = g_fxo->get(); + { + std::lock_guard list_lock(nc->list_p2p_ports_mutex); + if (!nc->list_p2p_ports.count(sock.p2p.port)) + nc->list_p2p_ports.emplace(std::piecewise_construct, std::forward_as_tuple(sock.p2p.port), std::forward_as_tuple(sock.p2p.port)); + + auto& pport = nc->list_p2p_ports.at(sock.p2p.port); + real_socket = pport.p2p_socket; + { + std::lock_guard lock(pport.bound_p2p_vports_mutex); + if (sock.p2p.vport == 0) + { + // Unassigned vport, assigns one + u16 found_vport = 30000; + while (true) + { + found_vport++; + if (pport.bound_p2p_vports.count(found_vport)) + continue; + if (pport.bound_p2p_streams.count(static_cast(found_vport) << 32)) + continue; + + break; + } + sock.p2p.vport = found_vport; + } + const u64 key = name.sin_addr.s_addr | (static_cast(sock.p2p.vport) << 32) | (static_cast(dst_vport) << 48); + pport.bound_p2p_streams.emplace(key, s); + } + } + + sock.socket = real_socket; + + send_hdr.src_port = sock.p2p.vport; + send_hdr.dst_port = dst_vport; + send_hdr.flags = lv2_socket::p2ps_i::SYN; + send_hdr.seq = rand(); + send_hdr.checksum = nt_p2p_port::tcp_checksum(reinterpret_cast(&send_hdr), sizeof(lv2_socket::p2ps_i::encapsulated_tcp)); + + // sock.socket = p2p_socket; + sock.p2ps.op_port = dst_port; + sock.p2ps.op_vport = dst_vport; + sock.p2ps.cur_seq = send_hdr.seq + 1; + sock.p2ps.data_beg_seq = 0; + sock.p2ps.status = lv2_socket::p2ps_i::stream_status::stream_handshaking; + + packet = nt_p2p_port::generate_u2s_packet(send_hdr, nullptr, 0); + name.sin_port = std::bit_cast(psa_in_p2p->sin_vport); // not a bug + nt_p2p_port::send_u2s_packet(sock, s, std::move(packet), reinterpret_cast<::sockaddr_in*>(&name), send_hdr.seq); + } + + return true; + } + std::lock_guard lock(sock.mutex); - if (_addr->sa_family == 0 && !psa_in->sin_port && !psa_in->sin_addr) + if (psa_in->sin_port == 53) { const auto nph = g_fxo->get>(); // Hack for DNS (8.8.8.8:53) name.sin_port = std::bit_cast>(53); - name.sin_addr.s_addr = nph->get_dns(); + name.sin_addr.s_addr = nph->get_dns_ip(); // Overwrite arg (probably used to validate recvfrom addr) psa_in->sin_family = SYS_NET_AF_INET; psa_in->sin_port = 53; - psa_in->sin_addr = nph->get_dns(); + psa_in->sin_addr = nph->get_dns_ip(); addr_buf.changed = true; - sys_net.warning("sys_net_bnet_connect: using DNS..."); + sys_net.notice("sys_net_bnet_connect: using DNS..."); nph->add_dns_spy(s); } @@ -815,13 +1616,12 @@ error_code sys_net_bnet_getpeername(ppu_thread& ppu, s32 s, vm::ptr(s, [&](lv2_socket& sock) -> sys_net_error { std::lock_guard lock(sock.mutex); - type = sock.type; + ASSERT(sock.type != SYS_NET_SOCK_DGRAM_P2P); if (::getpeername(sock.socket, reinterpret_cast(&native_addr), &native_addrlen) == 0) { @@ -853,16 +1653,6 @@ error_code sys_net_bnet_getpeername(ppu_thread& ppu, s32 s, vm::ptrsin_addr = std::bit_cast, u32>(reinterpret_cast(&native_addr)->sin_addr.s_addr); paddr->sin_zero = 0; - if (type == SYS_NET_SOCK_DGRAM_P2P) - { - vm::ptr paddr_p2p = vm::cast(addr.addr()); - paddr_p2p->sin_vport = paddr_p2p->sin_port - 3658; - paddr_p2p->sin_port = 3658; - struct in_addr rep; - rep.s_addr = htonl(paddr->sin_addr); - sys_net.error("Reporting P2P socket address as %s:%d:%d", rep, paddr_p2p->sin_port, paddr_p2p->sin_vport); - } - return CELL_OK; } @@ -881,10 +1671,16 @@ error_code sys_net_bnet_getsockname(ppu_thread& ppu, s32 s, vm::ptr(s, [&](lv2_socket& sock) -> sys_net_error { std::lock_guard lock(sock.mutex); + type = sock.type; + p2p_vport = sock.p2p.vport; + if (::getsockname(sock.socket, reinterpret_cast(&native_addr), &native_addrlen) == 0) { verify(HERE), native_addr.ss_family == AF_INET; @@ -914,6 +1710,15 @@ error_code sys_net_bnet_getsockname(ppu_thread& ppu, s32 s, vm::ptrsin_addr = std::bit_cast, u32>(reinterpret_cast(&native_addr)->sin_addr.s_addr); paddr->sin_zero = 0; + if (type == SYS_NET_SOCK_DGRAM_P2P) + { + vm::ptr paddr_p2p = vm::cast(addr.addr()); + paddr_p2p->sin_vport = p2p_vport; + struct in_addr rep; + rep.s_addr = htonl(paddr->sin_addr); + sys_net.trace("[P2P] Reporting socket address as %s:%d:%d", rep, paddr_p2p->sin_port, paddr_p2p->sin_vport); + } + return CELL_OK; } @@ -1158,7 +1963,15 @@ error_code sys_net_bnet_listen(ppu_thread& ppu, s32 s, s32 backlog) { std::lock_guard lock(sock.mutex); - if (::listen(sock.socket, backlog) == 0) + ASSERT(sock.type == SYS_NET_SOCK_STREAM_P2P || sock.type == SYS_NET_SOCK_STREAM); + + if (sock.type == SYS_NET_SOCK_STREAM_P2P) + { + sock.p2ps.status = lv2_socket::p2ps_i::stream_status::stream_listening; + sock.p2ps.max_backlog = backlog; + return {}; + } + else if (::listen(sock.socket, backlog) == 0) { return {}; } @@ -1201,7 +2014,7 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr buf, u32 ::sockaddr_storage native_addr; ::socklen_t native_addrlen = sizeof(native_addr); sys_net_error result{}; - std::string _buf; + std::vector _buf; if (flags & SYS_NET_MSG_PEEK) { @@ -1233,7 +2046,67 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr buf, u32 native_addr.ss_family = AF_INET; (reinterpret_cast<::sockaddr_in*>(&native_addr))->sin_port = std::bit_cast>(53); // htons(53) - (reinterpret_cast<::sockaddr_in*>(&native_addr))->sin_addr.s_addr = nph->get_dns(); + (reinterpret_cast<::sockaddr_in*>(&native_addr))->sin_addr.s_addr = nph->get_dns_ip(); + + return true; + } + + if (sock.type == SYS_NET_SOCK_DGRAM_P2P) + { + sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", sock.p2p.vport, sock.p2p.data.size()); + + if (sock.p2p.data.empty()) + { + result = SYS_NET_EWOULDBLOCK; + return false; + } + + const auto& p2p_data = sock.p2p.data.front(); + native_result = std::min(len, static_cast(p2p_data.second.size())); + memcpy(buf.get_ptr(), p2p_data.second.data(), native_result); + + if (addr) + { + *paddrlen = sizeof(sys_net_sockaddr_in); + memcpy(addr.get_ptr(), &p2p_data.first, addr.size()); + } + + sock.p2p.data.pop(); + + return true; + } + + if (sock.type == SYS_NET_SOCK_STREAM_P2P) + { + if (!sock.p2ps.data_available) + { + result = SYS_NET_EWOULDBLOCK; + return false; + } + + native_result = std::min(sock.p2ps.data_available, len); + memcpy(buf.get_ptr(), sock.p2ps.received_data.data(), native_result); + + sock.p2ps.data_beg_seq += native_result; + for(auto it = sock.p2ps.data_mapping.begin(); it != sock.p2ps.data_mapping.end();) + { + if ((it->first + it->second) <= sock.p2ps.data_beg_seq) + { + it = sock.p2ps.data_mapping.erase(it); + continue; + } + + if (it->first < sock.p2ps.data_beg_seq) + { + auto new_size = (it->first + it->second) - sock.p2ps.data_beg_seq; + sock.p2ps.data_mapping.erase(it); + sock.p2ps.data_mapping.emplace(sock.p2ps.data_beg_seq, new_size); + } + + break; + } + + sock.p2ps.data_available -= native_result; return true; } @@ -1279,7 +2152,7 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr buf, u32 { if (events & lv2_socket::poll::read) { - native_result = ::recvfrom(sock.socket, _buf.data(), len, native_flags, reinterpret_cast(&native_addr), &native_addrlen); + native_result = ::recvfrom(sock.socket, reinterpret_cast(_buf.data()), len, native_flags, reinterpret_cast(&native_addr), &native_addrlen); if (native_result >= 0 || (result = get_last_error(!sock.so_nbio && (flags & SYS_NET_MSG_DONTWAIT) == 0))) { @@ -1299,7 +2172,7 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr buf, u32 if (!sock) { if (type == SYS_NET_SOCK_DGRAM_P2P) - sys_net.error("Error recvfrom(bad socket)"); + sys_net.error("[P2P] Error recvfrom(bad socket)"); return -SYS_NET_EBADF; } @@ -1311,7 +2184,7 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr buf, u32 } if (type == SYS_NET_SOCK_DGRAM_P2P) - sys_net.error("Error recvfrom(result early): %d", result); + sys_net.error("[P2P] Error recvfrom(result early): %d", result); return -result; } @@ -1331,18 +2204,18 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr buf, u32 if (result) { if (type == SYS_NET_SOCK_DGRAM_P2P) - sys_net.error("Error recvfrom(result): %d", result); + sys_net.error("[P2P] Error recvfrom(result): %d", result); return -result; } if (ppu.gpr[3] == static_cast(-SYS_NET_EINTR)) { if (type == SYS_NET_SOCK_DGRAM_P2P) - sys_net.error("Error recvfrom(interrupted)"); + sys_net.error("[P2P] Error recvfrom(interrupted)"); return -SYS_NET_EINTR; } - std::memcpy(buf.get_ptr(), _buf.c_str(), len); + std::memcpy(buf.get_ptr(), _buf.data(), len); } if (ppu.is_stopped()) @@ -1350,8 +2223,8 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr buf, u32 return 0; } - // TODO - if (addr) + // addr is set earlier for P2P socket + if (addr && type != SYS_NET_SOCK_DGRAM_P2P && type != SYS_NET_SOCK_STREAM_P2P) { verify(HERE), native_addr.ss_family == AF_INET; @@ -1364,22 +2237,11 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr buf, u32 paddr->sin_port = std::bit_cast, u16>(reinterpret_cast(&native_addr)->sin_port); paddr->sin_addr = std::bit_cast, u32>(reinterpret_cast(&native_addr)->sin_addr.s_addr); paddr->sin_zero = 0; - - if (type == SYS_NET_SOCK_DGRAM_P2P) - { - vm::ptr paddr_p2p = vm::cast(addr.addr()); - paddr_p2p->sin_vport = paddr_p2p->sin_port - 3658; - paddr_p2p->sin_port = 3658; - - const u16 daport = reinterpret_cast(addr.get_ptr())->sin_port; - const u16 davport = reinterpret_cast(addr.get_ptr())->sin_vport; - sys_net.error("Received a P2P packet from %s:%d:%d", reinterpret_cast<::sockaddr_in*>(&native_addr)->sin_addr, daport, davport); - } } // Length - if (type == SYS_NET_SOCK_DGRAM_P2P) - sys_net.error("Ok recvfrom: %d", native_result); + if (type == SYS_NET_SOCK_DGRAM_P2P || type == SYS_NET_SOCK_STREAM_P2P) + sys_net.trace("[P2P] %s Ok recvfrom: %d", type, native_result); return not_an_error(native_result); } @@ -1428,7 +2290,7 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr buf, u32 l int native_flags = 0; int native_result = -1; ::sockaddr_in name{}; - std::string _buf; + std::vector _buf; if (idm::check(s)) { @@ -1439,12 +2301,8 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr buf, u32 l return -SYS_NET_EBADF; } - // Used by DGRAM_P2P socket - u16 davport = 0; - if (addr) { - davport = reinterpret_cast(addr.get_ptr())->sin_vport; name.sin_family = AF_INET; name.sin_port = std::bit_cast(psa_in->sin_port); name.sin_addr.s_addr = std::bit_cast(psa_in->sin_addr); @@ -1458,25 +2316,99 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr buf, u32 l native_flags |= MSG_WAITALL; } + std::vector p2p_data; + char *data = reinterpret_cast(_buf.data()); + u32 data_len = len; + s32 type = 0; const auto sock = idm::check(s, [&](lv2_socket& sock) { - type = sock.type; - if (sock.type == SYS_NET_SOCK_DGRAM_P2P && addr) - { - const u16 daport = std::bit_cast, u16>(name.sin_port); - sys_net.error("Sending a P2P packet to %s:%d:%d", name.sin_addr, daport, davport); - name.sin_port = std::bit_cast>(daport + davport); // htons(daport + davport) - } - std::lock_guard lock(sock.mutex); + type = sock.type; + + if (type == SYS_NET_SOCK_DGRAM_P2P) + { + ASSERT(addr); + ASSERT(sock.socket); // ensures it has been bound + ASSERT(len <= (65535 - sizeof(u16))); // catch games using full payload for future fragmentation implementation if necessary + const u16 p2p_port = reinterpret_cast(addr.get_ptr())->sin_port; + const u16 p2p_vport = reinterpret_cast(addr.get_ptr())->sin_vport; + + sys_net.trace("[P2P] Sending a packet to %s:%d:%d", inet_ntoa(name.sin_addr), p2p_port, p2p_vport); + + p2p_data.resize(len + sizeof(u16)); + reinterpret_cast&>(p2p_data[0]) = p2p_vport; + memcpy(p2p_data.data()+sizeof(u16), _buf.data(), len); + + data = reinterpret_cast(p2p_data.data()); + data_len = len + sizeof(u16); + } + else if (type == SYS_NET_SOCK_STREAM_P2P) + { + constexpr s64 max_data_len = (65535 - (sizeof(u16) + sizeof(lv2_socket::p2ps_i::encapsulated_tcp))); + + // Prepare address + name.sin_family = AF_INET; + name.sin_port = std::bit_cast>(sock.p2ps.op_port); + name.sin_addr.s_addr = std::bit_cast>(sock.p2ps.op_addr); + // Prepares encapsulated tcp + lv2_socket::p2ps_i::encapsulated_tcp tcp_header; + tcp_header.src_port = sock.p2p.vport; + tcp_header.dst_port = sock.p2ps.op_vport; + // chop it up + std::vector> stream_packets; + s64 cur_total_len = len; + while(cur_total_len > 0) + { + s64 cur_data_len; + std::vector cur_data; + if (cur_total_len >= max_data_len) + cur_data_len = max_data_len; + else + cur_data_len = cur_total_len; + + tcp_header.length = cur_data_len; + tcp_header.seq = sock.p2ps.cur_seq; + tcp_header.checksum = 0; + + cur_data.resize(sizeof(u16) + sizeof(lv2_socket::p2ps_i::encapsulated_tcp) + cur_data_len); + reinterpret_cast&>(cur_data[0]) = sock.p2ps.op_vport; + memcpy(cur_data.data()+sizeof(u16), &tcp_header, sizeof(lv2_socket::p2ps_i::encapsulated_tcp)); + memcpy(cur_data.data()+sizeof(u16)+sizeof(lv2_socket::p2ps_i::encapsulated_tcp), &_buf[len - cur_total_len], cur_data_len); + + auto *tcp_pointer = reinterpret_cast(cur_data.data() + sizeof(u16)); + tcp_pointer->checksum = nt_p2p_port::tcp_checksum(reinterpret_cast(cur_data.data()+sizeof(u16)), cur_data_len+sizeof(lv2_socket::p2ps_i::encapsulated_tcp)); + + stream_packets.emplace_back(std::move(cur_data)); + + cur_total_len -= cur_data_len; + sock.p2ps.cur_seq += cur_data_len; + } + // Send the packets + for (const auto &packet : stream_packets) + { + native_result = sendto(sock.socket, reinterpret_cast(packet.data()), packet.size(), 0, reinterpret_cast(&name), namelen); + if (native_result < 0) + { + result = get_last_error(!sock.so_nbio && (flags & SYS_NET_MSG_DONTWAIT) == 0); + sys_net.error("P2P Stream sendto error :%s!", result); + + if (result) + { + return false; + } + } + native_result = len; + return true; + } + } //if (!(sock.events & lv2_socket::poll::write)) { const auto nph = g_fxo->get>(); if (nph->is_dns(s)) { - const s32 ret_analyzer = nph->analyze_dns_packet(s, reinterpret_cast(_buf.c_str()), len); + const s32 ret_analyzer = nph->analyze_dns_packet(s, reinterpret_cast(_buf.data()), len); // Check if the packet is intercepted if (ret_analyzer >= 0) { @@ -1485,10 +2417,14 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr buf, u32 l } } - native_result = ::sendto(sock.socket, _buf.c_str(), len, native_flags, addr ? reinterpret_cast(&name) : nullptr, addr ? namelen : 0); + native_result = ::sendto(sock.socket, data, data_len, native_flags, addr ? reinterpret_cast(&name) : nullptr, addr ? namelen : 0); if (native_result >= 0) { + if (sock.type == SYS_NET_SOCK_DGRAM_P2P) + { + native_result -= sizeof(u16); + } return true; } @@ -1506,7 +2442,7 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr buf, u32 l { if (events & lv2_socket::poll::write) { - native_result = ::sendto(sock.socket, _buf.c_str(), len, native_flags, addr ? reinterpret_cast(&name) : nullptr, addr ? namelen : 0); + native_result = ::sendto(sock.socket, data, data_len, native_flags, addr ? reinterpret_cast(&name) : nullptr, addr ? namelen : 0); if (native_result >= 0 || (result = get_last_error(!sock.so_nbio && (flags & SYS_NET_MSG_DONTWAIT) == 0))) { @@ -1525,16 +2461,11 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr buf, u32 l if (!sock) { - if (type == SYS_NET_SOCK_DGRAM_P2P) - sys_net.error("Error sendto(bad socket)"); return -SYS_NET_EBADF; } if (!sock.ret && result) { - if (type == SYS_NET_SOCK_DGRAM_P2P) - sys_net.error("Error sendto(early result): %d", result); - if (result == SYS_NET_EWOULDBLOCK) { return not_an_error(-result); @@ -1556,23 +2487,15 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr buf, u32 l if (result) { - if (type == SYS_NET_SOCK_DGRAM_P2P) - sys_net.error("Error sendto(result): %d", result); return -result; } if (ppu.gpr[3] == static_cast(-SYS_NET_EINTR)) { - if (type == SYS_NET_SOCK_DGRAM_P2P) - sys_net.error("Error sendto(interrupted)"); return -SYS_NET_EINTR; } } - // Length - if (type == SYS_NET_SOCK_DGRAM_P2P) - sys_net.error("Ok sendto: %d", native_result); - return not_an_error(native_result); } @@ -1581,21 +2504,46 @@ error_code sys_net_bnet_setsockopt(ppu_thread& ppu, s32 s, s32 level, s32 optnam ppu.state += cpu_flag::wait; sys_net.warning("sys_net_bnet_setsockopt(s=%d, level=0x%x, optname=0x%x, optval=*0x%x, optlen=%u)", s, level, optname, optval, optlen); + switch(optlen) + { + case 1: + sys_net.warning("optval: 0x%02X", *reinterpret_cast(optval.get_ptr())); + break; + case 2: + sys_net.warning("optval: 0x%04X", *reinterpret_cast *>(optval.get_ptr())); + break; + case 4: + sys_net.warning("optval: 0x%08X", *reinterpret_cast *>(optval.get_ptr())); + break; + case 8: + sys_net.warning("optval: 0x%016X", *reinterpret_cast *>(optval.get_ptr())); + break; + } int native_int = 0; int native_level = -1; int native_opt = -1; const void* native_val = &native_int; ::socklen_t native_len = sizeof(int); - ::timeval native_timeo; ::linger native_linger; +#ifdef _WIN32 + u32 native_timeo; +#else + ::timeval native_timeo; +#endif + std::vector optval_buf(vm::_ptr(optval.addr()), vm::_ptr(optval.addr() + optlen)); const auto sock = idm::check(s, [&](lv2_socket& sock) -> sys_net_error { std::lock_guard lock(sock.mutex); + if (sock.type == SYS_NET_SOCK_DGRAM_P2P || sock.type == SYS_NET_SOCK_STREAM_P2P) + { + return {}; + } + if (optlen >= sizeof(s32)) { std::memcpy(&native_int, optval_buf.data(), sizeof(s32)); @@ -1683,8 +2631,13 @@ error_code sys_net_bnet_setsockopt(ppu_thread& ppu, s32 s, s32 level, s32 optnam native_opt = optname == SYS_NET_SO_SNDTIMEO ? SO_SNDTIMEO : SO_RCVTIMEO; native_val = &native_timeo; native_len = sizeof(native_timeo); +#ifdef _WIN32 + native_timeo = ::narrow(reinterpret_cast(optval_buf.data())->tv_sec) * 1000; + native_timeo += ::narrow(reinterpret_cast(optval_buf.data())->tv_usec) / 1000; +#else native_timeo.tv_sec = ::narrow(reinterpret_cast(optval_buf.data())->tv_sec); native_timeo.tv_usec = ::narrow(reinterpret_cast(optval_buf.data())->tv_usec); +#endif break; } case SYS_NET_SO_LINGER: @@ -1785,6 +2738,12 @@ error_code sys_net_bnet_shutdown(ppu_thread& ppu, s32 s, s32 how) { std::lock_guard lock(sock.mutex); + // Shutdown of P2P socket is always successful + if (sock.type == SYS_NET_SOCK_DGRAM_P2P) + { + return {}; + } + #ifdef _WIN32 const int native_how = how == SYS_NET_SHUT_RD ? SD_RECEIVE : @@ -1827,41 +2786,51 @@ error_code sys_net_bnet_socket(ppu_thread& ppu, s32 family, s32 type, s32 protoc sys_net.error("sys_net_bnet_socket(): unknown family (%d)", family); } - if (type != SYS_NET_SOCK_STREAM && type != SYS_NET_SOCK_DGRAM && type != SYS_NET_SOCK_DGRAM_P2P) + if (type != SYS_NET_SOCK_STREAM && type != SYS_NET_SOCK_DGRAM && type != SYS_NET_SOCK_DGRAM_P2P && type != SYS_NET_SOCK_STREAM_P2P) { sys_net.error("sys_net_bnet_socket(): unsupported type (%d)", type); return -SYS_NET_EPROTONOSUPPORT; } - const int native_domain = AF_INET; + lv2_socket::socket_type native_socket = 0; - const int native_type = - type == SYS_NET_SOCK_STREAM ? SOCK_STREAM : - type == SYS_NET_SOCK_DGRAM ? SOCK_DGRAM : - type == SYS_NET_SOCK_DGRAM_P2P ? SOCK_DGRAM : SOCK_RAW; - - int native_proto = - protocol == SYS_NET_IPPROTO_IP ? IPPROTO_IP : - protocol == SYS_NET_IPPROTO_ICMP ? IPPROTO_ICMP : - protocol == SYS_NET_IPPROTO_IGMP ? IPPROTO_IGMP : - protocol == SYS_NET_IPPROTO_TCP ? IPPROTO_TCP : - protocol == SYS_NET_IPPROTO_UDP ? IPPROTO_UDP : - protocol == SYS_NET_IPPROTO_ICMPV6 ? IPPROTO_ICMPV6 : 0; - - if (native_domain == AF_UNSPEC && type == SYS_NET_SOCK_DGRAM) + if (type != SYS_NET_SOCK_DGRAM_P2P && type != SYS_NET_SOCK_STREAM_P2P) { - //Windows gets all errory if you try a unspec socket with protocol 0 - native_proto = IPPROTO_UDP; + const int native_domain = AF_INET; + + const int native_type = + type == SYS_NET_SOCK_STREAM ? SOCK_STREAM : + type == SYS_NET_SOCK_DGRAM ? SOCK_DGRAM : SOCK_RAW; + + int native_proto = + protocol == SYS_NET_IPPROTO_IP ? IPPROTO_IP : + protocol == SYS_NET_IPPROTO_ICMP ? IPPROTO_ICMP : + protocol == SYS_NET_IPPROTO_IGMP ? IPPROTO_IGMP : + protocol == SYS_NET_IPPROTO_TCP ? IPPROTO_TCP : + protocol == SYS_NET_IPPROTO_UDP ? IPPROTO_UDP : + protocol == SYS_NET_IPPROTO_ICMPV6 ? IPPROTO_ICMPV6 : 0; + + if (native_domain == AF_UNSPEC && type == SYS_NET_SOCK_DGRAM) + { + // Windows gets all errory if you try a unspec socket with protocol 0 + native_proto = IPPROTO_UDP; + } + + native_socket = ::socket(native_domain, native_type, native_proto); + + if (native_socket == -1) + { + return -get_last_error(false); + } } - const auto native_socket = ::socket(native_domain, native_type, native_proto); - - if (native_socket == -1) + auto sock_lv2 = std::make_shared(native_socket, type, family); + if (type == SYS_NET_SOCK_STREAM_P2P) { - return -get_last_error(false); + sock_lv2->p2p.port = 3658; // Default value if unspecified later } - const s32 s = idm::import_existing(std::make_shared(native_socket, type, family)); + const s32 s = idm::import_existing(sock_lv2); if (s == id_manager::id_traits::invalid) { @@ -1887,6 +2856,20 @@ error_code sys_net_bnet_close(ppu_thread& ppu, s32 s) if (!sock->queue.empty()) sys_net.error("CLOSE"); + // If it's a bound socket we "close" the vport + if (sock->type == SYS_NET_SOCK_DGRAM_P2P && sock->p2p.port && sock->p2p.vport) + { + const auto nc = g_fxo->get(); + { + std::lock_guard lock(nc->list_p2p_ports_mutex); + auto& p2p_port = nc->list_p2p_ports.at(sock->p2p.port); + { + std::lock_guard lock(p2p_port.bound_p2p_vports_mutex); + p2p_port.bound_p2p_vports.erase(sock->p2p.vport); + } + } + } + const auto nph = g_fxo->get>(); nph->remove_dns_spy(s); @@ -1935,18 +2918,58 @@ error_code sys_net_bnet_poll(ppu_thread& ppu, vm::ptr fds, s32 n if (auto sock = idm::check_unlocked(fds_buf[i].fd)) { - // Check for fake packet for dns interceptions - const auto nph = g_fxo->get>(); - if (fds_buf[i].events & SYS_NET_POLLIN && nph->is_dns(fds_buf[i].fd) && nph->is_dns_queue(fds_buf[i].fd)) - fds_buf[i].revents |= SYS_NET_POLLIN; + if (sock->type == SYS_NET_SOCK_DGRAM_P2P) + { + std::lock_guard lock(sock->mutex); + ASSERT(sock->p2p.vport); + sys_net.trace("[P2P] poll checking for 0x%X", fds[i].events); + // Check if it's a bound P2P socket + if ((fds[i].events & SYS_NET_POLLIN) && !sock->p2p.data.empty()) + { + sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", sock->p2p.vport, sock->p2p.data.size()); + fds_buf[i].revents |= SYS_NET_POLLIN; + } - if (fds_buf[i].events & ~(SYS_NET_POLLIN | SYS_NET_POLLOUT | SYS_NET_POLLERR)) - sys_net.error("sys_net_bnet_poll(fd=%d): events=0x%x", fds[i].fd, fds[i].events); - _fds[i].fd = sock->socket; - if (fds_buf[i].events & SYS_NET_POLLIN) - _fds[i].events |= POLLIN; - if (fds_buf[i].events & SYS_NET_POLLOUT) - _fds[i].events |= POLLOUT; + if (fds[i].events & SYS_NET_POLLOUT) + fds_buf[i].revents |= SYS_NET_POLLOUT; + + if (fds_buf[i].revents) + signaled++; + } + else if (sock->type == SYS_NET_SOCK_STREAM_P2P) + { + std::lock_guard lock(sock->mutex); + sys_net.trace("[P2PS] poll checking for 0x%X", fds[i].events); + if (sock->p2ps.status == lv2_socket::p2ps_i::stream_status::stream_connected) + { + if ((fds[i].events & SYS_NET_POLLIN) && sock->p2ps.data_available) + { + sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", sock->p2p.vport, sock->p2p.data.size()); + fds_buf[i].revents |= SYS_NET_POLLIN; + } + + if (fds[i].events & SYS_NET_POLLOUT) + fds_buf[i].revents |= SYS_NET_POLLOUT; + + if (fds_buf[i].revents) + signaled++; + } + } + else + { + // Check for fake packet for dns interceptions + const auto nph = g_fxo->get>(); + if (fds_buf[i].events & SYS_NET_POLLIN && nph->is_dns(fds_buf[i].fd) && nph->is_dns_queue(fds_buf[i].fd)) + fds_buf[i].revents |= SYS_NET_POLLIN; + + if (fds_buf[i].events & ~(SYS_NET_POLLIN | SYS_NET_POLLOUT | SYS_NET_POLLERR)) + sys_net.warning("sys_net_bnet_poll(fd=%d): events=0x%x", fds[i].fd, fds[i].events); + _fds[i].fd = sock->socket; + if (fds_buf[i].events & SYS_NET_POLLIN) + _fds[i].events |= POLLIN; + if (fds_buf[i].events & SYS_NET_POLLOUT) + _fds[i].events |= POLLOUT; + } #ifdef _WIN32 connecting[i] = sock->is_connecting; #endif @@ -2133,11 +3156,26 @@ error_code sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr((lv2_socket::id_base & -1024) + i)) { - _fds[i].fd = sock->socket; - if (selected & lv2_socket::poll::read) - _fds[i].events |= POLLIN; - if (selected & lv2_socket::poll::write) - _fds[i].events |= POLLOUT; + + if (sock->type != SYS_NET_SOCK_DGRAM_P2P) + { + _fds[i].fd = sock->socket; + if (selected & lv2_socket::poll::read) + _fds[i].events |= POLLIN; + if (selected & lv2_socket::poll::write) + _fds[i].events |= POLLOUT; + } + else + { + std::lock_guard lock(sock->mutex); + // Check if it's a bound P2P socket + if ((selected & lv2_socket::poll::read) && sock->p2p.vport && !sock->p2p.data.empty()) + { + sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", sock->p2p.vport, sock->p2p.data.size()); + rread.set(i); + signaled++; + } + } #ifdef _WIN32 connecting[i] = sock->is_connecting; #endif @@ -2354,11 +3392,11 @@ error_code sys_net_infoctl(ppu_thread& ppu, s32 cmd, vm::ptr arg) std::memcpy(buffer, nameserver.data(), nameserver.size()); const auto nph = g_fxo->get>(); - if (nph->get_dns()) + if (nph->get_dns_ip()) { struct sockaddr_in serv; std::memset(&serv, 0, sizeof(serv)); - serv.sin_addr.s_addr = nph->get_dns(); + serv.sin_addr.s_addr = nph->get_dns_ip(); inet_ntop(AF_INET, &serv.sin_addr, buffer + nameserver.size() - 1, sizeof(buffer) - nameserver.size()); } else diff --git a/rpcs3/Emu/Cell/lv2/sys_net.h b/rpcs3/Emu/Cell/lv2/sys_net.h index 28a14787be..7d69049b46 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net.h +++ b/rpcs3/Emu/Cell/lv2/sys_net.h @@ -8,6 +8,7 @@ #include #include #include +#include // Error codes enum sys_net_error : s32 @@ -356,6 +357,69 @@ struct lv2_socket final const lv2_socket_type type; const lv2_socket_family family; + // SYS_NET_SOCK_DGRAM_P2P and SYS_NET_SOCK_STREAM_P2P socket specific information + struct p2p_i + { + // Port(actual bound port) and Virtual Port(indicated by u16 at the start of the packet) + u16 port = 0, vport = 0; + // Queue containing received packets from network_thread for SYS_NET_SOCK_DGRAM_P2P sockets + std::queue>> data{}; + } p2p; + + struct p2ps_i + { + enum tcp_flags : u8 + { + FIN = (1 << 0), + SYN = (1 << 1), + RST = (1 << 2), + PSH = (1 << 3), + ACK = (1 << 4), + URG = (1 << 5), + ECE = (1 << 6), + CWR = (1 << 7), + }; + + static constexpr be_t U2S_sig = static_cast('U') << 24 | static_cast('2') << 16 | static_cast('S') << 8 | static_cast('0'); + static constexpr std::size_t MAX_RECEIVED_BUFFER = (1024*1024*10); + + // P2P stream socket specific + struct encapsulated_tcp + { + be_t signature = lv2_socket::p2ps_i::U2S_sig; // Signature to verify it's P2P Stream data + be_t length = 0; // Length of data + be_t seq = 0; // This should be u32 but changed to u64 for simplicity + be_t ack = 0; + be_t src_port = 0; // fake source tcp port + be_t dst_port = 0; // fake dest tcp port(should be == vport) + be_t checksum = 0; + u8 flags = 0; + }; + + enum stream_status + { + stream_closed, // Default when port is not listening nor connected + stream_listening, // Stream is listening, accepting SYN packets + stream_handshaking, // Currently handshaking + stream_connected, // This is an established connection(after tcp handshake) + }; + + stream_status status = stream_status::stream_closed; + + std::size_t max_backlog = 0; // set on listen + std::queue backlog; + + u16 op_port = 0, op_vport = 0; + u32 op_addr = 0; + + std::vector received_data; // resized if needed, on recv will give all continuous data available and move data to beginning of vector + u64 data_beg_seq = 0; // Seq of first byte of received_data + u32 data_available = 0; // Amount of continuous data available(calculated on ACK send) + std::map data_mapping; // holds seq/size of data received + + u32 cur_seq = 0; // SEQ of next packet to be sent + } p2ps; + // Value keepers #ifdef _WIN32 s32 so_reuseaddr = 0; diff --git a/rpcs3/Emu/NP/fb_helpers.cpp b/rpcs3/Emu/NP/fb_helpers.cpp new file mode 100644 index 0000000000..6a40c5fa02 --- /dev/null +++ b/rpcs3/Emu/NP/fb_helpers.cpp @@ -0,0 +1,406 @@ +#include "stdafx.h" +#include "np_handler.h" + +LOG_CHANNEL(rpcn_log, "rpcn"); + +void np_handler::BinAttr_to_SceNpMatching2BinAttr(const flatbuffers::Vector>* fb_attr, vm::ptr binattr_info) +{ + for (flatbuffers::uoffset_t i = 0; i < fb_attr->size(); i++) + { + auto bin_attr = fb_attr->Get(i); + binattr_info[i].id = bin_attr->id(); + binattr_info[i].size = bin_attr->data()->size(); + binattr_info[i].ptr = allocate(binattr_info[i].size); + for (flatbuffers::uoffset_t tmp_index = 0; tmp_index < bin_attr->data()->size(); tmp_index++) + { + binattr_info[i].ptr[tmp_index] = bin_attr->data()->Get(tmp_index); + } + } +} + +void np_handler::RoomGroup_to_SceNpMatching2RoomGroup(const flatbuffers::Vector>* fb_group, vm::ptr group_info) +{ + for (flatbuffers::uoffset_t i = 0; i < fb_group->size(); i++) + { + auto group = fb_group->Get(i); + group_info[i].groupId = group->groupId(); + group_info[i].withPassword = group->withPassword(); + group_info[i].withLabel = group->withLabel(); + if (group->label()) + { + for (flatbuffers::uoffset_t l_index = 0; l_index < group->label()->size(); l_index++) + { + group_info[i].label.data[l_index] = group->label()->Get(l_index); + } + } + group_info[i].slotNum = group->slotNum(); + group_info[i].curGroupMemberNum = group->curGroupMemberNum(); + } +} + +void np_handler::UserInfo2_to_SceNpUserInfo2(const UserInfo2* user, SceNpUserInfo2* user_info) +{ + if (user->npId()) + memcpy(user_info->npId.handle.data, user->npId()->c_str(), std::min(sizeof(user_info->npId.handle.data), static_cast(user->npId()->size()))); + + if (user->onlineName()) + { + user_info->onlineName.set(allocate(sizeof(SceNpOnlineName))); + memcpy(user_info->onlineName->data, user->onlineName()->c_str(), std::min(sizeof(user_info->onlineName->data), static_cast(user->onlineName()->size()))); + } + if (user->avatarUrl()) + { + user_info->avatarUrl.set(allocate(sizeof(SceNpAvatarUrl))); + memcpy(user_info->avatarUrl->data, user->avatarUrl()->c_str(), std::min(sizeof(user_info->avatarUrl->data), static_cast(user->avatarUrl()->size()))); + } +} + +void np_handler::SearchRoomReponse_to_SceNpMatching2SearchRoomResponse(const SearchRoomResponse* resp, SceNpMatching2SearchRoomResponse* search_resp) +{ + search_resp->range.size = resp->size(); + search_resp->range.startIndex = resp->startIndex(); + search_resp->range.total = resp->total(); + + if (resp->rooms() && resp->rooms()->size() != 0) + { + vm::addr_t previous_next = vm::cast(0); + for (flatbuffers::uoffset_t i = 0; i < resp->rooms()->size(); i++) + { + auto room = resp->rooms()->Get(i); + vm::ptr room_info(allocate(sizeof(SceNpMatching2RoomDataExternal))); + + if (i > 0) + { + vm::ptr prev_room(previous_next); + prev_room->next.set(room_info.addr()); + } + else + { + search_resp->roomDataExternal = room_info; + } + + previous_next = vm::cast(room_info.addr()); + + room_info->serverId = room->serverId(); + room_info->worldId = room->worldId(); + room_info->publicSlotNum = room->publicSlotNum(); + room_info->privateSlotNum = room->privateSlotNum(); + room_info->lobbyId = room->lobbyId(); + room_info->roomId = room->roomId(); + room_info->openPublicSlotNum = room->openPublicSlotNum(); + room_info->maxSlot = room->maxSlot(); + room_info->openPrivateSlotNum = room->openPrivateSlotNum(); + room_info->curMemberNum = room->curMemberNum(); + room_info->passwordSlotMask = room->curMemberNum(); + if (auto owner = room->owner()) + { + vm::ptr owner_info(allocate(sizeof(SceNpUserInfo2))); + UserInfo2_to_SceNpUserInfo2(owner, owner_info.get_ptr()); + room_info->owner = owner_info; + } + + if (room->roomGroup() && room->roomGroup()->size() != 0) + { + room_info->roomGroupNum = room->roomGroup()->size(); + vm::ptr group_info(allocate(sizeof(SceNpMatching2RoomGroup) * room_info->roomGroupNum)); + RoomGroup_to_SceNpMatching2RoomGroup(room->roomGroup(), group_info); + room_info->roomGroup = group_info; + } + + room_info->flagAttr = room->flagAttr(); + + if (room->roomSearchableIntAttrExternal() && room->roomSearchableIntAttrExternal()->size() != 0) + { + room_info->roomSearchableIntAttrExternalNum = room->roomSearchableIntAttrExternal()->size(); + vm::ptr intattr_info(allocate(sizeof(SceNpMatching2IntAttr) * room_info->roomSearchableIntAttrExternalNum)); + for (flatbuffers::uoffset_t a_index = 0; a_index < room->roomSearchableIntAttrExternal()->size(); a_index++) + { + auto int_attr = room->roomSearchableIntAttrExternal()->Get(a_index); + intattr_info[a_index].id = int_attr->id(); + intattr_info[a_index].num = int_attr->num(); + } + room_info->roomSearchableIntAttrExternal = intattr_info; + } + + if (room->roomSearchableBinAttrExternal() && room->roomSearchableBinAttrExternal()->size() != 0) + { + room_info->roomSearchableBinAttrExternalNum = room->roomSearchableBinAttrExternal()->size(); + vm::ptr binattr_info(allocate(sizeof(SceNpMatching2BinAttr) * room_info->roomSearchableBinAttrExternalNum)); + BinAttr_to_SceNpMatching2BinAttr(room->roomSearchableBinAttrExternal(), binattr_info); + room_info->roomSearchableBinAttrExternal = binattr_info; + } + + if (room->roomBinAttrExternal() && room->roomBinAttrExternal()->size() != 0) + { + room_info->roomBinAttrExternalNum = room->roomBinAttrExternal()->size(); + vm::ptr binattr_info(allocate(sizeof(SceNpMatching2BinAttr) * room_info->roomBinAttrExternalNum)); + BinAttr_to_SceNpMatching2BinAttr(room->roomBinAttrExternal(), binattr_info); + room_info->roomBinAttrExternal = binattr_info; + } + } + } + else + { + search_resp->roomDataExternal.set(0); + } +} + +u16 np_handler::RoomDataInternal_to_SceNpMatching2RoomDataInternal(const RoomDataInternal* resp, SceNpMatching2RoomDataInternal* room_info, const SceNpId& npid) +{ + u16 member_id = 0; + room_info->serverId = resp->serverId(); + room_info->worldId = resp->worldId(); + room_info->lobbyId = resp->lobbyId(); + room_info->roomId = resp->roomId(); + room_info->passwordSlotMask = resp->passwordSlotMask(); + room_info->maxSlot = resp->maxSlot(); + room_info->memberList.membersNum = resp->memberList()->size(); + + if (resp->roomGroup() && resp->roomGroup()->size() != 0) + { + room_info->roomGroupNum = resp->roomGroup()->size(); + vm::ptr group_info(allocate(sizeof(SceNpMatching2RoomGroup) * room_info->roomGroupNum)); + RoomGroup_to_SceNpMatching2RoomGroup(resp->roomGroup(), group_info); + room_info->roomGroup = group_info; + } + + vm::ptr prev_member; + for (flatbuffers::uoffset_t i = 0; i < resp->memberList()->size(); i++) + { + auto member = resp->memberList()->Get(i); + vm::ptr member_info(allocate(sizeof(SceNpMatching2RoomMemberDataInternal))); + + if (i > 0) + { + prev_member->next = member_info; + } + else + { + room_info->memberList.members = member_info; + room_info->memberList.membersNum = static_cast(resp->memberList()->size()); + } + + prev_member = member_info; + + UserInfo2_to_SceNpUserInfo2(member->userInfo(), &member_info->userInfo); + member_info->joinDate.tick = member->joinDate(); + member_info->memberId = member->memberId(); + member_info->teamId = member->teamId(); + + // Look for id + if (member->roomGroup() != 0) + { + bool found = false; + for (u32 g_index = 0; g_index < room_info->roomGroupNum; g_index++) + { + if (room_info->roomGroup[g_index].groupId == member->roomGroup()) + { + member_info->roomGroup = vm::cast(room_info->roomGroup.addr() + (sizeof(SceNpMatching2RoomGroup) * g_index)); + found = true; + } + } + ASSERT(found); + } + + member_info->natType = member->natType(); + member_info->flagAttr = member->flagAttr(); + + if (member->roomMemberBinAttrInternal() && member->roomMemberBinAttrInternal()->size() != 0) + { + member_info->roomMemberBinAttrInternalNum = member->roomMemberBinAttrInternal()->size(); + vm::ptr binattr_info(allocate(sizeof(SceNpMatching2RoomMemberBinAttrInternal) * member_info->roomMemberBinAttrInternalNum)); + for (u32 b_index = 0; b_index < member_info->roomMemberBinAttrInternalNum; b_index++) + { + const auto battr = member->roomMemberBinAttrInternal()->Get(b_index); + binattr_info[b_index].updateDate.tick = battr->updateDate(); + + binattr_info[b_index].data.id = battr->data()->id(); + binattr_info[b_index].data.size = battr->data()->data()->size(); + binattr_info[b_index].data.ptr = allocate(binattr_info[b_index].data.size); + for (flatbuffers::uoffset_t tmp_index = 0; tmp_index < binattr_info[b_index].data.size; tmp_index++) + { + binattr_info[b_index].data.ptr[tmp_index] = battr->data()->data()->Get(tmp_index); + } + } + member_info->roomMemberBinAttrInternal = binattr_info; + } + } + + vm::ptr ptr = room_info->memberList.members; + while (ptr) + { + if (strcmp(ptr->userInfo.npId.handle.data, npid.handle.data) == 0) + { + room_info->memberList.me = ptr; + member_id = ptr->memberId; + break; + } + ptr = ptr->next; + } + + ptr = room_info->memberList.members; + while (ptr) + { + if (ptr->memberId == resp->ownerId()) + { + room_info->memberList.owner = ptr; + break; + } + ptr = ptr->next; + } + + room_info->flagAttr = resp->flagAttr(); + + if (resp->roomBinAttrInternal() && resp->roomBinAttrInternal()->size() != 0) + { + room_info->roomBinAttrInternalNum = resp->roomBinAttrInternal()->size(); + vm::ptr binattrint_info(allocate(sizeof(SceNpMatching2RoomBinAttrInternal) * room_info->roomBinAttrInternalNum)); + + for (u32 b_index = 0; b_index < room_info->roomBinAttrInternalNum; b_index++) + { + auto battr = resp->roomBinAttrInternal()->Get(b_index); + binattrint_info[b_index].updateDate.tick = battr->updateDate(); + binattrint_info[b_index].updateMemberId = battr->updateMemberId(); + + binattrint_info[b_index].data.id = battr->data()->id(); + binattrint_info[b_index].data.size = battr->data()->data()->size(); + binattrint_info[b_index].data.ptr = allocate(binattrint_info[b_index].data.size); + for (flatbuffers::uoffset_t tmp_index = 0; tmp_index < binattrint_info[b_index].data.size; tmp_index++) + { + binattrint_info[b_index].data.ptr[tmp_index] = battr->data()->data()->Get(tmp_index); + } + } + + room_info->roomBinAttrInternal = binattrint_info; + } + + return member_id; +} + +void np_handler::RoomMemberUpdateInfo_to_SceNpMatching2RoomMemberUpdateInfo(const RoomMemberUpdateInfo* update_info, SceNpMatching2RoomMemberUpdateInfo* sce_update_info) +{ + sce_update_info->eventCause = 0; + if (update_info->optData()) + { + sce_update_info->optData.length = update_info->optData()->data()->size(); + for (size_t i = 0; i < 16; i++) + { + sce_update_info->optData.data[i] = update_info->optData()->data()->Get(i); + } + } + + if (update_info->roomMemberDataInternal()) + { + auto member = update_info->roomMemberDataInternal(); + vm::ptr member_info(allocate(sizeof(SceNpMatching2RoomMemberDataInternal))); + sce_update_info->roomMemberDataInternal = member_info; + + UserInfo2_to_SceNpUserInfo2(member->userInfo(), &member_info->userInfo); + member_info->joinDate.tick = member->joinDate(); + member_info->memberId = member->memberId(); + member_info->teamId = member->teamId(); + + // Look for id + // TODO + + member_info->natType = member->natType(); + member_info->flagAttr = member->flagAttr(); + + if (member->roomMemberBinAttrInternal() && member->roomMemberBinAttrInternal()->size() != 0) + { + member_info->roomMemberBinAttrInternalNum = member->roomMemberBinAttrInternal()->size(); + vm::ptr binattr_info(allocate(sizeof(SceNpMatching2RoomMemberBinAttrInternal) * member_info->roomMemberBinAttrInternalNum)); + for (u32 b_index = 0; b_index < member_info->roomMemberBinAttrInternalNum; b_index++) + { + const auto battr = member->roomMemberBinAttrInternal()->Get(b_index); + binattr_info[b_index].updateDate.tick = battr->updateDate(); + + binattr_info[b_index].data.id = battr->data()->id(); + binattr_info[b_index].data.size = battr->data()->data()->size(); + binattr_info[b_index].data.ptr = allocate(binattr_info[b_index].data.size); + for (flatbuffers::uoffset_t tmp_index = 0; tmp_index < binattr_info[b_index].data.size; tmp_index++) + { + binattr_info[b_index].data.ptr[tmp_index] = battr->data()->data()->Get(tmp_index); + } + } + member_info->roomMemberBinAttrInternal = binattr_info; + } + } +} + +void np_handler::RoomUpdateInfo_to_SceNpMatching2RoomUpdateInfo(const RoomUpdateInfo* update_info, SceNpMatching2RoomUpdateInfo* sce_update_info) +{ + sce_update_info->errorCode = 0; + sce_update_info->eventCause = 0; + if (update_info->optData()) + { + sce_update_info->optData.length = update_info->optData()->data()->size(); + for (size_t i = 0; i < 16; i++) + { + sce_update_info->optData.data[i] = update_info->optData()->data()->Get(i); + } + } +} + +void np_handler::GetPingInfoResponse_to_SceNpMatching2SignalingGetPingInfoResponse(const GetPingInfoResponse* resp, SceNpMatching2SignalingGetPingInfoResponse* sce_resp) +{ + sce_resp->serverId = resp->serverId(); + sce_resp->worldId = resp->worldId(); + sce_resp->roomId = resp->roomId(); + sce_resp->rtt = resp->rtt(); +} + +void np_handler::RoomMessageInfo_to_SceNpMatching2RoomMessageInfo(const RoomMessageInfo* mi, SceNpMatching2RoomMessageInfo* sce_mi) +{ + sce_mi->filtered = mi->filtered(); + sce_mi->castType = mi->castType(); + + if (sce_mi->castType != SCE_NP_MATCHING2_CASTTYPE_BROADCAST) + { + vm::ptr dst_info(allocate(sizeof(SceNpMatching2RoomMessageDestination))); + sce_mi->dst = dst_info; + } + + switch(sce_mi->castType) + { + case SCE_NP_MATCHING2_CASTTYPE_BROADCAST: + break; + case SCE_NP_MATCHING2_CASTTYPE_UNICAST: + sce_mi->dst->unicastTarget = mi->dst()->Get(0); + break; + case SCE_NP_MATCHING2_CASTTYPE_MULTICAST: + { + sce_mi->dst->multicastTarget.memberIdNum = mi->dst()->size(); + vm::ptr> member_list(allocate(sizeof(u16) * mi->dst()->size())); + sce_mi->dst->multicastTarget.memberId = member_list; + for (u32 i = 0; i < mi->dst()->size(); i++) + { + sce_mi->dst->multicastTarget.memberId[i] = mi->dst()->Get(i); + } + break; + } + case SCE_NP_MATCHING2_CASTTYPE_MULTICAST_TEAM: + sce_mi->dst->multicastTargetTeamId = mi->dst()->Get(0); + break; + default: + ASSERT(false); + } + + if (auto src_member = mi->srcMember()) + { + vm::ptr src_info(allocate(sizeof(SceNpUserInfo2))); + UserInfo2_to_SceNpUserInfo2(src_member, src_info.get_ptr()); + sce_mi->srcMember = src_info; + } + + if (auto msg = mi->msg()) + { + sce_mi->msgLen = msg->size(); + vm::ptr msg_data(allocate(msg->size())); + for (u32 i = 0; i < msg->size(); i++) + { + msg_data[i] = msg->Get(i); + } + sce_mi->msg = msg_data; + } +} diff --git a/rpcs3/Emu/NP/generated/np2_structs.fbs b/rpcs3/Emu/NP/generated/np2_structs.fbs new file mode 100644 index 0000000000..c1e016234e --- /dev/null +++ b/rpcs3/Emu/NP/generated/np2_structs.fbs @@ -0,0 +1,225 @@ +table BinAttr { + id:uint16; + data:[uint8]; +} + +table IntAttr { + id:uint16; + num:uint32; +} + +table MemberBinAttrInternal { + updateDate:uint64; + data:BinAttr; +} + +table BinAttrInternal { + updateDate:uint64; + updateMemberId:uint16; + data:BinAttr; +} + +table OptParam { + type:uint8; + flag:uint8; + hubMemberId:uint16 ; +} + +table GroupConfig { + slotNum:uint32; + withLabel:bool; + label:[uint8]; + withPassword:bool; +} + +table UserInfo2 { + npId:string; + onlineName:string; + avatarUrl:string; +} + +table RoomMemberDataInternal { + userInfo:UserInfo2; + joinDate:uint64; + memberId:uint16; + teamId:uint8; + roomGroup:uint8; + natType:uint8; + flagAttr:uint32; + roomMemberBinAttrInternal:[MemberBinAttrInternal]; +} + +table RoomGroup { + groupId:uint8; + withPassword:bool; + withLabel:bool; + label:[uint8]; + slotNum:uint32; + curGroupMemberNum:uint32; +} + +table RoomDataInternal { + serverId:uint16; + worldId:uint32; + lobbyId:uint64; + roomId:uint64; + passwordSlotMask:uint64; + maxSlot:uint32; + memberList:[RoomMemberDataInternal]; + ownerId:uint16; + roomGroup:[RoomGroup]; + flagAttr:uint32; + roomBinAttrInternal:[BinAttrInternal]; +} + +table RoomDataExternal { + serverId:uint16; + worldId:uint32; + publicSlotNum:uint16; + privateSlotNum:uint16; + lobbyId:uint64; + roomId:uint64; + openPublicSlotNum:uint16; + maxSlot:uint16; + openPrivateSlotNum:uint16; + curMemberNum:uint16; + passwordSlotMask:uint64; + owner:UserInfo2; + roomGroup:[RoomGroup]; + flagAttr:uint32; + roomSearchableIntAttrExternal:[IntAttr]; + roomSearchableBinAttrExternal:[BinAttr]; + roomBinAttrExternal:[BinAttr]; +} + +table IntSearchFilter { + searchOperator:uint8; + attr:IntAttr; +} + +table BinSearchFilter { + searchOperator:uint8; + attr:BinAttr; +} + +table PresenceOptionData { + data:[uint8]; + len:uint32; +} + +table RoomGroupPasswordConfig { + groupId:[uint8]; + withPassword:bool; +} + +table SearchRoomRequest { + option:int32; + worldId:uint32; + lobbyId:uint64; + rangeFilter_startIndex:uint32; + rangeFilter_max:uint32; + flagFilter:uint32; + flagAttr:uint32; + intFilter:[IntSearchFilter]; + binFilter:[BinSearchFilter]; + attrId:[uint16]; +} + +table SearchRoomResponse { + startIndex:uint32; + total:uint32; + size:uint32; + rooms:[RoomDataExternal]; +} + +table CreateJoinRoomRequest { + worldId:uint32; + lobbyId:uint64; + maxSlot:uint32; + flagAttr:uint32; + roomBinAttrInternal:[BinAttr]; + roomSearchableIntAttrExternal:[IntAttr]; + roomSearchableBinAttrExternal:[BinAttr]; + roomBinAttrExternal:[BinAttr]; + roomPassword:[uint8]; + groupConfig:[GroupConfig]; + passwordSlotMask:uint64; + allowedUser:[string]; + blockedUser:[string]; + + joinRoomGroupLabel:[uint8]; + roomMemberBinAttrInternal:[BinAttr]; + teamId:uint8; + sigOptParam:OptParam; +} + +table JoinRoomRequest { + roomId:uint64; + roomPassword:[uint8]; + joinRoomGroupLabel:[uint8]; + roomMemberBinAttrInternal:[BinAttr]; + optData:PresenceOptionData; + teamId:uint8; +} + +table LeaveRoomRequest { + roomId:uint64; + optData:PresenceOptionData; +} + +table SetRoomDataExternalRequest { + roomId:uint64; + roomSearchableIntAttrExternal:[IntAttr]; + roomSearchableBinAttrExternal:[BinAttr]; + roomBinAttrExternal:[BinAttr]; +} + +table SetRoomDataInternalRequest { + roomId:uint64; + flagFilter:uint32; + flagAttr:uint32; + roomBinAttrInternal:[BinAttr]; + passwordConfig:[RoomGroupPasswordConfig]; + passwordSlotMask:uint64; + ownerPrivilegeRank:[uint16]; +} + +table GetRoomDataInternalRequest { + roomId:uint64; + attrId:[uint16]; +} + +table RoomMemberUpdateInfo { + roomMemberDataInternal:RoomMemberDataInternal; + eventCause:uint8; + optData:PresenceOptionData; +} + +table RoomUpdateInfo { + eventCause:uint8; + errorCode:int32; + optData:PresenceOptionData; +} + +table GetPingInfoResponse { + serverId:uint16; + worldId:uint32; + roomId:uint64; + rtt:uint32; +} + +table SendRoomMessageRequest { + roomId:uint64; + castType:uint8; + dst:[uint16]; + msg:[uint8]; + option:uint8; +} + +table RoomMessageInfo { + filtered:bool; + castType:uint8; + dst:[uint16]; + srcMember:UserInfo2; + msg:[uint8]; +} diff --git a/rpcs3/Emu/NP/generated/np2_structs_generated.h b/rpcs3/Emu/NP/generated/np2_structs_generated.h new file mode 100644 index 0000000000..28930c35ae --- /dev/null +++ b/rpcs3/Emu/NP/generated/np2_structs_generated.h @@ -0,0 +1,2779 @@ +// automatically generated by the FlatBuffers compiler, do not modify + + +#ifndef FLATBUFFERS_GENERATED_NP2STRUCTS_H_ +#define FLATBUFFERS_GENERATED_NP2STRUCTS_H_ + +#include "flatbuffers/flatbuffers.h" + +struct BinAttr; + +struct IntAttr; + +struct MemberBinAttrInternal; + +struct BinAttrInternal; + +struct OptParam; + +struct GroupConfig; + +struct UserInfo2; + +struct RoomMemberDataInternal; + +struct RoomGroup; + +struct RoomDataInternal; + +struct RoomDataExternal; + +struct IntSearchFilter; + +struct BinSearchFilter; + +struct PresenceOptionData; + +struct RoomGroupPasswordConfig; + +struct SearchRoomRequest; + +struct SearchRoomResponse; + +struct CreateJoinRoomRequest; + +struct JoinRoomRequest; + +struct LeaveRoomRequest; + +struct SetRoomDataExternalRequest; + +struct SetRoomDataInternalRequest; + +struct GetRoomDataInternalRequest; + +struct RoomMemberUpdateInfo; + +struct RoomUpdateInfo; + +struct GetPingInfoResponse; + +struct SendRoomMessageRequest; + +struct RoomMessageInfo; + +struct BinAttr FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ID = 4, + VT_DATA = 6 + }; + uint16_t id() const { + return GetField(VT_ID, 0); + } + const flatbuffers::Vector *data() const { + return GetPointer *>(VT_DATA); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_ID) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + verifier.EndTable(); + } +}; + +struct BinAttrBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_id(uint16_t id) { + fbb_.AddElement(BinAttr::VT_ID, id, 0); + } + void add_data(flatbuffers::Offset> data) { + fbb_.AddOffset(BinAttr::VT_DATA, data); + } + explicit BinAttrBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + BinAttrBuilder &operator=(const BinAttrBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateBinAttr( + flatbuffers::FlatBufferBuilder &_fbb, + uint16_t id = 0, + flatbuffers::Offset> data = 0) { + BinAttrBuilder builder_(_fbb); + builder_.add_data(data); + builder_.add_id(id); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateBinAttrDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint16_t id = 0, + const std::vector *data = nullptr) { + auto data__ = data ? _fbb.CreateVector(*data) : 0; + return CreateBinAttr( + _fbb, + id, + data__); +} + +struct IntAttr FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ID = 4, + VT_NUM = 6 + }; + uint16_t id() const { + return GetField(VT_ID, 0); + } + uint32_t num() const { + return GetField(VT_NUM, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_ID) && + VerifyField(verifier, VT_NUM) && + verifier.EndTable(); + } +}; + +struct IntAttrBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_id(uint16_t id) { + fbb_.AddElement(IntAttr::VT_ID, id, 0); + } + void add_num(uint32_t num) { + fbb_.AddElement(IntAttr::VT_NUM, num, 0); + } + explicit IntAttrBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + IntAttrBuilder &operator=(const IntAttrBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateIntAttr( + flatbuffers::FlatBufferBuilder &_fbb, + uint16_t id = 0, + uint32_t num = 0) { + IntAttrBuilder builder_(_fbb); + builder_.add_num(num); + builder_.add_id(id); + return builder_.Finish(); +} + +struct MemberBinAttrInternal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_UPDATEDATE = 4, + VT_DATA = 6 + }; + uint64_t updateDate() const { + return GetField(VT_UPDATEDATE, 0); + } + const BinAttr *data() const { + return GetPointer(VT_DATA); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_UPDATEDATE) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyTable(data()) && + verifier.EndTable(); + } +}; + +struct MemberBinAttrInternalBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_updateDate(uint64_t updateDate) { + fbb_.AddElement(MemberBinAttrInternal::VT_UPDATEDATE, updateDate, 0); + } + void add_data(flatbuffers::Offset data) { + fbb_.AddOffset(MemberBinAttrInternal::VT_DATA, data); + } + explicit MemberBinAttrInternalBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + MemberBinAttrInternalBuilder &operator=(const MemberBinAttrInternalBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateMemberBinAttrInternal( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t updateDate = 0, + flatbuffers::Offset data = 0) { + MemberBinAttrInternalBuilder builder_(_fbb); + builder_.add_updateDate(updateDate); + builder_.add_data(data); + return builder_.Finish(); +} + +struct BinAttrInternal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_UPDATEDATE = 4, + VT_UPDATEMEMBERID = 6, + VT_DATA = 8 + }; + uint64_t updateDate() const { + return GetField(VT_UPDATEDATE, 0); + } + uint16_t updateMemberId() const { + return GetField(VT_UPDATEMEMBERID, 0); + } + const BinAttr *data() const { + return GetPointer(VT_DATA); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_UPDATEDATE) && + VerifyField(verifier, VT_UPDATEMEMBERID) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyTable(data()) && + verifier.EndTable(); + } +}; + +struct BinAttrInternalBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_updateDate(uint64_t updateDate) { + fbb_.AddElement(BinAttrInternal::VT_UPDATEDATE, updateDate, 0); + } + void add_updateMemberId(uint16_t updateMemberId) { + fbb_.AddElement(BinAttrInternal::VT_UPDATEMEMBERID, updateMemberId, 0); + } + void add_data(flatbuffers::Offset data) { + fbb_.AddOffset(BinAttrInternal::VT_DATA, data); + } + explicit BinAttrInternalBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + BinAttrInternalBuilder &operator=(const BinAttrInternalBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateBinAttrInternal( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t updateDate = 0, + uint16_t updateMemberId = 0, + flatbuffers::Offset data = 0) { + BinAttrInternalBuilder builder_(_fbb); + builder_.add_updateDate(updateDate); + builder_.add_data(data); + builder_.add_updateMemberId(updateMemberId); + return builder_.Finish(); +} + +struct OptParam FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_TYPE = 4, + VT_FLAG = 6, + VT_HUBMEMBERID = 8 + }; + uint8_t type() const { + return GetField(VT_TYPE, 0); + } + uint8_t flag() const { + return GetField(VT_FLAG, 0); + } + uint16_t hubMemberId() const { + return GetField(VT_HUBMEMBERID, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_TYPE) && + VerifyField(verifier, VT_FLAG) && + VerifyField(verifier, VT_HUBMEMBERID) && + verifier.EndTable(); + } +}; + +struct OptParamBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_type(uint8_t type) { + fbb_.AddElement(OptParam::VT_TYPE, type, 0); + } + void add_flag(uint8_t flag) { + fbb_.AddElement(OptParam::VT_FLAG, flag, 0); + } + void add_hubMemberId(uint16_t hubMemberId) { + fbb_.AddElement(OptParam::VT_HUBMEMBERID, hubMemberId, 0); + } + explicit OptParamBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + OptParamBuilder &operator=(const OptParamBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateOptParam( + flatbuffers::FlatBufferBuilder &_fbb, + uint8_t type = 0, + uint8_t flag = 0, + uint16_t hubMemberId = 0) { + OptParamBuilder builder_(_fbb); + builder_.add_hubMemberId(hubMemberId); + builder_.add_flag(flag); + builder_.add_type(type); + return builder_.Finish(); +} + +struct GroupConfig FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SLOTNUM = 4, + VT_WITHLABEL = 6, + VT_LABEL = 8, + VT_WITHPASSWORD = 10 + }; + uint32_t slotNum() const { + return GetField(VT_SLOTNUM, 0); + } + bool withLabel() const { + return GetField(VT_WITHLABEL, 0) != 0; + } + const flatbuffers::Vector *label() const { + return GetPointer *>(VT_LABEL); + } + bool withPassword() const { + return GetField(VT_WITHPASSWORD, 0) != 0; + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_SLOTNUM) && + VerifyField(verifier, VT_WITHLABEL) && + VerifyOffset(verifier, VT_LABEL) && + verifier.VerifyVector(label()) && + VerifyField(verifier, VT_WITHPASSWORD) && + verifier.EndTable(); + } +}; + +struct GroupConfigBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_slotNum(uint32_t slotNum) { + fbb_.AddElement(GroupConfig::VT_SLOTNUM, slotNum, 0); + } + void add_withLabel(bool withLabel) { + fbb_.AddElement(GroupConfig::VT_WITHLABEL, static_cast(withLabel), 0); + } + void add_label(flatbuffers::Offset> label) { + fbb_.AddOffset(GroupConfig::VT_LABEL, label); + } + void add_withPassword(bool withPassword) { + fbb_.AddElement(GroupConfig::VT_WITHPASSWORD, static_cast(withPassword), 0); + } + explicit GroupConfigBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + GroupConfigBuilder &operator=(const GroupConfigBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateGroupConfig( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t slotNum = 0, + bool withLabel = false, + flatbuffers::Offset> label = 0, + bool withPassword = false) { + GroupConfigBuilder builder_(_fbb); + builder_.add_label(label); + builder_.add_slotNum(slotNum); + builder_.add_withPassword(withPassword); + builder_.add_withLabel(withLabel); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateGroupConfigDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t slotNum = 0, + bool withLabel = false, + const std::vector *label = nullptr, + bool withPassword = false) { + auto label__ = label ? _fbb.CreateVector(*label) : 0; + return CreateGroupConfig( + _fbb, + slotNum, + withLabel, + label__, + withPassword); +} + +struct UserInfo2 FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_NPID = 4, + VT_ONLINENAME = 6, + VT_AVATARURL = 8 + }; + const flatbuffers::String *npId() const { + return GetPointer(VT_NPID); + } + const flatbuffers::String *onlineName() const { + return GetPointer(VT_ONLINENAME); + } + const flatbuffers::String *avatarUrl() const { + return GetPointer(VT_AVATARURL); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_NPID) && + verifier.VerifyString(npId()) && + VerifyOffset(verifier, VT_ONLINENAME) && + verifier.VerifyString(onlineName()) && + VerifyOffset(verifier, VT_AVATARURL) && + verifier.VerifyString(avatarUrl()) && + verifier.EndTable(); + } +}; + +struct UserInfo2Builder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_npId(flatbuffers::Offset npId) { + fbb_.AddOffset(UserInfo2::VT_NPID, npId); + } + void add_onlineName(flatbuffers::Offset onlineName) { + fbb_.AddOffset(UserInfo2::VT_ONLINENAME, onlineName); + } + void add_avatarUrl(flatbuffers::Offset avatarUrl) { + fbb_.AddOffset(UserInfo2::VT_AVATARURL, avatarUrl); + } + explicit UserInfo2Builder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + UserInfo2Builder &operator=(const UserInfo2Builder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateUserInfo2( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset npId = 0, + flatbuffers::Offset onlineName = 0, + flatbuffers::Offset avatarUrl = 0) { + UserInfo2Builder builder_(_fbb); + builder_.add_avatarUrl(avatarUrl); + builder_.add_onlineName(onlineName); + builder_.add_npId(npId); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateUserInfo2Direct( + flatbuffers::FlatBufferBuilder &_fbb, + const char *npId = nullptr, + const char *onlineName = nullptr, + const char *avatarUrl = nullptr) { + auto npId__ = npId ? _fbb.CreateString(npId) : 0; + auto onlineName__ = onlineName ? _fbb.CreateString(onlineName) : 0; + auto avatarUrl__ = avatarUrl ? _fbb.CreateString(avatarUrl) : 0; + return CreateUserInfo2( + _fbb, + npId__, + onlineName__, + avatarUrl__); +} + +struct RoomMemberDataInternal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_USERINFO = 4, + VT_JOINDATE = 6, + VT_MEMBERID = 8, + VT_TEAMID = 10, + VT_ROOMGROUP = 12, + VT_NATTYPE = 14, + VT_FLAGATTR = 16, + VT_ROOMMEMBERBINATTRINTERNAL = 18 + }; + const UserInfo2 *userInfo() const { + return GetPointer(VT_USERINFO); + } + uint64_t joinDate() const { + return GetField(VT_JOINDATE, 0); + } + uint16_t memberId() const { + return GetField(VT_MEMBERID, 0); + } + uint8_t teamId() const { + return GetField(VT_TEAMID, 0); + } + uint8_t roomGroup() const { + return GetField(VT_ROOMGROUP, 0); + } + uint8_t natType() const { + return GetField(VT_NATTYPE, 0); + } + uint32_t flagAttr() const { + return GetField(VT_FLAGATTR, 0); + } + const flatbuffers::Vector> *roomMemberBinAttrInternal() const { + return GetPointer> *>(VT_ROOMMEMBERBINATTRINTERNAL); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_USERINFO) && + verifier.VerifyTable(userInfo()) && + VerifyField(verifier, VT_JOINDATE) && + VerifyField(verifier, VT_MEMBERID) && + VerifyField(verifier, VT_TEAMID) && + VerifyField(verifier, VT_ROOMGROUP) && + VerifyField(verifier, VT_NATTYPE) && + VerifyField(verifier, VT_FLAGATTR) && + VerifyOffset(verifier, VT_ROOMMEMBERBINATTRINTERNAL) && + verifier.VerifyVector(roomMemberBinAttrInternal()) && + verifier.VerifyVectorOfTables(roomMemberBinAttrInternal()) && + verifier.EndTable(); + } +}; + +struct RoomMemberDataInternalBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_userInfo(flatbuffers::Offset userInfo) { + fbb_.AddOffset(RoomMemberDataInternal::VT_USERINFO, userInfo); + } + void add_joinDate(uint64_t joinDate) { + fbb_.AddElement(RoomMemberDataInternal::VT_JOINDATE, joinDate, 0); + } + void add_memberId(uint16_t memberId) { + fbb_.AddElement(RoomMemberDataInternal::VT_MEMBERID, memberId, 0); + } + void add_teamId(uint8_t teamId) { + fbb_.AddElement(RoomMemberDataInternal::VT_TEAMID, teamId, 0); + } + void add_roomGroup(uint8_t roomGroup) { + fbb_.AddElement(RoomMemberDataInternal::VT_ROOMGROUP, roomGroup, 0); + } + void add_natType(uint8_t natType) { + fbb_.AddElement(RoomMemberDataInternal::VT_NATTYPE, natType, 0); + } + void add_flagAttr(uint32_t flagAttr) { + fbb_.AddElement(RoomMemberDataInternal::VT_FLAGATTR, flagAttr, 0); + } + void add_roomMemberBinAttrInternal(flatbuffers::Offset>> roomMemberBinAttrInternal) { + fbb_.AddOffset(RoomMemberDataInternal::VT_ROOMMEMBERBINATTRINTERNAL, roomMemberBinAttrInternal); + } + explicit RoomMemberDataInternalBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + RoomMemberDataInternalBuilder &operator=(const RoomMemberDataInternalBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRoomMemberDataInternal( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset userInfo = 0, + uint64_t joinDate = 0, + uint16_t memberId = 0, + uint8_t teamId = 0, + uint8_t roomGroup = 0, + uint8_t natType = 0, + uint32_t flagAttr = 0, + flatbuffers::Offset>> roomMemberBinAttrInternal = 0) { + RoomMemberDataInternalBuilder builder_(_fbb); + builder_.add_joinDate(joinDate); + builder_.add_roomMemberBinAttrInternal(roomMemberBinAttrInternal); + builder_.add_flagAttr(flagAttr); + builder_.add_userInfo(userInfo); + builder_.add_memberId(memberId); + builder_.add_natType(natType); + builder_.add_roomGroup(roomGroup); + builder_.add_teamId(teamId); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateRoomMemberDataInternalDirect( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset userInfo = 0, + uint64_t joinDate = 0, + uint16_t memberId = 0, + uint8_t teamId = 0, + uint8_t roomGroup = 0, + uint8_t natType = 0, + uint32_t flagAttr = 0, + const std::vector> *roomMemberBinAttrInternal = nullptr) { + auto roomMemberBinAttrInternal__ = roomMemberBinAttrInternal ? _fbb.CreateVector>(*roomMemberBinAttrInternal) : 0; + return CreateRoomMemberDataInternal( + _fbb, + userInfo, + joinDate, + memberId, + teamId, + roomGroup, + natType, + flagAttr, + roomMemberBinAttrInternal__); +} + +struct RoomGroup FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_GROUPID = 4, + VT_WITHPASSWORD = 6, + VT_WITHLABEL = 8, + VT_LABEL = 10, + VT_SLOTNUM = 12, + VT_CURGROUPMEMBERNUM = 14 + }; + uint8_t groupId() const { + return GetField(VT_GROUPID, 0); + } + bool withPassword() const { + return GetField(VT_WITHPASSWORD, 0) != 0; + } + bool withLabel() const { + return GetField(VT_WITHLABEL, 0) != 0; + } + const flatbuffers::Vector *label() const { + return GetPointer *>(VT_LABEL); + } + uint32_t slotNum() const { + return GetField(VT_SLOTNUM, 0); + } + uint32_t curGroupMemberNum() const { + return GetField(VT_CURGROUPMEMBERNUM, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_GROUPID) && + VerifyField(verifier, VT_WITHPASSWORD) && + VerifyField(verifier, VT_WITHLABEL) && + VerifyOffset(verifier, VT_LABEL) && + verifier.VerifyVector(label()) && + VerifyField(verifier, VT_SLOTNUM) && + VerifyField(verifier, VT_CURGROUPMEMBERNUM) && + verifier.EndTable(); + } +}; + +struct RoomGroupBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_groupId(uint8_t groupId) { + fbb_.AddElement(RoomGroup::VT_GROUPID, groupId, 0); + } + void add_withPassword(bool withPassword) { + fbb_.AddElement(RoomGroup::VT_WITHPASSWORD, static_cast(withPassword), 0); + } + void add_withLabel(bool withLabel) { + fbb_.AddElement(RoomGroup::VT_WITHLABEL, static_cast(withLabel), 0); + } + void add_label(flatbuffers::Offset> label) { + fbb_.AddOffset(RoomGroup::VT_LABEL, label); + } + void add_slotNum(uint32_t slotNum) { + fbb_.AddElement(RoomGroup::VT_SLOTNUM, slotNum, 0); + } + void add_curGroupMemberNum(uint32_t curGroupMemberNum) { + fbb_.AddElement(RoomGroup::VT_CURGROUPMEMBERNUM, curGroupMemberNum, 0); + } + explicit RoomGroupBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + RoomGroupBuilder &operator=(const RoomGroupBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRoomGroup( + flatbuffers::FlatBufferBuilder &_fbb, + uint8_t groupId = 0, + bool withPassword = false, + bool withLabel = false, + flatbuffers::Offset> label = 0, + uint32_t slotNum = 0, + uint32_t curGroupMemberNum = 0) { + RoomGroupBuilder builder_(_fbb); + builder_.add_curGroupMemberNum(curGroupMemberNum); + builder_.add_slotNum(slotNum); + builder_.add_label(label); + builder_.add_withLabel(withLabel); + builder_.add_withPassword(withPassword); + builder_.add_groupId(groupId); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateRoomGroupDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint8_t groupId = 0, + bool withPassword = false, + bool withLabel = false, + const std::vector *label = nullptr, + uint32_t slotNum = 0, + uint32_t curGroupMemberNum = 0) { + auto label__ = label ? _fbb.CreateVector(*label) : 0; + return CreateRoomGroup( + _fbb, + groupId, + withPassword, + withLabel, + label__, + slotNum, + curGroupMemberNum); +} + +struct RoomDataInternal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SERVERID = 4, + VT_WORLDID = 6, + VT_LOBBYID = 8, + VT_ROOMID = 10, + VT_PASSWORDSLOTMASK = 12, + VT_MAXSLOT = 14, + VT_MEMBERLIST = 16, + VT_OWNERID = 18, + VT_ROOMGROUP = 20, + VT_FLAGATTR = 22, + VT_ROOMBINATTRINTERNAL = 24 + }; + uint16_t serverId() const { + return GetField(VT_SERVERID, 0); + } + uint32_t worldId() const { + return GetField(VT_WORLDID, 0); + } + uint64_t lobbyId() const { + return GetField(VT_LOBBYID, 0); + } + uint64_t roomId() const { + return GetField(VT_ROOMID, 0); + } + uint64_t passwordSlotMask() const { + return GetField(VT_PASSWORDSLOTMASK, 0); + } + uint32_t maxSlot() const { + return GetField(VT_MAXSLOT, 0); + } + const flatbuffers::Vector> *memberList() const { + return GetPointer> *>(VT_MEMBERLIST); + } + uint16_t ownerId() const { + return GetField(VT_OWNERID, 0); + } + const flatbuffers::Vector> *roomGroup() const { + return GetPointer> *>(VT_ROOMGROUP); + } + uint32_t flagAttr() const { + return GetField(VT_FLAGATTR, 0); + } + const flatbuffers::Vector> *roomBinAttrInternal() const { + return GetPointer> *>(VT_ROOMBINATTRINTERNAL); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_SERVERID) && + VerifyField(verifier, VT_WORLDID) && + VerifyField(verifier, VT_LOBBYID) && + VerifyField(verifier, VT_ROOMID) && + VerifyField(verifier, VT_PASSWORDSLOTMASK) && + VerifyField(verifier, VT_MAXSLOT) && + VerifyOffset(verifier, VT_MEMBERLIST) && + verifier.VerifyVector(memberList()) && + verifier.VerifyVectorOfTables(memberList()) && + VerifyField(verifier, VT_OWNERID) && + VerifyOffset(verifier, VT_ROOMGROUP) && + verifier.VerifyVector(roomGroup()) && + verifier.VerifyVectorOfTables(roomGroup()) && + VerifyField(verifier, VT_FLAGATTR) && + VerifyOffset(verifier, VT_ROOMBINATTRINTERNAL) && + verifier.VerifyVector(roomBinAttrInternal()) && + verifier.VerifyVectorOfTables(roomBinAttrInternal()) && + verifier.EndTable(); + } +}; + +struct RoomDataInternalBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_serverId(uint16_t serverId) { + fbb_.AddElement(RoomDataInternal::VT_SERVERID, serverId, 0); + } + void add_worldId(uint32_t worldId) { + fbb_.AddElement(RoomDataInternal::VT_WORLDID, worldId, 0); + } + void add_lobbyId(uint64_t lobbyId) { + fbb_.AddElement(RoomDataInternal::VT_LOBBYID, lobbyId, 0); + } + void add_roomId(uint64_t roomId) { + fbb_.AddElement(RoomDataInternal::VT_ROOMID, roomId, 0); + } + void add_passwordSlotMask(uint64_t passwordSlotMask) { + fbb_.AddElement(RoomDataInternal::VT_PASSWORDSLOTMASK, passwordSlotMask, 0); + } + void add_maxSlot(uint32_t maxSlot) { + fbb_.AddElement(RoomDataInternal::VT_MAXSLOT, maxSlot, 0); + } + void add_memberList(flatbuffers::Offset>> memberList) { + fbb_.AddOffset(RoomDataInternal::VT_MEMBERLIST, memberList); + } + void add_ownerId(uint16_t ownerId) { + fbb_.AddElement(RoomDataInternal::VT_OWNERID, ownerId, 0); + } + void add_roomGroup(flatbuffers::Offset>> roomGroup) { + fbb_.AddOffset(RoomDataInternal::VT_ROOMGROUP, roomGroup); + } + void add_flagAttr(uint32_t flagAttr) { + fbb_.AddElement(RoomDataInternal::VT_FLAGATTR, flagAttr, 0); + } + void add_roomBinAttrInternal(flatbuffers::Offset>> roomBinAttrInternal) { + fbb_.AddOffset(RoomDataInternal::VT_ROOMBINATTRINTERNAL, roomBinAttrInternal); + } + explicit RoomDataInternalBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + RoomDataInternalBuilder &operator=(const RoomDataInternalBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRoomDataInternal( + flatbuffers::FlatBufferBuilder &_fbb, + uint16_t serverId = 0, + uint32_t worldId = 0, + uint64_t lobbyId = 0, + uint64_t roomId = 0, + uint64_t passwordSlotMask = 0, + uint32_t maxSlot = 0, + flatbuffers::Offset>> memberList = 0, + uint16_t ownerId = 0, + flatbuffers::Offset>> roomGroup = 0, + uint32_t flagAttr = 0, + flatbuffers::Offset>> roomBinAttrInternal = 0) { + RoomDataInternalBuilder builder_(_fbb); + builder_.add_passwordSlotMask(passwordSlotMask); + builder_.add_roomId(roomId); + builder_.add_lobbyId(lobbyId); + builder_.add_roomBinAttrInternal(roomBinAttrInternal); + builder_.add_flagAttr(flagAttr); + builder_.add_roomGroup(roomGroup); + builder_.add_memberList(memberList); + builder_.add_maxSlot(maxSlot); + builder_.add_worldId(worldId); + builder_.add_ownerId(ownerId); + builder_.add_serverId(serverId); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateRoomDataInternalDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint16_t serverId = 0, + uint32_t worldId = 0, + uint64_t lobbyId = 0, + uint64_t roomId = 0, + uint64_t passwordSlotMask = 0, + uint32_t maxSlot = 0, + const std::vector> *memberList = nullptr, + uint16_t ownerId = 0, + const std::vector> *roomGroup = nullptr, + uint32_t flagAttr = 0, + const std::vector> *roomBinAttrInternal = nullptr) { + auto memberList__ = memberList ? _fbb.CreateVector>(*memberList) : 0; + auto roomGroup__ = roomGroup ? _fbb.CreateVector>(*roomGroup) : 0; + auto roomBinAttrInternal__ = roomBinAttrInternal ? _fbb.CreateVector>(*roomBinAttrInternal) : 0; + return CreateRoomDataInternal( + _fbb, + serverId, + worldId, + lobbyId, + roomId, + passwordSlotMask, + maxSlot, + memberList__, + ownerId, + roomGroup__, + flagAttr, + roomBinAttrInternal__); +} + +struct RoomDataExternal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SERVERID = 4, + VT_WORLDID = 6, + VT_PUBLICSLOTNUM = 8, + VT_PRIVATESLOTNUM = 10, + VT_LOBBYID = 12, + VT_ROOMID = 14, + VT_OPENPUBLICSLOTNUM = 16, + VT_MAXSLOT = 18, + VT_OPENPRIVATESLOTNUM = 20, + VT_CURMEMBERNUM = 22, + VT_PASSWORDSLOTMASK = 24, + VT_OWNER = 26, + VT_ROOMGROUP = 28, + VT_FLAGATTR = 30, + VT_ROOMSEARCHABLEINTATTREXTERNAL = 32, + VT_ROOMSEARCHABLEBINATTREXTERNAL = 34, + VT_ROOMBINATTREXTERNAL = 36 + }; + uint16_t serverId() const { + return GetField(VT_SERVERID, 0); + } + uint32_t worldId() const { + return GetField(VT_WORLDID, 0); + } + uint16_t publicSlotNum() const { + return GetField(VT_PUBLICSLOTNUM, 0); + } + uint16_t privateSlotNum() const { + return GetField(VT_PRIVATESLOTNUM, 0); + } + uint64_t lobbyId() const { + return GetField(VT_LOBBYID, 0); + } + uint64_t roomId() const { + return GetField(VT_ROOMID, 0); + } + uint16_t openPublicSlotNum() const { + return GetField(VT_OPENPUBLICSLOTNUM, 0); + } + uint16_t maxSlot() const { + return GetField(VT_MAXSLOT, 0); + } + uint16_t openPrivateSlotNum() const { + return GetField(VT_OPENPRIVATESLOTNUM, 0); + } + uint16_t curMemberNum() const { + return GetField(VT_CURMEMBERNUM, 0); + } + uint64_t passwordSlotMask() const { + return GetField(VT_PASSWORDSLOTMASK, 0); + } + const UserInfo2 *owner() const { + return GetPointer(VT_OWNER); + } + const flatbuffers::Vector> *roomGroup() const { + return GetPointer> *>(VT_ROOMGROUP); + } + uint32_t flagAttr() const { + return GetField(VT_FLAGATTR, 0); + } + const flatbuffers::Vector> *roomSearchableIntAttrExternal() const { + return GetPointer> *>(VT_ROOMSEARCHABLEINTATTREXTERNAL); + } + const flatbuffers::Vector> *roomSearchableBinAttrExternal() const { + return GetPointer> *>(VT_ROOMSEARCHABLEBINATTREXTERNAL); + } + const flatbuffers::Vector> *roomBinAttrExternal() const { + return GetPointer> *>(VT_ROOMBINATTREXTERNAL); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_SERVERID) && + VerifyField(verifier, VT_WORLDID) && + VerifyField(verifier, VT_PUBLICSLOTNUM) && + VerifyField(verifier, VT_PRIVATESLOTNUM) && + VerifyField(verifier, VT_LOBBYID) && + VerifyField(verifier, VT_ROOMID) && + VerifyField(verifier, VT_OPENPUBLICSLOTNUM) && + VerifyField(verifier, VT_MAXSLOT) && + VerifyField(verifier, VT_OPENPRIVATESLOTNUM) && + VerifyField(verifier, VT_CURMEMBERNUM) && + VerifyField(verifier, VT_PASSWORDSLOTMASK) && + VerifyOffset(verifier, VT_OWNER) && + verifier.VerifyTable(owner()) && + VerifyOffset(verifier, VT_ROOMGROUP) && + verifier.VerifyVector(roomGroup()) && + verifier.VerifyVectorOfTables(roomGroup()) && + VerifyField(verifier, VT_FLAGATTR) && + VerifyOffset(verifier, VT_ROOMSEARCHABLEINTATTREXTERNAL) && + verifier.VerifyVector(roomSearchableIntAttrExternal()) && + verifier.VerifyVectorOfTables(roomSearchableIntAttrExternal()) && + VerifyOffset(verifier, VT_ROOMSEARCHABLEBINATTREXTERNAL) && + verifier.VerifyVector(roomSearchableBinAttrExternal()) && + verifier.VerifyVectorOfTables(roomSearchableBinAttrExternal()) && + VerifyOffset(verifier, VT_ROOMBINATTREXTERNAL) && + verifier.VerifyVector(roomBinAttrExternal()) && + verifier.VerifyVectorOfTables(roomBinAttrExternal()) && + verifier.EndTable(); + } +}; + +struct RoomDataExternalBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_serverId(uint16_t serverId) { + fbb_.AddElement(RoomDataExternal::VT_SERVERID, serverId, 0); + } + void add_worldId(uint32_t worldId) { + fbb_.AddElement(RoomDataExternal::VT_WORLDID, worldId, 0); + } + void add_publicSlotNum(uint16_t publicSlotNum) { + fbb_.AddElement(RoomDataExternal::VT_PUBLICSLOTNUM, publicSlotNum, 0); + } + void add_privateSlotNum(uint16_t privateSlotNum) { + fbb_.AddElement(RoomDataExternal::VT_PRIVATESLOTNUM, privateSlotNum, 0); + } + void add_lobbyId(uint64_t lobbyId) { + fbb_.AddElement(RoomDataExternal::VT_LOBBYID, lobbyId, 0); + } + void add_roomId(uint64_t roomId) { + fbb_.AddElement(RoomDataExternal::VT_ROOMID, roomId, 0); + } + void add_openPublicSlotNum(uint16_t openPublicSlotNum) { + fbb_.AddElement(RoomDataExternal::VT_OPENPUBLICSLOTNUM, openPublicSlotNum, 0); + } + void add_maxSlot(uint16_t maxSlot) { + fbb_.AddElement(RoomDataExternal::VT_MAXSLOT, maxSlot, 0); + } + void add_openPrivateSlotNum(uint16_t openPrivateSlotNum) { + fbb_.AddElement(RoomDataExternal::VT_OPENPRIVATESLOTNUM, openPrivateSlotNum, 0); + } + void add_curMemberNum(uint16_t curMemberNum) { + fbb_.AddElement(RoomDataExternal::VT_CURMEMBERNUM, curMemberNum, 0); + } + void add_passwordSlotMask(uint64_t passwordSlotMask) { + fbb_.AddElement(RoomDataExternal::VT_PASSWORDSLOTMASK, passwordSlotMask, 0); + } + void add_owner(flatbuffers::Offset owner) { + fbb_.AddOffset(RoomDataExternal::VT_OWNER, owner); + } + void add_roomGroup(flatbuffers::Offset>> roomGroup) { + fbb_.AddOffset(RoomDataExternal::VT_ROOMGROUP, roomGroup); + } + void add_flagAttr(uint32_t flagAttr) { + fbb_.AddElement(RoomDataExternal::VT_FLAGATTR, flagAttr, 0); + } + void add_roomSearchableIntAttrExternal(flatbuffers::Offset>> roomSearchableIntAttrExternal) { + fbb_.AddOffset(RoomDataExternal::VT_ROOMSEARCHABLEINTATTREXTERNAL, roomSearchableIntAttrExternal); + } + void add_roomSearchableBinAttrExternal(flatbuffers::Offset>> roomSearchableBinAttrExternal) { + fbb_.AddOffset(RoomDataExternal::VT_ROOMSEARCHABLEBINATTREXTERNAL, roomSearchableBinAttrExternal); + } + void add_roomBinAttrExternal(flatbuffers::Offset>> roomBinAttrExternal) { + fbb_.AddOffset(RoomDataExternal::VT_ROOMBINATTREXTERNAL, roomBinAttrExternal); + } + explicit RoomDataExternalBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + RoomDataExternalBuilder &operator=(const RoomDataExternalBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRoomDataExternal( + flatbuffers::FlatBufferBuilder &_fbb, + uint16_t serverId = 0, + uint32_t worldId = 0, + uint16_t publicSlotNum = 0, + uint16_t privateSlotNum = 0, + uint64_t lobbyId = 0, + uint64_t roomId = 0, + uint16_t openPublicSlotNum = 0, + uint16_t maxSlot = 0, + uint16_t openPrivateSlotNum = 0, + uint16_t curMemberNum = 0, + uint64_t passwordSlotMask = 0, + flatbuffers::Offset owner = 0, + flatbuffers::Offset>> roomGroup = 0, + uint32_t flagAttr = 0, + flatbuffers::Offset>> roomSearchableIntAttrExternal = 0, + flatbuffers::Offset>> roomSearchableBinAttrExternal = 0, + flatbuffers::Offset>> roomBinAttrExternal = 0) { + RoomDataExternalBuilder builder_(_fbb); + builder_.add_passwordSlotMask(passwordSlotMask); + builder_.add_roomId(roomId); + builder_.add_lobbyId(lobbyId); + builder_.add_roomBinAttrExternal(roomBinAttrExternal); + builder_.add_roomSearchableBinAttrExternal(roomSearchableBinAttrExternal); + builder_.add_roomSearchableIntAttrExternal(roomSearchableIntAttrExternal); + builder_.add_flagAttr(flagAttr); + builder_.add_roomGroup(roomGroup); + builder_.add_owner(owner); + builder_.add_worldId(worldId); + builder_.add_curMemberNum(curMemberNum); + builder_.add_openPrivateSlotNum(openPrivateSlotNum); + builder_.add_maxSlot(maxSlot); + builder_.add_openPublicSlotNum(openPublicSlotNum); + builder_.add_privateSlotNum(privateSlotNum); + builder_.add_publicSlotNum(publicSlotNum); + builder_.add_serverId(serverId); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateRoomDataExternalDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint16_t serverId = 0, + uint32_t worldId = 0, + uint16_t publicSlotNum = 0, + uint16_t privateSlotNum = 0, + uint64_t lobbyId = 0, + uint64_t roomId = 0, + uint16_t openPublicSlotNum = 0, + uint16_t maxSlot = 0, + uint16_t openPrivateSlotNum = 0, + uint16_t curMemberNum = 0, + uint64_t passwordSlotMask = 0, + flatbuffers::Offset owner = 0, + const std::vector> *roomGroup = nullptr, + uint32_t flagAttr = 0, + const std::vector> *roomSearchableIntAttrExternal = nullptr, + const std::vector> *roomSearchableBinAttrExternal = nullptr, + const std::vector> *roomBinAttrExternal = nullptr) { + auto roomGroup__ = roomGroup ? _fbb.CreateVector>(*roomGroup) : 0; + auto roomSearchableIntAttrExternal__ = roomSearchableIntAttrExternal ? _fbb.CreateVector>(*roomSearchableIntAttrExternal) : 0; + auto roomSearchableBinAttrExternal__ = roomSearchableBinAttrExternal ? _fbb.CreateVector>(*roomSearchableBinAttrExternal) : 0; + auto roomBinAttrExternal__ = roomBinAttrExternal ? _fbb.CreateVector>(*roomBinAttrExternal) : 0; + return CreateRoomDataExternal( + _fbb, + serverId, + worldId, + publicSlotNum, + privateSlotNum, + lobbyId, + roomId, + openPublicSlotNum, + maxSlot, + openPrivateSlotNum, + curMemberNum, + passwordSlotMask, + owner, + roomGroup__, + flagAttr, + roomSearchableIntAttrExternal__, + roomSearchableBinAttrExternal__, + roomBinAttrExternal__); +} + +struct IntSearchFilter FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SEARCHOPERATOR = 4, + VT_ATTR = 6 + }; + uint8_t searchOperator() const { + return GetField(VT_SEARCHOPERATOR, 0); + } + const IntAttr *attr() const { + return GetPointer(VT_ATTR); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_SEARCHOPERATOR) && + VerifyOffset(verifier, VT_ATTR) && + verifier.VerifyTable(attr()) && + verifier.EndTable(); + } +}; + +struct IntSearchFilterBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_searchOperator(uint8_t searchOperator) { + fbb_.AddElement(IntSearchFilter::VT_SEARCHOPERATOR, searchOperator, 0); + } + void add_attr(flatbuffers::Offset attr) { + fbb_.AddOffset(IntSearchFilter::VT_ATTR, attr); + } + explicit IntSearchFilterBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + IntSearchFilterBuilder &operator=(const IntSearchFilterBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateIntSearchFilter( + flatbuffers::FlatBufferBuilder &_fbb, + uint8_t searchOperator = 0, + flatbuffers::Offset attr = 0) { + IntSearchFilterBuilder builder_(_fbb); + builder_.add_attr(attr); + builder_.add_searchOperator(searchOperator); + return builder_.Finish(); +} + +struct BinSearchFilter FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SEARCHOPERATOR = 4, + VT_ATTR = 6 + }; + uint8_t searchOperator() const { + return GetField(VT_SEARCHOPERATOR, 0); + } + const BinAttr *attr() const { + return GetPointer(VT_ATTR); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_SEARCHOPERATOR) && + VerifyOffset(verifier, VT_ATTR) && + verifier.VerifyTable(attr()) && + verifier.EndTable(); + } +}; + +struct BinSearchFilterBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_searchOperator(uint8_t searchOperator) { + fbb_.AddElement(BinSearchFilter::VT_SEARCHOPERATOR, searchOperator, 0); + } + void add_attr(flatbuffers::Offset attr) { + fbb_.AddOffset(BinSearchFilter::VT_ATTR, attr); + } + explicit BinSearchFilterBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + BinSearchFilterBuilder &operator=(const BinSearchFilterBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateBinSearchFilter( + flatbuffers::FlatBufferBuilder &_fbb, + uint8_t searchOperator = 0, + flatbuffers::Offset attr = 0) { + BinSearchFilterBuilder builder_(_fbb); + builder_.add_attr(attr); + builder_.add_searchOperator(searchOperator); + return builder_.Finish(); +} + +struct PresenceOptionData FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_DATA = 4, + VT_LEN = 6 + }; + const flatbuffers::Vector *data() const { + return GetPointer *>(VT_DATA); + } + uint32_t len() const { + return GetField(VT_LEN, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_DATA) && + verifier.VerifyVector(data()) && + VerifyField(verifier, VT_LEN) && + verifier.EndTable(); + } +}; + +struct PresenceOptionDataBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_data(flatbuffers::Offset> data) { + fbb_.AddOffset(PresenceOptionData::VT_DATA, data); + } + void add_len(uint32_t len) { + fbb_.AddElement(PresenceOptionData::VT_LEN, len, 0); + } + explicit PresenceOptionDataBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + PresenceOptionDataBuilder &operator=(const PresenceOptionDataBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreatePresenceOptionData( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset> data = 0, + uint32_t len = 0) { + PresenceOptionDataBuilder builder_(_fbb); + builder_.add_len(len); + builder_.add_data(data); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreatePresenceOptionDataDirect( + flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *data = nullptr, + uint32_t len = 0) { + auto data__ = data ? _fbb.CreateVector(*data) : 0; + return CreatePresenceOptionData( + _fbb, + data__, + len); +} + +struct RoomGroupPasswordConfig FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_GROUPID = 4, + VT_WITHPASSWORD = 6 + }; + const flatbuffers::Vector *groupId() const { + return GetPointer *>(VT_GROUPID); + } + bool withPassword() const { + return GetField(VT_WITHPASSWORD, 0) != 0; + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_GROUPID) && + verifier.VerifyVector(groupId()) && + VerifyField(verifier, VT_WITHPASSWORD) && + verifier.EndTable(); + } +}; + +struct RoomGroupPasswordConfigBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_groupId(flatbuffers::Offset> groupId) { + fbb_.AddOffset(RoomGroupPasswordConfig::VT_GROUPID, groupId); + } + void add_withPassword(bool withPassword) { + fbb_.AddElement(RoomGroupPasswordConfig::VT_WITHPASSWORD, static_cast(withPassword), 0); + } + explicit RoomGroupPasswordConfigBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + RoomGroupPasswordConfigBuilder &operator=(const RoomGroupPasswordConfigBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRoomGroupPasswordConfig( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset> groupId = 0, + bool withPassword = false) { + RoomGroupPasswordConfigBuilder builder_(_fbb); + builder_.add_groupId(groupId); + builder_.add_withPassword(withPassword); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateRoomGroupPasswordConfigDirect( + flatbuffers::FlatBufferBuilder &_fbb, + const std::vector *groupId = nullptr, + bool withPassword = false) { + auto groupId__ = groupId ? _fbb.CreateVector(*groupId) : 0; + return CreateRoomGroupPasswordConfig( + _fbb, + groupId__, + withPassword); +} + +struct SearchRoomRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_OPTION = 4, + VT_WORLDID = 6, + VT_LOBBYID = 8, + VT_RANGEFILTER_STARTINDEX = 10, + VT_RANGEFILTER_MAX = 12, + VT_FLAGFILTER = 14, + VT_FLAGATTR = 16, + VT_INTFILTER = 18, + VT_BINFILTER = 20, + VT_ATTRID = 22 + }; + int32_t option() const { + return GetField(VT_OPTION, 0); + } + uint32_t worldId() const { + return GetField(VT_WORLDID, 0); + } + uint64_t lobbyId() const { + return GetField(VT_LOBBYID, 0); + } + uint32_t rangeFilter_startIndex() const { + return GetField(VT_RANGEFILTER_STARTINDEX, 0); + } + uint32_t rangeFilter_max() const { + return GetField(VT_RANGEFILTER_MAX, 0); + } + uint32_t flagFilter() const { + return GetField(VT_FLAGFILTER, 0); + } + uint32_t flagAttr() const { + return GetField(VT_FLAGATTR, 0); + } + const flatbuffers::Vector> *intFilter() const { + return GetPointer> *>(VT_INTFILTER); + } + const flatbuffers::Vector> *binFilter() const { + return GetPointer> *>(VT_BINFILTER); + } + const flatbuffers::Vector *attrId() const { + return GetPointer *>(VT_ATTRID); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_OPTION) && + VerifyField(verifier, VT_WORLDID) && + VerifyField(verifier, VT_LOBBYID) && + VerifyField(verifier, VT_RANGEFILTER_STARTINDEX) && + VerifyField(verifier, VT_RANGEFILTER_MAX) && + VerifyField(verifier, VT_FLAGFILTER) && + VerifyField(verifier, VT_FLAGATTR) && + VerifyOffset(verifier, VT_INTFILTER) && + verifier.VerifyVector(intFilter()) && + verifier.VerifyVectorOfTables(intFilter()) && + VerifyOffset(verifier, VT_BINFILTER) && + verifier.VerifyVector(binFilter()) && + verifier.VerifyVectorOfTables(binFilter()) && + VerifyOffset(verifier, VT_ATTRID) && + verifier.VerifyVector(attrId()) && + verifier.EndTable(); + } +}; + +struct SearchRoomRequestBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_option(int32_t option) { + fbb_.AddElement(SearchRoomRequest::VT_OPTION, option, 0); + } + void add_worldId(uint32_t worldId) { + fbb_.AddElement(SearchRoomRequest::VT_WORLDID, worldId, 0); + } + void add_lobbyId(uint64_t lobbyId) { + fbb_.AddElement(SearchRoomRequest::VT_LOBBYID, lobbyId, 0); + } + void add_rangeFilter_startIndex(uint32_t rangeFilter_startIndex) { + fbb_.AddElement(SearchRoomRequest::VT_RANGEFILTER_STARTINDEX, rangeFilter_startIndex, 0); + } + void add_rangeFilter_max(uint32_t rangeFilter_max) { + fbb_.AddElement(SearchRoomRequest::VT_RANGEFILTER_MAX, rangeFilter_max, 0); + } + void add_flagFilter(uint32_t flagFilter) { + fbb_.AddElement(SearchRoomRequest::VT_FLAGFILTER, flagFilter, 0); + } + void add_flagAttr(uint32_t flagAttr) { + fbb_.AddElement(SearchRoomRequest::VT_FLAGATTR, flagAttr, 0); + } + void add_intFilter(flatbuffers::Offset>> intFilter) { + fbb_.AddOffset(SearchRoomRequest::VT_INTFILTER, intFilter); + } + void add_binFilter(flatbuffers::Offset>> binFilter) { + fbb_.AddOffset(SearchRoomRequest::VT_BINFILTER, binFilter); + } + void add_attrId(flatbuffers::Offset> attrId) { + fbb_.AddOffset(SearchRoomRequest::VT_ATTRID, attrId); + } + explicit SearchRoomRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + SearchRoomRequestBuilder &operator=(const SearchRoomRequestBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSearchRoomRequest( + flatbuffers::FlatBufferBuilder &_fbb, + int32_t option = 0, + uint32_t worldId = 0, + uint64_t lobbyId = 0, + uint32_t rangeFilter_startIndex = 0, + uint32_t rangeFilter_max = 0, + uint32_t flagFilter = 0, + uint32_t flagAttr = 0, + flatbuffers::Offset>> intFilter = 0, + flatbuffers::Offset>> binFilter = 0, + flatbuffers::Offset> attrId = 0) { + SearchRoomRequestBuilder builder_(_fbb); + builder_.add_lobbyId(lobbyId); + builder_.add_attrId(attrId); + builder_.add_binFilter(binFilter); + builder_.add_intFilter(intFilter); + builder_.add_flagAttr(flagAttr); + builder_.add_flagFilter(flagFilter); + builder_.add_rangeFilter_max(rangeFilter_max); + builder_.add_rangeFilter_startIndex(rangeFilter_startIndex); + builder_.add_worldId(worldId); + builder_.add_option(option); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateSearchRoomRequestDirect( + flatbuffers::FlatBufferBuilder &_fbb, + int32_t option = 0, + uint32_t worldId = 0, + uint64_t lobbyId = 0, + uint32_t rangeFilter_startIndex = 0, + uint32_t rangeFilter_max = 0, + uint32_t flagFilter = 0, + uint32_t flagAttr = 0, + const std::vector> *intFilter = nullptr, + const std::vector> *binFilter = nullptr, + const std::vector *attrId = nullptr) { + auto intFilter__ = intFilter ? _fbb.CreateVector>(*intFilter) : 0; + auto binFilter__ = binFilter ? _fbb.CreateVector>(*binFilter) : 0; + auto attrId__ = attrId ? _fbb.CreateVector(*attrId) : 0; + return CreateSearchRoomRequest( + _fbb, + option, + worldId, + lobbyId, + rangeFilter_startIndex, + rangeFilter_max, + flagFilter, + flagAttr, + intFilter__, + binFilter__, + attrId__); +} + +struct SearchRoomResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_STARTINDEX = 4, + VT_TOTAL = 6, + VT_SIZE = 8, + VT_ROOMS = 10 + }; + uint32_t startIndex() const { + return GetField(VT_STARTINDEX, 0); + } + uint32_t total() const { + return GetField(VT_TOTAL, 0); + } + uint32_t size() const { + return GetField(VT_SIZE, 0); + } + const flatbuffers::Vector> *rooms() const { + return GetPointer> *>(VT_ROOMS); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_STARTINDEX) && + VerifyField(verifier, VT_TOTAL) && + VerifyField(verifier, VT_SIZE) && + VerifyOffset(verifier, VT_ROOMS) && + verifier.VerifyVector(rooms()) && + verifier.VerifyVectorOfTables(rooms()) && + verifier.EndTable(); + } +}; + +struct SearchRoomResponseBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_startIndex(uint32_t startIndex) { + fbb_.AddElement(SearchRoomResponse::VT_STARTINDEX, startIndex, 0); + } + void add_total(uint32_t total) { + fbb_.AddElement(SearchRoomResponse::VT_TOTAL, total, 0); + } + void add_size(uint32_t size) { + fbb_.AddElement(SearchRoomResponse::VT_SIZE, size, 0); + } + void add_rooms(flatbuffers::Offset>> rooms) { + fbb_.AddOffset(SearchRoomResponse::VT_ROOMS, rooms); + } + explicit SearchRoomResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + SearchRoomResponseBuilder &operator=(const SearchRoomResponseBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSearchRoomResponse( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t startIndex = 0, + uint32_t total = 0, + uint32_t size = 0, + flatbuffers::Offset>> rooms = 0) { + SearchRoomResponseBuilder builder_(_fbb); + builder_.add_rooms(rooms); + builder_.add_size(size); + builder_.add_total(total); + builder_.add_startIndex(startIndex); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateSearchRoomResponseDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t startIndex = 0, + uint32_t total = 0, + uint32_t size = 0, + const std::vector> *rooms = nullptr) { + auto rooms__ = rooms ? _fbb.CreateVector>(*rooms) : 0; + return CreateSearchRoomResponse( + _fbb, + startIndex, + total, + size, + rooms__); +} + +struct CreateJoinRoomRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_WORLDID = 4, + VT_LOBBYID = 6, + VT_MAXSLOT = 8, + VT_FLAGATTR = 10, + VT_ROOMBINATTRINTERNAL = 12, + VT_ROOMSEARCHABLEINTATTREXTERNAL = 14, + VT_ROOMSEARCHABLEBINATTREXTERNAL = 16, + VT_ROOMBINATTREXTERNAL = 18, + VT_ROOMPASSWORD = 20, + VT_GROUPCONFIG = 22, + VT_PASSWORDSLOTMASK = 24, + VT_ALLOWEDUSER = 26, + VT_BLOCKEDUSER = 28, + VT_JOINROOMGROUPLABEL = 30, + VT_ROOMMEMBERBINATTRINTERNAL = 32, + VT_TEAMID = 34, + VT_SIGOPTPARAM = 36 + }; + uint32_t worldId() const { + return GetField(VT_WORLDID, 0); + } + uint64_t lobbyId() const { + return GetField(VT_LOBBYID, 0); + } + uint32_t maxSlot() const { + return GetField(VT_MAXSLOT, 0); + } + uint32_t flagAttr() const { + return GetField(VT_FLAGATTR, 0); + } + const flatbuffers::Vector> *roomBinAttrInternal() const { + return GetPointer> *>(VT_ROOMBINATTRINTERNAL); + } + const flatbuffers::Vector> *roomSearchableIntAttrExternal() const { + return GetPointer> *>(VT_ROOMSEARCHABLEINTATTREXTERNAL); + } + const flatbuffers::Vector> *roomSearchableBinAttrExternal() const { + return GetPointer> *>(VT_ROOMSEARCHABLEBINATTREXTERNAL); + } + const flatbuffers::Vector> *roomBinAttrExternal() const { + return GetPointer> *>(VT_ROOMBINATTREXTERNAL); + } + const flatbuffers::Vector *roomPassword() const { + return GetPointer *>(VT_ROOMPASSWORD); + } + const flatbuffers::Vector> *groupConfig() const { + return GetPointer> *>(VT_GROUPCONFIG); + } + uint64_t passwordSlotMask() const { + return GetField(VT_PASSWORDSLOTMASK, 0); + } + const flatbuffers::Vector> *allowedUser() const { + return GetPointer> *>(VT_ALLOWEDUSER); + } + const flatbuffers::Vector> *blockedUser() const { + return GetPointer> *>(VT_BLOCKEDUSER); + } + const flatbuffers::Vector *joinRoomGroupLabel() const { + return GetPointer *>(VT_JOINROOMGROUPLABEL); + } + const flatbuffers::Vector> *roomMemberBinAttrInternal() const { + return GetPointer> *>(VT_ROOMMEMBERBINATTRINTERNAL); + } + uint8_t teamId() const { + return GetField(VT_TEAMID, 0); + } + const OptParam *sigOptParam() const { + return GetPointer(VT_SIGOPTPARAM); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_WORLDID) && + VerifyField(verifier, VT_LOBBYID) && + VerifyField(verifier, VT_MAXSLOT) && + VerifyField(verifier, VT_FLAGATTR) && + VerifyOffset(verifier, VT_ROOMBINATTRINTERNAL) && + verifier.VerifyVector(roomBinAttrInternal()) && + verifier.VerifyVectorOfTables(roomBinAttrInternal()) && + VerifyOffset(verifier, VT_ROOMSEARCHABLEINTATTREXTERNAL) && + verifier.VerifyVector(roomSearchableIntAttrExternal()) && + verifier.VerifyVectorOfTables(roomSearchableIntAttrExternal()) && + VerifyOffset(verifier, VT_ROOMSEARCHABLEBINATTREXTERNAL) && + verifier.VerifyVector(roomSearchableBinAttrExternal()) && + verifier.VerifyVectorOfTables(roomSearchableBinAttrExternal()) && + VerifyOffset(verifier, VT_ROOMBINATTREXTERNAL) && + verifier.VerifyVector(roomBinAttrExternal()) && + verifier.VerifyVectorOfTables(roomBinAttrExternal()) && + VerifyOffset(verifier, VT_ROOMPASSWORD) && + verifier.VerifyVector(roomPassword()) && + VerifyOffset(verifier, VT_GROUPCONFIG) && + verifier.VerifyVector(groupConfig()) && + verifier.VerifyVectorOfTables(groupConfig()) && + VerifyField(verifier, VT_PASSWORDSLOTMASK) && + VerifyOffset(verifier, VT_ALLOWEDUSER) && + verifier.VerifyVector(allowedUser()) && + verifier.VerifyVectorOfStrings(allowedUser()) && + VerifyOffset(verifier, VT_BLOCKEDUSER) && + verifier.VerifyVector(blockedUser()) && + verifier.VerifyVectorOfStrings(blockedUser()) && + VerifyOffset(verifier, VT_JOINROOMGROUPLABEL) && + verifier.VerifyVector(joinRoomGroupLabel()) && + VerifyOffset(verifier, VT_ROOMMEMBERBINATTRINTERNAL) && + verifier.VerifyVector(roomMemberBinAttrInternal()) && + verifier.VerifyVectorOfTables(roomMemberBinAttrInternal()) && + VerifyField(verifier, VT_TEAMID) && + VerifyOffset(verifier, VT_SIGOPTPARAM) && + verifier.VerifyTable(sigOptParam()) && + verifier.EndTable(); + } +}; + +struct CreateJoinRoomRequestBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_worldId(uint32_t worldId) { + fbb_.AddElement(CreateJoinRoomRequest::VT_WORLDID, worldId, 0); + } + void add_lobbyId(uint64_t lobbyId) { + fbb_.AddElement(CreateJoinRoomRequest::VT_LOBBYID, lobbyId, 0); + } + void add_maxSlot(uint32_t maxSlot) { + fbb_.AddElement(CreateJoinRoomRequest::VT_MAXSLOT, maxSlot, 0); + } + void add_flagAttr(uint32_t flagAttr) { + fbb_.AddElement(CreateJoinRoomRequest::VT_FLAGATTR, flagAttr, 0); + } + void add_roomBinAttrInternal(flatbuffers::Offset>> roomBinAttrInternal) { + fbb_.AddOffset(CreateJoinRoomRequest::VT_ROOMBINATTRINTERNAL, roomBinAttrInternal); + } + void add_roomSearchableIntAttrExternal(flatbuffers::Offset>> roomSearchableIntAttrExternal) { + fbb_.AddOffset(CreateJoinRoomRequest::VT_ROOMSEARCHABLEINTATTREXTERNAL, roomSearchableIntAttrExternal); + } + void add_roomSearchableBinAttrExternal(flatbuffers::Offset>> roomSearchableBinAttrExternal) { + fbb_.AddOffset(CreateJoinRoomRequest::VT_ROOMSEARCHABLEBINATTREXTERNAL, roomSearchableBinAttrExternal); + } + void add_roomBinAttrExternal(flatbuffers::Offset>> roomBinAttrExternal) { + fbb_.AddOffset(CreateJoinRoomRequest::VT_ROOMBINATTREXTERNAL, roomBinAttrExternal); + } + void add_roomPassword(flatbuffers::Offset> roomPassword) { + fbb_.AddOffset(CreateJoinRoomRequest::VT_ROOMPASSWORD, roomPassword); + } + void add_groupConfig(flatbuffers::Offset>> groupConfig) { + fbb_.AddOffset(CreateJoinRoomRequest::VT_GROUPCONFIG, groupConfig); + } + void add_passwordSlotMask(uint64_t passwordSlotMask) { + fbb_.AddElement(CreateJoinRoomRequest::VT_PASSWORDSLOTMASK, passwordSlotMask, 0); + } + void add_allowedUser(flatbuffers::Offset>> allowedUser) { + fbb_.AddOffset(CreateJoinRoomRequest::VT_ALLOWEDUSER, allowedUser); + } + void add_blockedUser(flatbuffers::Offset>> blockedUser) { + fbb_.AddOffset(CreateJoinRoomRequest::VT_BLOCKEDUSER, blockedUser); + } + void add_joinRoomGroupLabel(flatbuffers::Offset> joinRoomGroupLabel) { + fbb_.AddOffset(CreateJoinRoomRequest::VT_JOINROOMGROUPLABEL, joinRoomGroupLabel); + } + void add_roomMemberBinAttrInternal(flatbuffers::Offset>> roomMemberBinAttrInternal) { + fbb_.AddOffset(CreateJoinRoomRequest::VT_ROOMMEMBERBINATTRINTERNAL, roomMemberBinAttrInternal); + } + void add_teamId(uint8_t teamId) { + fbb_.AddElement(CreateJoinRoomRequest::VT_TEAMID, teamId, 0); + } + void add_sigOptParam(flatbuffers::Offset sigOptParam) { + fbb_.AddOffset(CreateJoinRoomRequest::VT_SIGOPTPARAM, sigOptParam); + } + explicit CreateJoinRoomRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + CreateJoinRoomRequestBuilder &operator=(const CreateJoinRoomRequestBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateCreateJoinRoomRequest( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t worldId = 0, + uint64_t lobbyId = 0, + uint32_t maxSlot = 0, + uint32_t flagAttr = 0, + flatbuffers::Offset>> roomBinAttrInternal = 0, + flatbuffers::Offset>> roomSearchableIntAttrExternal = 0, + flatbuffers::Offset>> roomSearchableBinAttrExternal = 0, + flatbuffers::Offset>> roomBinAttrExternal = 0, + flatbuffers::Offset> roomPassword = 0, + flatbuffers::Offset>> groupConfig = 0, + uint64_t passwordSlotMask = 0, + flatbuffers::Offset>> allowedUser = 0, + flatbuffers::Offset>> blockedUser = 0, + flatbuffers::Offset> joinRoomGroupLabel = 0, + flatbuffers::Offset>> roomMemberBinAttrInternal = 0, + uint8_t teamId = 0, + flatbuffers::Offset sigOptParam = 0) { + CreateJoinRoomRequestBuilder builder_(_fbb); + builder_.add_passwordSlotMask(passwordSlotMask); + builder_.add_lobbyId(lobbyId); + builder_.add_sigOptParam(sigOptParam); + builder_.add_roomMemberBinAttrInternal(roomMemberBinAttrInternal); + builder_.add_joinRoomGroupLabel(joinRoomGroupLabel); + builder_.add_blockedUser(blockedUser); + builder_.add_allowedUser(allowedUser); + builder_.add_groupConfig(groupConfig); + builder_.add_roomPassword(roomPassword); + builder_.add_roomBinAttrExternal(roomBinAttrExternal); + builder_.add_roomSearchableBinAttrExternal(roomSearchableBinAttrExternal); + builder_.add_roomSearchableIntAttrExternal(roomSearchableIntAttrExternal); + builder_.add_roomBinAttrInternal(roomBinAttrInternal); + builder_.add_flagAttr(flagAttr); + builder_.add_maxSlot(maxSlot); + builder_.add_worldId(worldId); + builder_.add_teamId(teamId); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateCreateJoinRoomRequestDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t worldId = 0, + uint64_t lobbyId = 0, + uint32_t maxSlot = 0, + uint32_t flagAttr = 0, + const std::vector> *roomBinAttrInternal = nullptr, + const std::vector> *roomSearchableIntAttrExternal = nullptr, + const std::vector> *roomSearchableBinAttrExternal = nullptr, + const std::vector> *roomBinAttrExternal = nullptr, + const std::vector *roomPassword = nullptr, + const std::vector> *groupConfig = nullptr, + uint64_t passwordSlotMask = 0, + const std::vector> *allowedUser = nullptr, + const std::vector> *blockedUser = nullptr, + const std::vector *joinRoomGroupLabel = nullptr, + const std::vector> *roomMemberBinAttrInternal = nullptr, + uint8_t teamId = 0, + flatbuffers::Offset sigOptParam = 0) { + auto roomBinAttrInternal__ = roomBinAttrInternal ? _fbb.CreateVector>(*roomBinAttrInternal) : 0; + auto roomSearchableIntAttrExternal__ = roomSearchableIntAttrExternal ? _fbb.CreateVector>(*roomSearchableIntAttrExternal) : 0; + auto roomSearchableBinAttrExternal__ = roomSearchableBinAttrExternal ? _fbb.CreateVector>(*roomSearchableBinAttrExternal) : 0; + auto roomBinAttrExternal__ = roomBinAttrExternal ? _fbb.CreateVector>(*roomBinAttrExternal) : 0; + auto roomPassword__ = roomPassword ? _fbb.CreateVector(*roomPassword) : 0; + auto groupConfig__ = groupConfig ? _fbb.CreateVector>(*groupConfig) : 0; + auto allowedUser__ = allowedUser ? _fbb.CreateVector>(*allowedUser) : 0; + auto blockedUser__ = blockedUser ? _fbb.CreateVector>(*blockedUser) : 0; + auto joinRoomGroupLabel__ = joinRoomGroupLabel ? _fbb.CreateVector(*joinRoomGroupLabel) : 0; + auto roomMemberBinAttrInternal__ = roomMemberBinAttrInternal ? _fbb.CreateVector>(*roomMemberBinAttrInternal) : 0; + return CreateCreateJoinRoomRequest( + _fbb, + worldId, + lobbyId, + maxSlot, + flagAttr, + roomBinAttrInternal__, + roomSearchableIntAttrExternal__, + roomSearchableBinAttrExternal__, + roomBinAttrExternal__, + roomPassword__, + groupConfig__, + passwordSlotMask, + allowedUser__, + blockedUser__, + joinRoomGroupLabel__, + roomMemberBinAttrInternal__, + teamId, + sigOptParam); +} + +struct JoinRoomRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ROOMID = 4, + VT_ROOMPASSWORD = 6, + VT_JOINROOMGROUPLABEL = 8, + VT_ROOMMEMBERBINATTRINTERNAL = 10, + VT_OPTDATA = 12, + VT_TEAMID = 14 + }; + uint64_t roomId() const { + return GetField(VT_ROOMID, 0); + } + const flatbuffers::Vector *roomPassword() const { + return GetPointer *>(VT_ROOMPASSWORD); + } + const flatbuffers::Vector *joinRoomGroupLabel() const { + return GetPointer *>(VT_JOINROOMGROUPLABEL); + } + const flatbuffers::Vector> *roomMemberBinAttrInternal() const { + return GetPointer> *>(VT_ROOMMEMBERBINATTRINTERNAL); + } + const PresenceOptionData *optData() const { + return GetPointer(VT_OPTDATA); + } + uint8_t teamId() const { + return GetField(VT_TEAMID, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_ROOMID) && + VerifyOffset(verifier, VT_ROOMPASSWORD) && + verifier.VerifyVector(roomPassword()) && + VerifyOffset(verifier, VT_JOINROOMGROUPLABEL) && + verifier.VerifyVector(joinRoomGroupLabel()) && + VerifyOffset(verifier, VT_ROOMMEMBERBINATTRINTERNAL) && + verifier.VerifyVector(roomMemberBinAttrInternal()) && + verifier.VerifyVectorOfTables(roomMemberBinAttrInternal()) && + VerifyOffset(verifier, VT_OPTDATA) && + verifier.VerifyTable(optData()) && + VerifyField(verifier, VT_TEAMID) && + verifier.EndTable(); + } +}; + +struct JoinRoomRequestBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_roomId(uint64_t roomId) { + fbb_.AddElement(JoinRoomRequest::VT_ROOMID, roomId, 0); + } + void add_roomPassword(flatbuffers::Offset> roomPassword) { + fbb_.AddOffset(JoinRoomRequest::VT_ROOMPASSWORD, roomPassword); + } + void add_joinRoomGroupLabel(flatbuffers::Offset> joinRoomGroupLabel) { + fbb_.AddOffset(JoinRoomRequest::VT_JOINROOMGROUPLABEL, joinRoomGroupLabel); + } + void add_roomMemberBinAttrInternal(flatbuffers::Offset>> roomMemberBinAttrInternal) { + fbb_.AddOffset(JoinRoomRequest::VT_ROOMMEMBERBINATTRINTERNAL, roomMemberBinAttrInternal); + } + void add_optData(flatbuffers::Offset optData) { + fbb_.AddOffset(JoinRoomRequest::VT_OPTDATA, optData); + } + void add_teamId(uint8_t teamId) { + fbb_.AddElement(JoinRoomRequest::VT_TEAMID, teamId, 0); + } + explicit JoinRoomRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + JoinRoomRequestBuilder &operator=(const JoinRoomRequestBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateJoinRoomRequest( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t roomId = 0, + flatbuffers::Offset> roomPassword = 0, + flatbuffers::Offset> joinRoomGroupLabel = 0, + flatbuffers::Offset>> roomMemberBinAttrInternal = 0, + flatbuffers::Offset optData = 0, + uint8_t teamId = 0) { + JoinRoomRequestBuilder builder_(_fbb); + builder_.add_roomId(roomId); + builder_.add_optData(optData); + builder_.add_roomMemberBinAttrInternal(roomMemberBinAttrInternal); + builder_.add_joinRoomGroupLabel(joinRoomGroupLabel); + builder_.add_roomPassword(roomPassword); + builder_.add_teamId(teamId); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateJoinRoomRequestDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t roomId = 0, + const std::vector *roomPassword = nullptr, + const std::vector *joinRoomGroupLabel = nullptr, + const std::vector> *roomMemberBinAttrInternal = nullptr, + flatbuffers::Offset optData = 0, + uint8_t teamId = 0) { + auto roomPassword__ = roomPassword ? _fbb.CreateVector(*roomPassword) : 0; + auto joinRoomGroupLabel__ = joinRoomGroupLabel ? _fbb.CreateVector(*joinRoomGroupLabel) : 0; + auto roomMemberBinAttrInternal__ = roomMemberBinAttrInternal ? _fbb.CreateVector>(*roomMemberBinAttrInternal) : 0; + return CreateJoinRoomRequest( + _fbb, + roomId, + roomPassword__, + joinRoomGroupLabel__, + roomMemberBinAttrInternal__, + optData, + teamId); +} + +struct LeaveRoomRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ROOMID = 4, + VT_OPTDATA = 6 + }; + uint64_t roomId() const { + return GetField(VT_ROOMID, 0); + } + const PresenceOptionData *optData() const { + return GetPointer(VT_OPTDATA); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_ROOMID) && + VerifyOffset(verifier, VT_OPTDATA) && + verifier.VerifyTable(optData()) && + verifier.EndTable(); + } +}; + +struct LeaveRoomRequestBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_roomId(uint64_t roomId) { + fbb_.AddElement(LeaveRoomRequest::VT_ROOMID, roomId, 0); + } + void add_optData(flatbuffers::Offset optData) { + fbb_.AddOffset(LeaveRoomRequest::VT_OPTDATA, optData); + } + explicit LeaveRoomRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + LeaveRoomRequestBuilder &operator=(const LeaveRoomRequestBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateLeaveRoomRequest( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t roomId = 0, + flatbuffers::Offset optData = 0) { + LeaveRoomRequestBuilder builder_(_fbb); + builder_.add_roomId(roomId); + builder_.add_optData(optData); + return builder_.Finish(); +} + +struct SetRoomDataExternalRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ROOMID = 4, + VT_ROOMSEARCHABLEINTATTREXTERNAL = 6, + VT_ROOMSEARCHABLEBINATTREXTERNAL = 8, + VT_ROOMBINATTREXTERNAL = 10 + }; + uint64_t roomId() const { + return GetField(VT_ROOMID, 0); + } + const flatbuffers::Vector> *roomSearchableIntAttrExternal() const { + return GetPointer> *>(VT_ROOMSEARCHABLEINTATTREXTERNAL); + } + const flatbuffers::Vector> *roomSearchableBinAttrExternal() const { + return GetPointer> *>(VT_ROOMSEARCHABLEBINATTREXTERNAL); + } + const flatbuffers::Vector> *roomBinAttrExternal() const { + return GetPointer> *>(VT_ROOMBINATTREXTERNAL); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_ROOMID) && + VerifyOffset(verifier, VT_ROOMSEARCHABLEINTATTREXTERNAL) && + verifier.VerifyVector(roomSearchableIntAttrExternal()) && + verifier.VerifyVectorOfTables(roomSearchableIntAttrExternal()) && + VerifyOffset(verifier, VT_ROOMSEARCHABLEBINATTREXTERNAL) && + verifier.VerifyVector(roomSearchableBinAttrExternal()) && + verifier.VerifyVectorOfTables(roomSearchableBinAttrExternal()) && + VerifyOffset(verifier, VT_ROOMBINATTREXTERNAL) && + verifier.VerifyVector(roomBinAttrExternal()) && + verifier.VerifyVectorOfTables(roomBinAttrExternal()) && + verifier.EndTable(); + } +}; + +struct SetRoomDataExternalRequestBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_roomId(uint64_t roomId) { + fbb_.AddElement(SetRoomDataExternalRequest::VT_ROOMID, roomId, 0); + } + void add_roomSearchableIntAttrExternal(flatbuffers::Offset>> roomSearchableIntAttrExternal) { + fbb_.AddOffset(SetRoomDataExternalRequest::VT_ROOMSEARCHABLEINTATTREXTERNAL, roomSearchableIntAttrExternal); + } + void add_roomSearchableBinAttrExternal(flatbuffers::Offset>> roomSearchableBinAttrExternal) { + fbb_.AddOffset(SetRoomDataExternalRequest::VT_ROOMSEARCHABLEBINATTREXTERNAL, roomSearchableBinAttrExternal); + } + void add_roomBinAttrExternal(flatbuffers::Offset>> roomBinAttrExternal) { + fbb_.AddOffset(SetRoomDataExternalRequest::VT_ROOMBINATTREXTERNAL, roomBinAttrExternal); + } + explicit SetRoomDataExternalRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + SetRoomDataExternalRequestBuilder &operator=(const SetRoomDataExternalRequestBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSetRoomDataExternalRequest( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t roomId = 0, + flatbuffers::Offset>> roomSearchableIntAttrExternal = 0, + flatbuffers::Offset>> roomSearchableBinAttrExternal = 0, + flatbuffers::Offset>> roomBinAttrExternal = 0) { + SetRoomDataExternalRequestBuilder builder_(_fbb); + builder_.add_roomId(roomId); + builder_.add_roomBinAttrExternal(roomBinAttrExternal); + builder_.add_roomSearchableBinAttrExternal(roomSearchableBinAttrExternal); + builder_.add_roomSearchableIntAttrExternal(roomSearchableIntAttrExternal); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateSetRoomDataExternalRequestDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t roomId = 0, + const std::vector> *roomSearchableIntAttrExternal = nullptr, + const std::vector> *roomSearchableBinAttrExternal = nullptr, + const std::vector> *roomBinAttrExternal = nullptr) { + auto roomSearchableIntAttrExternal__ = roomSearchableIntAttrExternal ? _fbb.CreateVector>(*roomSearchableIntAttrExternal) : 0; + auto roomSearchableBinAttrExternal__ = roomSearchableBinAttrExternal ? _fbb.CreateVector>(*roomSearchableBinAttrExternal) : 0; + auto roomBinAttrExternal__ = roomBinAttrExternal ? _fbb.CreateVector>(*roomBinAttrExternal) : 0; + return CreateSetRoomDataExternalRequest( + _fbb, + roomId, + roomSearchableIntAttrExternal__, + roomSearchableBinAttrExternal__, + roomBinAttrExternal__); +} + +struct SetRoomDataInternalRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ROOMID = 4, + VT_FLAGFILTER = 6, + VT_FLAGATTR = 8, + VT_ROOMBINATTRINTERNAL = 10, + VT_PASSWORDCONFIG = 12, + VT_PASSWORDSLOTMASK = 14, + VT_OWNERPRIVILEGERANK = 16 + }; + uint64_t roomId() const { + return GetField(VT_ROOMID, 0); + } + uint32_t flagFilter() const { + return GetField(VT_FLAGFILTER, 0); + } + uint32_t flagAttr() const { + return GetField(VT_FLAGATTR, 0); + } + const flatbuffers::Vector> *roomBinAttrInternal() const { + return GetPointer> *>(VT_ROOMBINATTRINTERNAL); + } + const flatbuffers::Vector> *passwordConfig() const { + return GetPointer> *>(VT_PASSWORDCONFIG); + } + uint64_t passwordSlotMask() const { + return GetField(VT_PASSWORDSLOTMASK, 0); + } + const flatbuffers::Vector *ownerPrivilegeRank() const { + return GetPointer *>(VT_OWNERPRIVILEGERANK); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_ROOMID) && + VerifyField(verifier, VT_FLAGFILTER) && + VerifyField(verifier, VT_FLAGATTR) && + VerifyOffset(verifier, VT_ROOMBINATTRINTERNAL) && + verifier.VerifyVector(roomBinAttrInternal()) && + verifier.VerifyVectorOfTables(roomBinAttrInternal()) && + VerifyOffset(verifier, VT_PASSWORDCONFIG) && + verifier.VerifyVector(passwordConfig()) && + verifier.VerifyVectorOfTables(passwordConfig()) && + VerifyField(verifier, VT_PASSWORDSLOTMASK) && + VerifyOffset(verifier, VT_OWNERPRIVILEGERANK) && + verifier.VerifyVector(ownerPrivilegeRank()) && + verifier.EndTable(); + } +}; + +struct SetRoomDataInternalRequestBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_roomId(uint64_t roomId) { + fbb_.AddElement(SetRoomDataInternalRequest::VT_ROOMID, roomId, 0); + } + void add_flagFilter(uint32_t flagFilter) { + fbb_.AddElement(SetRoomDataInternalRequest::VT_FLAGFILTER, flagFilter, 0); + } + void add_flagAttr(uint32_t flagAttr) { + fbb_.AddElement(SetRoomDataInternalRequest::VT_FLAGATTR, flagAttr, 0); + } + void add_roomBinAttrInternal(flatbuffers::Offset>> roomBinAttrInternal) { + fbb_.AddOffset(SetRoomDataInternalRequest::VT_ROOMBINATTRINTERNAL, roomBinAttrInternal); + } + void add_passwordConfig(flatbuffers::Offset>> passwordConfig) { + fbb_.AddOffset(SetRoomDataInternalRequest::VT_PASSWORDCONFIG, passwordConfig); + } + void add_passwordSlotMask(uint64_t passwordSlotMask) { + fbb_.AddElement(SetRoomDataInternalRequest::VT_PASSWORDSLOTMASK, passwordSlotMask, 0); + } + void add_ownerPrivilegeRank(flatbuffers::Offset> ownerPrivilegeRank) { + fbb_.AddOffset(SetRoomDataInternalRequest::VT_OWNERPRIVILEGERANK, ownerPrivilegeRank); + } + explicit SetRoomDataInternalRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + SetRoomDataInternalRequestBuilder &operator=(const SetRoomDataInternalRequestBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSetRoomDataInternalRequest( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t roomId = 0, + uint32_t flagFilter = 0, + uint32_t flagAttr = 0, + flatbuffers::Offset>> roomBinAttrInternal = 0, + flatbuffers::Offset>> passwordConfig = 0, + uint64_t passwordSlotMask = 0, + flatbuffers::Offset> ownerPrivilegeRank = 0) { + SetRoomDataInternalRequestBuilder builder_(_fbb); + builder_.add_passwordSlotMask(passwordSlotMask); + builder_.add_roomId(roomId); + builder_.add_ownerPrivilegeRank(ownerPrivilegeRank); + builder_.add_passwordConfig(passwordConfig); + builder_.add_roomBinAttrInternal(roomBinAttrInternal); + builder_.add_flagAttr(flagAttr); + builder_.add_flagFilter(flagFilter); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateSetRoomDataInternalRequestDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t roomId = 0, + uint32_t flagFilter = 0, + uint32_t flagAttr = 0, + const std::vector> *roomBinAttrInternal = nullptr, + const std::vector> *passwordConfig = nullptr, + uint64_t passwordSlotMask = 0, + const std::vector *ownerPrivilegeRank = nullptr) { + auto roomBinAttrInternal__ = roomBinAttrInternal ? _fbb.CreateVector>(*roomBinAttrInternal) : 0; + auto passwordConfig__ = passwordConfig ? _fbb.CreateVector>(*passwordConfig) : 0; + auto ownerPrivilegeRank__ = ownerPrivilegeRank ? _fbb.CreateVector(*ownerPrivilegeRank) : 0; + return CreateSetRoomDataInternalRequest( + _fbb, + roomId, + flagFilter, + flagAttr, + roomBinAttrInternal__, + passwordConfig__, + passwordSlotMask, + ownerPrivilegeRank__); +} + +struct GetRoomDataInternalRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ROOMID = 4, + VT_ATTRID = 6 + }; + uint64_t roomId() const { + return GetField(VT_ROOMID, 0); + } + const flatbuffers::Vector *attrId() const { + return GetPointer *>(VT_ATTRID); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_ROOMID) && + VerifyOffset(verifier, VT_ATTRID) && + verifier.VerifyVector(attrId()) && + verifier.EndTable(); + } +}; + +struct GetRoomDataInternalRequestBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_roomId(uint64_t roomId) { + fbb_.AddElement(GetRoomDataInternalRequest::VT_ROOMID, roomId, 0); + } + void add_attrId(flatbuffers::Offset> attrId) { + fbb_.AddOffset(GetRoomDataInternalRequest::VT_ATTRID, attrId); + } + explicit GetRoomDataInternalRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + GetRoomDataInternalRequestBuilder &operator=(const GetRoomDataInternalRequestBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateGetRoomDataInternalRequest( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t roomId = 0, + flatbuffers::Offset> attrId = 0) { + GetRoomDataInternalRequestBuilder builder_(_fbb); + builder_.add_roomId(roomId); + builder_.add_attrId(attrId); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateGetRoomDataInternalRequestDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t roomId = 0, + const std::vector *attrId = nullptr) { + auto attrId__ = attrId ? _fbb.CreateVector(*attrId) : 0; + return CreateGetRoomDataInternalRequest( + _fbb, + roomId, + attrId__); +} + +struct RoomMemberUpdateInfo FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ROOMMEMBERDATAINTERNAL = 4, + VT_EVENTCAUSE = 6, + VT_OPTDATA = 8 + }; + const RoomMemberDataInternal *roomMemberDataInternal() const { + return GetPointer(VT_ROOMMEMBERDATAINTERNAL); + } + uint8_t eventCause() const { + return GetField(VT_EVENTCAUSE, 0); + } + const PresenceOptionData *optData() const { + return GetPointer(VT_OPTDATA); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_ROOMMEMBERDATAINTERNAL) && + verifier.VerifyTable(roomMemberDataInternal()) && + VerifyField(verifier, VT_EVENTCAUSE) && + VerifyOffset(verifier, VT_OPTDATA) && + verifier.VerifyTable(optData()) && + verifier.EndTable(); + } +}; + +struct RoomMemberUpdateInfoBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_roomMemberDataInternal(flatbuffers::Offset roomMemberDataInternal) { + fbb_.AddOffset(RoomMemberUpdateInfo::VT_ROOMMEMBERDATAINTERNAL, roomMemberDataInternal); + } + void add_eventCause(uint8_t eventCause) { + fbb_.AddElement(RoomMemberUpdateInfo::VT_EVENTCAUSE, eventCause, 0); + } + void add_optData(flatbuffers::Offset optData) { + fbb_.AddOffset(RoomMemberUpdateInfo::VT_OPTDATA, optData); + } + explicit RoomMemberUpdateInfoBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + RoomMemberUpdateInfoBuilder &operator=(const RoomMemberUpdateInfoBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRoomMemberUpdateInfo( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset roomMemberDataInternal = 0, + uint8_t eventCause = 0, + flatbuffers::Offset optData = 0) { + RoomMemberUpdateInfoBuilder builder_(_fbb); + builder_.add_optData(optData); + builder_.add_roomMemberDataInternal(roomMemberDataInternal); + builder_.add_eventCause(eventCause); + return builder_.Finish(); +} + +struct RoomUpdateInfo FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_EVENTCAUSE = 4, + VT_ERRORCODE = 6, + VT_OPTDATA = 8 + }; + uint8_t eventCause() const { + return GetField(VT_EVENTCAUSE, 0); + } + int32_t errorCode() const { + return GetField(VT_ERRORCODE, 0); + } + const PresenceOptionData *optData() const { + return GetPointer(VT_OPTDATA); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_EVENTCAUSE) && + VerifyField(verifier, VT_ERRORCODE) && + VerifyOffset(verifier, VT_OPTDATA) && + verifier.VerifyTable(optData()) && + verifier.EndTable(); + } +}; + +struct RoomUpdateInfoBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_eventCause(uint8_t eventCause) { + fbb_.AddElement(RoomUpdateInfo::VT_EVENTCAUSE, eventCause, 0); + } + void add_errorCode(int32_t errorCode) { + fbb_.AddElement(RoomUpdateInfo::VT_ERRORCODE, errorCode, 0); + } + void add_optData(flatbuffers::Offset optData) { + fbb_.AddOffset(RoomUpdateInfo::VT_OPTDATA, optData); + } + explicit RoomUpdateInfoBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + RoomUpdateInfoBuilder &operator=(const RoomUpdateInfoBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRoomUpdateInfo( + flatbuffers::FlatBufferBuilder &_fbb, + uint8_t eventCause = 0, + int32_t errorCode = 0, + flatbuffers::Offset optData = 0) { + RoomUpdateInfoBuilder builder_(_fbb); + builder_.add_optData(optData); + builder_.add_errorCode(errorCode); + builder_.add_eventCause(eventCause); + return builder_.Finish(); +} + +struct GetPingInfoResponse FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_SERVERID = 4, + VT_WORLDID = 6, + VT_ROOMID = 8, + VT_RTT = 10 + }; + uint16_t serverId() const { + return GetField(VT_SERVERID, 0); + } + uint32_t worldId() const { + return GetField(VT_WORLDID, 0); + } + uint64_t roomId() const { + return GetField(VT_ROOMID, 0); + } + uint32_t rtt() const { + return GetField(VT_RTT, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_SERVERID) && + VerifyField(verifier, VT_WORLDID) && + VerifyField(verifier, VT_ROOMID) && + VerifyField(verifier, VT_RTT) && + verifier.EndTable(); + } +}; + +struct GetPingInfoResponseBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_serverId(uint16_t serverId) { + fbb_.AddElement(GetPingInfoResponse::VT_SERVERID, serverId, 0); + } + void add_worldId(uint32_t worldId) { + fbb_.AddElement(GetPingInfoResponse::VT_WORLDID, worldId, 0); + } + void add_roomId(uint64_t roomId) { + fbb_.AddElement(GetPingInfoResponse::VT_ROOMID, roomId, 0); + } + void add_rtt(uint32_t rtt) { + fbb_.AddElement(GetPingInfoResponse::VT_RTT, rtt, 0); + } + explicit GetPingInfoResponseBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + GetPingInfoResponseBuilder &operator=(const GetPingInfoResponseBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateGetPingInfoResponse( + flatbuffers::FlatBufferBuilder &_fbb, + uint16_t serverId = 0, + uint32_t worldId = 0, + uint64_t roomId = 0, + uint32_t rtt = 0) { + GetPingInfoResponseBuilder builder_(_fbb); + builder_.add_roomId(roomId); + builder_.add_rtt(rtt); + builder_.add_worldId(worldId); + builder_.add_serverId(serverId); + return builder_.Finish(); +} + +struct SendRoomMessageRequest FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_ROOMID = 4, + VT_CASTTYPE = 6, + VT_DST = 8, + VT_MSG = 10, + VT_OPTION = 12 + }; + uint64_t roomId() const { + return GetField(VT_ROOMID, 0); + } + uint8_t castType() const { + return GetField(VT_CASTTYPE, 0); + } + const flatbuffers::Vector *dst() const { + return GetPointer *>(VT_DST); + } + const flatbuffers::Vector *msg() const { + return GetPointer *>(VT_MSG); + } + uint8_t option() const { + return GetField(VT_OPTION, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_ROOMID) && + VerifyField(verifier, VT_CASTTYPE) && + VerifyOffset(verifier, VT_DST) && + verifier.VerifyVector(dst()) && + VerifyOffset(verifier, VT_MSG) && + verifier.VerifyVector(msg()) && + VerifyField(verifier, VT_OPTION) && + verifier.EndTable(); + } +}; + +struct SendRoomMessageRequestBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_roomId(uint64_t roomId) { + fbb_.AddElement(SendRoomMessageRequest::VT_ROOMID, roomId, 0); + } + void add_castType(uint8_t castType) { + fbb_.AddElement(SendRoomMessageRequest::VT_CASTTYPE, castType, 0); + } + void add_dst(flatbuffers::Offset> dst) { + fbb_.AddOffset(SendRoomMessageRequest::VT_DST, dst); + } + void add_msg(flatbuffers::Offset> msg) { + fbb_.AddOffset(SendRoomMessageRequest::VT_MSG, msg); + } + void add_option(uint8_t option) { + fbb_.AddElement(SendRoomMessageRequest::VT_OPTION, option, 0); + } + explicit SendRoomMessageRequestBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + SendRoomMessageRequestBuilder &operator=(const SendRoomMessageRequestBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateSendRoomMessageRequest( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t roomId = 0, + uint8_t castType = 0, + flatbuffers::Offset> dst = 0, + flatbuffers::Offset> msg = 0, + uint8_t option = 0) { + SendRoomMessageRequestBuilder builder_(_fbb); + builder_.add_roomId(roomId); + builder_.add_msg(msg); + builder_.add_dst(dst); + builder_.add_option(option); + builder_.add_castType(castType); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateSendRoomMessageRequestDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t roomId = 0, + uint8_t castType = 0, + const std::vector *dst = nullptr, + const std::vector *msg = nullptr, + uint8_t option = 0) { + auto dst__ = dst ? _fbb.CreateVector(*dst) : 0; + auto msg__ = msg ? _fbb.CreateVector(*msg) : 0; + return CreateSendRoomMessageRequest( + _fbb, + roomId, + castType, + dst__, + msg__, + option); +} + +struct RoomMessageInfo FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_FILTERED = 4, + VT_CASTTYPE = 6, + VT_DST = 8, + VT_SRCMEMBER = 10, + VT_MSG = 12 + }; + bool filtered() const { + return GetField(VT_FILTERED, 0) != 0; + } + uint8_t castType() const { + return GetField(VT_CASTTYPE, 0); + } + const flatbuffers::Vector *dst() const { + return GetPointer *>(VT_DST); + } + const UserInfo2 *srcMember() const { + return GetPointer(VT_SRCMEMBER); + } + const flatbuffers::Vector *msg() const { + return GetPointer *>(VT_MSG); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_FILTERED) && + VerifyField(verifier, VT_CASTTYPE) && + VerifyOffset(verifier, VT_DST) && + verifier.VerifyVector(dst()) && + VerifyOffset(verifier, VT_SRCMEMBER) && + verifier.VerifyTable(srcMember()) && + VerifyOffset(verifier, VT_MSG) && + verifier.VerifyVector(msg()) && + verifier.EndTable(); + } +}; + +struct RoomMessageInfoBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_filtered(bool filtered) { + fbb_.AddElement(RoomMessageInfo::VT_FILTERED, static_cast(filtered), 0); + } + void add_castType(uint8_t castType) { + fbb_.AddElement(RoomMessageInfo::VT_CASTTYPE, castType, 0); + } + void add_dst(flatbuffers::Offset> dst) { + fbb_.AddOffset(RoomMessageInfo::VT_DST, dst); + } + void add_srcMember(flatbuffers::Offset srcMember) { + fbb_.AddOffset(RoomMessageInfo::VT_SRCMEMBER, srcMember); + } + void add_msg(flatbuffers::Offset> msg) { + fbb_.AddOffset(RoomMessageInfo::VT_MSG, msg); + } + explicit RoomMessageInfoBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + RoomMessageInfoBuilder &operator=(const RoomMessageInfoBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRoomMessageInfo( + flatbuffers::FlatBufferBuilder &_fbb, + bool filtered = false, + uint8_t castType = 0, + flatbuffers::Offset> dst = 0, + flatbuffers::Offset srcMember = 0, + flatbuffers::Offset> msg = 0) { + RoomMessageInfoBuilder builder_(_fbb); + builder_.add_msg(msg); + builder_.add_srcMember(srcMember); + builder_.add_dst(dst); + builder_.add_castType(castType); + builder_.add_filtered(filtered); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateRoomMessageInfoDirect( + flatbuffers::FlatBufferBuilder &_fbb, + bool filtered = false, + uint8_t castType = 0, + const std::vector *dst = nullptr, + flatbuffers::Offset srcMember = 0, + const std::vector *msg = nullptr) { + auto dst__ = dst ? _fbb.CreateVector(*dst) : 0; + auto msg__ = msg ? _fbb.CreateVector(*msg) : 0; + return CreateRoomMessageInfo( + _fbb, + filtered, + castType, + dst__, + srcMember, + msg__); +} + +#endif // FLATBUFFERS_GENERATED_NP2STRUCTS_H_ diff --git a/rpcs3/Emu/NP/np_handler.cpp b/rpcs3/Emu/NP/np_handler.cpp index 08892ddaad..557ace30a0 100644 --- a/rpcs3/Emu/NP/np_handler.cpp +++ b/rpcs3/Emu/NP/np_handler.cpp @@ -9,6 +9,9 @@ #include "Utilities/StrUtil.h" #include "Emu/Cell/Modules/cellSysutil.h" #include "Emu/IdManager.h" +#include "np_structs_extra.h" +#include "Emu/System.h" +#include "Emu/NP/rpcn_config.h" #ifdef _WIN32 #include @@ -17,6 +20,7 @@ #include #include #include +#include #include #endif @@ -24,102 +28,167 @@ LOG_CHANNEL(sys_net); LOG_CHANNEL(sceNp2); LOG_CHANNEL(sceNp); +LOG_CHANNEL(rpcn_log, "rpcn"); +LOG_CHANNEL(nph_log, "NPHandler"); + np_handler::np_handler() { + g_cfg_rpcn.load(); + is_connected = (g_cfg.net.net_active == np_internet_status::enabled); is_psn_active = (g_cfg.net.psn_status >= np_psn_status::fake); - // Validate IP/Get from host? if (get_net_status() == CELL_NET_CTL_STATE_IPObtained) { - // cur_ip = g_cfg.net.ip_address; - - // Attempt to get actual IP address - const int dns_port = 53; - - struct sockaddr_in serv; - const int sock = static_cast(socket(AF_INET, SOCK_DGRAM, 0)); - - ASSERT(sock >= 0); - - memset(&serv, 0, sizeof(serv)); - serv.sin_family = AF_INET; - serv.sin_addr.s_addr = 0x08'08'08'08; // 8.8.8.8 google_dns_server - serv.sin_port = std::bit_cast>(dns_port); // htons(dns_port) - - int err = connect(sock, reinterpret_cast(&serv), sizeof(serv)); - if (err < 0) + if (!discover_ip_address()) { - sys_net.error("Failed to connect to google dns for IP discovery"); + nph_log.error("Failed to discover local IP!"); is_connected = false; - cur_ip = "0.0.0.0"; + is_psn_active = false; + } + + // Convert dns address + // TODO: recover actual user dns through OS specific API + in_addr conv{}; + if (!inet_pton(AF_INET, g_cfg.net.dns.to_string().c_str(), &conv)) + { + // Do not set to disconnected on invalid IP just error and continue using default(google's 8.8.8.8) + nph_log.error("Provided IP(%s) address for DNS is invalid!", g_cfg.net.dns.to_string()); } else { - struct sockaddr_in name; - socklen_t namelen = sizeof(name); - err = getsockname(sock, reinterpret_cast(&name), &namelen); - - char buffer[80]; - const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, 80); - - if (p == nullptr) - { - sys_net.error("Failed to convert address for IP discovery"); - is_connected = false; - cur_ip = "0.0.0.0"; - } - else - { - cur_ip = p; - } - - struct in_addr addr; - inet_pton(AF_INET, cur_ip.c_str(), &addr); - cur_addr = addr.s_addr; + dns_ip = conv.s_addr; } -#ifdef _WIN32 - closesocket(sock); -#else - close(sock); -#endif - - // Convert dns address - std::string s_dns = g_cfg.net.dns; - in_addr conv; - if (!inet_pton(AF_INET, s_dns.c_str(), &conv)) - { - sys_net.error("Provided IP(%s) address for DNS is invalid!", s_dns); - is_connected = false; - conv.s_addr = 0; - cur_ip = "0.0.0.0"; - } - dns = conv.s_addr; - // Init switch map for dns - auto swaps = fmt::split(g_cfg.net.swap_list, {"&&"}); + auto swaps = fmt::split(g_cfg.net.swap_list.to_string(), {"&&"}); for (std::size_t i = 0; i < swaps.size(); i++) { auto host_and_ip = fmt::split(swaps[i], {"="}); if (host_and_ip.size() != 2) + { + nph_log.error("Pattern <%s> contains more than one '='", swaps[i]); continue; + } in_addr conv; if (!inet_pton(AF_INET, host_and_ip[1].c_str(), &conv)) { - sys_net.error("IP(%s) provided for %s in the switch list is invalid!", host_and_ip[1], host_and_ip[0]); - conv.s_addr = 0; + nph_log.error("IP(%s) provided for %s in the switch list is invalid!", host_and_ip[1], host_and_ip[0]); + } + else + { + switch_map[host_and_ip[0]] = conv.s_addr; } - - switch_map[host_and_ip[0]] = conv.s_addr; } } - else +} + +bool np_handler::discover_ip_address() +{ + std::array hostname; + + if (gethostname(hostname.data(), hostname.size()) == -1) { - cur_ip = "0.0.0.0"; - dns = 0; + nph_log.error("gethostname failed in IP discovery!"); + return false; } + + hostent *host = gethostbyname(hostname.data()); + if (!host) + { + nph_log.error("gethostbyname failed in IP discovery!"); + return false; + } + + if (host->h_addrtype != AF_INET) + { + nph_log.error("Could only find IPv6 addresses for current host!"); + return false; + } + + // First address is used for now, (TODO combobox with possible local addresses to use?) + local_ip_addr = *reinterpret_cast(host->h_addr_list[0]); + + // Set public address to local discovered address for now, may be updated later; + public_ip_addr = local_ip_addr; + + return true; +} + +u32 np_handler::get_local_ip_addr() const +{ + return local_ip_addr; +} + +u32 np_handler::get_public_ip_addr() const +{ + return public_ip_addr; +} + +u32 np_handler::get_dns_ip() const +{ + return dns_ip; +} + +s32 np_handler::get_net_status() const +{ + return is_connected ? CELL_NET_CTL_STATE_IPObtained : CELL_NET_CTL_STATE_Disconnected; +} + +s32 np_handler::get_psn_status() const +{ + return is_psn_active ? SCE_NP_MANAGER_STATUS_ONLINE : SCE_NP_MANAGER_STATUS_OFFLINE; +} + +const SceNpId& np_handler::get_npid() const +{ + return npid; +} + +const SceNpOnlineId& np_handler::get_online_id() const +{ + return npid.handle; +} + +const SceNpOnlineName& np_handler::get_online_name() const +{ + return online_name; +} + +const SceNpAvatarUrl& np_handler::get_avatar_url() const +{ + return avatar_url; +} + +std::string np_handler::ip_to_string(u32 ip_addr) +{ + std::string result; + in_addr addr; + addr.s_addr = ip_addr; + + result = inet_ntoa(addr); + + return result; +} + +void np_handler::string_to_npid(const char* str, SceNpId* npid) +{ + strncpy(npid->handle.data, str, sizeof(npid->handle.data)); + npid->handle.term = 0; + npid->reserved[0] = 1; +} + +void np_handler::string_to_online_name(const char* str, SceNpOnlineName* online_name) +{ + strncpy(online_name->data, str, sizeof(online_name->data)); + online_name->term = 0; +} + +void np_handler::string_to_avatar_url(const char* str, SceNpAvatarUrl* avatar_url) +{ + strncpy(avatar_url->data, str, sizeof(avatar_url->data)); + avatar_url->term = 0; } void np_handler::init_NP(u32 poolsize, vm::ptr poolptr) @@ -136,11 +205,10 @@ void np_handler::init_NP(u32 poolsize, vm::ptr poolptr) if (g_cfg.net.psn_status >= np_psn_status::fake) { - std::string s_npid = g_cfg.net.psn_npid; - ASSERT(s_npid != ""); // It should be generated in settings window if empty + std::string s_npid = g_cfg_rpcn.get_npid(); + ASSERT(!s_npid.empty()); // It should have been generated before this - strcpy_trunc(npid.handle.data, s_npid); - npid.reserved[0] = 1; + np_handler::string_to_npid(s_npid.c_str(), &npid); } switch (g_cfg.net.psn_status) @@ -149,8 +217,34 @@ void np_handler::init_NP(u32 poolsize, vm::ptr poolptr) break; case np_psn_status::fake: { - strcpy_trunc(online_name.data, "RPCS3's user"); - strcpy_trunc(avatar_url.data, "https://i.imgur.com/AfWIyQP.jpg"); + np_handler::string_to_online_name("RPCS3's user", &online_name); + np_handler::string_to_avatar_url("https://rpcs3.net/cdn/netplay/DefaultAvatar.png", &avatar_url); + break; + } + case np_psn_status::rpcn: + { + if (!is_psn_active) + break; + + // Connect RPCN client + if (!rpcn.connect(g_cfg_rpcn.get_host())) + { + rpcn_log.error("Connection to RPCN Failed!"); + is_psn_active = false; + return; + } + + if (!rpcn.login(g_cfg_rpcn.get_npid(), g_cfg_rpcn.get_password(), g_cfg_rpcn.get_token())) + { + rpcn_log.error("RPCN login attempt failed!"); + is_psn_active = false; + return; + } + + np_handler::string_to_online_name(rpcn.get_online_name().c_str(), &online_name); + np_handler::string_to_avatar_url(rpcn.get_avatar_url().c_str(), &avatar_url); + public_ip_addr = rpcn.get_addr_sig(); + break; } default: @@ -160,13 +254,19 @@ void np_handler::init_NP(u32 poolsize, vm::ptr poolptr) void np_handler::terminate_NP() { - is_psn_active = false; + // is_psn_active = false; // Reset memory pool mpool.set(0); mpool_size = 0; mpool_avail = 0; mpool_allocs.clear(); + + if (g_cfg.net.psn_status == np_psn_status::rpcn) + { + rpcn_log.error("Disconnecting from RPCN!"); + rpcn.disconnect(); + } } vm::addr_t np_handler::allocate(u32 size) @@ -207,51 +307,698 @@ vm::addr_t np_handler::allocate(u32 size) memset((static_cast(mpool.get_ptr())) + last_free, 0, alloc_size); + sceNp.trace("Allocation off:%d size:%d psize:%d, pavail:%d", last_free, alloc_size, mpool_size, mpool_avail); + return vm::cast(mpool.addr() + last_free); } +std::vector np_handler::get_match2_server_list(SceNpMatching2ContextId ctx_id) +{ + std::vector server_list{}; + + if (g_cfg.net.psn_status == np_psn_status::rpcn) + { + if (!rpcn.get_server_list(get_req_id(0), idm::get(ctx_id)->communicationId.data, server_list)) + { + rpcn_log.error("Disconnecting from RPCN!"); + is_psn_active = false; + } + } + + return server_list; +} + +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 event_key = get_event_key(); + + SceNpMatching2GetServerInfoResponse* serv_info = reinterpret_cast(allocate_req_result(event_key, sizeof(SceNpMatching2GetServerInfoResponse))); + serv_info->server.serverId = server_id; + serv_info->server.status = SCE_NP_MATCHING2_SERVER_STATUS_AVAILABLE; + + const auto cb_info = std::move(pending_requests.at(req_id)); + pending_requests.erase(req_id); + + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_GetServerInfo, event_key, 0, sizeof(SceNpMatching2GetServerInfoResponse), cb_info.cb_arg); + return 0; + }); + + 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(); + + const auto cb_info = std::move(pending_requests.at(req_id)); + pending_requests.erase(req_id); + + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_CreateServerContext, event_key, 0, 0, cb_info.cb_arg); + return 0; + }); + + 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); + + if (!rpcn.get_world_list(req_id, server_id)) + { + rpcn_log.error("Disconnecting from RPCN!"); + is_psn_active = false; + } + + return req_id; +} + +u32 np_handler::create_join_room(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2CreateJoinRoomRequest* req) +{ + u32 req_id = generate_callback_info(ctx_id, optParam); + + if (!rpcn.createjoin_room(req_id, req)) + { + rpcn_log.error("Disconnecting from RPCN!"); + is_psn_active = false; + } + + return req_id; +} + +u32 np_handler::join_room(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2JoinRoomRequest* req) +{ + u32 req_id = generate_callback_info(ctx_id, optParam); + + if (!rpcn.join_room(req_id, req)) + { + rpcn_log.error("Disconnecting from RPCN!"); + is_psn_active = false; + } + + return req_id; +} + +u32 np_handler::leave_room(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2LeaveRoomRequest* req) +{ + u32 req_id = generate_callback_info(ctx_id, optParam); + + if (!rpcn.leave_room(req_id, req)) + { + rpcn_log.error("Disconnecting from RPCN!"); + is_psn_active = false; + } + + return req_id; +} + +u32 np_handler::search_room(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SearchRoomRequest* req) +{ + u32 req_id = generate_callback_info(ctx_id, optParam); + + if (!rpcn.search_room(req_id, req)) + { + rpcn_log.error("Disconnecting from RPCN!"); + is_psn_active = false; + } + + return req_id; +} + +u32 np_handler::set_roomdata_external(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SetRoomDataExternalRequest* req) +{ + u32 req_id = generate_callback_info(ctx_id, optParam); + + extra_nps::print_set_roomdata_ext_req(req); + + if (!rpcn.set_roomdata_external(req_id, req)) + { + rpcn_log.error("Disconnecting from RPCN!"); + is_psn_active = false; + } + + return req_id; +} + +u32 np_handler::get_roomdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2GetRoomDataInternalRequest* req) +{ + u32 req_id = generate_callback_info(ctx_id, optParam); + + if (!rpcn.get_roomdata_internal(req_id, req)) + { + rpcn_log.error("Disconnecting from RPCN!"); + is_psn_active = false; + } + + return req_id; +} + +u32 np_handler::set_roomdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SetRoomDataInternalRequest* req) +{ + u32 req_id = generate_callback_info(ctx_id, optParam); + + extra_nps::print_set_roomdata_int_req(req); + + if (!rpcn.set_roomdata_internal(req_id, req)) + { + rpcn_log.error("Disconnecting from RPCN!"); + is_psn_active = false; + } + + return req_id; +} + +u32 np_handler::get_ping_info(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SignalingGetPingInfoRequest* req) +{ + u32 req_id = generate_callback_info(ctx_id, optParam); + + if (!rpcn.ping_room_owner(req_id, req->roomId)) + { + rpcn_log.error("Disconnecting from RPCN!"); + is_psn_active = false; + } + + return req_id; +} + +u32 np_handler::send_room_message(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SendRoomMessageRequest* req) +{ + u32 req_id = generate_callback_info(ctx_id, optParam); + + if (!rpcn.send_room_message(req_id, req)) + { + rpcn_log.error("Disconnecting from RPCN!"); + is_psn_active = false; + } + + return req_id; +} + +u32 np_handler::get_match2_event(SceNpMatching2EventKey event_key, u8* dest, u32 size) +{ + std::lock_guard lock(mutex_req_results); + + if (!match2_req_results.count(event_key)) + return 0; + + u32 size_copied = std::min(size, static_cast(match2_req_results.at(event_key).size())); + memcpy(dest, match2_req_results.at(event_key).data(), size_copied); + + return size_copied; +} + void np_handler::operator()() { + if (g_cfg.net.psn_status != np_psn_status::rpcn) + return; + + while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped()) + { + if (!rpcn.manage_connection()) + { + std::this_thread::sleep_for(200ms); + continue; + } + + auto replies = rpcn.get_replies(); + for (auto& reply : replies) + { + const u16 command = reply.second.first; + const u32 req_id = reply.first; + std::vector& data = reply.second.second; + + switch (command) + { + case CommandType::GetWorldList: reply_get_world_list(req_id, data); break; + case CommandType::CreateRoom: reply_create_join_room(req_id, data); break; + case CommandType::JoinRoom: reply_join_room(req_id, data); break; + case CommandType::LeaveRoom: reply_leave_room(req_id, data); break; + case CommandType::SearchRoom: reply_search_room(req_id, data); break; + case CommandType::SetRoomDataExternal: reply_set_roomdata_external(req_id, data); break; + case CommandType::GetRoomDataInternal: reply_get_roomdata_internal(req_id, data); break; + case CommandType::SetRoomDataInternal: reply_set_roomdata_internal(req_id, data); break; + case CommandType::PingRoomOwner: reply_get_ping_info(req_id, data); break; + case CommandType::SendRoomMessage: reply_send_room_message(req_id, data); break; + default: rpcn_log.error("Unknown reply(%d) received!", command); break; + } + } + + auto notifications = rpcn.get_notifications(); + for (auto& notif : notifications) + { + switch (notif.first) + { + case NotificationType::UserJoinedRoom: notif_user_joined_room(notif.second); break; + case NotificationType::UserLeftRoom: notif_user_left_room(notif.second); break; + case NotificationType::RoomDestroyed: notif_room_destroyed(notif.second); break; + case NotificationType::SignalP2PEstablished: notif_p2p_established(notif.second); break; + case NotificationType::RoomMessageReceived: notif_room_message_received(notif.second); break; + default: rpcn_log.error("Unknown notification(%d) received!", notif.first); break; + } + } + } } -s32 np_handler::get_net_status() const +bool np_handler::reply_get_world_list(u32 req_id, std::vector& reply_data) { - return is_connected ? CELL_NET_CTL_STATE_IPObtained : CELL_NET_CTL_STATE_Disconnected; + if (pending_requests.count(req_id) == 0) + return error_and_disconnect("Unexpected reply ID to GetWorldList"); + + const auto cb_info = std::move(pending_requests.at(req_id)); + pending_requests.erase(req_id); + + vec_stream reply(reply_data, 1); + + std::vector world_list; + u32 num_worlds = reply.get(); + for (u32 i = 0; i < num_worlds; i++) + { + world_list.push_back(reply.get()); + } + + if (reply.is_error()) + { + world_list.clear(); + return error_and_disconnect("Malformed reply to GetWorldList command"); + } + + u32 event_key = get_event_key(); + + SceNpMatching2GetWorldInfoListResponse* world_info = reinterpret_cast(allocate_req_result(event_key, sizeof(SceNpMatching2GetWorldInfoListResponse))); + world_info->worldNum = world_list.size(); + + if (!world_list.empty()) + { + world_info->world.set(allocate(sizeof(SceNpMatching2World) * world_list.size())); + for (size_t i = 0; i < world_list.size(); i++) + { + world_info->world[i].worldId = world_list[i]; + world_info->world[i].numOfLobby = 1; // TODO + world_info->world[i].maxNumOfTotalLobbyMember = 10000; + world_info->world[i].curNumOfTotalLobbyMember = 1; + world_info->world[i].curNumOfRoom = 1; + world_info->world[i].curNumOfTotalRoomMember = 1; + } + } + + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_GetWorldInfoList, event_key, 0, sizeof(SceNpMatching2GetWorldInfoListResponse), cb_info.cb_arg); + return 0; + }); + + return true; } -s32 np_handler::get_psn_status() const +bool np_handler::reply_create_join_room(u32 req_id, std::vector& reply_data) { - return is_psn_active ? SCE_NP_MANAGER_STATUS_ONLINE : SCE_NP_MANAGER_STATUS_OFFLINE; + if (pending_requests.count(req_id) == 0) + return error_and_disconnect("Unexpected reply ID to CreateRoom"); + + const auto cb_info = std::move(pending_requests.at(req_id)); + pending_requests.erase(req_id); + + vec_stream reply(reply_data, 1); + auto create_room_resp = reply.get_rawdata(); + + if (reply.is_error()) + return error_and_disconnect("Malformed reply to CreateRoom command"); + + u32 event_key = get_event_key(); + + auto resp = flatbuffers::GetRoot(create_room_resp.data()); + + SceNpMatching2CreateJoinRoomResponse* room_resp = reinterpret_cast(allocate_req_result(event_key, sizeof(SceNpMatching2CreateJoinRoomResponse))); + vm::ptr room_info(allocate(sizeof(SceNpMatching2RoomDataInternal))); + room_resp->roomDataInternal = room_info; + + RoomDataInternal_to_SceNpMatching2RoomDataInternal(resp, room_info.get_ptr(), npid); + + auto& info = p2p_info[room_info->roomId][1]; + info.connStatus = SCE_NP_SIGNALING_CONN_STATUS_ACTIVE; + info.addr = rpcn.get_addr_sig(); + info.port = rpcn.get_port_sig(); + + extra_nps::print_create_room_resp(room_resp); + + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_CreateJoinRoom, event_key, 0, sizeof(SceNpMatching2CreateJoinRoomResponse), cb_info.cb_arg); + return 0; + }); + + return true; } -const std::string& np_handler::get_ip() const +bool np_handler::reply_join_room(u32 req_id, std::vector& reply_data) { - return cur_ip; + if (pending_requests.count(req_id) == 0) + return error_and_disconnect("Unexpected reply ID to JoinRoom"); + + const auto cb_info = std::move(pending_requests.at(req_id)); + pending_requests.erase(req_id); + + vec_stream reply(reply_data, 1); + + auto join_room_resp = reply.get_rawdata(); + + if (reply.is_error()) + return error_and_disconnect("Malformed reply to JoinRoom command"); + + u32 event_key = get_event_key(); + + auto resp = flatbuffers::GetRoot(join_room_resp.data()); + + SceNpMatching2JoinRoomResponse* room_resp = reinterpret_cast(allocate_req_result(event_key, sizeof(SceNpMatching2JoinRoomResponse))); + vm::ptr room_info(allocate(sizeof(SceNpMatching2RoomDataInternal))); + room_resp->roomDataInternal = room_info; + + u16 member_id = RoomDataInternal_to_SceNpMatching2RoomDataInternal(resp, room_info.get_ptr(), npid); + + extra_nps::print_room_data_internal(room_resp->roomDataInternal.get_ptr()); + + auto& info = p2p_info[room_info->roomId][member_id]; + info.connStatus = SCE_NP_SIGNALING_CONN_STATUS_ACTIVE; + info.addr = rpcn.get_addr_sig(); + info.port = rpcn.get_port_sig(); + + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_JoinRoom, event_key, 0, sizeof(SceNpMatching2JoinRoomResponse), cb_info.cb_arg); + return 0; + }); + + return true; } -u32 np_handler::get_dns() const +bool np_handler::reply_leave_room(u32 req_id, std::vector& reply_data) { - return dns; + if (pending_requests.count(req_id) == 0) + return error_and_disconnect("Unexpected reply ID to LeaveRoom"); + + const auto cb_info = std::move(pending_requests.at(req_id)); + pending_requests.erase(req_id); + + 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 + + p2p_info.erase(room_id); + + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_LeaveRoom, event_key, 0, 0, cb_info.cb_arg); + return 0; + }); + + return true; } -const SceNpId& np_handler::get_npid() const +bool np_handler::reply_search_room(u32 req_id, std::vector& reply_data) { - return npid; + if (pending_requests.count(req_id) == 0) + return error_and_disconnect("Unexpected reply ID to SearchRoom"); + + const auto cb_info = std::move(pending_requests.at(req_id)); + pending_requests.erase(req_id); + + vec_stream reply(reply_data, 1); + auto search_room_resp = reply.get_rawdata(); + if (reply.is_error()) + return error_and_disconnect("Malformed reply to SearchRoom command"); + + u32 event_key = get_event_key(); + + auto resp = flatbuffers::GetRoot(search_room_resp.data()); + SceNpMatching2SearchRoomResponse* search_resp = reinterpret_cast(allocate_req_result(event_key, sizeof(SceNpMatching2SearchRoomResponse))); + + SearchRoomReponse_to_SceNpMatching2SearchRoomResponse(resp, search_resp); + + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SearchRoom, event_key, 0, sizeof(SceNpMatching2SearchRoomResponse), cb_info.cb_arg); + return 0; + }); + + return true; } -const SceNpOnlineId& np_handler::get_online_id() const +bool np_handler::reply_set_roomdata_external(u32 req_id, std::vector& reply_data) { - return npid.handle; + if (pending_requests.count(req_id) == 0) + return error_and_disconnect("Unexpected reply ID to SetRoomDataExternal"); + + const auto cb_info = std::move(pending_requests.at(req_id)); + pending_requests.erase(req_id); + + u32 event_key = get_event_key(); // Unsure if necessary if there is no data + + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataExternal, event_key, 0, 0, cb_info.cb_arg); + return 0; + }); + + return true; } -const SceNpOnlineName& np_handler::get_online_name() const +bool np_handler::reply_get_roomdata_internal(u32 req_id, std::vector& reply_data) { - return online_name; + if (pending_requests.count(req_id) == 0) + return error_and_disconnect("Unexpected reply ID to GetRoomDataInternal"); + + const auto cb_info = std::move(pending_requests.at(req_id)); + pending_requests.erase(req_id); + + vec_stream reply(reply_data, 1); + + auto internal_data = reply.get_rawdata(); + + if (reply.is_error()) + return error_and_disconnect("Malformed reply to GetRoomDataInternal command"); + + u32 event_key = get_event_key(); + + auto resp = flatbuffers::GetRoot(internal_data.data()); + SceNpMatching2GetRoomDataInternalResponse* room_resp = reinterpret_cast(allocate_req_result(event_key, sizeof(SceNpMatching2GetRoomDataInternalResponse))); + vm::ptr room_info(allocate(sizeof(SceNpMatching2RoomDataInternal))); + room_resp->roomDataInternal = room_info; + RoomDataInternal_to_SceNpMatching2RoomDataInternal(resp, room_info.get_ptr(), npid); + + extra_nps::print_room_data_internal(room_resp->roomDataInternal.get_ptr()); + + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataInternal, event_key, 0, sizeof(SceNpMatching2GetRoomDataInternalResponse), cb_info.cb_arg); + return 0; + }); + + return true; } -const SceNpAvatarUrl& np_handler::get_avatar_url() const +bool np_handler::reply_set_roomdata_internal(u32 req_id, std::vector& reply_data) { - return avatar_url; + if (pending_requests.count(req_id) == 0) + return error_and_disconnect("Unexpected reply ID to SetRoomDataInternal"); + + const auto cb_info = std::move(pending_requests.at(req_id)); + pending_requests.erase(req_id); + + u32 event_key = get_event_key(); // Unsure if necessary if there is no data + + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataInternal, event_key, 0, 0, cb_info.cb_arg); + return 0; + }); + + return true; +} + +bool np_handler::reply_get_ping_info(u32 req_id, std::vector& reply_data) +{ + if (pending_requests.count(req_id) == 0) + return error_and_disconnect("Unexpected reply ID to PingRoomOwner"); + + const auto cb_info = std::move(pending_requests.at(req_id)); + pending_requests.erase(req_id); + + vec_stream reply(reply_data, 1); + + auto ping_resp = reply.get_rawdata(); + + if (reply.is_error()) + return error_and_disconnect("Malformed reply to PingRoomOwner command"); + + u32 event_key = get_event_key(); + + auto resp = flatbuffers::GetRoot(ping_resp.data()); + SceNpMatching2SignalingGetPingInfoResponse* final_ping_resp = reinterpret_cast(allocate_req_result(event_key, sizeof(SceNpMatching2SignalingGetPingInfoResponse))); + GetPingInfoResponse_to_SceNpMatching2SignalingGetPingInfoResponse(resp, final_ping_resp); + + sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 { + cb_info.cb(cb_ppu, cb_info.ctx_id, req_id, SCE_NP_MATCHING2_REQUEST_EVENT_SignalingGetPingInfo, event_key, 0, sizeof(SceNpMatching2SignalingGetPingInfoResponse), cb_info.cb_arg); + return 0; + }); + + return true; +} + +bool np_handler::reply_send_room_message(u32 req_id, std::vector& reply_data) +{ + if (pending_requests.count(req_id) == 0) + return error_and_disconnect("Unexpected reply ID to PingRoomOwner"); + + const auto cb_info = std::move(pending_requests.at(req_id)); + pending_requests.erase(req_id); + + 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; + }); + + return true; + +} + +void np_handler::notif_user_joined_room(std::vector& data) +{ + vec_stream noti(data); + u64 room_id = noti.get(); + auto update_info_raw = noti.get_rawdata(); + + if (noti.is_error()) + { + rpcn_log.error("Received faulty UserJoinedRoom notification"); + return; + } + + u32 event_key = get_event_key(); + + auto update_info = flatbuffers::GetRoot(update_info_raw.data()); + SceNpMatching2RoomMemberUpdateInfo* notif_data = reinterpret_cast(allocate_req_result(event_key, sizeof(SceNpMatching2RoomMemberUpdateInfo))); + RoomMemberUpdateInfo_to_SceNpMatching2RoomMemberUpdateInfo(update_info, notif_data); + + sysutil_register_cb([room_event_cb = this->room_event_cb, room_id, event_key, room_event_cb_ctx = this->room_event_cb_ctx, room_event_cb_arg = this->room_event_cb_arg](ppu_thread& cb_ppu) -> s32 { + room_event_cb(cb_ppu, room_event_cb_ctx, room_id, SCE_NP_MATCHING2_ROOM_EVENT_MemberJoined, event_key, 0, sizeof(SceNpMatching2RoomMemberUpdateInfo), room_event_cb_arg); + return 0; + }); +} + +void np_handler::notif_user_left_room(std::vector& data) +{ + vec_stream noti(data); + u64 room_id = noti.get(); + auto update_info_raw = noti.get_rawdata(); + + if (noti.is_error()) + { + rpcn_log.error("Received faulty UserLeftRoom notification"); + return; + } + + u32 event_key = get_event_key(); + + auto update_info = flatbuffers::GetRoot(update_info_raw.data()); + SceNpMatching2RoomMemberUpdateInfo* notif_data = reinterpret_cast(allocate_req_result(event_key, sizeof(SceNpMatching2RoomMemberUpdateInfo))); + RoomMemberUpdateInfo_to_SceNpMatching2RoomMemberUpdateInfo(update_info, notif_data); + + sysutil_register_cb([room_event_cb = this->room_event_cb, room_event_cb_ctx = this->room_event_cb_ctx, room_id, event_key, room_event_cb_arg = this->room_event_cb_arg](ppu_thread& cb_ppu) -> s32 { + room_event_cb(cb_ppu, room_event_cb_ctx, room_id, SCE_NP_MATCHING2_ROOM_EVENT_MemberLeft, event_key, 0, sizeof(SceNpMatching2RoomMemberUpdateInfo), room_event_cb_arg); + return 0; + }); +} + +void np_handler::notif_room_destroyed(std::vector& data) +{ + vec_stream noti(data); + u64 room_id = noti.get(); + auto update_info_raw = noti.get_rawdata(); + + if (noti.is_error()) + { + rpcn_log.error("Received faulty RoomDestroyed notification"); + return; + } + + u32 event_key = get_event_key(); + + auto update_info = flatbuffers::GetRoot(update_info_raw.data()); + SceNpMatching2RoomUpdateInfo* notif_data = reinterpret_cast(allocate_req_result(event_key, sizeof(SceNpMatching2RoomUpdateInfo))); + RoomUpdateInfo_to_SceNpMatching2RoomUpdateInfo(update_info, notif_data); + + sysutil_register_cb([room_event_cb = this->room_event_cb, room_event_cb_ctx = this->room_event_cb_ctx, room_id, event_key, room_event_cb_arg = this->room_event_cb_arg](ppu_thread& cb_ppu) -> s32 { + room_event_cb(cb_ppu, room_event_cb_ctx, room_id, SCE_NP_MATCHING2_ROOM_EVENT_RoomDestroyed, event_key, 0, sizeof(SceNpMatching2RoomUpdateInfo), room_event_cb_arg); + return 0; + }); +} + +void np_handler::notif_p2p_established(std::vector& data) +{ + if (data.size() != 16) + { + rpcn_log.error("Notification data for SignalP2PEstablished != 14"); + return; + } + + const u64 room_id = reinterpret_cast&>(data[0]); + const u16 member_id = reinterpret_cast&>(data[8]); + const u16 port_p2p = reinterpret_cast&>(data[10]); + const u32 addr_p2p = reinterpret_cast&>(data[12]); + + auto& info = p2p_info[room_id][member_id]; + info.connStatus = SCE_NP_SIGNALING_CONN_STATUS_ACTIVE; + info.addr = addr_p2p; + info.port = port_p2p; + + // Signal the callback + if (signal_event_cb) + { + sysutil_register_cb([signal_event_cb = this->signal_event_cb, signal_event_cb_ctx = this->signal_event_cb_ctx, room_id, member_id, signal_event_cb_arg = this->signal_event_cb_arg](ppu_thread& cb_ppu) -> s32 + { + signal_event_cb(cb_ppu, signal_event_cb_ctx, room_id, member_id, SCE_NP_MATCHING2_SIGNALING_EVENT_Established, 0, signal_event_cb_arg); + return 0; + }); + } + + in_addr da_addr; + da_addr.s_addr = addr_p2p; + + rpcn_log.notice("P2P Established(Room Id: %d | Member Id: %d): Address: [%s:%d]", room_id, member_id, inet_ntoa(da_addr), std::bit_cast>(port_p2p)); +} + +void np_handler::notif_room_message_received(std::vector& data) +{ + vec_stream noti(data); + u64 room_id = noti.get(); + u16 member_id = noti.get(); + auto message_info_raw = noti.get_rawdata(); + + if (noti.is_error()) + { + rpcn_log.error("Received faulty RoomMessageReceived notification"); + return; + } + + u32 event_key = get_event_key(); + + auto message_info = flatbuffers::GetRoot(message_info_raw.data()); + SceNpMatching2RoomMessageInfo* notif_data = reinterpret_cast(allocate_req_result(event_key, sizeof(SceNpMatching2RoomMessageInfo))); + RoomMessageInfo_to_SceNpMatching2RoomMessageInfo(message_info, notif_data); + + if (room_msg_cb) + { + sysutil_register_cb([room_msg_cb = this->room_msg_cb, room_msg_cb_ctx = this->room_msg_cb_ctx, room_id, member_id, event_key, room_msg_cb_arg = this->room_msg_cb_arg](ppu_thread& cb_ppu) -> s32 + { + room_msg_cb(cb_ppu, room_msg_cb_ctx, room_id, member_id, SCE_NP_MATCHING2_ROOM_MSG_EVENT_Message, event_key, 0, sizeof(SceNpMatching2RoomUpdateInfo), room_msg_cb_arg); + return 0; + }); + } +} + +const signaling_info& np_handler::get_peer_infos(u16 context_id, u64 room_id, u16 member_id) +{ + return p2p_info[room_id][member_id]; } void np_handler::add_dns_spy(u32 sock) @@ -397,12 +1144,99 @@ bool np_handler::destroy_match2_context(u16 ctx_id) { return idm::remove(static_cast(ctx_id)); } +std::shared_ptr np_handler::get_match2_context(u16 ctx_id) +{ + return idm::get_unlocked(ctx_id); +} -s32 np_handler::create_lookup_context(vm::cptr communicationId) +s32 np_handler::create_lookup_title_context(vm::cptr communicationId) { - return static_cast(idm::make(communicationId)); + return static_cast(idm::make(communicationId)); } -bool np_handler::destroy_lookup_context(s32 ctx_id) +bool np_handler::destroy_lookup_title_context(s32 ctx_id) { - return idm::remove(static_cast(ctx_id)); + return idm::remove(static_cast(ctx_id)); +} + +s32 np_handler::create_lookup_transaction_context(s32 lt_ctx) +{ + return static_cast(idm::make(lt_ctx)); +} +bool np_handler::destroy_lookup_transaction_context(s32 ctx_id) +{ + return idm::remove(static_cast(ctx_id)); +} + +s32 np_handler::create_commerce2_context(u32 version, vm::cptr npid, vm::ptr handler, vm::ptr arg) +{ + return static_cast(idm::make(version, npid, handler, arg)); +} +bool np_handler::destroy_commerce2_context(s32 ctx_id) +{ + return idm::remove(static_cast(ctx_id)); +} +std::shared_ptr np_handler::get_commerce2_context(u16 ctx_id) +{ + return idm::get_unlocked(ctx_id); +} + +s32 np_handler::create_signaling_context(vm::ptr npid, vm::ptr handler, vm::ptr arg) +{ + return static_cast(idm::make(npid, handler, arg)); +} +bool np_handler::destroy_signaling_context(s32 ctx_id) +{ + return idm::remove(static_cast(ctx_id)); +} + + + +bool np_handler::error_and_disconnect(const std::string& error_msg) +{ + rpcn_log.error("%s", error_msg); + rpcn.disconnect(); + + return false; +} + +u32 np_handler::generate_callback_info(SceNpMatching2ContextId ctx_id, vm::cptr optParam) +{ + callback_info ret; + + const auto ctx = get_match2_context(ctx_id); + ASSERT(ctx); + + const u32 req_id = get_req_id(optParam ? optParam->appReqId : ctx->default_match2_optparam.appReqId); + + ret.ctx_id = ctx_id; + ret.cb = optParam ? optParam->cbFunc : ctx->default_match2_optparam.cbFunc; + ret.cb_arg = optParam ? optParam->cbFuncArg : ctx->default_match2_optparam.cbFuncArg; + + nph_log.warning("Callback used is 0x%x", ret.cb); + + pending_requests[req_id] = std::move(ret); + + return req_id; +} + +u8* np_handler::allocate_req_result(u32 event_key, size_t size) +{ + std::lock_guard lock(mutex_req_results); + match2_req_results[event_key] = std::vector(size, 0); + return match2_req_results[event_key].data(); +} + +u32 np_handler::add_players_to_history(vm::cptr npids, u32 count) +{ + 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; + // }); + // } + + return req_id; } diff --git a/rpcs3/Emu/NP/np_handler.h b/rpcs3/Emu/NP/np_handler.h index 4b1e0c5907..f4ef7910e3 100644 --- a/rpcs3/Emu/NP/np_handler.h +++ b/rpcs3/Emu/NP/np_handler.h @@ -1,28 +1,47 @@ #pragma once +#include +#include + #include "Emu/Memory/vm_ptr.h" #include "Emu/Cell/Modules/sceNp.h" #include "Emu/Cell/Modules/sceNp2.h" +#include "Emu/Cell/Modules/sceNpCommerce2.h" -#include -#include +#include "Emu/NP/rpcn_client.h" +#include "generated/np2_structs_generated.h" + +struct signaling_info +{ + int connStatus = SCE_NP_SIGNALING_CONN_STATUS_INACTIVE; + u32 addr = 0; + u16 port = 0; +}; class np_handler { public: np_handler(); + u32 get_local_ip_addr() const; + u32 get_public_ip_addr() const; + u32 get_dns_ip() const; + s32 get_psn_status() const; s32 get_net_status() const; - const std::string& get_ip() const; - u32 get_dns() const; - const SceNpId& get_npid() const; const SceNpOnlineId& get_online_id() const; const SceNpOnlineName& get_online_name() const; const SceNpAvatarUrl& get_avatar_url() const; + // Public helpers + static std::string ip_to_string(u32 addr); + // Helpers for setting various structures from string + static void string_to_npid(const char* str, SceNpId* npid); + static void string_to_online_name(const char* str, SceNpOnlineName* online_name); + static void string_to_avatar_url(const char* str, SceNpAvatarUrl* avatar_url); + // DNS hooking functions void add_dns_spy(u32 sock); void remove_dns_spy(u32 sock); @@ -31,6 +50,17 @@ public: std::vector get_dns_packet(u32 sock); s32 analyze_dns_packet(s32 s, const u8* buf, u32 len); + enum NotificationType : u16 + { + UserJoinedRoom, + UserLeftRoom, + RoomDestroyed, + SignalP2PEstablished, + _SignalP2PDisconnected, + RoomMessageReceived, + }; + + // handles async messages from server(only needed for RPCN) void operator()(); void init_NP(u32 poolsize, vm::ptr poolptr); @@ -44,6 +74,26 @@ public: bool is_NP2_Match2_init = false; bool is_NP_Auth_init = false; + // NP Handlers/Callbacks + // Seems to be global + vm::ptr manager_cb{}; // Connection status and tickets + vm::ptr manager_cb_arg{}; + + // Registered by SceNpCommunicationId + vm::ptr basic_handler; + vm::ptr basic_handler_arg; + + // Those should probably be under match2 ctx + vm::ptr room_event_cb{}; // Room events + u16 room_event_cb_ctx = 0; + vm::ptr room_event_cb_arg{}; + vm::ptr signal_event_cb{}; // Room events + u16 signal_event_cb_ctx = 0; + vm::ptr signal_event_cb_arg{}; + vm::ptr room_msg_cb{}; + u16 room_msg_cb_ctx = 0; + vm::ptr room_msg_cb_arg{}; + // Score related struct score_ctx { @@ -56,9 +106,8 @@ public: static const u32 id_base = 1; static const u32 id_step = 1; static const u32 id_count = 32; - - SceNpCommunicationId communicationId; - SceNpCommunicationPassphrase passphrase; + SceNpCommunicationId communicationId{}; + SceNpCommunicationPassphrase passphrase{}; }; s32 create_score_context(vm::cptr communicationId, vm::cptr passphrase); bool destroy_score_context(s32 ctx_id); @@ -76,15 +125,21 @@ public: static const u32 id_step = 1; static const u32 id_count = 255; - SceNpCommunicationId communicationId; - SceNpCommunicationPassphrase passphrase; + SceNpCommunicationId communicationId{}; + SceNpCommunicationPassphrase passphrase{}; + + vm::ptr context_callback{}; + vm::ptr context_callback_param{}; + + SceNpMatching2RequestOptParam default_match2_optparam{}; }; u16 create_match2_context(vm::cptr communicationId, vm::cptr passphrase); + std::shared_ptr get_match2_context(u16 ctx_id); bool destroy_match2_context(u16 ctx_id); - struct lookup_ctx + struct lookup_title_ctx { - lookup_ctx(vm::cptr communicationId) + lookup_title_ctx(vm::cptr communicationId) { memcpy(&this->communicationId, communicationId.get_ptr(), sizeof(SceNpCommunicationId)); } @@ -93,22 +148,147 @@ public: static const u32 id_step = 1; static const u32 id_count = 32; - SceNpCommunicationId communicationId; - SceNpCommunicationPassphrase passphrase; + SceNpCommunicationId communicationId{}; + SceNpCommunicationPassphrase passphrase{}; }; - s32 create_lookup_context(vm::cptr communicationId); - bool destroy_lookup_context(s32 ctx_id); + s32 create_lookup_title_context(vm::cptr communicationId); + bool destroy_lookup_title_context(s32 ctx_id); + + struct lookup_transaction_ctx + { + lookup_transaction_ctx(s32 lt_ctx) + { + this->lt_ctx = lt_ctx; + } + + static const u32 id_base = 1; + static const u32 id_step = 1; + static const u32 id_count = 32; + + s32 lt_ctx = 0; + }; + s32 create_lookup_transaction_context(s32 lt_ctx); + bool destroy_lookup_transaction_context(s32 ctx_id); + + struct commerce2_ctx + { + commerce2_ctx(u32 version, vm::cptr npid, vm::ptr handler, vm::ptr arg) + { + this->version = version; + memcpy(&this->npid, npid.get_ptr(), sizeof(SceNpId)); + this->context_callback = handler; + this->context_callback_param = arg; + } + + static const u32 id_base = 1; + static const u32 id_step = 1; + static const u32 id_count = 32; + + u32 version{}; + SceNpId npid{}; + vm::ptr context_callback{}; + vm::ptr context_callback_param{}; + }; + s32 create_commerce2_context(u32 version, vm::cptr npid, vm::ptr handler, vm::ptr arg); + std::shared_ptr get_commerce2_context(u16 ctx_id); + bool destroy_commerce2_context(s32 ctx_id); + + struct signaling_ctx + { + signaling_ctx(vm::ptr npid, vm::ptr handler, vm::ptr arg) + { + memcpy(&this->npid, npid.get_ptr(), sizeof(SceNpId)); + this->handler = handler; + this->arg = arg; + } + + static const u32 id_base = 1; + static const u32 id_step = 1; + static const u32 id_count = 32; + + SceNpId npid{}; + vm::ptr handler{}; + vm::ptr arg{}; + }; + s32 create_signaling_context(vm::ptr npid, vm::ptr handler, vm::ptr arg); + bool destroy_signaling_context(s32 ctx_id); + + // Synchronous requests + std::vector get_match2_server_list(SceNpMatching2ContextId); + // Asynchronous requests + u32 get_server_status(SceNpMatching2ContextId ctx_id, vm::cptr optParam, u16 server_id); + u32 create_server_context(SceNpMatching2ContextId ctx_id, vm::cptr optParam, u16 server_id); + u32 get_world_list(SceNpMatching2ContextId ctx_id, vm::cptr optParam, u16 server_id); + u32 create_join_room(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2CreateJoinRoomRequest* req); + u32 join_room(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2JoinRoomRequest* req); + u32 leave_room(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2LeaveRoomRequest* req); + u32 search_room(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SearchRoomRequest* req); + u32 set_roomdata_external(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SetRoomDataExternalRequest* req); + u32 get_roomdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2GetRoomDataInternalRequest* req); + u32 set_roomdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SetRoomDataInternalRequest* req); + u32 get_ping_info(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SignalingGetPingInfoRequest* req); + u32 send_room_message(SceNpMatching2ContextId ctx_id, vm::cptr optParam, const SceNpMatching2SendRoomMessageRequest* req); + + u32 get_match2_event(SceNpMatching2EventKey event_key, u8* dest, u32 size); + const signaling_info& get_peer_infos(u16 context_id, u64 room_id, u16 member_id); + + // Misc stuff + u32 add_players_to_history(vm::cptr npids, u32 count); static constexpr std::string_view thread_name = "NP Handler Thread"; +protected: + // Various generic helpers + bool discover_ip_address(); + bool error_and_disconnect(const std::string& error_msg); + + // Notification handlers + void notif_user_joined_room(std::vector& data); + void notif_user_left_room(std::vector& data); + void notif_room_destroyed(std::vector& data); + void notif_p2p_established(std::vector& data); + void notif_room_message_received(std::vector& data); + + // Reply handlers + bool reply_get_world_list(u32 req_id, std::vector& reply_data); + bool reply_create_join_room(u32 req_id, std::vector& reply_data); + bool reply_join_room(u32 req_id, std::vector& reply_data); + bool reply_leave_room(u32 req_id, std::vector& reply_data); + bool reply_search_room(u32 req_id, std::vector& reply_data); + bool reply_set_roomdata_external(u32 req_id, std::vector& reply_data); + bool reply_get_roomdata_internal(u32 req_id, std::vector& reply_data); + bool reply_set_roomdata_internal(u32 req_id, std::vector& reply_data); + bool reply_get_ping_info(u32 req_id, std::vector& reply_data); + bool reply_send_room_message(u32 req_id, std::vector& reply_data); + + // Helper functions(fb=>np2) + void BinAttr_to_SceNpMatching2BinAttr(const flatbuffers::Vector>* fb_attr, vm::ptr binattr_info); + void RoomGroup_to_SceNpMatching2RoomGroup(const flatbuffers::Vector>* fb_group, vm::ptr group_info); + void UserInfo2_to_SceNpUserInfo2(const UserInfo2* user, SceNpUserInfo2* user_info); + void SearchRoomReponse_to_SceNpMatching2SearchRoomResponse(const SearchRoomResponse* resp, SceNpMatching2SearchRoomResponse* search_resp); + u16 RoomDataInternal_to_SceNpMatching2RoomDataInternal(const RoomDataInternal* resp, SceNpMatching2RoomDataInternal* room_resp, const SceNpId& npid); + void RoomMemberUpdateInfo_to_SceNpMatching2RoomMemberUpdateInfo(const RoomMemberUpdateInfo* resp, SceNpMatching2RoomMemberUpdateInfo* room_info); + void RoomUpdateInfo_to_SceNpMatching2RoomUpdateInfo(const RoomUpdateInfo* update_info, SceNpMatching2RoomUpdateInfo* sce_update_info); + void GetPingInfoResponse_to_SceNpMatching2SignalingGetPingInfoResponse(const GetPingInfoResponse* resp, SceNpMatching2SignalingGetPingInfoResponse* sce_resp); + void RoomMessageInfo_to_SceNpMatching2RoomMessageInfo(const RoomMessageInfo* mi, SceNpMatching2RoomMessageInfo* sce_mi); + + struct callback_info { + SceNpMatching2ContextId ctx_id; + vm::ptr cb; + vm::ptr cb_arg; + }; + u32 generate_callback_info(SceNpMatching2ContextId ctx_id, vm::cptr optParam); + + std::map pending_requests; + protected: bool is_connected = false; bool is_psn_active = false; - // Net infos - std::string cur_ip{}; - u32 cur_addr = 0; - u32 dns = 0x08080808; + // IP & DNS info + be_t local_ip_addr{}; + be_t public_ip_addr{}; + be_t dns_ip = 0x08080808; // User infos SceNpId npid{}; @@ -125,4 +305,28 @@ protected: u32 mpool_avail = 0; std::map mpool_allocs{}; // offset/size vm::addr_t allocate(u32 size); + + // Memory pool static objects( room_id , internals ) + std::map> room_infos; + + // Signal P2P infos (room_id / user_id) + std::map> p2p_info{}; + + // Requests(reqEventKey : data) + std::map> match2_req_results{}; + atomic_t match2_low_reqid_cnt = 1; + atomic_t match2_event_cnt = 1; + u32 get_req_id(u16 app_req) + { + return ((app_req << 16) | match2_low_reqid_cnt.fetch_add(1)); + } + u32 get_event_key() + { + return match2_event_cnt.fetch_add(1); + } + shared_mutex mutex_req_results; + u8* allocate_req_result(u32 event_key, size_t size); + + // RPCN + rpcn_client rpcn; }; diff --git a/rpcs3/Emu/NP/np_structs_extra.cpp b/rpcs3/Emu/NP/np_structs_extra.cpp new file mode 100644 index 0000000000..acadc8cd3f --- /dev/null +++ b/rpcs3/Emu/NP/np_structs_extra.cpp @@ -0,0 +1,212 @@ +#include "stdafx.h" +#include "Emu/System.h" +#include "Emu/NP/rpcn_client.h" +#include "np_structs_extra.h" + +LOG_CHANNEL(sceNp2); + +// Helper functions for printing + +namespace extra_nps +{ + void print_sigoptparam(const SceNpMatching2SignalingOptParam* opt) + { + sceNp2.warning("type: %d", opt->type); + sceNp2.warning("flag: %d", opt->flag); + sceNp2.warning("hubMemberId: %d", opt->hubMemberId); + } + + void print_bin_attr(const SceNpMatching2BinAttr* bin) + { + sceNp2.warning("Id: %d, Size: %d, ptr: 0x%x", bin->id, bin->size, bin->ptr); + + std::string dadata{}; + for (u32 i = 0; i < bin->size; i++) + { + dadata = fmt::format("%s %02X", dadata, bin->ptr[i]); + } + sceNp2.warning("Data: %s", dadata); + } + + void print_bin_attr_internal(const SceNpMatching2RoomBinAttrInternal* bin) + { + sceNp2.warning("updateDate: %llu updateMemberId: %d", bin->updateDate.tick, bin->updateMemberId); + print_bin_attr(&bin->data); + } + + void print_member_bin_attr_internal(const SceNpMatching2RoomMemberBinAttrInternal* bin) + { + sceNp2.warning("updateDate: %llu", bin->updateDate.tick); + print_bin_attr(&bin->data); + } + + void print_presence_data(const SceNpMatching2PresenceOptionData* opt) + { + std::string dadata{}; + for (int i = 0; i < 16; i++) + { + dadata = fmt::format("%s %02X", dadata, opt->data[i]); + } + sceNp2.warning("Data: %s", dadata); + } + + void print_range_filter(const SceNpMatching2RangeFilter* filt) + { + sceNp2.warning("startIndex: %d", filt->startIndex); + sceNp2.warning("max: %d", filt->max); + } + + void print_createjoinroom(const SceNpMatching2CreateJoinRoomRequest* req) + { + sceNp2.warning("SceNpMatching2CreateJoinRoomRequest:"); + sceNp2.warning("worldId: %d", req->worldId); + sceNp2.warning("lobbyId: %d", req->lobbyId); + sceNp2.warning("maxSlot: %d", req->maxSlot); + sceNp2.warning("flagAttr: 0x%x", req->flagAttr); + sceNp2.warning("roomBinAttrInternal: *0x%x", req->roomBinAttrInternal); + sceNp2.warning("roomBinAttrInternalNum: %d", req->roomBinAttrInternalNum); + sceNp2.warning("roomSearchableIntAttrExternal: *0x%x", req->roomSearchableIntAttrExternal); + sceNp2.warning("roomSearchableIntAttrExternalNum: %d", req->roomSearchableIntAttrExternalNum); + sceNp2.warning("roomSearchableBinAttrExternal: *0x%x", req->roomSearchableBinAttrExternal); + sceNp2.warning("roomSearchableBinAttrExternalNum: %d", req->roomSearchableBinAttrExternalNum); + sceNp2.warning("roomBinAttrExternal: *0x%x", req->roomBinAttrExternal); + sceNp2.warning("roomBinAttrExternalNum: %d", req->roomBinAttrExternalNum); + sceNp2.warning("roomPassword: *0x%x", req->roomPassword); + sceNp2.warning("groupConfig: *0x%x", req->groupConfig); + sceNp2.warning("groupConfigNum: %d", req->groupConfigNum); + sceNp2.warning("passwordSlotMask: *0x%x", req->passwordSlotMask); + sceNp2.warning("allowedUser: *0x%x", req->allowedUser); + sceNp2.warning("allowedUserNum: %d", req->allowedUserNum); + sceNp2.warning("blockedUser: *0x%x", req->blockedUser); + sceNp2.warning("blockedUserNum: %d", req->blockedUserNum); + sceNp2.warning("joinRoomGroupLabel: *0x%x", req->joinRoomGroupLabel); + sceNp2.warning("roomMemberBinAttrInternal: *0x%x", req->roomMemberBinAttrInternal); + sceNp2.warning("roomMemberBinAttrInternalNum: %d", req->roomMemberBinAttrInternalNum); + + for (u32 i = 0; i < req->roomMemberBinAttrInternalNum; i++) + print_bin_attr(&req->roomMemberBinAttrInternal[i]); + + sceNp2.warning("teamId: %d", req->teamId); + sceNp2.warning("sigOptParam: *0x%x", req->sigOptParam); + + if (req->sigOptParam) + print_sigoptparam(req->sigOptParam.get_ptr()); + + for (u32 i = 0; i < req->roomSearchableIntAttrExternalNum; i++) + { + sceNp2.warning("roomSearchableIntAttrExternal(%d) = %d", req->roomSearchableIntAttrExternal[i].id, req->roomSearchableIntAttrExternal[i].num); + } + } + + void print_joinroom(const SceNpMatching2JoinRoomRequest* req) + { + sceNp2.warning("SceNpMatching2JoinRoomRequest:"); + sceNp2.warning("roomId: %d", req->roomId); + sceNp2.warning("roomPassword: *0x%x", req->roomPassword); + sceNp2.warning("joinRoomGroupLabel: *0x%x", req->joinRoomGroupLabel); + sceNp2.warning("roomMemberBinAttrInternal: *0x%x", req->roomMemberBinAttrInternal); + sceNp2.warning("roomMemberBinAttrInternalNum: %d", req->roomMemberBinAttrInternalNum); + print_presence_data(&req->optData); + sceNp2.warning("teamId: %d", req->teamId); + + for (u32 i = 0; i < req->roomMemberBinAttrInternalNum; i++) + print_bin_attr(&req->roomMemberBinAttrInternal[i]); + } + + void print_search_room(const SceNpMatching2SearchRoomRequest* req) + { + sceNp2.warning("SceNpMatching2SearchRoomRequest:"); + sceNp2.warning("option: 0x%x", req->option); + sceNp2.warning("worldId: %d", req->worldId); + sceNp2.warning("lobbyId: %lld", req->lobbyId); + print_range_filter(&req->rangeFilter); + sceNp2.warning("flagFilter: 0x%x", req->flagFilter); + sceNp2.warning("flagAttr: 0x%x", req->flagAttr); + sceNp2.warning("intFilter: *0x%x", req->intFilter); + sceNp2.warning("intFilterNum: %d", req->intFilterNum); + sceNp2.warning("binFilter: *0x%x", req->binFilter); + sceNp2.warning("binFilterNum: %d", req->binFilterNum); + sceNp2.warning("attrId: *0x%x", req->attrId); + sceNp2.warning("attrIdNum: %d", req->attrIdNum); + } + + void print_room_member_data_internal(const SceNpMatching2RoomMemberDataInternal* member) + { + sceNp2.warning("SceNpMatching2RoomMemberDataInternal:"); + sceNp2.warning("next: *0x%x", member->next); + sceNp2.warning("npId: %s", member->userInfo.npId.handle.data); + sceNp2.warning("onlineName: %s", member->userInfo.onlineName->data); + sceNp2.warning("avatarUrl: %s", member->userInfo.avatarUrl->data); + sceNp2.warning("joinDate: %lld", member->joinDate.tick); + sceNp2.warning("memberId: %d", member->memberId); + sceNp2.warning("teamId: %d", member->teamId); + sceNp2.warning("roomGroup: *0x%x", member->roomGroup); + sceNp2.warning("natType: %d", member->natType); + sceNp2.warning("flagAttr: 0x%x", member->flagAttr); + sceNp2.warning("roomMemberBinAttrInternal: *0x%x", member->roomMemberBinAttrInternal); + sceNp2.warning("roomMemberBinAttrInternalNum: %d", member->roomMemberBinAttrInternalNum); + for (u32 i = 0; i < member->roomMemberBinAttrInternalNum; i++) + print_member_bin_attr_internal(&member->roomMemberBinAttrInternal[i]); + } + + void print_room_data_internal(const SceNpMatching2RoomDataInternal* room) + { + sceNp2.warning("SceNpMatching2RoomDataInternal:"); + sceNp2.warning("serverId: %d", room->serverId); + sceNp2.warning("worldId: %d", room->worldId); + sceNp2.warning("lobbyId: %lld", room->lobbyId); + sceNp2.warning("roomId: %lld", room->roomId); + sceNp2.warning("passwordSlotMask: %lld", room->passwordSlotMask); + sceNp2.warning("maxSlot: %d", room->maxSlot); + + sceNp2.warning("members: *0x%x", room->memberList.members); + print_room_member_data_internal(room->memberList.members.get_ptr()); + sceNp2.warning("membersNum: %d", room->memberList.membersNum); + sceNp2.warning("me: *0x%x", room->memberList.me); + sceNp2.warning("owner: *0x%x", room->memberList.owner); + + sceNp2.warning("roomGroup: *0x%x", room->roomGroup); + sceNp2.warning("roomGroupNum: %d", room->roomGroupNum); + sceNp2.warning("flagAttr: 0x%x", room->flagAttr); + sceNp2.warning("roomBinAttrInternal: *0x%x", room->roomBinAttrInternal); + sceNp2.warning("roomBinAttrInternalNum: %d", room->roomBinAttrInternalNum); + for (u32 i = 0; i < room->roomBinAttrInternalNum; i++) + print_bin_attr_internal(&room->roomBinAttrInternal[i]); + } + + void print_create_room_resp(const SceNpMatching2CreateJoinRoomResponse *resp) + { + sceNp2.warning("SceNpMatching2CreateJoinRoomResponse:"); + sceNp2.warning("roomDataInternal: *0x%x", resp->roomDataInternal); + if(resp->roomDataInternal) + print_room_data_internal(resp->roomDataInternal.get_ptr()); + } + + void print_set_roomdata_ext_req(const SceNpMatching2SetRoomDataExternalRequest* req) + { + sceNp2.warning("SceNpMatching2SetRoomDataExternalRequest:"); + sceNp2.warning("roomId: %d", req->roomId); + sceNp2.warning("roomSearchableIntAttrExternal: *0x%x", req->roomSearchableIntAttrExternal); + sceNp2.warning("roomSearchableIntAttrExternalNum: %d", req->roomSearchableIntAttrExternalNum); + sceNp2.warning("roomSearchableBinAttrExternal: *0x%x", req->roomSearchableBinAttrExternal); + sceNp2.warning("roomSearchableBinAttrExternalNum: %d", req->roomSearchableBinAttrExternalNum); + sceNp2.warning("roomBinAttrExternal: *0x%x", req->roomBinAttrExternal); + sceNp2.warning("roomBinAttrExternalNum: %d", req->roomBinAttrExternalNum); + } + + void print_set_roomdata_int_req(const SceNpMatching2SetRoomDataInternalRequest* req) + { + sceNp2.warning("SceNpMatching2SetRoomDataInternalRequest:"); + sceNp2.warning("roomId: %d", req->roomId); + sceNp2.warning("flagFilter: 0x%x", req->flagFilter); + sceNp2.warning("flagAttr: 0x%x", req->flagAttr); + sceNp2.warning("roomBinAttrInternal: *0x%x", req->roomBinAttrInternal); + sceNp2.warning("roomBinAttrInternalNum: %d", req->roomBinAttrInternalNum); + sceNp2.warning("passwordConfig: *0x%x", req->passwordConfig); + sceNp2.warning("passwordConfigNum: %d", req->passwordConfigNum); + sceNp2.warning("passwordSlotMask: *0x%x", req->passwordSlotMask); + sceNp2.warning("ownerPrivilegeRank: *0x%x", req->ownerPrivilegeRank); + sceNp2.warning("ownerPrivilegeRankNum: %d", req->ownerPrivilegeRankNum); + } + +} // namespace extra_nps diff --git a/rpcs3/Emu/NP/np_structs_extra.h b/rpcs3/Emu/NP/np_structs_extra.h new file mode 100644 index 0000000000..715b0cad9c --- /dev/null +++ b/rpcs3/Emu/NP/np_structs_extra.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Emu/Memory/vm_ptr.h" +#include "Emu/Cell/Modules/sceNp.h" +#include "Emu/Cell/Modules/sceNp2.h" +#include "Emu/NP/rpcn_client.h" + +namespace extra_nps +{ + void print_sigoptparam(const SceNpMatching2SignalingOptParam* opt); + void print_bin_attr(const SceNpMatching2BinAttr* bin); + void print_presence_data(const SceNpMatching2PresenceOptionData* opt); + void print_range_filter(const SceNpMatching2RangeFilter* filt); + void print_room_data_internal(const SceNpMatching2RoomDataInternal* room); + void print_room_member_data_internal(const SceNpMatching2RoomMemberDataInternal* member); + + void print_createjoinroom(const SceNpMatching2CreateJoinRoomRequest* req); + void print_create_room_resp(const SceNpMatching2CreateJoinRoomResponse* resp); + void print_joinroom(const SceNpMatching2JoinRoomRequest* req); + void print_search_room(const SceNpMatching2SearchRoomRequest* req); + void print_set_roomdata_ext_req(const SceNpMatching2SetRoomDataExternalRequest* req); + void print_set_roomdata_int_req(const SceNpMatching2SetRoomDataInternalRequest* req); +} // namespace extra_nps diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp new file mode 100644 index 0000000000..498f689f0b --- /dev/null +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -0,0 +1,1125 @@ +#include "stdafx.h" +#include +#include +#include +#include +#include +#include "rpcn_client.h" +#include "np_structs_extra.h" +#include "Utilities/StrUtil.h" +#include "Utilities/BEType.h" +#include "Utilities/Thread.h" +#include "Emu/IdManager.h" +#include "Emu/System.h" + +#include "generated/np2_structs_generated.h" + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +LOG_CHANNEL(rpcn_log, "rpcn"); + +#define RPCN_PROTOCOL_VERSION 7 +#define RPCN_HEADER_SIZE 9 + +rpcn_client::rpcn_client(bool in_config) + : in_config(in_config) +{ +#ifdef _WIN32 + WSADATA wsa_data; + WSAStartup(MAKEWORD(2, 2), &wsa_data); +#endif +} + +rpcn_client::~rpcn_client() +{ + disconnect(); +} + +std::string rpcn_client::get_wolfssl_error(int error) +{ + std::string error_string(80, '\0'); + auto wssl_err = wolfSSL_get_error(wssl, error); + wolfSSL_ERR_error_string(wssl_err, &error_string[0]); + return error_string; +} + +void rpcn_client::disconnect() +{ + std::lock_guard lock(mutex_socket); + + if (wssl) + { + wolfSSL_free(wssl); + wssl = nullptr; + } + + if (wssl_ctx) + { + wolfSSL_CTX_free(wssl_ctx); + wssl_ctx = nullptr; + } + + wolfSSL_Cleanup(); + + if (sockfd) + { +#ifdef _WIN32 + ::closesocket(sockfd); +#else + ::close(sockfd); +#endif + sockfd = 0; + } + + connected = false; + authentified = false; + server_info_received = false; +} + +rpcn_client::recvn_result rpcn_client::recvn(u8* buf, std::size_t n) +{ + u32 num_timeouts = 0; + + size_t n_recv = 0; + while (n_recv != n && !is_abort()) + { + std::lock_guard lock(mutex_socket); + + if (!connected) + return recvn_result::recvn_noconn; + + int res = wolfSSL_read(wssl, reinterpret_cast(buf) + n_recv, n - n_recv); + if (res <= 0) + { + if (wolfSSL_want_read(wssl)) + { + // If we received partially what we want try to wait longer + if (n_recv == 0) + return recvn_result::recvn_nodata; + + num_timeouts++; + if (num_timeouts >= 1000) + { + rpcn_log.error("recvn timeout with %d bytes received", n_recv); + return recvn_result::recvn_timeout; + } + } + else + { + rpcn_log.error("recvn failed with error: %s", get_wolfssl_error(res)); + return recvn_result::recvn_fatal; + } + + res = 0; + } + n_recv += res; + } + + return recvn_result::recvn_success; +} + +bool rpcn_client::send_packet(const std::vector& packet) +{ + u32 num_timeouts = 0; + std::size_t n_sent = 0; + while (n_sent != packet.size()) + { + std::lock_guard lock(mutex_socket); + + if (!connected) + return false; + + int res = wolfSSL_write(wssl, reinterpret_cast(packet.data()), packet.size()); + if (res <= 0) + { + if (wolfSSL_want_write(wssl)) + { + num_timeouts++; + if (num_timeouts >= 1000) + { + rpcn_log.error("send_packet timeout with %d bytes sent", n_sent); + return error_and_disconnect("Failed to send all the bytes"); + } + } + else + { + rpcn_log.error("send_packet failed with error: %s", get_wolfssl_error(res)); + return error_and_disconnect("Failed to send all the bytes"); + } + + res = 0; + } + n_sent += res; + } + + return true; +} + +bool rpcn_client::forge_send(u16 command, u32 packet_id, const std::vector& data) +{ + const auto sent_packet = forge_request(command, packet_id, data); + if (!send_packet(sent_packet)) + return false; + + return true; +} + +bool rpcn_client::forge_send_reply(u16 command, u32 packet_id, const std::vector& data, std::vector& reply_data) +{ + if (!forge_send(command, packet_id, data)) + return false; + + if (!get_reply(packet_id, reply_data)) + return false; + + if (is_error(static_cast(reply_data[0]))) + { + disconnect(); + return false; + } + + return true; +} + +bool rpcn_client::connect(const std::string& host) +{ + rpcn_log.warning("Attempting to connect to RPCN!"); + + // Cleans previous data if any + disconnect(); + + { + std::lock_guard lock(mutex_socket); + + if (wolfSSL_Init() != WOLFSSL_SUCCESS) + { + rpcn_log.fatal("Failed to initialize wolfssl"); + return false; + } + + if ((wssl_ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())) == nullptr) + { + rpcn_log.fatal("Failed to create wolfssl context"); + return false; + } + + wolfSSL_CTX_set_verify(wssl_ctx, SSL_VERIFY_NONE, 0); + + if ((wssl = wolfSSL_new(wssl_ctx)) == nullptr) + { + rpcn_log.fatal("Failed to create wolfssl object"); + return false; + } + + memset(&addr_rpcn, 0, sizeof(addr_rpcn)); + + addr_rpcn.sin_port = std::bit_cast>(31313); // htons + addr_rpcn.sin_family = AF_INET; + auto splithost = fmt::split(host, {":"}); + + if (splithost.size() != 1 && splithost.size() != 2) + { + rpcn_log.fatal("RPCN host is invalid!"); + return false; + } + + if (splithost.size() == 2) + addr_rpcn.sin_port = std::bit_cast>(std::stoul(splithost[1])); // htons + + hostent* host_addr = gethostbyname(splithost[0].c_str()); + if (!host_addr) + { + rpcn_log.fatal("Failed to resolve %s", splithost[0]); + return false; + } + + addr_rpcn.sin_addr.s_addr = *reinterpret_cast(host_addr->h_addr_list[0]); + + memcpy(&addr_rpcn_udp, &addr_rpcn, sizeof(addr_rpcn_udp)); + addr_rpcn_udp.sin_port = std::bit_cast>(3657); // htons + + sockfd = socket(AF_INET, SOCK_STREAM, 0); + +#ifdef _WIN32 + u32 timeout = 5; +#else + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 5000; +#endif + + if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast(&timeout), sizeof(timeout)) < 0) + { + rpcn_log.fatal("Failed to setsockopt!"); + return false; + } + + if (::connect(sockfd, reinterpret_cast(&addr_rpcn), sizeof(addr_rpcn)) != 0) + { + rpcn_log.fatal("Failed to connect to RPCN server!"); + return false; + } + + if (wolfSSL_set_fd(wssl, sockfd) != WOLFSSL_SUCCESS) + { + rpcn_log.fatal("Failed to associate wolfssl to the socket"); + return false; + } + + int ret_connect; + while((ret_connect = wolfSSL_connect(wssl)) != SSL_SUCCESS) + { + if(wolfSSL_want_read(wssl)) + continue; + + rpcn_log.fatal("Handshake failed with RPCN Server: %s", get_wolfssl_error(ret_connect)); + return false; + } + } + + connected = true; + + while (!server_info_received && connected && !is_abort()) + std::this_thread::sleep_for(5ms); + + if (received_version != RPCN_PROTOCOL_VERSION) + { + rpcn_log.fatal("Server returned protocol version: %d, expected: %d", received_version, RPCN_PROTOCOL_VERSION); + disconnect(); + return false; + } + + last_ping_time = std::chrono::system_clock::now() - std::chrono::seconds(5); + last_pong_time = last_ping_time; + + return true; +} + +bool rpcn_client::login(const std::string& npid, const std::string& password, const std::string& token) +{ + std::vector data{}; + std::copy(npid.begin(), npid.end(), std::back_inserter(data)); + data.push_back(0); + std::copy(password.begin(), password.end(), std::back_inserter(data)); + data.push_back(0); + std::copy(token.begin(), token.end(), std::back_inserter(data)); + data.push_back(0); + const auto sent_packet = forge_request(CommandType::Login, 1, data); + if (!send_packet(sent_packet)) + return false; + + std::vector packet_data{}; + if (!get_reply(1, packet_data)) + return false; + + vec_stream reply(packet_data); + u8 error = reply.get(); + online_name = reply.get_string(false); + avatar_url = reply.get_string(false); + user_id = reply.get(); + + if (is_error(static_cast(error))) + { + disconnect(); + return false; + } + + if (reply.is_error()) + return error_and_disconnect("Malformed reply to Login command"); + + rpcn_log.success("You are now logged in RPCN(%s | %s)!", npid, online_name); + authentified = true; + + // Make sure signaling works + if (!in_config) + { + auto start = std::chrono::system_clock::now(); + + while (!get_addr_sig() && (std::chrono::system_clock::now() - start) < std::chrono::seconds(5)) + { + std::this_thread::sleep_for(5ms); + } + + if (!get_addr_sig()) + return error_and_disconnect("Failed to get Signaling going with the server!"); + } + + return true; +} + +bool rpcn_client::create_user(const std::string& npid, const std::string& password, const std::string& online_name, const std::string& avatar_url, const std::string& email) +{ + std::vector data{}; + std::copy(npid.begin(), npid.end(), std::back_inserter(data)); + data.push_back(0); + std::copy(password.begin(), password.end(), std::back_inserter(data)); + data.push_back(0); + std::copy(online_name.begin(), online_name.end(), std::back_inserter(data)); + data.push_back(0); + std::copy(avatar_url.begin(), avatar_url.end(), std::back_inserter(data)); + data.push_back(0); + std::copy(email.begin(), email.end(), std::back_inserter(data)); + data.push_back(0); + const auto sent_packet = forge_request(CommandType::Create, 1, data); + if (!send_packet(sent_packet)) + return false; + + std::vector packet_data{}; + if (!get_reply(1, packet_data)) + return false; + + vec_stream reply(packet_data); + u8 error = reply.get(); + + if (is_error(static_cast(error))) + { + disconnect(); + return false; + } + + rpcn_log.success("You have successfully created a RPCN account(%s | %s)!", npid, online_name); + + return true; +} + +s32 send_packet_from_p2p_port(const std::vector& data, const sockaddr_in& addr); +std::queue> get_rpcn_msgs(); + +bool rpcn_client::manage_connection() +{ + if (!connected) + return false; + + if (authentified && !in_config) + { + // Ping the UDP Signaling Server + auto now = std::chrono::system_clock::now(); + + auto rpcn_msgs = get_rpcn_msgs(); + + while (!rpcn_msgs.empty()) + { + const auto& msg = rpcn_msgs.front(); + + if (msg.size() == 6) + { + addr_sig = reinterpret_cast(msg[0]); + port_sig = reinterpret_cast(msg[4]); + + in_addr orig{}; + orig.s_addr = addr_sig; + + last_pong_time = now; + } + else + { + rpcn_log.error("Received faulty RPCN UDP message!"); + } + + rpcn_msgs.pop(); + } + + // Send a packet every 5 seconds and then every 500 ms until reply is received + if ((now - last_pong_time) > std::chrono::seconds(5) && (now - last_ping_time) > std::chrono::milliseconds(500)) + { + std::vector ping(9); + ping[0] = 1; + *reinterpret_cast*>(&ping[1]) = user_id; + send_packet_from_p2p_port(ping, addr_rpcn_udp); + last_ping_time = now; + } + } + + u8 header[RPCN_HEADER_SIZE]; + auto res_recvn = recvn(header, RPCN_HEADER_SIZE); + + switch (res_recvn) + { + case recvn_result::recvn_noconn: return error_and_disconnect("Disconnected"); + case recvn_result::recvn_fatal: + case recvn_result::recvn_timeout: return error_and_disconnect("Failed to read a packet header on socket"); + case recvn_result::recvn_nodata: return false; + case recvn_result::recvn_success: break; + } + + const u8 packet_type = header[0]; + const u16 command = reinterpret_cast&>(header[1]); + const u16 packet_size = reinterpret_cast&>(header[3]); + const u32 packet_id = reinterpret_cast&>(header[5]); + + if (packet_size < RPCN_HEADER_SIZE) + return error_and_disconnect("Invalid packet size"); + + std::vector data{}; + if (packet_size > RPCN_HEADER_SIZE) + { + const u16 data_size = packet_size - RPCN_HEADER_SIZE; + data.resize(data_size); + if (recvn(data.data(), data_size) != recvn_result::recvn_success) + return error_and_disconnect("Failed to receive a whole packet"); + } + + switch (static_cast(packet_type)) + { + case PacketType::Request: return error_and_disconnect("Client shouldn't receive request packets!"); + case PacketType::Reply: + { + if (data.empty()) + return error_and_disconnect("Reply packet without result"); + + // Those commands are handled synchronously and won't be forwarded to NP Handler + if (command == CommandType::Login || command == CommandType::GetServerList || command == CommandType::Create) + { + std::lock_guard lock(mutex_replies_sync); + replies_sync.insert(std::make_pair(packet_id, std::make_pair(command, std::move(data)))); + } + else + { + std::lock_guard lock(mutex_replies); + replies.insert(std::make_pair(packet_id, std::make_pair(command, std::move(data)))); + } + + break; + } + case PacketType::Notification: + { + std::lock_guard lock(mutex_notifs); + notifications.push_back(std::make_pair(command, std::move(data))); + break; + } + case PacketType::ServerInfo: + { + if (data.size() != 4) + return error_and_disconnect("Invalid size of ServerInfo packet"); + + received_version = reinterpret_cast&>(data[0]); + server_info_received = true; + break; + } + + default: return error_and_disconnect("Unknown packet received!"); + } + + return true; +} + +std::vector>> rpcn_client::get_notifications() +{ + std::vector>> notifs; + + { + std::lock_guard lock(mutex_notifs); + notifs = std::move(notifications); + notifications.clear(); + } + + return notifs; +} + +std::unordered_map>> rpcn_client::get_replies() +{ + std::unordered_map>> ret_replies; + + { + std::lock_guard lock(mutex_replies); + ret_replies = std::move(replies); + replies.clear(); + } + + return ret_replies; +} + +bool rpcn_client::get_reply(const u32 expected_id, std::vector& data) +{ + auto check_for_reply = [this, expected_id, &data]() -> bool + { + std::lock_guard lock(mutex_replies_sync); + if (auto r = replies_sync.find(expected_id); r != replies_sync.end()) + { + data = std::move(r->second.second); + replies_sync.erase(r); + return true; + } + return false; + }; + + while (connected && !is_abort()) + { + if (check_for_reply()) + return true; + std::this_thread::sleep_for(5ms); + } + + if (check_for_reply()) + return true; + + return false; +} + +bool rpcn_client::get_server_list(u32 req_id, const std::string& communication_id, std::vector& server_list) +{ + std::vector data{}, reply_data{}; + std::copy(communication_id.begin(), communication_id.end(), std::back_inserter(data)); + data.push_back(0); + + if (!forge_send_reply(CommandType::GetServerList, req_id, data, reply_data)) + return false; + + vec_stream reply(reply_data, 1); + u16 num_servs = reply.get(); + server_list.clear(); + for (u16 i = 0; i < num_servs; i++) + { + server_list.push_back(reply.get()); + } + + if (reply.is_error()) + { + server_list.clear(); + return error_and_disconnect("Malformed reply to GetServerList command"); + } + + return true; +} + +bool rpcn_client::get_world_list(u32 req_id, u16 server_id) +{ + std::vector data(2); + reinterpret_cast&>(data[0]) = server_id; + + if (!forge_send(CommandType::GetWorldList, req_id, data)) + return false; + + return true; +} + +bool rpcn_client::createjoin_room(u32 req_id, const SceNpMatching2CreateJoinRoomRequest* req) +{ + std::vector data{}; + + extra_nps::print_createjoinroom(req); + + flatbuffers::FlatBufferBuilder builder(1024); + + flatbuffers::Offset>> final_binattrinternal_vec; + if (req->roomBinAttrInternalNum) + { + std::vector> davec; + for (u32 i = 0; i < req->roomBinAttrInternalNum; i++) + { + auto bin = CreateBinAttr(builder, req->roomBinAttrInternal[i].id, builder.CreateVector(req->roomBinAttrInternal[i].ptr.get_ptr(), req->roomBinAttrInternal[i].size)); + davec.push_back(bin); + } + final_binattrinternal_vec = builder.CreateVector(davec); + } + flatbuffers::Offset>> final_searchintattrexternal_vec; + if (req->roomSearchableIntAttrExternalNum) + { + std::vector> davec; + for (u32 i = 0; i < req->roomSearchableIntAttrExternalNum; i++) + { + auto bin = CreateIntAttr(builder, req->roomSearchableIntAttrExternal[i].id, req->roomSearchableIntAttrExternal[i].num); + davec.push_back(bin); + } + final_searchintattrexternal_vec = builder.CreateVector(davec); + } + flatbuffers::Offset>> final_searchbinattrexternal_vec; + if (req->roomSearchableBinAttrExternalNum) + { + std::vector> davec; + for (u32 i = 0; i < req->roomSearchableBinAttrExternalNum; i++) + { + auto bin = CreateBinAttr(builder, req->roomSearchableBinAttrExternal[i].id, builder.CreateVector(req->roomSearchableBinAttrExternal[i].ptr.get_ptr(), req->roomSearchableBinAttrExternal[i].size)); + davec.push_back(bin); + } + final_searchbinattrexternal_vec = builder.CreateVector(davec); + } + flatbuffers::Offset>> final_binattrexternal_vec; + if (req->roomBinAttrExternalNum) + { + std::vector> davec; + for (u32 i = 0; i < req->roomBinAttrExternalNum; i++) + { + auto bin = CreateBinAttr(builder, req->roomBinAttrExternal[i].id, builder.CreateVector(req->roomBinAttrExternal[i].ptr.get_ptr(), req->roomBinAttrExternal[i].size)); + davec.push_back(bin); + } + final_binattrexternal_vec = builder.CreateVector(davec); + } + flatbuffers::Offset> final_roompassword; + if (req->roomPassword) + final_roompassword = builder.CreateVector(req->roomPassword->data, 8); + flatbuffers::Offset>> final_groupconfigs_vec; + if (req->groupConfigNum) + { + std::vector> davec; + for (u32 i = 0; i < req->groupConfigNum; i++) + { + auto bin = CreateGroupConfig(builder, req->groupConfig[i].slotNum, req->groupConfig[i].withLabel, builder.CreateVector(req->groupConfig[i].label.data, 8), req->groupConfig[i].withPassword); + davec.push_back(bin); + } + final_groupconfigs_vec = builder.CreateVector(davec); + } + flatbuffers::Offset>> final_allowedusers_vec; + if (req->allowedUserNum) + { + std::vector> davec; + for (u32 i = 0; i < req->allowedUserNum; i++) + { + auto bin = builder.CreateString(req->allowedUser[i].handle.data); + davec.push_back(bin); + } + final_allowedusers_vec = builder.CreateVector(davec); + } + flatbuffers::Offset>> final_blockedusers_vec; + if (req->blockedUserNum) + { + std::vector> davec; + for (u32 i = 0; i < req->blockedUserNum; i++) + { + auto bin = builder.CreateString(req->blockedUser[i].handle.data); + davec.push_back(bin); + } + final_blockedusers_vec = builder.CreateVector(davec); + } + flatbuffers::Offset> final_grouplabel; + if (req->joinRoomGroupLabel) + final_grouplabel = builder.CreateVector(req->joinRoomGroupLabel->data, 8); + flatbuffers::Offset>> final_memberbinattrinternal_vec; + if (req->roomMemberBinAttrInternalNum) + { + std::vector> davec; + for (u32 i = 0; i < req->roomMemberBinAttrInternalNum; i++) + { + auto bin = CreateBinAttr( + builder, req->roomMemberBinAttrInternal[i].id, builder.CreateVector(reinterpret_cast(req->roomMemberBinAttrInternal[i].ptr.get_ptr()), req->roomMemberBinAttrInternal[i].size)); + davec.push_back(bin); + } + final_memberbinattrinternal_vec = builder.CreateVector(davec); + } + flatbuffers::Offset final_optparam; + if (req->sigOptParam) + final_optparam = CreateOptParam(builder, req->sigOptParam->type, req->sigOptParam->flag, req->sigOptParam->hubMemberId); + u64 final_passwordSlotMask = 0; + if (req->passwordSlotMask) + final_passwordSlotMask = *req->passwordSlotMask; + + auto req_finished = CreateCreateJoinRoomRequest(builder, req->worldId, req->lobbyId, req->maxSlot, req->flagAttr, final_binattrinternal_vec, final_searchintattrexternal_vec, + final_searchbinattrexternal_vec, final_binattrexternal_vec, final_roompassword, final_groupconfigs_vec, final_passwordSlotMask, final_allowedusers_vec, final_blockedusers_vec, final_grouplabel, + final_memberbinattrinternal_vec, req->teamId, final_optparam); + + builder.Finish(req_finished); + u8* buf = builder.GetBufferPointer(); + size_t bufsize = builder.GetSize(); + data.resize(sizeof(u32) + bufsize); + + reinterpret_cast&>(data[0]) = static_cast(bufsize); + memcpy(data.data() + sizeof(u32), buf, bufsize); + + if (!forge_send(CommandType::CreateRoom, req_id, data)) + return false; + + return true; +} + +bool rpcn_client::join_room(u32 req_id, const SceNpMatching2JoinRoomRequest* req) +{ + std::vector data{}; + + extra_nps::print_joinroom(req); + + flatbuffers::FlatBufferBuilder builder(1024); + + flatbuffers::Offset> final_roompassword; + if (req->roomPassword) + final_roompassword = builder.CreateVector(req->roomPassword->data, 8); + flatbuffers::Offset> final_grouplabel; + if (req->joinRoomGroupLabel) + final_grouplabel = builder.CreateVector(req->joinRoomGroupLabel->data, 8); + flatbuffers::Offset>> final_memberbinattrinternal_vec; + if (req->roomMemberBinAttrInternalNum) + { + std::vector> davec; + for (u32 i = 0; i < req->roomMemberBinAttrInternalNum; i++) + { + auto bin = CreateBinAttr(builder, req->roomMemberBinAttrInternal[i].id, builder.CreateVector(req->roomMemberBinAttrInternal[i].ptr.get_ptr(), req->roomMemberBinAttrInternal[i].size)); + davec.push_back(bin); + } + final_memberbinattrinternal_vec = builder.CreateVector(davec); + } + flatbuffers::Offset final_optdata = CreatePresenceOptionData(builder, builder.CreateVector(req->optData.data, 16), req->optData.length); + + auto req_finished = CreateJoinRoomRequest(builder, req->roomId, final_roompassword, final_grouplabel, final_memberbinattrinternal_vec, final_optdata, req->teamId); + + builder.Finish(req_finished); + u8* buf = builder.GetBufferPointer(); + size_t bufsize = builder.GetSize(); + data.resize(sizeof(u32) + bufsize); + + reinterpret_cast&>(data[0]) = static_cast(bufsize); + memcpy(data.data() + sizeof(u32), buf, bufsize); + + if (!forge_send(CommandType::JoinRoom, req_id, data)) + return false; + + return true; +} + +bool rpcn_client::leave_room(u32 req_id, const SceNpMatching2LeaveRoomRequest* req) +{ + std::vector data{}; + + flatbuffers::FlatBufferBuilder builder(1024); + flatbuffers::Offset final_optdata = CreatePresenceOptionData(builder, builder.CreateVector(req->optData.data, 16), req->optData.length); + auto req_finished = CreateLeaveRoomRequest(builder, req->roomId, final_optdata); + builder.Finish(req_finished); + u8* buf = builder.GetBufferPointer(); + size_t bufsize = builder.GetSize(); + data.resize(sizeof(u32) + bufsize); + + reinterpret_cast&>(data[0]) = static_cast(bufsize); + memcpy(data.data() + sizeof(u32), buf, bufsize); + + if (!forge_send(CommandType::LeaveRoom, req_id, data)) + return false; + + return true; +} + +bool rpcn_client::search_room(u32 req_id, const SceNpMatching2SearchRoomRequest* req) +{ + std::vector data{}; + + extra_nps::print_search_room(req); + + flatbuffers::FlatBufferBuilder builder(1024); + flatbuffers::Offset>> final_intfilter_vec; + if (req->intFilterNum) + { + std::vector> davec{}; + for (u32 i = 0; i < req->intFilterNum; i++) + { + auto int_attr = CreateIntAttr(builder, req->intFilter[i].attr.id, req->intFilter[i].attr.num); + auto bin = CreateIntSearchFilter(builder, req->intFilter[i].searchOperator, int_attr); + davec.push_back(bin); + } + final_intfilter_vec = builder.CreateVector(davec); + } + flatbuffers::Offset>> final_binfilter_vec; + if (req->binFilterNum) + { + std::vector> davec; + for (u32 i = 0; i < req->binFilterNum; i++) + { + auto bin_attr = CreateBinAttr(builder, req->binFilter[i].attr.id, builder.CreateVector(req->binFilter[i].attr.ptr.get_ptr(), req->binFilter[i].attr.size)); + auto bin = CreateBinSearchFilter(builder, req->binFilter[i].searchOperator, bin_attr); + davec.push_back(bin); + } + final_binfilter_vec = builder.CreateVector(davec); + } + flatbuffers::Offset> attrid_vec; + if (req->attrIdNum) + attrid_vec = builder.CreateVector(reinterpret_cast(req->attrId.get_ptr()), req->attrIdNum); + + SearchRoomRequestBuilder s_req(builder); + s_req.add_option(req->option); + s_req.add_worldId(req->worldId); + s_req.add_lobbyId(req->lobbyId); + s_req.add_rangeFilter_startIndex(req->rangeFilter.startIndex); + s_req.add_rangeFilter_max(req->rangeFilter.max); + s_req.add_flagFilter(req->flagFilter); + s_req.add_flagAttr(req->flagAttr); + if (req->intFilterNum) + s_req.add_intFilter(final_intfilter_vec); + if (req->binFilterNum) + s_req.add_binFilter(final_binfilter_vec); + if (req->attrIdNum) + s_req.add_attrId(attrid_vec); + + auto req_finished = s_req.Finish(); + builder.Finish(req_finished); + u8* buf = builder.GetBufferPointer(); + size_t bufsize = builder.GetSize(); + data.resize(bufsize + sizeof(u32)); + + reinterpret_cast&>(data[0]) = static_cast(bufsize); + memcpy(data.data() + sizeof(u32), buf, bufsize); + + if (!forge_send(CommandType::SearchRoom, req_id, data)) + return false; + + return true; +} + +bool rpcn_client::set_roomdata_external(u32 req_id, const SceNpMatching2SetRoomDataExternalRequest* req) +{ + std::vector data{}; + + flatbuffers::FlatBufferBuilder builder(1024); + flatbuffers::Offset>> final_searchintattrexternal_vec; + if (req->roomSearchableIntAttrExternalNum) + { + std::vector> davec; + for (u32 i = 0; i < req->roomSearchableIntAttrExternalNum; i++) + { + auto bin = CreateIntAttr(builder, req->roomSearchableIntAttrExternal[i].id, req->roomSearchableIntAttrExternal[i].num); + davec.push_back(bin); + } + final_searchintattrexternal_vec = builder.CreateVector(davec); + } + flatbuffers::Offset>> final_searchbinattrexternal_vec; + if (req->roomSearchableBinAttrExternalNum) + { + std::vector> davec; + for (u32 i = 0; i < req->roomSearchableBinAttrExternalNum; i++) + { + auto bin = CreateBinAttr(builder, req->roomSearchableBinAttrExternal[i].id, builder.CreateVector(req->roomSearchableBinAttrExternal[i].ptr.get_ptr(), req->roomSearchableBinAttrExternal[i].size)); + davec.push_back(bin); + } + final_searchbinattrexternal_vec = builder.CreateVector(davec); + } + flatbuffers::Offset>> final_binattrexternal_vec; + if (req->roomBinAttrExternalNum) + { + std::vector> davec; + for (u32 i = 0; i < req->roomBinAttrExternalNum; i++) + { + auto bin = CreateBinAttr(builder, req->roomBinAttrExternal[i].id, builder.CreateVector(req->roomBinAttrExternal[i].ptr.get_ptr(), req->roomBinAttrExternal[i].size)); + davec.push_back(bin); + } + final_binattrexternal_vec = builder.CreateVector(davec); + } + auto req_finished = CreateSetRoomDataExternalRequest(builder, req->roomId, final_searchintattrexternal_vec, final_searchbinattrexternal_vec, final_binattrexternal_vec); + + builder.Finish(req_finished); + u8* buf = builder.GetBufferPointer(); + size_t bufsize = builder.GetSize(); + data.resize(sizeof(u32) + bufsize); + + reinterpret_cast&>(data[0]) = static_cast(bufsize); + memcpy(data.data() + sizeof(u32), buf, bufsize); + + if (!forge_send(CommandType::SetRoomDataExternal, req_id, data)) + return false; + + return true; +} + +bool rpcn_client::get_roomdata_internal(u32 req_id, const SceNpMatching2GetRoomDataInternalRequest* req) +{ + std::vector data{}, reply_data{}; + + flatbuffers::FlatBufferBuilder builder(1024); + + flatbuffers::Offset> final_attr_ids_vec; + if (req->attrIdNum) + final_attr_ids_vec = builder.CreateVector(reinterpret_cast(req->attrId.get_ptr()), req->attrIdNum); + + auto req_finished = CreateGetRoomDataInternalRequest(builder, req->roomId, final_attr_ids_vec); + + builder.Finish(req_finished); + u8* buf = builder.GetBufferPointer(); + size_t bufsize = builder.GetSize(); + data.resize(sizeof(u32) + bufsize); + + reinterpret_cast&>(data[0]) = static_cast(bufsize); + memcpy(data.data() + sizeof(u32), buf, bufsize); + + if (!forge_send(CommandType::GetRoomDataInternal, req_id, data)) + return false; + + return true; +} + +bool rpcn_client::set_roomdata_internal(u32 req_id, const SceNpMatching2SetRoomDataInternalRequest* req) +{ + std::vector data{}; + + // extra_nps::print_set_roomdata_req(req); + + flatbuffers::FlatBufferBuilder builder(1024); + flatbuffers::Offset>> final_binattrinternal_vec; + if (req->roomBinAttrInternalNum) + { + std::vector> davec; + for (u32 i = 0; i < req->roomBinAttrInternalNum; i++) + { + auto bin = CreateBinAttr(builder, req->roomBinAttrInternal[i].id, builder.CreateVector(req->roomBinAttrInternal[i].ptr.get_ptr(), req->roomBinAttrInternal[i].size)); + davec.push_back(bin); + } + final_binattrinternal_vec = builder.CreateVector(davec); + } + flatbuffers::Offset>> final_grouppasswordconfig_vec; + if (req->passwordConfigNum) + { + std::vector> davec; + for (u32 i = 0; i < req->passwordConfigNum; i++) + { + auto rg = CreateRoomGroupPasswordConfig(builder, req->passwordConfig[i].groupId, req->passwordConfig[i].withPassword); + davec.push_back(rg); + } + final_grouppasswordconfig_vec = builder.CreateVector(davec); + } + u64 final_passwordSlotMask = 0; + if (req->passwordSlotMask) + final_passwordSlotMask = *req->passwordSlotMask; + + flatbuffers::Offset> final_ownerprivilege_vec; + if (req->ownerPrivilegeRankNum) + final_ownerprivilege_vec = builder.CreateVector(reinterpret_cast(req->ownerPrivilegeRank.get_ptr()), req->ownerPrivilegeRankNum); + + auto req_finished = + CreateSetRoomDataInternalRequest(builder, req->roomId, req->flagFilter, req->flagAttr, final_binattrinternal_vec, final_grouppasswordconfig_vec, final_passwordSlotMask, final_ownerprivilege_vec); + + builder.Finish(req_finished); + u8* buf = builder.GetBufferPointer(); + size_t bufsize = builder.GetSize(); + data.resize(sizeof(u32) + bufsize); + + reinterpret_cast&>(data[0]) = static_cast(bufsize); + memcpy(data.data() + sizeof(u32), buf, bufsize); + + if (!forge_send(CommandType::SetRoomDataInternal, req_id, data)) + return false; + + return true; +} + +bool rpcn_client::ping_room_owner(u32 req_id, u64 room_id) +{ + std::vector data{}; + + data.resize(8); + reinterpret_cast&>(data[0]) = room_id; + + if (!forge_send(CommandType::PingRoomOwner, req_id, data)) + return false; + + return true; +} + +bool rpcn_client::send_room_message(u32 req_id, const SceNpMatching2SendRoomMessageRequest* req) +{ + std::vector data{}; + flatbuffers::FlatBufferBuilder builder(1024); + + std::vector dst; + switch(req->castType) + { + case SCE_NP_MATCHING2_CASTTYPE_BROADCAST: + break; + case SCE_NP_MATCHING2_CASTTYPE_UNICAST: + dst.push_back(req->dst.unicastTarget); + break; + case SCE_NP_MATCHING2_CASTTYPE_MULTICAST: + for (u32 i = 0; i < req->dst.multicastTarget.memberIdNum; i++) + { + dst.push_back(req->dst.multicastTarget.memberId[i]); + } + break; + case SCE_NP_MATCHING2_CASTTYPE_MULTICAST_TEAM: + dst.push_back(req->dst.multicastTargetTeamId); + break; + default: + ASSERT(false); + break; + } + + auto req_finished = CreateSendRoomMessageRequest(builder, req->roomId, req->castType, builder.CreateVector(dst.data(), dst.size()), builder.CreateVector(reinterpret_cast(req->msg.get_ptr()), req->msgLen), req->option); + + builder.Finish(req_finished); + u8* buf = builder.GetBufferPointer(); + size_t bufsize = builder.GetSize(); + data.resize(sizeof(u32) + bufsize); + + reinterpret_cast&>(data[0]) = static_cast(bufsize); + memcpy(data.data() + sizeof(u32), buf, bufsize); + + if (!forge_send(CommandType::SendRoomMessage, req_id, data)) + return false; + + return true; +} + +std::vector rpcn_client::forge_request(u16 command, u32 packet_id, const std::vector& data) const +{ + u16 packet_size = data.size() + RPCN_HEADER_SIZE; + + std::vector packet(packet_size); + packet[0] = PacketType::Request; + reinterpret_cast&>(packet[1]) = command; + reinterpret_cast&>(packet[3]) = packet_size; + reinterpret_cast&>(packet[5]) = packet_id; + + memcpy(packet.data() + RPCN_HEADER_SIZE, data.data(), data.size()); + return packet; +} + +bool rpcn_client::is_error(ErrorType err) const +{ + if (err >= ErrorType::__error_last) + { + rpcn_log.error("Invalid error returned!"); + return true; + } + + switch (err) + { + case NoError: return false; + case Malformed: rpcn_log.error("Sent packet was malformed!"); break; + case Invalid: rpcn_log.error("Sent command was invalid!"); break; + case InvalidInput: rpcn_log.error("Sent data was invalid!"); break; + case ErrorLogin: rpcn_log.error("Sent password/token was incorrect!"); break; + case ErrorCreate: rpcn_log.error("Error creating an account!"); break; + case AlreadyLoggedIn: rpcn_log.error("User is already logged in!"); break; + case DbFail: rpcn_log.error("A db query failed on the server!"); break; + case NotFound: rpcn_log.error("A request replied not found!"); return false; + case Unsupported: rpcn_log.error("An unsupported operation was attempted!"); return false; + default: rpcn_log.fatal("Unhandled ErrorType reached the switch?"); break; + } + + return true; +} + +bool rpcn_client::error_and_disconnect(const std::string& error_msg) +{ + rpcn_log.error("%s", error_msg); + disconnect(); + return false; +} + +bool rpcn_client::is_abort() +{ + if (in_config) + { + if (abort_config) + return true; + } + else + { + if (Emu.IsStopped() || thread_ctrl::state() == thread_state::aborting) + return true; + } + + return false; +} + +void rpcn_client::abort() +{ + ASSERT(in_config); + abort_config = true; +} diff --git a/rpcs3/Emu/NP/rpcn_client.h b/rpcs3/Emu/NP/rpcn_client.h new file mode 100644 index 0000000000..836bd7e334 --- /dev/null +++ b/rpcs3/Emu/NP/rpcn_client.h @@ -0,0 +1,253 @@ +#pragma once + +#include +#include +#include +#include "Utilities/mutex.h" + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#endif + +#include "Emu/Memory/vm.h" +#include "Emu/Memory/vm_ptr.h" +#include "Emu/Cell/Modules/sceNp.h" +#include "Emu/Cell/Modules/sceNp2.h" + +#include "generated/np2_structs_generated.h" + +#include + +class vec_stream +{ +public: + vec_stream() = delete; + vec_stream(std::vector& _vec, size_t initial_index = 0) + : vec(_vec) + , i(initial_index){}; + bool is_error() const + { + return error; + } + + // Getters + + template + T get() + { + if (sizeof(T) + i > vec.size()) + { + error = true; + return 0; + } + T res = reinterpret_cast&>(vec[i]); + i += sizeof(T); + return res; + } + std::string get_string(bool empty) + { + std::string res{}; + while (i < vec.size() && vec[i] != 0) + { + res.push_back(vec[i]); + i++; + } + i++; + + if (!empty && res.size() == 0) + { + error = true; + } + + return res; + } + std::vector get_rawdata() + { + std::vector ret; + u32 size = get(); + + if ((vec.begin() + i + size) <= vec.end()) + std::copy(vec.begin() + i, vec.begin() + i + size, std::back_inserter(ret)); + else + error = true; + + return ret; + } + + // Setters + + template + void insert(T value) + { + value = reinterpret_cast>(value); + // resize + memcpy instead? + for (size_t index = 0; index < sizeof(T); index++) + { + vec.push_back(*(reinterpret_cast(&value) + index)); + } + } + void insert_string(const std::string& str) + { + std::copy(str.begin(), str.end(), std::back_inserter(vec)); + vec.push_back(0); + } + +protected: + std::vector& vec; + size_t i = 0; + bool error = false; +}; + +enum CommandType : u16 +{ + Login, + Terminate, + Create, + SendToken, + GetServerList, + GetWorldList, + CreateRoom, + JoinRoom, + LeaveRoom, + SearchRoom, + SetRoomDataExternal, + GetRoomDataInternal, + SetRoomDataInternal, + PingRoomOwner, + SendRoomMessage, +}; + +class rpcn_client +{ + enum PacketType : u8 + { + Request, + Reply, + Notification, + ServerInfo, + }; + + enum ErrorType : u8 + { + NoError, + Malformed, + Invalid, + InvalidInput, + ErrorLogin, + ErrorCreate, + AlreadyLoggedIn, + DbFail, + NotFound, + Unsupported, + __error_last + }; + +public: + rpcn_client(bool in_config = false); + ~rpcn_client(); + + bool connect(const std::string& host); + bool login(const std::string& npid, const std::string& password, const std::string& token); + bool create_user(const std::string& npid, const std::string& password, const std::string& online_name, const std::string& avatar_url, const std::string& email); + void disconnect(); + bool manage_connection(); + std::vector>> get_notifications(); + std::unordered_map>> get_replies(); + void abort(); + + // Synchronous requests + bool get_server_list(u32 req_id, const std::string& communication_id, std::vector& server_list); + // Asynchronous requests + bool get_world_list(u32 req_id, u16 server_id); + bool createjoin_room(u32 req_id, const SceNpMatching2CreateJoinRoomRequest* req); + bool join_room(u32 req_id, const SceNpMatching2JoinRoomRequest* req); + bool leave_room(u32 req_id, const SceNpMatching2LeaveRoomRequest* req); + bool search_room(u32 req_id, const SceNpMatching2SearchRoomRequest* req); + bool set_roomdata_external(u32 req_id, const SceNpMatching2SetRoomDataExternalRequest* req); + bool get_roomdata_internal(u32 req_id, const SceNpMatching2GetRoomDataInternalRequest* req); + bool set_roomdata_internal(u32 req_id, const SceNpMatching2SetRoomDataInternalRequest* req); + bool ping_room_owner(u32 req_id, u64 room_id); + bool send_room_message(u32 req_id, const SceNpMatching2SendRoomMessageRequest* req); + + const std::string& get_online_name() const + { + return online_name; + } + const std::string& get_avatar_url() const + { + return avatar_url; + }; + + u32 get_addr_sig() const + { + return addr_sig.load(); + } + u16 get_port_sig() const + { + return port_sig.load(); + } + +protected: + enum class recvn_result + { + recvn_success, + recvn_nodata, + recvn_timeout, + recvn_noconn, + recvn_fatal, + }; + + recvn_result recvn(u8* buf, std::size_t n); + + bool get_reply(u32 expected_id, std::vector& data); + + std::vector forge_request(u16 command, u32 packet_id, const std::vector& data) const; + bool send_packet(const std::vector& packet); + bool forge_send(u16 command, u32 packet_id, const std::vector& data); + bool forge_send_reply(u16 command, u32 packet_id, const std::vector& data, std::vector& reply_data); + + bool is_error(ErrorType err) const; + bool error_and_disconnect(const std::string& error_mgs); + bool is_abort(); + + std::string get_wolfssl_error(int error); + +protected: + atomic_t connected = false; + atomic_t authentified = false; + + WOLFSSL_CTX* wssl_ctx = nullptr; + WOLFSSL* wssl = nullptr; + + bool in_config = false; + bool abort_config = false; + + atomic_t server_info_received = false; + u32 received_version = 0; + + // UDP Signaling related + std::chrono::time_point last_ping_time{}, last_pong_time{}; + + sockaddr_in addr_rpcn{}; + sockaddr_in addr_rpcn_udp{}; + int sockfd = 0; + shared_mutex mutex_socket; + + shared_mutex mutex_notifs, mutex_replies, mutex_replies_sync; + std::vector>> notifications; // notif type / data + std::unordered_map>> replies; // req id / (command / data) + std::unordered_map>> replies_sync; // same but for sync replies(Login, Create, GetServerList) + + std::string online_name{}; + std::string avatar_url{}; + + s64 user_id = 0; + + atomic_t addr_sig{}; + atomic_t port_sig{}; +}; diff --git a/rpcs3/Emu/NP/rpcn_config.cpp b/rpcs3/Emu/NP/rpcn_config.cpp new file mode 100644 index 0000000000..a8963d2e90 --- /dev/null +++ b/rpcs3/Emu/NP/rpcn_config.cpp @@ -0,0 +1,110 @@ +#include "stdafx.h" +#include "rpcn_config.h" +#include "Emu/System.h" + +cfg_rpcn g_cfg_rpcn; + +LOG_CHANNEL(rpcn_cfg_log, "rpcn config"); + +void cfg_rpcn::load() +{ + fs::file cfg_file(cfg_rpcn::get_path(), fs::read); + if (cfg_file) + { + from_string(cfg_file.to_string()); + } +} + +void cfg_rpcn::save() const +{ +#ifdef _WIN32 + const std::string path_to_cfg = fs::get_config_dir() + "config/"; + if (!fs::create_path(path_to_cfg)) + { + rpcn_cfg_log.error("Could not create path: %s", path_to_cfg); + } +#endif + + fs::file cfg_file(cfg_rpcn::get_path(), fs::rewrite); + if (cfg_file) + { + cfg_file.write(to_string()); + } + else + { + rpcn_cfg_log.error("Could not save config: %s", cfg_rpcn::get_path()); + } +} + +std::string cfg_rpcn::get_path() +{ +#ifdef _WIN32 + return fs::get_config_dir() + "config/rpcn.yml"; +#else + return fs::get_config_dir() + "rpcn.yml"; +#endif +} + +std::string cfg_rpcn::generate_npid() +{ + std::string gen_npid = "RPCS3_"; + + const char list_chars[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; + + std::srand(time(nullptr)); + + for (int i = 0; i < 10; i++) + { + gen_npid += list_chars[std::rand() % (sizeof(list_chars))]; + } + + return gen_npid; +} + +std::string cfg_rpcn::get_host() const +{ + return host.to_string(); +} + +std::string cfg_rpcn::get_npid() +{ + std::string final_npid = npid.to_string(); + if (final_npid == "") + { + final_npid = cfg_rpcn::generate_npid(); + save(); + } + + return final_npid; +} + +std::string cfg_rpcn::get_password() const +{ + return password.to_string(); +} + +std::string cfg_rpcn::get_token() const +{ + return token.to_string(); +} + +void cfg_rpcn::set_host(const std::string& host) +{ + this->host.from_string(host); +} + +void cfg_rpcn::set_npid(const std::string& npid) +{ + this->npid.from_string(npid); +} + +void cfg_rpcn::set_password(const std::string& password) +{ + this->password.from_string(password); +} + +void cfg_rpcn::set_token(const std::string& token) +{ + this->token.from_string(token); +} diff --git a/rpcs3/Emu/NP/rpcn_config.h b/rpcs3/Emu/NP/rpcn_config.h new file mode 100644 index 0000000000..d1d0e0a272 --- /dev/null +++ b/rpcs3/Emu/NP/rpcn_config.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Utilities/Config.h" + +LOG_CHANNEL(rpcn_cfg, "RPCN Config"); + +struct cfg_rpcn : cfg::node +{ + cfg::string host{this, "Host", "np.rpcs3.net"}; + cfg::string npid{this, "NPID", ""}; + cfg::string password{this, "Password", ""}; + cfg::string token{this, "Token", ""}; + + void load(); + void save() const; + + std::string get_host() const; + std::string get_npid(); // not const because it can save if npid is requested and it has never been set + std::string get_password() const; + std::string get_token() const; + + void set_host(const std::string& host); + void set_npid(const std::string& npid); + void set_password(const std::string& password); + void set_token(const std::string& token); + + private: + static std::string get_path(); + static std::string generate_npid(); +}; + +extern cfg_rpcn g_cfg_rpcn; diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index 84005af783..652b9e03d2 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -262,7 +262,6 @@ struct cfg_root : cfg::node cfg::string swap_list{this, "IP swap list", ""}; cfg::_enum psn_status{this, "PSN status", np_psn_status::disabled}; - cfg::string psn_npid{this, "NPID", ""}; } net{this}; struct node_misc : cfg::node diff --git a/rpcs3/Emu/system_config_types.cpp b/rpcs3/Emu/system_config_types.cpp index a22055aefb..0cbd305f03 100644 --- a/rpcs3/Emu/system_config_types.cpp +++ b/rpcs3/Emu/system_config_types.cpp @@ -230,6 +230,7 @@ void fmt_class_string::format(std::string& out, u64 arg) { case np_psn_status::disabled: return "Disconnected"; case np_psn_status::fake: return "Simulated"; + case np_psn_status::rpcn: return "RPCN"; } return unknown; diff --git a/rpcs3/Emu/system_config_types.h b/rpcs3/Emu/system_config_types.h index 4dfcc07c50..35dd8085a3 100644 --- a/rpcs3/Emu/system_config_types.h +++ b/rpcs3/Emu/system_config_types.h @@ -186,6 +186,7 @@ enum np_psn_status { disabled, fake, + rpcn, }; enum class shader_mode diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 291a777d6f..b1889a0990 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -64,7 +64,7 @@ Use - ..\3rdparty\libusb\libusb;..\3rdparty\zlib;..\llvm\include;..\llvm_build\include;$(VULKAN_SDK)\Include + ..\3rdparty\wolfssl\;..\3rdparty\flatbuffers\include;..\3rdparty\libusb\libusb;..\3rdparty\zlib;..\llvm\include;..\llvm_build\include;$(VULKAN_SDK)\Include MaxSpeed @@ -100,6 +100,7 @@ + @@ -107,7 +108,10 @@ + + + NotUsing @@ -483,7 +487,11 @@ + + + + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 5a4eabec1a..fbf0e91898 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -926,6 +926,9 @@ Emu\GPU\RSX\Overlays + + Emu\NP + Emu\NP @@ -953,6 +956,15 @@ Utilities + + Emu\NP + + + Emu\NP + + + Emu\NP + @@ -1828,6 +1840,18 @@ Utilities + + Emu\NP + + + Emu\NP + + + Emu\NP + + + Emu\NP + diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 399be6a6a2..95562f03d5 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -175,7 +175,7 @@ - ..\3rdparty\wolfssl;..\3rdparty\curl\include;..\3rdparty\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\release;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;%(AdditionalIncludeDirectories) + ..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl;..\3rdparty\curl\include;..\3rdparty\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\release;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;%(AdditionalIncludeDirectories) -Zc:strictStrings -Zc:throwingNew -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) release\ false @@ -226,7 +226,7 @@ - ..\3rdparty\wolfssl;..\3rdparty\curl\include;..\3rdparty\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\debug;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;%(AdditionalIncludeDirectories) + ..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl;..\3rdparty\curl\include;..\3rdparty\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\debug;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;%(AdditionalIncludeDirectories) -Zc:strictStrings -Zc:throwingNew -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) debug\ false @@ -278,7 +278,7 @@ - ..\3rdparty\wolfssl;..\3rdparty\curl\include;..\3rdparty\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\debug;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;%(AdditionalIncludeDirectories) + ..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl;..\3rdparty\curl\include;..\3rdparty\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\debug;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;%(AdditionalIncludeDirectories) -Zc:strictStrings -Zc:throwingNew -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) debug\ false @@ -541,6 +541,11 @@ true true + + true + true + true + true true @@ -831,6 +836,11 @@ true true + + true + true + true + true true @@ -1141,6 +1151,11 @@ true true + + true + true + true + true true @@ -1431,6 +1446,11 @@ true true + + true + true + true + true true @@ -1542,6 +1562,7 @@ + @@ -2349,6 +2370,24 @@ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl" "-I.\..\3rdparty\curl\include" "-I.\..\3rdparty\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl" "-I.\..\3rdparty\curl\include" "-I.\..\3rdparty\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl" "-I.\..\3rdparty\curl\include" "-I.\..\3rdparty\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl" "-I.\..\3rdparty\curl\include" "-I.\..\3rdparty\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing %(Identity)... + .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl" "-I.\..\3rdparty\curl\include" "-I.\..\3rdparty\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" + $(QTDIR)\bin\moc.exe;%(FullPath) diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 594edda969..9a8d8910f4 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -1075,6 +1075,21 @@ Generated Files\Debug - LLVM + + Gui\settings + + + Generated Files\Release - LLVM + + + Generated Files\Debug + + + Generated Files\Release + + + Generated Files\Debug - LLVM + @@ -1397,6 +1412,9 @@ Gui\network + + Gui\settings + diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index 151f15d046..268628da6e 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -44,6 +44,7 @@ qt_utils.cpp register_editor_dialog.cpp render_creator.cpp + rpcn_settings_dialog.cpp rsx_debugger.cpp save_data_dialog.cpp save_data_info_dialog.cpp diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index e926e846d9..de99239ae7 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -127,7 +127,6 @@ enum class emu_settings_type DNSAddress, IpSwapList, PSNStatus, - PSNNPID, // System Language, @@ -268,7 +267,6 @@ static const QMap settings_location = { emu_settings_type::DNSAddress, { "Net", "DNS address"}}, { emu_settings_type::IpSwapList, { "Net", "IP swap list"}}, { emu_settings_type::PSNStatus, { "Net", "PSN status"}}, - { emu_settings_type::PSNNPID, { "Net", "NPID"}}, // System { emu_settings_type::Language, { "System", "Language"}}, diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 605cb0b3c4..08e3b670ca 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -12,6 +12,7 @@ #include "debugger_frame.h" #include "log_frame.h" #include "settings_dialog.h" +#include "rpcn_settings_dialog.h" #include "auto_pause_settings_dialog.h" #include "cg_disasm_window.h" #include "memory_string_searcher.h" @@ -1590,6 +1591,12 @@ void main_window::CreateConnects() connect(ui->confPadsAct, &QAction::triggered, open_pad_settings); + connect(ui->confRPCNAct, &QAction::triggered, [this]() + { + rpcn_settings_dialog dlg(this); + dlg.exec(); + }); + connect(ui->confAutopauseManagerAct, &QAction::triggered, [this]() { auto_pause_settings_dialog dlg(this); diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index 7608c34a1f..e55d0100c1 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -141,7 +141,7 @@ 0 0 1058 - 26 + 30 @@ -226,6 +226,7 @@ + @@ -1101,6 +1102,14 @@ Game Patches + + + RPCN + + + Configure RPCN + + diff --git a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp new file mode 100644 index 0000000000..4b1aac25aa --- /dev/null +++ b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp @@ -0,0 +1,229 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rpcn_settings_dialog.h" +#include "Emu/NP/rpcn_config.h" +#include "Emu/NP/rpcn_client.h" + +#include +#include + +rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent) + : QDialog(parent) +{ + setWindowTitle(tr("RPCN Configuration")); + setObjectName("rpcn_settings_dialog"); + setMinimumSize(QSize(400, 200)); + + QVBoxLayout* vbox_global = new QVBoxLayout(); + QHBoxLayout* hbox_labels_and_edits = new QHBoxLayout(); + QVBoxLayout* vbox_labels = new QVBoxLayout(); + QVBoxLayout* vbox_edits = new QVBoxLayout(); + QHBoxLayout* hbox_buttons = new QHBoxLayout(); + + QLabel* label_host = new QLabel(tr("Host:")); + m_edit_host = new QLineEdit(); + QLabel* label_npid = new QLabel(tr("NPID (username):")); + m_edit_npid = new QLineEdit(); + m_edit_npid->setMaxLength(16); + m_edit_npid->setValidator(new QRegExpValidator(QRegExp("^[a-zA-Z0-9_\\-]*$"), this)); + QLabel* label_pass = new QLabel(tr("Password:")); + QPushButton* btn_chg_pass = new QPushButton(tr("Set Password")); + QLabel *label_token = new QLabel(tr("Token:")); + m_edit_token = new QLineEdit(); + + QPushButton* btn_create = new QPushButton(tr("Create Account"), this); + QPushButton* btn_save = new QPushButton(tr("Save"), this); + + vbox_labels->addWidget(label_host); + vbox_labels->addWidget(label_npid); + vbox_labels->addWidget(label_pass); + vbox_labels->addWidget(label_token); + + vbox_edits->addWidget(m_edit_host); + vbox_edits->addWidget(m_edit_npid); + vbox_edits->addWidget(btn_chg_pass); + vbox_edits->addWidget(m_edit_token); + + hbox_buttons->addWidget(btn_create); + hbox_buttons->addStretch(); + hbox_buttons->addWidget(btn_save); + + hbox_labels_and_edits->addLayout(vbox_labels); + hbox_labels_and_edits->addLayout(vbox_edits); + + vbox_global->addLayout(hbox_labels_and_edits); + vbox_global->addLayout(hbox_buttons); + + setLayout(vbox_global); + + connect(btn_chg_pass, &QAbstractButton::clicked, [this]() + { + QString password; + + while (true) + { + bool clicked_ok = false; + password = QInputDialog::getText(this, "Set/Change Password", "Set your password", QLineEdit::Password, "", &clicked_ok); + if (!clicked_ok) + return; + + if (password.isEmpty()) + { + QMessageBox::critical(this, tr("Wrong input"), tr("You need to enter a password!"), QMessageBox::Ok); + } + else + { + break; + } + } + + std::string pass_str = password.toStdString(); + std::string salt_str = "No matter where you go, everybody's connected."; + + u8 salted_pass[SHA_DIGEST_SIZE]; + + wolfSSL_PKCS5_PBKDF2_HMAC_SHA1(pass_str.c_str(), pass_str.size(), reinterpret_cast(salt_str.c_str()), salt_str.size(), 1000, SHA_DIGEST_SIZE, salted_pass); + + std::string hash("0000000000000000000000000000000000000000"); + for (u32 i = 0; i < 20; i++) + { + constexpr auto pal = "0123456789abcdef"; + hash[i * 2] = pal[salted_pass[i] >> 4]; + hash[1 + i * 2] = pal[salted_pass[i] & 15]; + } + + g_cfg_rpcn.set_password(hash); + g_cfg_rpcn.save(); + }); + + connect(btn_save, &QAbstractButton::clicked, [this]() + { + if (this->save_config()) + this->close(); + }); + connect(btn_create, &QAbstractButton::clicked, [this]() { this->create_account(); }); + + g_cfg_rpcn.load(); + + m_edit_host->setText(QString::fromStdString(g_cfg_rpcn.get_host())); + m_edit_npid->setText(QString::fromStdString(g_cfg_rpcn.get_npid())); + m_edit_token->setText(QString::fromStdString(g_cfg_rpcn.get_token())); +} + +bool rpcn_settings_dialog::save_config() +{ + const auto host = m_edit_host->text().toStdString(); + const auto npid = m_edit_npid->text().toStdString(); + const auto token = m_edit_token->text().toStdString(); + + auto validate = [](const std::string& input) -> bool + { + if (input.length() < 3 || input.length() > 16) + return false; + + for (const auto c : input) + { + if (!std::isalnum(c) && c != '-' && c != '_') + return false; + } + return true; + }; + + if (host.empty()) + { + QMessageBox::critical(this, tr("Missing host"), tr("You need to enter a host for rpcn!"), QMessageBox::Ok); + return false; + } + + if (npid.empty() || g_cfg_rpcn.get_password().empty()) + { + QMessageBox::critical(this, tr("Wrong input"), tr("You need to enter a username and a password!"), QMessageBox::Ok); + return false; + } + + if (!validate(npid)) + { + QMessageBox::critical(this, tr("Invalid character"), tr("NPID must be between 3 and 16 characters and can only contain '-', '_' or alphanumeric characters."), QMessageBox::Ok); + return false; + } + + g_cfg_rpcn.set_host(host); + g_cfg_rpcn.set_npid(npid); + g_cfg_rpcn.set_token(token); + + g_cfg_rpcn.save(); + + return true; +} + +bool rpcn_settings_dialog::create_account() +{ + // Validate and save + if (!save_config()) + return false; + + QString email; + QRegExpValidator simple_email_validator(QRegExp("^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$")); + + while (true) + { + bool clicked_ok = false; + email = QInputDialog::getText(this, "Email address", "An email address is required, please note:\n*A valid email is needed to validate your account.\n*Your email won't be used for anything beyond sending you the token.\n*Upon successful creation a token will be sent to your email which you'll need to login.\n\n", QLineEdit::Normal, "", &clicked_ok); + if (!clicked_ok) + return false; + + int pos = 0; + if (email.isEmpty() || simple_email_validator.validate(email, pos) != QValidator::Acceptable) + { + QMessageBox::critical(this, tr("Wrong input"), tr("You need to enter a valid email!"), QMessageBox::Ok); + } + else + { + break; + } + } + + const auto rpcn = std::make_shared(true); + + const auto host = g_cfg_rpcn.get_host(); + const auto npid = g_cfg_rpcn.get_npid(); + const auto online_name = npid; + const auto avatar_url = "https://rpcs3.net/cdn/netplay/DefaultAvatar.png"; + const auto password = g_cfg_rpcn.get_password(); + + std::thread( + [](const std::shared_ptr rpcn) + { + while (rpcn.use_count() != 1) + rpcn->manage_connection(); + + rpcn->disconnect(); + }, + rpcn) + .detach(); + + if (!rpcn->connect(host)) + { + QMessageBox::critical(this, tr("Error Connecting"), tr("Failed to connect to RPCN server"), QMessageBox::Ok); + rpcn->abort(); + return false; + } + + if (!rpcn->create_user(npid, password, online_name, avatar_url, email.toStdString())) + { + QMessageBox::critical(this, tr("Error Creating Account"), tr("Failed to create the account"), QMessageBox::Ok); + rpcn->abort(); + return false; + } + + QMessageBox::information(this, tr("Account created!"), tr("Your account has been created successfully!\nCheck your email for your token!"), QMessageBox::Ok); + rpcn->abort(); + return true; +} diff --git a/rpcs3/rpcs3qt/rpcn_settings_dialog.h b/rpcs3/rpcs3qt/rpcn_settings_dialog.h new file mode 100644 index 0000000000..cf5fee5793 --- /dev/null +++ b/rpcs3/rpcs3qt/rpcn_settings_dialog.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +class rpcn_settings_dialog : public QDialog +{ + Q_OBJECT + +public: + rpcn_settings_dialog(QWidget* parent = nullptr); + + bool save_config(); + bool create_account(); + +protected: + QLineEdit *m_edit_host, *m_edit_npid, *m_edit_token; + + std::string generate_npid(); +}; diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 6a8bddb6e7..391db37736 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -875,9 +875,6 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceLineEdit(ui->edit_dns, emu_settings_type::DNSAddress); SubscribeTooltip(ui->gb_edit_dns, tooltips.settings.dns); - m_emu_settings->EnhanceLineEdit(ui->edit_npid, emu_settings_type::PSNNPID); - SubscribeTooltip(ui->gb_edit_npid, tooltips.settings.psn_npid); - m_emu_settings->EnhanceLineEdit(ui->edit_swaps, emu_settings_type::IpSwapList); SubscribeTooltip(ui->gb_edit_swaps, tooltips.settings.dns_swap); @@ -892,28 +889,6 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std }); ui->edit_dns->setEnabled(ui->netStatusBox->currentIndex() > 0); - connect(ui->psnStatusBox, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) - { - ui->edit_npid->setEnabled(index > 0); - - if (index > 0 && ui->edit_npid->text() == "") - { - QString gen_npid = "RPCS3_"; - - constexpr char list_chars[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', - 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; - - std::srand(time(0)); - - for (int i = 0; i < 10; i++) - { - gen_npid += list_chars[std::rand() % (sizeof(list_chars) - 1)]; - } - - ui->edit_npid->setText(gen_npid); - } - }); - 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 860c5671d6..534f63dadd 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -1676,39 +1676,6 @@ - - - - PSN settings - - - - - - - - NPID: - - - - - - - - 0 - 0 - - - - 16 - - - - - - - - diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 7863c6f90c..c894b9acd2 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -180,11 +180,10 @@ public: // network - const QString net_status = tr("If set to Connected, RPCS3 will allow programs to use your internet connection."); - const QString psn_status = tr("If set to Simulated, RPCS3 will fake PSN connection as best as it can."); - const QString dns = tr("DNS used to resolve hostnames by applications."); - const QString psn_npid = tr("Identifier representing your PSN account."); - const QString dns_swap = tr("DNS Swap List."); + const QString net_status = tr("If set to Connected, RPCS3 will allow programs to use your internet connection."); + const QString psn_status = tr("If set to Simulated, RPCS3 will fake PSN connection as best as it can."); + const QString dns = tr("DNS used to resolve hostnames by applications."); + const QString dns_swap = tr("DNS Swap List."); // system