From 4cbfd699f8e62de812dc80c41385cb207d6b9b9b Mon Sep 17 00:00:00 2001 From: Shingo INADA Date: Wed, 5 Oct 2022 21:17:11 +0900 Subject: [PATCH 1/2] add gdxsv-format.sh and apply it --- core/gdxsv/gdx_rpc.h | 22 +- core/gdxsv/gdxsv.cpp | 1156 ++++++++++++------------- core/gdxsv/gdxsv.h | 134 +-- core/gdxsv/gdxsv_CustomTexture.cpp | 172 ++-- core/gdxsv/gdxsv_CustomTexture.h | 11 +- core/gdxsv/gdxsv_backend_replay.cpp | 891 ++++++++++--------- core/gdxsv/gdxsv_backend_replay.h | 88 +- core/gdxsv/gdxsv_backend_rollback.cpp | 799 ++++++++--------- core/gdxsv/gdxsv_backend_rollback.h | 78 +- core/gdxsv/gdxsv_backend_tcp.h | 163 ++-- core/gdxsv/gdxsv_backend_udp.h | 602 +++++++------ core/gdxsv/gdxsv_emu_hooks.cpp | 408 +++++---- core/gdxsv/gdxsv_emu_hooks.h | 1 - core/gdxsv/gdxsv_network.cpp | 975 ++++++++++----------- core/gdxsv/gdxsv_network.h | 205 +++-- core/gdxsv/gdxsv_translation.cpp | 142 +-- core/gdxsv/gdxsv_translation.h | 76 +- core/gdxsv/lbs_message.h | 401 +++++---- core/gdxsv/mcs_message.h | 429 +++++---- gdxsv-format.sh | 2 + 20 files changed, 3322 insertions(+), 3433 deletions(-) create mode 100644 gdxsv-format.sh diff --git a/core/gdxsv/gdx_rpc.h b/core/gdxsv/gdx_rpc.h index 26ff5ac013..aa5db4ff3c 100644 --- a/core/gdxsv/gdx_rpc.h +++ b/core/gdxsv/gdx_rpc.h @@ -5,18 +5,18 @@ #define InetBufSize 1024 enum { - GDX_RPC_SOCK_OPEN = 1, - GDX_RPC_SOCK_CLOSE = 2, - GDX_RPC_SOCK_READ = 3, - GDX_RPC_SOCK_WRITE = 4, - GDX_RPC_SOCK_POLL = 5, + GDX_RPC_SOCK_OPEN = 1, + GDX_RPC_SOCK_CLOSE = 2, + GDX_RPC_SOCK_READ = 3, + GDX_RPC_SOCK_WRITE = 4, + GDX_RPC_SOCK_POLL = 5, }; struct gdx_rpc_t { - u32 request; - u32 response; - u32 param1; - u32 param2; - u32 param3; - u32 param4; + u32 request; + u32 response; + u32 param1; + u32 param2; + u32 param3; + u32 param4; }; \ No newline at end of file diff --git a/core/gdxsv/gdxsv.cpp b/core/gdxsv/gdxsv.cpp index 3ef5983825..4160283444 100644 --- a/core/gdxsv/gdxsv.cpp +++ b/core/gdxsv/gdxsv.cpp @@ -15,676 +15,668 @@ #include "reios/reios.h" #include "version.h" -bool Gdxsv::InGame() const { - return enabled && !testmode && (netmode == NetMode::McsUdp || netmode == NetMode::McsRollback); -} +bool Gdxsv::InGame() const { return enabled && !testmode && (netmode == NetMode::McsUdp || netmode == NetMode::McsRollback); } bool Gdxsv::Enabled() const { return enabled; } -void Gdxsv::DisplayOSD() { - rollback_net.DisplayOSD(); -} +void Gdxsv::DisplayOSD() { rollback_net.DisplayOSD(); } void Gdxsv::Reset() { - lbs_net.Reset(); - udp_net.Reset(); - RestoreOnlinePatch(); + lbs_net.Reset(); + udp_net.Reset(); + RestoreOnlinePatch(); - // Automatically add ContentPath if it is empty. - if (config::ContentPath.get().empty()) { - config::ContentPath.get().push_back("./"); - } + // Automatically add ContentPath if it is empty. + if (config::ContentPath.get().empty()) { + config::ContentPath.get().push_back("./"); + } - auto game_id = std::string(ip_meta.product_number, sizeof(ip_meta.product_number)); - if (game_id != "T13306M ") { - enabled = false; - return; - } - enabled = true; + auto game_id = std::string(ip_meta.product_number, sizeof(ip_meta.product_number)); + if (game_id != "T13306M ") { + enabled = false; + return; + } + enabled = true; - server = cfgLoadStr("gdxsv", "server", "zdxsv.net"); - loginkey = cfgLoadStr("gdxsv", "loginkey", ""); + server = cfgLoadStr("gdxsv", "server", "zdxsv.net"); + loginkey = cfgLoadStr("gdxsv", "loginkey", ""); - if (loginkey.empty()) { - loginkey = GenerateLoginKey(); - } + if (loginkey.empty()) { + loginkey = GenerateLoginKey(); + } - cfgSaveStr("gdxsv", "server", server.c_str()); - cfgSaveStr("gdxsv", "loginkey", loginkey.c_str()); + cfgSaveStr("gdxsv", "server", server.c_str()); + cfgSaveStr("gdxsv", "loginkey", loginkey.c_str()); - std::string disk_num(ip_meta.disk_num, 1); - if (disk_num == "1") disk = 1; - if (disk_num == "2") disk = 2; + std::string disk_num(ip_meta.disk_num, 1); + if (disk_num == "1") disk = 1; + if (disk_num == "2") disk = 2; - udp_port = config::GdxLocalPort; + udp_port = config::GdxLocalPort; #ifdef __APPLE__ - signal(SIGPIPE, SIG_IGN); + signal(SIGPIPE, SIG_IGN); #endif - NOTICE_LOG(COMMON, "gdxsv disk:%d server:%s loginkey:%s udp_port:%d", (int)disk, server.c_str(), loginkey.c_str(), - udp_port); - - lbs_net.callback_lbs_packet([this](const LbsMessage &lbs_msg) { - if (lbs_msg.command == LbsMessage::lbsUserRegist || lbs_msg.command == LbsMessage::lbsUserDecide) { - std::string id(6, ' '); - for (int i = 0; i < 6; i++) { - id[i] = lbs_msg.body[2 + i]; - } - user_id = id; - } - if (lbs_msg.command == LbsMessage::lbsUserRegist || lbs_msg.command == LbsMessage::lbsUserDecide || - lbs_msg.command == LbsMessage::lbsLineCheck) { - if (udp.Initialized()) { - if (!lbs_remote.is_open()) { - lbs_remote.Open(lbs_net.RemoteHost().c_str(), lbs_net.RemotePort()); - } - - proto::Packet pkt; - pkt.set_type(proto::MessageType::HelloLbs); - pkt.mutable_hello_lbs_data()->set_user_id(user_id); - char buf[128]; - if (pkt.SerializePartialToArray((void *)buf, (int)sizeof(buf))) { - udp.SendTo((const char *)buf, pkt.GetCachedSize(), lbs_remote); - } else { - ERROR_LOG(COMMON, "packet serialize error"); - } - } - - lbs_net.Send(GeneratePlatformInfoPacket()); - } - if (lbs_msg.command == LbsMessage::lbsP2PMatching) { - proto::P2PMatching matching; - if (matching.ParseFromArray(lbs_msg.body.data(), lbs_msg.body.size())) { - int port = udp.bind_port(); - udp.Close(); - lbs_remote.Close(); - rollback_net.Prepare(matching, port); - } else { - ERROR_LOG(COMMON, "p2p matching deserialize error"); - } - } - if (lbs_msg.command == LbsMessage::lbsReadyBattle) { - // Reset current patches for no-patched game - RestoreOnlinePatch(); - } - if (lbs_msg.command == LbsMessage::lbsGamePatch) { - // Reset current patches and update patch_list - RestoreOnlinePatch(); - if (patch_list.ParseFromArray(lbs_msg.body.data(), lbs_msg.body.size())) { - ApplyOnlinePatch(true); - } else { - ERROR_LOG(COMMON, "patch_list deserialize error"); - } - } - if (lbs_msg.command == LbsMessage::lbsBattleUserCount && disk == 2 && - GdxsvLanguage::Language() != GdxsvLanguage::Lang::Disabled) { - u32 battle_user_count = - u32(lbs_msg.body[0]) << 24 | u32(lbs_msg.body[1]) << 16 | u32(lbs_msg.body[2]) << 8 | lbs_msg.body[3]; - const u32 offset = 0x8C000000 + 0x00010000; - gdxsv_WriteMem32(offset + 0x3839FC, battle_user_count); - } - }); + NOTICE_LOG(COMMON, "gdxsv disk:%d server:%s loginkey:%s udp_port:%d", (int)disk, server.c_str(), loginkey.c_str(), udp_port); + + lbs_net.callback_lbs_packet([this](const LbsMessage &lbs_msg) { + if (lbs_msg.command == LbsMessage::lbsUserRegist || lbs_msg.command == LbsMessage::lbsUserDecide) { + std::string id(6, ' '); + for (int i = 0; i < 6; i++) { + id[i] = lbs_msg.body[2 + i]; + } + user_id = id; + } + if (lbs_msg.command == LbsMessage::lbsUserRegist || lbs_msg.command == LbsMessage::lbsUserDecide || + lbs_msg.command == LbsMessage::lbsLineCheck) { + if (udp.Initialized()) { + if (!lbs_remote.is_open()) { + lbs_remote.Open(lbs_net.RemoteHost().c_str(), lbs_net.RemotePort()); + } + + proto::Packet pkt; + pkt.set_type(proto::MessageType::HelloLbs); + pkt.mutable_hello_lbs_data()->set_user_id(user_id); + char buf[128]; + if (pkt.SerializePartialToArray((void *)buf, (int)sizeof(buf))) { + udp.SendTo((const char *)buf, pkt.GetCachedSize(), lbs_remote); + } else { + ERROR_LOG(COMMON, "packet serialize error"); + } + } + + lbs_net.Send(GeneratePlatformInfoPacket()); + } + if (lbs_msg.command == LbsMessage::lbsP2PMatching) { + proto::P2PMatching matching; + if (matching.ParseFromArray(lbs_msg.body.data(), lbs_msg.body.size())) { + int port = udp.bind_port(); + udp.Close(); + lbs_remote.Close(); + rollback_net.Prepare(matching, port); + } else { + ERROR_LOG(COMMON, "p2p matching deserialize error"); + } + } + if (lbs_msg.command == LbsMessage::lbsReadyBattle) { + // Reset current patches for no-patched game + RestoreOnlinePatch(); + } + if (lbs_msg.command == LbsMessage::lbsGamePatch) { + // Reset current patches and update patch_list + RestoreOnlinePatch(); + if (patch_list.ParseFromArray(lbs_msg.body.data(), lbs_msg.body.size())) { + ApplyOnlinePatch(true); + } else { + ERROR_LOG(COMMON, "patch_list deserialize error"); + } + } + if (lbs_msg.command == LbsMessage::lbsBattleUserCount && disk == 2 && GdxsvLanguage::Language() != GdxsvLanguage::Lang::Disabled) { + u32 battle_user_count = u32(lbs_msg.body[0]) << 24 | u32(lbs_msg.body[1]) << 16 | u32(lbs_msg.body[2]) << 8 | lbs_msg.body[3]; + const u32 offset = 0x8C000000 + 0x00010000; + gdxsv_WriteMem32(offset + 0x3839FC, battle_user_count); + } + }); } void Gdxsv::Update() { - if (!enabled) return; + if (!enabled) return; - if (InGame()) { - settings.input.fastForwardMode = false; - } + if (InGame()) { + settings.input.fastForwardMode = false; + } - if (!ggpo::active()) { - // Don't edit memory at vsync if ggpo::active - WritePatch(); - } + if (!ggpo::active()) { + // Don't edit memory at vsync if ggpo::active + WritePatch(); + } } void Gdxsv::HookMainUiLoop() { - if (enabled) { - if (netmode == NetMode::McsRollback) { + if (enabled) { + if (netmode == NetMode::McsRollback) { gdxsv.rollback_net.OnMainUiLoop(); - } - } + } + } } std::string Gdxsv::GeneratePlatformInfoString() { - std::stringstream ss; - ss << "cpu=" - << + std::stringstream ss; + ss << "cpu=" + << #if HOST_CPU == CPU_X86 - "x86" + "x86" #elif HOST_CPU == CPU_ARM - "ARM" + "ARM" #elif HOST_CPU == CPU_MIPS - "MIPS" + "MIPS" #elif HOST_CPU == CPU_X64 - "x86/64" + "x86/64" #elif HOST_CPU == CPU_GENERIC - "Generic" + "Generic" #elif HOST_CPU == CPU_ARM64 - "ARM64" + "ARM64" #else - "Unknown" + "Unknown" #endif - << "\n"; - ss << "os=" - << + << "\n"; + ss << "os=" + << #ifdef __ANDROID__ - "Android" + "Android" #elif defined(__unix__) - "Linux" + "Linux" #elif defined(__APPLE__) #ifdef TARGET_IPHONE - "iOS" + "iOS" #else - "macOS" + "macOS" #endif #elif defined(_WIN32) - "Windows" + "Windows" #else - "Unknown" + "Unknown" #endif - << "\n"; - ss << "flycast=" << GIT_VERSION << "\n"; - ss << "git_hash=" << GIT_HASH << "\n"; - ss << "build_date=" << BUILD_DATE << "\n"; - ss << "disk=" << (int)disk << "\n"; - ss << "wireless=" << (int)(os_GetConnectionMedium() == "Wireless") << "\n"; - ss << "patch_id=" << symbols[":patch_id"] << "\n"; - ss << "local_ip=" << lbs_net.LocalIP() << "\n"; - ss << "udp_port=" << udp_port << "\n"; - std::string machine_id = os_GetMachineID(); - if (machine_id.length()) { - auto digest = XXH64(machine_id.c_str(), machine_id.size(), 37); - ss << "machine_id=" << std::hex << digest << std::dec << "\n"; - } - - if (gcp_ping_test_finished) { - for (const auto &res : gcp_ping_test_result) { - ss << res.first << "=" << res.second << "\n"; - } - } - return ss.str(); + << "\n"; + ss << "flycast=" << GIT_VERSION << "\n"; + ss << "git_hash=" << GIT_HASH << "\n"; + ss << "build_date=" << BUILD_DATE << "\n"; + ss << "disk=" << (int)disk << "\n"; + ss << "wireless=" << (int)(os_GetConnectionMedium() == "Wireless") << "\n"; + ss << "patch_id=" << symbols[":patch_id"] << "\n"; + ss << "local_ip=" << lbs_net.LocalIP() << "\n"; + ss << "udp_port=" << udp_port << "\n"; + std::string machine_id = os_GetMachineID(); + if (machine_id.length()) { + auto digest = XXH64(machine_id.c_str(), machine_id.size(), 37); + ss << "machine_id=" << std::hex << digest << std::dec << "\n"; + } + + if (gcp_ping_test_finished) { + for (const auto &res : gcp_ping_test_result) { + ss << res.first << "=" << res.second << "\n"; + } + } + return ss.str(); } std::vector Gdxsv::GeneratePlatformInfoPacket() { - std::vector packet = {0x81, 0xff, 0x99, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff}; - auto s = GeneratePlatformInfoString(); - packet.push_back((s.size() >> 8) & 0xffu); - packet.push_back(s.size() & 0xffu); - std::copy(std::begin(s), std::end(s), std::back_inserter(packet)); - std::vector e_loginkey(loginkey.size()); - static const int magic[] = {0x46, 0xcf, 0x2d, 0x55}; - for (int i = 0; i < e_loginkey.size(); ++i) e_loginkey[i] ^= loginkey[i] ^ magic[i & 3]; - packet.push_back((e_loginkey.size() >> 8) & 0xffu); - packet.push_back(e_loginkey.size() & 0xffu); - std::copy(std::begin(e_loginkey), std::end(e_loginkey), std::back_inserter(packet)); - u16 payload_size = (u16)(packet.size() - 12); - packet[4] = (payload_size >> 8) & 0xffu; - packet[5] = payload_size & 0xffu; - return packet; + std::vector packet = {0x81, 0xff, 0x99, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff}; + auto s = GeneratePlatformInfoString(); + packet.push_back((s.size() >> 8) & 0xffu); + packet.push_back(s.size() & 0xffu); + std::copy(std::begin(s), std::end(s), std::back_inserter(packet)); + std::vector e_loginkey(loginkey.size()); + static const int magic[] = {0x46, 0xcf, 0x2d, 0x55}; + for (int i = 0; i < e_loginkey.size(); ++i) e_loginkey[i] ^= loginkey[i] ^ magic[i & 3]; + packet.push_back((e_loginkey.size() >> 8) & 0xffu); + packet.push_back(e_loginkey.size() & 0xffu); + std::copy(std::begin(e_loginkey), std::end(e_loginkey), std::back_inserter(packet)); + u16 payload_size = (u16)(packet.size() - 12); + packet[4] = (payload_size >> 8) & 0xffu; + packet[5] = payload_size & 0xffu; + return packet; } void Gdxsv::HandleRPC() { - u32 gdx_rpc_addr = symbols["gdx_rpc"]; - if (gdx_rpc_addr == 0) { - return; - } - - u32 response = 0; - gdx_rpc_t gdx_rpc{}; - gdx_rpc.request = gdxsv_ReadMem32(gdx_rpc_addr); - gdx_rpc.response = gdxsv_ReadMem32(gdx_rpc_addr + 4); - gdx_rpc.param1 = gdxsv_ReadMem32(gdx_rpc_addr + 8); - gdx_rpc.param2 = gdxsv_ReadMem32(gdx_rpc_addr + 12); - gdx_rpc.param3 = gdxsv_ReadMem32(gdx_rpc_addr + 16); - gdx_rpc.param4 = gdxsv_ReadMem32(gdx_rpc_addr + 20); - - if (gdx_rpc.request == GDX_RPC_SOCK_OPEN) { - u32 tolobby = gdx_rpc.param1; - u32 host_ip = gdx_rpc.param2; - u32 port_no = gdx_rpc.param3; - - std::string host = server; - u16 port = port_no; - - if (netmode == NetMode::Replay) { - replay_net.Open(); - } - else if (tolobby == 1) { - udp_net.CloseMcsRemoteWithReason("cl_to_lobby"); - if (lbs_net.Connect(host, port)) { - netmode = NetMode::Lbs; - lbs_net.Send(GeneratePlatformInfoPacket()); - - lbs_remote.Open(host.c_str(), port); - if (udp.Bind(udp_port)) { - if (udp_port != udp.bind_port()) { - config::GdxLocalPort = udp_port = udp.bind_port(); - } - - if (config::EnableUPnP && upnp_port != udp.bind_port()) { - upnp_port = udp.bind_port(); - upnp_result = std::async(std::launch::async, [this]() -> std::string { - return (upnp.Init() && upnp.AddPortMapping(upnp_port, false)) ? "Success" : upnp.getLastError(); - }); - } - } - } else { - netmode = NetMode::Offline; - } - } else { - lbs_net.Close(); - if (~host_ip == 0) { - lbs_remote.Close(); - udp.Close(); - rollback_net.Open(); - netmode = NetMode::McsRollback; - } else { - char addr_buf[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &host_ip, addr_buf, INET_ADDRSTRLEN); - host = std::string(addr_buf); - if (udp_net.Connect(host, port)) { - netmode = NetMode::McsUdp; - } else { - netmode = NetMode::Offline; - } - } - } - } - - if (gdx_rpc.request == GDX_RPC_SOCK_CLOSE) { - if (netmode == NetMode::Replay) { - replay_net.Close(); - } else if (netmode == NetMode::McsRollback) { - rollback_net.Close(); - netmode = NetMode::Offline; - } else { - lbs_net.Close(); - - if (gdx_rpc.param2 == 0) { - udp_net.CloseMcsRemoteWithReason("cl_app_close"); - } else if (gdx_rpc.param2 == 1) { - udp_net.CloseMcsRemoteWithReason("cl_ppp_close"); - } else if (gdx_rpc.param2 == 2) { - udp_net.CloseMcsRemoteWithReason("cl_soft_reset"); - } else { - udp_net.CloseMcsRemoteWithReason("cl_tcp_close"); - } - - netmode = NetMode::Offline; - } - } - - if (gdx_rpc.request == GDX_RPC_SOCK_READ) { - if (netmode == NetMode::Lbs) { - response = lbs_net.OnSockRead(gdx_rpc.param1, gdx_rpc.param2); - } else if (netmode == NetMode::McsUdp) { - response = udp_net.OnSockRead(gdx_rpc.param1, gdx_rpc.param2); - } else if (netmode == NetMode::Replay) { - response = replay_net.OnSockRead(gdx_rpc.param1, gdx_rpc.param2); - } else if (netmode == NetMode::McsRollback) { - response = rollback_net.OnSockRead(gdx_rpc.param1, gdx_rpc.param2); - } - } - - if (gdx_rpc.request == GDX_RPC_SOCK_WRITE) { - if (netmode == NetMode::Lbs) { - response = lbs_net.OnSockWrite(gdx_rpc.param1, gdx_rpc.param2); - } else if (netmode == NetMode::McsUdp) { - response = udp_net.OnSockWrite(gdx_rpc.param1, gdx_rpc.param2); - } else if (netmode == NetMode::Replay) { - response = replay_net.OnSockWrite(gdx_rpc.param1, gdx_rpc.param2); - } else if (netmode == NetMode::McsRollback) { - response = rollback_net.OnSockWrite(gdx_rpc.param1, gdx_rpc.param2); - } - } - - if (gdx_rpc.request == GDX_RPC_SOCK_POLL) { - if (netmode == NetMode::Lbs) { - response = lbs_net.OnSockPoll(); - } else if (netmode == NetMode::McsUdp) { - response = udp_net.OnSockPoll(); - } else if (netmode == NetMode::Replay) { - response = replay_net.OnSockPoll(); - } else if (netmode == NetMode::McsRollback) { - response = rollback_net.OnSockPoll(); - } - } - - gdxsv_WriteMem32(gdx_rpc_addr, 0); - gdxsv_WriteMem32(gdx_rpc_addr + 4, response); - gdxsv_WriteMem32(gdx_rpc_addr + 8, 0); - gdxsv_WriteMem32(gdx_rpc_addr + 12, 0); - gdxsv_WriteMem32(gdx_rpc_addr + 16, 0); - gdxsv_WriteMem32(gdx_rpc_addr + 20, 0); - - gdxsv_WriteMem32(symbols["is_online"], netmode != NetMode::Offline); + u32 gdx_rpc_addr = symbols["gdx_rpc"]; + if (gdx_rpc_addr == 0) { + return; + } + + u32 response = 0; + gdx_rpc_t gdx_rpc{}; + gdx_rpc.request = gdxsv_ReadMem32(gdx_rpc_addr); + gdx_rpc.response = gdxsv_ReadMem32(gdx_rpc_addr + 4); + gdx_rpc.param1 = gdxsv_ReadMem32(gdx_rpc_addr + 8); + gdx_rpc.param2 = gdxsv_ReadMem32(gdx_rpc_addr + 12); + gdx_rpc.param3 = gdxsv_ReadMem32(gdx_rpc_addr + 16); + gdx_rpc.param4 = gdxsv_ReadMem32(gdx_rpc_addr + 20); + + if (gdx_rpc.request == GDX_RPC_SOCK_OPEN) { + u32 tolobby = gdx_rpc.param1; + u32 host_ip = gdx_rpc.param2; + u32 port_no = gdx_rpc.param3; + + std::string host = server; + u16 port = port_no; + + if (netmode == NetMode::Replay) { + replay_net.Open(); + } else if (tolobby == 1) { + udp_net.CloseMcsRemoteWithReason("cl_to_lobby"); + if (lbs_net.Connect(host, port)) { + netmode = NetMode::Lbs; + lbs_net.Send(GeneratePlatformInfoPacket()); + + lbs_remote.Open(host.c_str(), port); + if (udp.Bind(udp_port)) { + if (udp_port != udp.bind_port()) { + config::GdxLocalPort = udp_port = udp.bind_port(); + } + + if (config::EnableUPnP && upnp_port != udp.bind_port()) { + upnp_port = udp.bind_port(); + upnp_result = std::async(std::launch::async, [this]() -> std::string { + return (upnp.Init() && upnp.AddPortMapping(upnp_port, false)) ? "Success" : upnp.getLastError(); + }); + } + } + } else { + netmode = NetMode::Offline; + } + } else { + lbs_net.Close(); + if (~host_ip == 0) { + lbs_remote.Close(); + udp.Close(); + rollback_net.Open(); + netmode = NetMode::McsRollback; + } else { + char addr_buf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &host_ip, addr_buf, INET_ADDRSTRLEN); + host = std::string(addr_buf); + if (udp_net.Connect(host, port)) { + netmode = NetMode::McsUdp; + } else { + netmode = NetMode::Offline; + } + } + } + } + + if (gdx_rpc.request == GDX_RPC_SOCK_CLOSE) { + if (netmode == NetMode::Replay) { + replay_net.Close(); + } else if (netmode == NetMode::McsRollback) { + rollback_net.Close(); + netmode = NetMode::Offline; + } else { + lbs_net.Close(); + + if (gdx_rpc.param2 == 0) { + udp_net.CloseMcsRemoteWithReason("cl_app_close"); + } else if (gdx_rpc.param2 == 1) { + udp_net.CloseMcsRemoteWithReason("cl_ppp_close"); + } else if (gdx_rpc.param2 == 2) { + udp_net.CloseMcsRemoteWithReason("cl_soft_reset"); + } else { + udp_net.CloseMcsRemoteWithReason("cl_tcp_close"); + } + + netmode = NetMode::Offline; + } + } + + if (gdx_rpc.request == GDX_RPC_SOCK_READ) { + if (netmode == NetMode::Lbs) { + response = lbs_net.OnSockRead(gdx_rpc.param1, gdx_rpc.param2); + } else if (netmode == NetMode::McsUdp) { + response = udp_net.OnSockRead(gdx_rpc.param1, gdx_rpc.param2); + } else if (netmode == NetMode::Replay) { + response = replay_net.OnSockRead(gdx_rpc.param1, gdx_rpc.param2); + } else if (netmode == NetMode::McsRollback) { + response = rollback_net.OnSockRead(gdx_rpc.param1, gdx_rpc.param2); + } + } + + if (gdx_rpc.request == GDX_RPC_SOCK_WRITE) { + if (netmode == NetMode::Lbs) { + response = lbs_net.OnSockWrite(gdx_rpc.param1, gdx_rpc.param2); + } else if (netmode == NetMode::McsUdp) { + response = udp_net.OnSockWrite(gdx_rpc.param1, gdx_rpc.param2); + } else if (netmode == NetMode::Replay) { + response = replay_net.OnSockWrite(gdx_rpc.param1, gdx_rpc.param2); + } else if (netmode == NetMode::McsRollback) { + response = rollback_net.OnSockWrite(gdx_rpc.param1, gdx_rpc.param2); + } + } + + if (gdx_rpc.request == GDX_RPC_SOCK_POLL) { + if (netmode == NetMode::Lbs) { + response = lbs_net.OnSockPoll(); + } else if (netmode == NetMode::McsUdp) { + response = udp_net.OnSockPoll(); + } else if (netmode == NetMode::Replay) { + response = replay_net.OnSockPoll(); + } else if (netmode == NetMode::McsRollback) { + response = rollback_net.OnSockPoll(); + } + } + + gdxsv_WriteMem32(gdx_rpc_addr, 0); + gdxsv_WriteMem32(gdx_rpc_addr + 4, response); + gdxsv_WriteMem32(gdx_rpc_addr + 8, 0); + gdxsv_WriteMem32(gdx_rpc_addr + 12, 0); + gdxsv_WriteMem32(gdx_rpc_addr + 16, 0); + gdxsv_WriteMem32(gdx_rpc_addr + 20, 0); + + gdxsv_WriteMem32(symbols["is_online"], netmode != NetMode::Offline); } void Gdxsv::StartPingTest() { - std::thread([this]() { - std::this_thread::sleep_for(std::chrono::seconds(3)); - GcpPingTest(); - }).detach(); + std::thread([this]() { + std::this_thread::sleep_for(std::chrono::seconds(3)); + GcpPingTest(); + }).detach(); } void Gdxsv::GcpPingTest() { - // powered by https://github.com/cloudharmony/network - static const std::string get_path = "/probe/ping.js"; - static const std::map gcp_region_hosts = { - {"asia-east1", "asia-east1-gce.cloudharmony.net"}, - {"asia-east2", "asia-east2-gce.cloudharmony.net"}, - {"asia-northeast1", "asia-northeast1-gce.cloudharmony.net"}, - {"asia-northeast2", "asia-northeast2-gce.cloudharmony.net"}, - {"asia-northeast3", "asia-northeast3-gce.cloudharmony.net"}, - // {"asia-south1", "asia-south1-gce.cloudharmony.net"}, // inactive now. - {"asia-southeast1", "asia-southeast1-gce.cloudharmony.net"}, - {"australia-southeast1", "australia-southeast1-gce.cloudharmony.net"}, - {"europe-north1", "europe-north1-gce.cloudharmony.net"}, - {"europe-west1", "europe-west1-gce.cloudharmony.net"}, - {"europe-west2", "europe-west2-gce.cloudharmony.net"}, - {"europe-west3", "europe-west3-gce.cloudharmony.net"}, - {"europe-west4", "europe-west4-gce.cloudharmony.net"}, - {"europe-west6", "europe-west6-gce.cloudharmony.net"}, - {"northamerica-northeast1", "northamerica-northeast1-gce.cloudharmony.net"}, - {"southamerica-east1", "southamerica-east1-gce.cloudharmony.net"}, - {"us-central1", "us-central1-gce.cloudharmony.net"}, - {"us-east1", "us-east1-gce.cloudharmony.net"}, - {"us-east4", "us-east4-gce.cloudharmony.net"}, - {"us-west1", "us-west1-gce.cloudharmony.net"}, - {"us-west2", "us-west2-a-gce.cloudharmony.net"}, - {"us-west3", "us-west3-gce.cloudharmony.net"}, - }; - - for (const auto ®ion_host : gcp_region_hosts) { - gui_display_notification("Ping testing...", 1000); - TcpClient client; - std::stringstream ss; - ss << "HEAD " << get_path << " HTTP/1.1" - << "\r\n"; - ss << "Host: " << region_host.second << "\r\n"; - ss << "User-Agent: flycast for gdxsv" - << "\r\n"; - ss << "Accept: */*" - << "\r\n"; - ss << "\r\n"; // end of header - - if (!client.Connect(region_host.second.c_str(), 80)) { - ERROR_LOG(COMMON, "connect failed : %s", region_host.first.c_str()); - continue; - } - - auto request_header = ss.str(); - auto t1 = std::chrono::high_resolution_clock::now(); - int n = client.Send(request_header.c_str(), request_header.size()); - if (n < request_header.size()) { - ERROR_LOG(COMMON, "send failed : %s", region_host.first.c_str()); - client.Close(); - continue; - } - - char buf[1024] = {0}; - n = client.Recv(buf, 1024); - if (n <= 0) { - ERROR_LOG(COMMON, "recv failed : %s", region_host.first.c_str()); - client.Close(); - continue; - } - - auto t2 = std::chrono::high_resolution_clock::now(); - int rtt = (int)std::chrono::duration_cast(t2 - t1).count(); - const std::string response_header(buf, n); - if (response_header.find("200 OK") != std::string::npos) { - gcp_ping_test_result[region_host.first] = rtt; - char latency_str[256]; - snprintf(latency_str, 256, "%s : %d[ms]", region_host.first.c_str(), rtt); - NOTICE_LOG(COMMON, "%s", latency_str); - } else { - ERROR_LOG(COMMON, "error response : %s", response_header.c_str()); - } - client.Close(); - } - gcp_ping_test_finished = true; - gui_display_notification("Ping test finished", 3000); + // powered by https://github.com/cloudharmony/network + static const std::string get_path = "/probe/ping.js"; + static const std::map gcp_region_hosts = { + {"asia-east1", "asia-east1-gce.cloudharmony.net"}, + {"asia-east2", "asia-east2-gce.cloudharmony.net"}, + {"asia-northeast1", "asia-northeast1-gce.cloudharmony.net"}, + {"asia-northeast2", "asia-northeast2-gce.cloudharmony.net"}, + {"asia-northeast3", "asia-northeast3-gce.cloudharmony.net"}, + // {"asia-south1", "asia-south1-gce.cloudharmony.net"}, // inactive now. + {"asia-southeast1", "asia-southeast1-gce.cloudharmony.net"}, + {"australia-southeast1", "australia-southeast1-gce.cloudharmony.net"}, + {"europe-north1", "europe-north1-gce.cloudharmony.net"}, + {"europe-west1", "europe-west1-gce.cloudharmony.net"}, + {"europe-west2", "europe-west2-gce.cloudharmony.net"}, + {"europe-west3", "europe-west3-gce.cloudharmony.net"}, + {"europe-west4", "europe-west4-gce.cloudharmony.net"}, + {"europe-west6", "europe-west6-gce.cloudharmony.net"}, + {"northamerica-northeast1", "northamerica-northeast1-gce.cloudharmony.net"}, + {"southamerica-east1", "southamerica-east1-gce.cloudharmony.net"}, + {"us-central1", "us-central1-gce.cloudharmony.net"}, + {"us-east1", "us-east1-gce.cloudharmony.net"}, + {"us-east4", "us-east4-gce.cloudharmony.net"}, + {"us-west1", "us-west1-gce.cloudharmony.net"}, + {"us-west2", "us-west2-a-gce.cloudharmony.net"}, + {"us-west3", "us-west3-gce.cloudharmony.net"}, + }; + + for (const auto ®ion_host : gcp_region_hosts) { + gui_display_notification("Ping testing...", 1000); + TcpClient client; + std::stringstream ss; + ss << "HEAD " << get_path << " HTTP/1.1" + << "\r\n"; + ss << "Host: " << region_host.second << "\r\n"; + ss << "User-Agent: flycast for gdxsv" + << "\r\n"; + ss << "Accept: */*" + << "\r\n"; + ss << "\r\n"; // end of header + + if (!client.Connect(region_host.second.c_str(), 80)) { + ERROR_LOG(COMMON, "connect failed : %s", region_host.first.c_str()); + continue; + } + + auto request_header = ss.str(); + auto t1 = std::chrono::high_resolution_clock::now(); + int n = client.Send(request_header.c_str(), request_header.size()); + if (n < request_header.size()) { + ERROR_LOG(COMMON, "send failed : %s", region_host.first.c_str()); + client.Close(); + continue; + } + + char buf[1024] = {0}; + n = client.Recv(buf, 1024); + if (n <= 0) { + ERROR_LOG(COMMON, "recv failed : %s", region_host.first.c_str()); + client.Close(); + continue; + } + + auto t2 = std::chrono::high_resolution_clock::now(); + int rtt = (int)std::chrono::duration_cast(t2 - t1).count(); + const std::string response_header(buf, n); + if (response_header.find("200 OK") != std::string::npos) { + gcp_ping_test_result[region_host.first] = rtt; + char latency_str[256]; + snprintf(latency_str, 256, "%s : %d[ms]", region_host.first.c_str(), rtt); + NOTICE_LOG(COMMON, "%s", latency_str); + } else { + ERROR_LOG(COMMON, "error response : %s", response_header.c_str()); + } + client.Close(); + } + gcp_ping_test_finished = true; + gui_display_notification("Ping test finished", 3000); } std::string Gdxsv::GenerateLoginKey() { - const int n = 8; - uint64_t seed = std::chrono::high_resolution_clock::now().time_since_epoch().count(); - std::mt19937 gen(seed); - std::string chars = "0123456789"; - std::uniform_int_distribution<> dist(0, chars.length() - 1); - std::string key(n, 0); - std::generate_n(key.begin(), n, [&]() { return chars[dist(gen)]; }); - return key; + const int n = 8; + uint64_t seed = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + std::mt19937 gen(seed); + std::string chars = "0123456789"; + std::uniform_int_distribution<> dist(0, chars.length() - 1); + std::string key(n, 0); + std::generate_n(key.begin(), n, [&]() { return chars[dist(gen)]; }); + return key; } void Gdxsv::ApplyOnlinePatch(bool first_time) { - for (int i = 0; i < patch_list.patches_size(); ++i) { - auto &patch = patch_list.patches(i); - if (patch.write_once() && !first_time) { - continue; - } - if (first_time) { - NOTICE_LOG(COMMON, "patch apply: %s", patch.name().c_str()); - } - for (int j = 0; j < patch.codes_size(); ++j) { - auto &code = patch.codes(j); - if (code.size() == 8) { - gdxsv_WriteMem8(code.address(), (u8)(code.changed() & 0xff)); - } - if (code.size() == 16) { - gdxsv_WriteMem16(code.address(), (u16)(code.changed() & 0xffff)); - } - if (code.size() == 32) { - gdxsv_WriteMem32(code.address(), code.changed()); - } - } - } + for (int i = 0; i < patch_list.patches_size(); ++i) { + auto &patch = patch_list.patches(i); + if (patch.write_once() && !first_time) { + continue; + } + if (first_time) { + NOTICE_LOG(COMMON, "patch apply: %s", patch.name().c_str()); + } + for (int j = 0; j < patch.codes_size(); ++j) { + auto &code = patch.codes(j); + if (code.size() == 8) { + gdxsv_WriteMem8(code.address(), (u8)(code.changed() & 0xff)); + } + if (code.size() == 16) { + gdxsv_WriteMem16(code.address(), (u16)(code.changed() & 0xffff)); + } + if (code.size() == 32) { + gdxsv_WriteMem32(code.address(), code.changed()); + } + } + } } void Gdxsv::RestoreOnlinePatch() { - for (int i = 0; i < patch_list.patches_size(); ++i) { - auto &patch = patch_list.patches(i); - NOTICE_LOG(COMMON, "patch restore: %s", patch.name().c_str()); - for (int j = 0; j < patch.codes_size(); ++j) { - auto &code = patch.codes(j); - if (code.size() == 8) { - gdxsv_WriteMem8(code.address(), (u8)(code.original() & 0xff)); - } - if (code.size() == 16) { - gdxsv_WriteMem16(code.address(), (u16)(code.original() & 0xffff)); - } - if (code.size() == 32) { - gdxsv_WriteMem32(code.address(), code.original()); - } - } - } - patch_list.clear_patches(); + for (int i = 0; i < patch_list.patches_size(); ++i) { + auto &patch = patch_list.patches(i); + NOTICE_LOG(COMMON, "patch restore: %s", patch.name().c_str()); + for (int j = 0; j < patch.codes_size(); ++j) { + auto &code = patch.codes(j); + if (code.size() == 8) { + gdxsv_WriteMem8(code.address(), (u8)(code.original() & 0xff)); + } + if (code.size() == 16) { + gdxsv_WriteMem16(code.address(), (u16)(code.original() & 0xffff)); + } + if (code.size() == 32) { + gdxsv_WriteMem32(code.address(), code.original()); + } + } + } + patch_list.clear_patches(); } void Gdxsv::WritePatch() { - if (disk == 1) WritePatchDisk1(); - if (disk == 2) WritePatchDisk2(); - if (symbols["patch_id"] == 0 || gdxsv_ReadMem32(symbols["patch_id"]) != symbols[":patch_id"]) { - NOTICE_LOG(COMMON, "patch %d %d", gdxsv_ReadMem32(symbols["patch_id"]), symbols[":patch_id"]); + if (disk == 1) WritePatchDisk1(); + if (disk == 2) WritePatchDisk2(); + if (symbols["patch_id"] == 0 || gdxsv_ReadMem32(symbols["patch_id"]) != symbols[":patch_id"]) { + NOTICE_LOG(COMMON, "patch %d %d", gdxsv_ReadMem32(symbols["patch_id"]), symbols[":patch_id"]); #include "gdxsv_patch.inc" - gdxsv_WriteMem32(symbols["disk"], (int)disk); - } + gdxsv_WriteMem32(symbols["disk"], (int)disk); + } - if (symbols["lang_patch_id"] == 0 || gdxsv_ReadMem32(symbols["lang_patch_id"]) != symbols[":lang_patch_id"] || - symbols[":lang_patch_lang"] != (u8)GdxsvLanguage::Language()) { - NOTICE_LOG(COMMON, "lang_patch id=%d prev=%d lang=%d", gdxsv_ReadMem32(symbols["lang_patch_id"]), - symbols[":lang_patch_id"], GdxsvLanguage::Language()); + if (symbols["lang_patch_id"] == 0 || gdxsv_ReadMem32(symbols["lang_patch_id"]) != symbols[":lang_patch_id"] || + symbols[":lang_patch_lang"] != (u8)GdxsvLanguage::Language()) { + NOTICE_LOG(COMMON, "lang_patch id=%d prev=%d lang=%d", gdxsv_ReadMem32(symbols["lang_patch_id"]), symbols[":lang_patch_id"], + GdxsvLanguage::Language()); #include "gdxsv_translation_patch.inc" - } + } } void Gdxsv::WritePatchDisk1() { - const u32 offset = 0x8C000000 + 0x00010000; - - // Max Rebattle Patch - gdxsv_WriteMem8(0x0c0345b0, 5); - - // Fix cost 300 to 295 - gdxsv_WriteMem16(0x0c1b0fd0, 295); - - // Send key message every frame - gdxsv_WriteMem8(0x0c310450, 1); - - // Reduce max lag-frame - gdxsv_WriteMem8(0x0c310451, maxlag); - - // Modem connection fix - const char *atm1 = "ATM1\r "; - for (int i = 0; i < strlen(atm1); ++i) { - gdxsv_WriteMem8(offset + 0x0015e703 + i, u8(atm1[i])); - } - - // Overwrite serve address (max 20 chars) - for (int i = 0; i < 20; ++i) { - gdxsv_WriteMem8(offset + 0x0015e788 + i, (i < server.length()) ? u8(server[i]) : u8(0)); - } - - // Skip form validation - gdxsv_WriteMem16(offset + 0x0003b0c4, u16(9)); // nop - gdxsv_WriteMem16(offset + 0x0003b0cc, u16(9)); // nop - gdxsv_WriteMem16(offset + 0x0003b0d4, u16(9)); // nop - gdxsv_WriteMem16(offset + 0x0003b0dc, u16(9)); // nop - - // Write LoginKey - if (gdxsv_ReadMem8(offset - 0x10000 + 0x002f6924) == 0) { - for (int i = 0; i < std::min(loginkey.length(), size_t(8)) + 1; ++i) { - gdxsv_WriteMem8(offset - 0x10000 + 0x002f6924 + i, (i < loginkey.length()) ? u8(loginkey[i]) : u8(0)); - } - } - - // Ally HP - u16 hp_offset = 0x0180; - if (InGame()) { - u8 player_index = gdxsv_ReadMem8(0x0c2f6652); - if (player_index) { - player_index--; - // depend on 4 player battle - u8 ally_index = player_index - (player_index & 1) + !(player_index & 1); - u16 ally_hp = gdxsv_ReadMem16(0x0c3369d6 + ally_index * 0x2000); - gdxsv_WriteMem16(0x0c3369d2 + player_index * 0x2000, ally_hp); - } - hp_offset -= 2; - } - gdxsv_WriteMem16(0x0c01d336, hp_offset); - gdxsv_WriteMem16(0x0c01d56e, hp_offset); - gdxsv_WriteMem16(0x0c01d678, hp_offset); - gdxsv_WriteMem16(0x0c01d89e, hp_offset); - - // Disable soft reset - gdxsv_WriteMem8(0x0c2f6657, InGame() ? 1 : 0); - - // Online patch - ApplyOnlinePatch(false); + const u32 offset = 0x8C000000 + 0x00010000; + + // Max Rebattle Patch + gdxsv_WriteMem8(0x0c0345b0, 5); + + // Fix cost 300 to 295 + gdxsv_WriteMem16(0x0c1b0fd0, 295); + + // Send key message every frame + gdxsv_WriteMem8(0x0c310450, 1); + + // Reduce max lag-frame + gdxsv_WriteMem8(0x0c310451, maxlag); + + // Modem connection fix + const char *atm1 = "ATM1\r "; + for (int i = 0; i < strlen(atm1); ++i) { + gdxsv_WriteMem8(offset + 0x0015e703 + i, u8(atm1[i])); + } + + // Overwrite serve address (max 20 chars) + for (int i = 0; i < 20; ++i) { + gdxsv_WriteMem8(offset + 0x0015e788 + i, (i < server.length()) ? u8(server[i]) : u8(0)); + } + + // Skip form validation + gdxsv_WriteMem16(offset + 0x0003b0c4, u16(9)); // nop + gdxsv_WriteMem16(offset + 0x0003b0cc, u16(9)); // nop + gdxsv_WriteMem16(offset + 0x0003b0d4, u16(9)); // nop + gdxsv_WriteMem16(offset + 0x0003b0dc, u16(9)); // nop + + // Write LoginKey + if (gdxsv_ReadMem8(offset - 0x10000 + 0x002f6924) == 0) { + for (int i = 0; i < std::min(loginkey.length(), size_t(8)) + 1; ++i) { + gdxsv_WriteMem8(offset - 0x10000 + 0x002f6924 + i, (i < loginkey.length()) ? u8(loginkey[i]) : u8(0)); + } + } + + // Ally HP + u16 hp_offset = 0x0180; + if (InGame()) { + u8 player_index = gdxsv_ReadMem8(0x0c2f6652); + if (player_index) { + player_index--; + // depend on 4 player battle + u8 ally_index = player_index - (player_index & 1) + !(player_index & 1); + u16 ally_hp = gdxsv_ReadMem16(0x0c3369d6 + ally_index * 0x2000); + gdxsv_WriteMem16(0x0c3369d2 + player_index * 0x2000, ally_hp); + } + hp_offset -= 2; + } + gdxsv_WriteMem16(0x0c01d336, hp_offset); + gdxsv_WriteMem16(0x0c01d56e, hp_offset); + gdxsv_WriteMem16(0x0c01d678, hp_offset); + gdxsv_WriteMem16(0x0c01d89e, hp_offset); + + // Disable soft reset + gdxsv_WriteMem8(0x0c2f6657, InGame() ? 1 : 0); + + // Online patch + ApplyOnlinePatch(false); } void Gdxsv::WritePatchDisk2() { - const u32 offset = 0x8C000000 + 0x00010000; - - // Max Rebattle Patch - gdxsv_WriteMem8(0x0c0219ec, 5); - - // Fix cost 300 to 295 - gdxsv_WriteMem16(0x0c21bfec, 295); - gdxsv_WriteMem16(0x0c21bff4, 295); - gdxsv_WriteMem16(0x0c21c034, 295); - - // Send key message every frame - gdxsv_WriteMem8(0x0c3abb90, 1); - - // Reduce max lag-frame - gdxsv_WriteMem8(0x0c3abb91, maxlag); - - // Modem connection fix - const char *atm1 = "ATM1\r "; - for (int i = 0; i < strlen(atm1); ++i) { - gdxsv_WriteMem8(offset + 0x001be7c7 + i, u8(atm1[i])); - } - - // Overwrite serve address (max 20 chars) - for (int i = 0; i < 20; ++i) { - gdxsv_WriteMem8(offset + 0x001be84c + i, (i < server.length()) ? u8(server[i]) : u8(0)); - } - - // Skip form validation - gdxsv_WriteMem16(offset + 0x000284f0, u16(9)); // nop - gdxsv_WriteMem16(offset + 0x000284f8, u16(9)); // nop - gdxsv_WriteMem16(offset + 0x00028500, u16(9)); // nop - gdxsv_WriteMem16(offset + 0x00028508, u16(9)); // nop - - // Write LoginKey - if (gdxsv_ReadMem8(offset - 0x10000 + 0x00392064) == 0) { - for (int i = 0; i < std::min(loginkey.length(), size_t(8)) + 1; ++i) { - gdxsv_WriteMem8(offset - 0x10000 + 0x00392064 + i, (i < loginkey.length()) ? u8(loginkey[i]) : u8(0)); - } - } - - // Ally HP - u16 hp_offset = 0x0180; - if (InGame()) { - u8 player_index = gdxsv_ReadMem8(0x0c391d92); - if (player_index) { - player_index--; - // depend on 4 player battle - u8 ally_index = player_index - (player_index & 1) + !(player_index & 1); - u16 ally_hp = gdxsv_ReadMem16(0x0c3d1e56 + ally_index * 0x2000); - gdxsv_WriteMem16(0x0c3d1e52 + player_index * 0x2000, ally_hp); - } - hp_offset -= 2; - } - gdxsv_WriteMem16(0x0c11da88, hp_offset); - gdxsv_WriteMem16(0x0c11dbbc, hp_offset); - gdxsv_WriteMem16(0x0c11dcc0, hp_offset); - gdxsv_WriteMem16(0x0c11ddd6, hp_offset); - gdxsv_WriteMem16(0x0c11df08, hp_offset); - gdxsv_WriteMem16(0x0c11e01a, hp_offset); - - // Disable soft reset - gdxsv_WriteMem8(0x0c391d97, InGame() ? 1 : 0); - - // Online patch - ApplyOnlinePatch(false); + const u32 offset = 0x8C000000 + 0x00010000; + + // Max Rebattle Patch + gdxsv_WriteMem8(0x0c0219ec, 5); + + // Fix cost 300 to 295 + gdxsv_WriteMem16(0x0c21bfec, 295); + gdxsv_WriteMem16(0x0c21bff4, 295); + gdxsv_WriteMem16(0x0c21c034, 295); + + // Send key message every frame + gdxsv_WriteMem8(0x0c3abb90, 1); + + // Reduce max lag-frame + gdxsv_WriteMem8(0x0c3abb91, maxlag); + + // Modem connection fix + const char *atm1 = "ATM1\r "; + for (int i = 0; i < strlen(atm1); ++i) { + gdxsv_WriteMem8(offset + 0x001be7c7 + i, u8(atm1[i])); + } + + // Overwrite serve address (max 20 chars) + for (int i = 0; i < 20; ++i) { + gdxsv_WriteMem8(offset + 0x001be84c + i, (i < server.length()) ? u8(server[i]) : u8(0)); + } + + // Skip form validation + gdxsv_WriteMem16(offset + 0x000284f0, u16(9)); // nop + gdxsv_WriteMem16(offset + 0x000284f8, u16(9)); // nop + gdxsv_WriteMem16(offset + 0x00028500, u16(9)); // nop + gdxsv_WriteMem16(offset + 0x00028508, u16(9)); // nop + + // Write LoginKey + if (gdxsv_ReadMem8(offset - 0x10000 + 0x00392064) == 0) { + for (int i = 0; i < std::min(loginkey.length(), size_t(8)) + 1; ++i) { + gdxsv_WriteMem8(offset - 0x10000 + 0x00392064 + i, (i < loginkey.length()) ? u8(loginkey[i]) : u8(0)); + } + } + + // Ally HP + u16 hp_offset = 0x0180; + if (InGame()) { + u8 player_index = gdxsv_ReadMem8(0x0c391d92); + if (player_index) { + player_index--; + // depend on 4 player battle + u8 ally_index = player_index - (player_index & 1) + !(player_index & 1); + u16 ally_hp = gdxsv_ReadMem16(0x0c3d1e56 + ally_index * 0x2000); + gdxsv_WriteMem16(0x0c3d1e52 + player_index * 0x2000, ally_hp); + } + hp_offset -= 2; + } + gdxsv_WriteMem16(0x0c11da88, hp_offset); + gdxsv_WriteMem16(0x0c11dbbc, hp_offset); + gdxsv_WriteMem16(0x0c11dcc0, hp_offset); + gdxsv_WriteMem16(0x0c11ddd6, hp_offset); + gdxsv_WriteMem16(0x0c11df08, hp_offset); + gdxsv_WriteMem16(0x0c11e01a, hp_offset); + + // Disable soft reset + gdxsv_WriteMem8(0x0c391d97, InGame() ? 1 : 0); + + // Online patch + ApplyOnlinePatch(false); } bool Gdxsv::StartReplayFile(const char *path) { - testmode = true; - replay_net.Reset(); - if (replay_net.StartFile(path)) { - netmode = NetMode::Replay; - return true; - } - - auto str = std::string(path); - if (4 <= str.length() && str.substr(0, 4) == "http") { - auto resp = os_FetchStringFromURL(str); - if (0 < resp.size() && replay_net.StartBuffer(resp.data(), resp.size())) { - netmode = NetMode::Replay; - return true; - } - } - return false; + testmode = true; + replay_net.Reset(); + if (replay_net.StartFile(path)) { + netmode = NetMode::Replay; + return true; + } + + auto str = std::string(path); + if (4 <= str.length() && str.substr(0, 4) == "http") { + auto resp = os_FetchStringFromURL(str); + if (0 < resp.size() && replay_net.StartBuffer(resp.data(), resp.size())) { + netmode = NetMode::Replay; + return true; + } + } + return false; } bool Gdxsv::StartRollbackTest(const char *param) { - testmode = true; - rollback_net.Reset(); + testmode = true; + rollback_net.Reset(); - if (rollback_net.StartLocalTest(param)) { - netmode = NetMode::McsRollback; - return true; - } + if (rollback_net.StartLocalTest(param)) { + netmode = NetMode::McsRollback; + return true; + } - return false; + return false; } Gdxsv gdxsv; diff --git a/core/gdxsv/gdxsv.h b/core/gdxsv/gdxsv.h index 8402ec0ea9..ee0148d681 100644 --- a/core/gdxsv/gdxsv.h +++ b/core/gdxsv/gdxsv.h @@ -1,107 +1,109 @@ #pragma once -#include -#include -#include #include +#include +#include +#include #include "gdxsv.pb.h" -#include "gdxsv_backend_tcp.h" -#include "gdxsv_backend_udp.h" #include "gdxsv_backend_replay.h" #include "gdxsv_backend_rollback.h" -#include "types.h" +#include "gdxsv_backend_tcp.h" +#include "gdxsv_backend_udp.h" #include "network/miniupnp.h" - +#include "types.h" class Gdxsv { -public: - enum class NetMode { - Offline, - Lbs, - McsUdp, - McsRollback, - Replay, - }; + public: + enum class NetMode { + Offline, + Lbs, + McsUdp, + McsRollback, + Replay, + }; + + Gdxsv() + : lbs_net(symbols), + udp_net(symbols, maxlag), + replay_net(symbols, maxlag), + rollback_net(symbols, maxlag), + upnp_port(0), + udp_port(0){}; + + bool Enabled() const; - Gdxsv() : lbs_net(symbols), - udp_net(symbols, maxlag), - replay_net(symbols, maxlag), - rollback_net(symbols, maxlag), - upnp_port(0), udp_port(0) {}; + bool InGame() const; - bool Enabled() const; + void DisplayOSD(); - bool InGame() const; + void Reset(); - void DisplayOSD(); + void Update(); - void Reset(); + void HookMainUiLoop(); - void Update(); + void HandleRPC(); - void HookMainUiLoop(); + void RestoreOnlinePatch(); - void HandleRPC(); + void StartPingTest(); - void RestoreOnlinePatch(); + bool StartReplayFile(const char* path); - void StartPingTest(); + bool StartRollbackTest(const char* param); - bool StartReplayFile(const char* path); + void WritePatch(); - bool StartRollbackTest(const char* param); + int Disk() const { return disk; } - void WritePatch(); + MiniUPnP& UPnP() { return upnp; } - int Disk() const { return disk; } + private: + void GcpPingTest(); - MiniUPnP& UPnP() { return upnp; } -private: - void GcpPingTest(); + static std::string GenerateLoginKey(); - static std::string GenerateLoginKey(); + std::vector GeneratePlatformInfoPacket(); - std::vector GeneratePlatformInfoPacket(); + std::string GeneratePlatformInfoString(); - std::string GeneratePlatformInfoString(); + void ApplyOnlinePatch(bool first_time); - void ApplyOnlinePatch(bool first_time); + void WritePatchDisk1(); - void WritePatchDisk1(); + void WritePatchDisk2(); - void WritePatchDisk2(); - - void WriteWidescreenPatchDisk2(); + void WriteWidescreenPatchDisk2(); - NetMode netmode = NetMode::Offline; - std::atomic testmode; - std::atomic enabled; - std::atomic disk; - std::atomic maxlag; + NetMode netmode = NetMode::Offline; + std::atomic testmode; + std::atomic enabled; + std::atomic disk; + std::atomic maxlag; - std::string server; - std::string loginkey; - std::map symbols; + std::string server; + std::string loginkey; + std::map symbols; - MiniUPnP upnp; - std::future upnp_result; - int upnp_port; - int udp_port; - std::string user_id; + MiniUPnP upnp; + std::future upnp_result; + int upnp_port; + int udp_port; + std::string user_id; - UdpRemote lbs_remote; - UdpClient udp; + UdpRemote lbs_remote; + UdpClient udp; - proto::GamePatchList patch_list; + proto::GamePatchList patch_list; - GdxsvBackendTcp lbs_net; - GdxsvBackendUdp udp_net; - GdxsvBackendReplay replay_net; - GdxsvBackendRollback rollback_net; + GdxsvBackendTcp lbs_net; + GdxsvBackendUdp udp_net; + GdxsvBackendReplay replay_net; + GdxsvBackendRollback rollback_net; - std::atomic gcp_ping_test_finished; - std::map gcp_ping_test_result; + std::atomic gcp_ping_test_finished; + std::map gcp_ping_test_result; }; extern Gdxsv gdxsv; diff --git a/core/gdxsv/gdxsv_CustomTexture.cpp b/core/gdxsv/gdxsv_CustomTexture.cpp index 3cf2faa7f7..1fcb84e8ec 100644 --- a/core/gdxsv/gdxsv_CustomTexture.cpp +++ b/core/gdxsv/gdxsv_CustomTexture.cpp @@ -8,121 +8,115 @@ #if defined(__APPLE__) || defined(_WIN32) #ifdef _WIN32 -#define _AMD64_ // Fixing GitHub runner's winnt.h error +#define _AMD64_ // Fixing GitHub runner's winnt.h error #endif #include "gdxsv_CustomTexture.h" + #include "gdxsv_translation.h" #ifdef __APPLE__ -#include "oslib/directory.h" #include + #include + +#include "oslib/directory.h" #elif _WIN32 +#include #include -#include #include -#include +#include #endif GdxsvCustomTexture gdx_custom_texture; -bool GdxsvCustomTexture::Init() -{ - if (!initialized) - { - initialized = true; - std::string game_id = GetGameId(); - if (GetGameId() == "T13306M") - { +bool GdxsvCustomTexture::Init() { + if (!initialized) { + initialized = true; + std::string game_id = GetGameId(); + if (GetGameId() == "T13306M") { #ifdef __APPLE__ - uint32_t bufSize = PATH_MAX + 1; - char result[bufSize]; - if (_NSGetExecutablePath(result, &bufSize) == 0) - { - textures_path = std::string(result); - textures_path.replace(textures_path.find("MacOS/Flycast"), sizeof("MacOS/Flycast") - 1, "Resources/Textures/"); - textures_path += GdxsvLanguage::TextureDirectoryName(); - } - - DIR* dir = flycast::opendir(textures_path.c_str()); - if (dir != nullptr) - { - INFO_LOG(RENDERER, "Found custom textures directory: %s", textures_path.c_str()); - custom_textures_available = true; - closedir(dir); - loader_thread.Start(); - } + uint32_t bufSize = PATH_MAX + 1; + char result[bufSize]; + if (_NSGetExecutablePath(result, &bufSize) == 0) { + textures_path = std::string(result); + textures_path.replace(textures_path.find("MacOS/Flycast"), sizeof("MacOS/Flycast") - 1, "Resources/Textures/"); + textures_path += GdxsvLanguage::TextureDirectoryName(); + } + + DIR* dir = flycast::opendir(textures_path.c_str()); + if (dir != nullptr) { + INFO_LOG(RENDERER, "Found custom textures directory: %s", textures_path.c_str()); + custom_textures_available = true; + closedir(dir); + loader_thread.Start(); + } #elif _WIN32 - custom_textures_available = true; - loader_thread.Start(); + custom_textures_available = true; + loader_thread.Start(); #endif - } - } - return custom_textures_available; + } + } + return custom_textures_available; } #ifdef _WIN32 -static BOOL CALLBACK StaticEnumRCNamesFunc(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG_PTR lParam) -{ - //Only add target language's hash & resource index into texture_map - auto mapping = reinterpret_cast*>(lParam); - const auto name = std::string(lpName); - auto lang_dir = GdxsvLanguage::TextureDirectoryName(); - std::transform(lang_dir.begin(), lang_dir.end(), lang_dir.begin(), ::toupper); - - if (name.find(lang_dir) == 0 || name.find("COMMON") == 0) { - auto tex_hash = name.substr(name.find_last_of('_') + 1); - u32 hash = strtoul(tex_hash.c_str(), NULL, 16); - mapping->emplace(hash, name); - } - - return true; +static BOOL CALLBACK StaticEnumRCNamesFunc(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG_PTR lParam) { + // Only add target language's hash & resource index into texture_map + auto mapping = reinterpret_cast*>(lParam); + const auto name = std::string(lpName); + auto lang_dir = GdxsvLanguage::TextureDirectoryName(); + std::transform(lang_dir.begin(), lang_dir.end(), lang_dir.begin(), ::toupper); + + if (name.find(lang_dir) == 0 || name.find("COMMON") == 0) { + auto tex_hash = name.substr(name.find_last_of('_') + 1); + u32 hash = strtoul(tex_hash.c_str(), NULL, 16); + mapping->emplace(hash, name); + } + + return true; } -void GdxsvCustomTexture::LoadMap() -{ - texture_map.clear(); - std::map mapping; - - auto ret = EnumResourceNames(GetModuleHandle(NULL), "GDXSV_TEXTURE", (ENUMRESNAMEPROC)&StaticEnumRCNamesFunc, reinterpret_cast(&mapping)); - if (!ret) { - ERROR_LOG(COMMON, "EnumResourceNames error:%d", GetLastError()); - } - - texture_map = mapping; - custom_textures_available = !texture_map.empty(); +void GdxsvCustomTexture::LoadMap() { + texture_map.clear(); + std::map mapping; + + auto ret = EnumResourceNames(GetModuleHandle(NULL), "GDXSV_TEXTURE", (ENUMRESNAMEPROC)&StaticEnumRCNamesFunc, + reinterpret_cast(&mapping)); + if (!ret) { + ERROR_LOG(COMMON, "EnumResourceNames error:%d", GetLastError()); + } + + texture_map = mapping; + custom_textures_available = !texture_map.empty(); } -u8* GdxsvCustomTexture::LoadCustomTexture(u32 hash, int& width, int& height) -{ - auto it = texture_map.find(hash); - if (it == texture_map.end()) - return nullptr; - - u8 *imgData = nullptr; - - //Load PNG data from resource - HRSRC source = FindResourceA(GetModuleHandle(NULL), it->second.c_str(), "GDXSV_TEXTURE"); - if (source != NULL) - { - unsigned int size = SizeofResource(NULL, source); - HGLOBAL memory = LoadResource(NULL, source); - - if (memory != NULL) { - void* data = LockResource(memory); - - int n; - stbi_set_flip_vertically_on_load(1); - imgData = stbi_load_from_memory(static_cast(data), size, &width, &height, &n, STBI_rgb_alpha); - - FreeResource(memory); - } - } - - return imgData; +u8* GdxsvCustomTexture::LoadCustomTexture(u32 hash, int& width, int& height) { + auto it = texture_map.find(hash); + if (it == texture_map.end()) return nullptr; + + u8* imgData = nullptr; + + // Load PNG data from resource + HRSRC source = FindResourceA(GetModuleHandle(NULL), it->second.c_str(), "GDXSV_TEXTURE"); + if (source != NULL) { + unsigned int size = SizeofResource(NULL, source); + HGLOBAL memory = LoadResource(NULL, source); + + if (memory != NULL) { + void* data = LockResource(memory); + + int n; + stbi_set_flip_vertically_on_load(1); + imgData = stbi_load_from_memory(static_cast(data), size, &width, &height, &n, STBI_rgb_alpha); + + FreeResource(memory); + } + } + + return imgData; } #endif -#endif // __APPLE__ || _WIN32 +#endif // __APPLE__ || _WIN32 diff --git a/core/gdxsv/gdxsv_CustomTexture.h b/core/gdxsv/gdxsv_CustomTexture.h index 5ecb72c1fe..d1f26fd80f 100644 --- a/core/gdxsv/gdxsv_CustomTexture.h +++ b/core/gdxsv/gdxsv_CustomTexture.h @@ -11,12 +11,13 @@ #include "../rend/CustomTexture.h" class GdxsvCustomTexture : public CustomTexture { -public: - bool Init() override; + public: + bool Init() override; #ifdef _WIN32 - u8* LoadCustomTexture(u32 hash, int& width, int& height) override; -protected: - void LoadMap() override; + u8* LoadCustomTexture(u32 hash, int& width, int& height) override; + + protected: + void LoadMap() override; #endif }; extern GdxsvCustomTexture gdx_custom_texture; diff --git a/core/gdxsv/gdxsv_backend_replay.cpp b/core/gdxsv/gdxsv_backend_replay.cpp index b6441aaa90..1692aaaeff 100644 --- a/core/gdxsv/gdxsv_backend_replay.cpp +++ b/core/gdxsv/gdxsv_backend_replay.cpp @@ -1,502 +1,489 @@ #include "gdxsv_backend_replay.h" -#include "libs.h" + #include "gdx_rpc.h" +#include "libs.h" void GdxsvBackendReplay::Reset() { - RestorePatch(); - state_ = State::None; - lbs_tx_reader_.Clear(); - mcs_tx_reader_.Clear(); - log_file_.Clear(); - recv_buf_.clear(); - recv_delay_ = 0; - me_ = 0; - msg_list_.clear(); - for (int i = 0; i < 4; ++i) { - key_msg_index_[i].clear(); - } - std::fill(start_index_.begin(), start_index_.end(), 0); + RestorePatch(); + state_ = State::None; + lbs_tx_reader_.Clear(); + mcs_tx_reader_.Clear(); + log_file_.Clear(); + recv_buf_.clear(); + recv_delay_ = 0; + me_ = 0; + msg_list_.clear(); + for (int i = 0; i < 4; ++i) { + key_msg_index_[i].clear(); + } + std::fill(start_index_.begin(), start_index_.end(), 0); } bool GdxsvBackendReplay::StartFile(const char *path) { #ifdef NOWIDE_CONFIG_H_INCLUDED - FILE *fp = nowide::fopen(path, "rb"); + FILE *fp = nowide::fopen(path, "rb"); #else - FILE *fp = fopen(path, "rb"); + FILE *fp = fopen(path, "rb"); #endif - if (fp == nullptr) { - NOTICE_LOG(COMMON, "fopen failed"); - return false; - } - - bool ok = log_file_.ParseFromFileDescriptor(fileno(fp)); - if (!ok) { - NOTICE_LOG(COMMON, "ParseFromFileDescriptor failed"); - return false; - } - fclose(fp); - - return Start(); + if (fp == nullptr) { + NOTICE_LOG(COMMON, "fopen failed"); + return false; + } + + bool ok = log_file_.ParseFromFileDescriptor(fileno(fp)); + if (!ok) { + NOTICE_LOG(COMMON, "ParseFromFileDescriptor failed"); + return false; + } + fclose(fp); + + return Start(); } bool GdxsvBackendReplay::StartBuffer(const char *buf, int size) { - bool ok = log_file_.ParseFromArray(buf, size); - if (!ok) { - NOTICE_LOG(COMMON, "ParseFromArray failed"); - return false; - } - return Start(); + bool ok = log_file_.ParseFromArray(buf, size); + if (!ok) { + NOTICE_LOG(COMMON, "ParseFromArray failed"); + return false; + } + return Start(); } void GdxsvBackendReplay::Open() { - recv_buf_.assign({0x0e, 0x61, 0x00, 0x22, 0x10, 0x31, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd}); - state_ = State::McsSessionExchange; - ApplyPatch(true); + recv_buf_.assign({0x0e, 0x61, 0x00, 0x22, 0x10, 0x31, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd}); + state_ = State::McsSessionExchange; + ApplyPatch(true); } void GdxsvBackendReplay::Close() { - if (state_ != State::End) { - PrintDisconnectionSummary(); - } - RestorePatch(); - state_ = State::End; + if (state_ != State::End) { + PrintDisconnectionSummary(); + } + RestorePatch(); + state_ = State::End; } u32 GdxsvBackendReplay::OnSockWrite(u32 addr, u32 size) { - u8 buf[InetBufSize]; - for (int i = 0; i < size; ++i) { - buf[i] = gdxsv_ReadMem8(addr + i); - } - - if (state_ <= State::LbsStartBattleFlow) { - lbs_tx_reader_.Write((const char *) buf, size); - } else { - mcs_tx_reader_.Write((const char *) buf, size); - } - - if (state_ <= State::LbsStartBattleFlow) { - ProcessLbsMessage(); - } else { - ProcessMcsMessage(); - } - - ApplyPatch(false); - - return size; + u8 buf[InetBufSize]; + for (int i = 0; i < size; ++i) { + buf[i] = gdxsv_ReadMem8(addr + i); + } + + if (state_ <= State::LbsStartBattleFlow) { + lbs_tx_reader_.Write((const char *)buf, size); + } else { + mcs_tx_reader_.Write((const char *)buf, size); + } + + if (state_ <= State::LbsStartBattleFlow) { + ProcessLbsMessage(); + } else { + ProcessMcsMessage(); + } + + ApplyPatch(false); + + return size; } u32 GdxsvBackendReplay::OnSockRead(u32 addr, u32 size) { - if (state_ <= State::LbsStartBattleFlow) { - ProcessLbsMessage(); - } - - if (recv_buf_.empty()) { - return 0; - } - - int n = std::min(recv_buf_.size(), size); - for (int i = 0; i < n; ++i) { - gdxsv_WriteMem8(addr + i, recv_buf_.front()); - recv_buf_.pop_front(); - } - return n; + if (state_ <= State::LbsStartBattleFlow) { + ProcessLbsMessage(); + } + + if (recv_buf_.empty()) { + return 0; + } + + int n = std::min(recv_buf_.size(), size); + for (int i = 0; i < n; ++i) { + gdxsv_WriteMem8(addr + i, recv_buf_.front()); + recv_buf_.pop_front(); + } + return n; } u32 GdxsvBackendReplay::OnSockPoll() { - if (state_ <= State::LbsStartBattleFlow) { - ProcessLbsMessage(); - } - if (0 < recv_delay_) { - recv_delay_--; - return 0; - } - return recv_buf_.size(); + if (state_ <= State::LbsStartBattleFlow) { + ProcessLbsMessage(); + } + if (0 < recv_delay_) { + recv_delay_--; + return 0; + } + return recv_buf_.size(); } bool GdxsvBackendReplay::Start() { - NOTICE_LOG(COMMON, "game_disk = %s", log_file_.game_disk().c_str()); - - McsMessageReader r; - McsMessage msg; - - if (log_file_.log_file_version() < 20210802) { - for (int i = 0; i < log_file_.battle_data_size(); ++i) { - auto data = log_file_.mutable_battle_data(i); - const auto &fields = proto::BattleLogFile::GetReflection()->GetUnknownFields(*data); - if (!fields.empty()) { - for (int j = 0; j < fields.field_count(); ++j) { - const auto &field = fields.field(j); - if (j == 0 && field.type() == google::protobuf::UnknownField::TYPE_LENGTH_DELIMITED) { - const auto &body = field.length_delimited(); - data->set_body(body.data(), body.size()); - } - if (j == 1 && field.type() == google::protobuf::UnknownField::TYPE_VARINT) { - data->set_seq(field.varint()); - } - } - } - } - - std::map player_position; - for (int i = 0; i < log_file_.battle_data_size(); ++i) { - const auto &data = log_file_.battle_data(i); - if (player_position.find(data.user_id()) == player_position.end()) { - r.Write(data.body().data(), data.body().size()); - while (r.Read(msg)) { - if (msg.Type() == McsMessage::MsgType::PingMsg) { - player_position[data.user_id()] = msg.Sender(); - break; - } - } - } - if (log_file_.users_size() == player_position.size()) { - break; - } - } - - for (int i = 0; i < log_file_.users_size(); ++i) { - int pos = player_position[log_file_.users(i).user_id()]; - log_file_.mutable_users(i)->set_pos(pos + 1); - log_file_.mutable_users(i)->set_team(1 + pos / 2); - // NOTE: Surprisingly, player's grade seems to affect the game. - log_file_.mutable_users(i)->set_grade(std::min(14, log_file_.users(i).win_count() / 100)); - log_file_.mutable_users(i)->set_user_name_sjis(log_file_.users(i).user_id()); - } - - std::sort(log_file_.mutable_users()->begin(), log_file_.mutable_users()->end(), - [](const proto::BattleLogUser &a, const proto::BattleLogUser &b) { return a.pos() < b.pos(); }); - } - - msg_list_.clear(); - r.Clear(); - for (int i = 0; i < log_file_.battle_data_size(); ++i) { - const auto &data = log_file_.battle_data(i); - r.Write(data.body().data(), data.body().size()); - while (r.Read(msg)) { - // NOTICE_LOG(COMMON, "MSG:%s", msg.ToHex().c_str()); - if (msg.Type() == McsMessage::KeyMsg2) { - msg_list_.emplace_back(msg.FirstKeyMsg()); - msg_list_.emplace_back(msg.SecondKeyMsg()); - } else { - msg_list_.emplace_back(msg); - } - } - } - - NOTICE_LOG(COMMON, "users = %d", log_file_.users_size()); - NOTICE_LOG(COMMON, "patch_size = %d", log_file_.patches_size()); - NOTICE_LOG(COMMON, "msg_list.size = %d", msg_list_.size()); - PrintDisconnectionSummary(); - - std::fill(start_index_.begin(), start_index_.end(), 0); - state_ = State::Start; - maxlag_ = 1; - NOTICE_LOG(COMMON, "Replay Start"); - return true; + NOTICE_LOG(COMMON, "game_disk = %s", log_file_.game_disk().c_str()); + + McsMessageReader r; + McsMessage msg; + + if (log_file_.log_file_version() < 20210802) { + for (int i = 0; i < log_file_.battle_data_size(); ++i) { + auto data = log_file_.mutable_battle_data(i); + const auto &fields = proto::BattleLogFile::GetReflection()->GetUnknownFields(*data); + if (!fields.empty()) { + for (int j = 0; j < fields.field_count(); ++j) { + const auto &field = fields.field(j); + if (j == 0 && field.type() == google::protobuf::UnknownField::TYPE_LENGTH_DELIMITED) { + const auto &body = field.length_delimited(); + data->set_body(body.data(), body.size()); + } + if (j == 1 && field.type() == google::protobuf::UnknownField::TYPE_VARINT) { + data->set_seq(field.varint()); + } + } + } + } + + std::map player_position; + for (int i = 0; i < log_file_.battle_data_size(); ++i) { + const auto &data = log_file_.battle_data(i); + if (player_position.find(data.user_id()) == player_position.end()) { + r.Write(data.body().data(), data.body().size()); + while (r.Read(msg)) { + if (msg.Type() == McsMessage::MsgType::PingMsg) { + player_position[data.user_id()] = msg.Sender(); + break; + } + } + } + if (log_file_.users_size() == player_position.size()) { + break; + } + } + + for (int i = 0; i < log_file_.users_size(); ++i) { + int pos = player_position[log_file_.users(i).user_id()]; + log_file_.mutable_users(i)->set_pos(pos + 1); + log_file_.mutable_users(i)->set_team(1 + pos / 2); + // NOTE: Surprisingly, player's grade seems to affect the game. + log_file_.mutable_users(i)->set_grade(std::min(14, log_file_.users(i).win_count() / 100)); + log_file_.mutable_users(i)->set_user_name_sjis(log_file_.users(i).user_id()); + } + + std::sort(log_file_.mutable_users()->begin(), log_file_.mutable_users()->end(), + [](const proto::BattleLogUser &a, const proto::BattleLogUser &b) { return a.pos() < b.pos(); }); + } + + msg_list_.clear(); + r.Clear(); + for (int i = 0; i < log_file_.battle_data_size(); ++i) { + const auto &data = log_file_.battle_data(i); + r.Write(data.body().data(), data.body().size()); + while (r.Read(msg)) { + // NOTICE_LOG(COMMON, "MSG:%s", msg.ToHex().c_str()); + if (msg.Type() == McsMessage::KeyMsg2) { + msg_list_.emplace_back(msg.FirstKeyMsg()); + msg_list_.emplace_back(msg.SecondKeyMsg()); + } else { + msg_list_.emplace_back(msg); + } + } + } + + NOTICE_LOG(COMMON, "users = %d", log_file_.users_size()); + NOTICE_LOG(COMMON, "patch_size = %d", log_file_.patches_size()); + NOTICE_LOG(COMMON, "msg_list.size = %d", msg_list_.size()); + PrintDisconnectionSummary(); + + std::fill(start_index_.begin(), start_index_.end(), 0); + state_ = State::Start; + maxlag_ = 1; + NOTICE_LOG(COMMON, "Replay Start"); + return true; } void GdxsvBackendReplay::PrintDisconnectionSummary() { - std::vector last_keymsg_seq(log_file_.users_size()); - std::vector last_force_msg_index(log_file_.users_size()); - for (int i = 0; i < msg_list_.size(); ++i) { - const auto& msg = msg_list_[i]; - if (msg.Type() == McsMessage::KeyMsg1) { - last_keymsg_seq[msg.Sender()] = msg.FirstSeq(); - last_force_msg_index[msg.Sender()] = 0; - } - if (msg.Type() == McsMessage::KeyMsg2) { - last_keymsg_seq[msg.Sender()] = msg.SecondSeq(); - last_force_msg_index[msg.Sender()] = 0; - } - if (msg.Type() == McsMessage::ForceMsg) { - last_force_msg_index[msg.Sender()] = i; - } - } - - NOTICE_LOG(COMMON, "== Disconnection Summary =="); - NOTICE_LOG(COMMON, " KeyCount LastForceMsg UserID Name"); - for (int i = 0; i < log_file_.users_size(); ++i) { - NOTICE_LOG(COMMON, "%9d %12d %6s %s", - last_keymsg_seq[i], - last_force_msg_index[i], - log_file_.users(i).user_id().c_str(), - log_file_.users(i).user_name().c_str()); - } - - const auto it_seq_min = std::min_element(begin(last_keymsg_seq), end(last_keymsg_seq)); - const auto it_seq_max = std::max_element(begin(last_keymsg_seq), end(last_keymsg_seq)); - if (*it_seq_min != *it_seq_max) { - int i = it_seq_min - begin(last_keymsg_seq); - bool no_force_msg = last_force_msg_index[i] == 0; - bool other_player_send_force_msg = std::count(begin(last_force_msg_index), end(last_force_msg_index), 0) == 1; - if (no_force_msg && other_player_send_force_msg) { - NOTICE_LOG(COMMON, "!! Disconnected Player Detected !!"); - NOTICE_LOG(COMMON, " KeyCount LastForceMsg UserID Name"); - NOTICE_LOG(COMMON, "%9d %12d %6s %s", - last_keymsg_seq[i], - last_force_msg_index[i], - log_file_.users(i).user_id().c_str(), - log_file_.users(i).user_name().c_str()); - } - } + std::vector last_keymsg_seq(log_file_.users_size()); + std::vector last_force_msg_index(log_file_.users_size()); + for (int i = 0; i < msg_list_.size(); ++i) { + const auto &msg = msg_list_[i]; + if (msg.Type() == McsMessage::KeyMsg1) { + last_keymsg_seq[msg.Sender()] = msg.FirstSeq(); + last_force_msg_index[msg.Sender()] = 0; + } + if (msg.Type() == McsMessage::KeyMsg2) { + last_keymsg_seq[msg.Sender()] = msg.SecondSeq(); + last_force_msg_index[msg.Sender()] = 0; + } + if (msg.Type() == McsMessage::ForceMsg) { + last_force_msg_index[msg.Sender()] = i; + } + } + + NOTICE_LOG(COMMON, "== Disconnection Summary =="); + NOTICE_LOG(COMMON, " KeyCount LastForceMsg UserID Name"); + for (int i = 0; i < log_file_.users_size(); ++i) { + NOTICE_LOG(COMMON, "%9d %12d %6s %s", last_keymsg_seq[i], last_force_msg_index[i], log_file_.users(i).user_id().c_str(), + log_file_.users(i).user_name().c_str()); + } + + const auto it_seq_min = std::min_element(begin(last_keymsg_seq), end(last_keymsg_seq)); + const auto it_seq_max = std::max_element(begin(last_keymsg_seq), end(last_keymsg_seq)); + if (*it_seq_min != *it_seq_max) { + int i = it_seq_min - begin(last_keymsg_seq); + bool no_force_msg = last_force_msg_index[i] == 0; + bool other_player_send_force_msg = std::count(begin(last_force_msg_index), end(last_force_msg_index), 0) == 1; + if (no_force_msg && other_player_send_force_msg) { + NOTICE_LOG(COMMON, "!! Disconnected Player Detected !!"); + NOTICE_LOG(COMMON, " KeyCount LastForceMsg UserID Name"); + NOTICE_LOG(COMMON, "%9d %12d %6s %s", last_keymsg_seq[i], last_force_msg_index[i], log_file_.users(i).user_id().c_str(), + log_file_.users(i).user_name().c_str()); + } + } } void GdxsvBackendReplay::PrepareKeyMsgIndex() { - for (int p = 0; p < log_file_.users_size(); ++p) { - key_msg_index_[p].clear(); - - for (int i = start_index_[p]; i < msg_list_.size(); ++i) { - const auto &msg = msg_list_[i]; - if (msg.Sender() == p) { - if (!key_msg_index_[p].empty()) { - if (msg.Type() == McsMessage::MsgType::StartMsg) { - start_index_[p] = i + 1; - break; - } - } - - if (msg.Type() == McsMessage::MsgType::KeyMsg1) { - if (!key_msg_index_[p].empty()) { - verify(msg_list_[key_msg_index_[p].back()].FirstSeq() + 1 == msg.FirstSeq()); - } - key_msg_index_[p].emplace_back(i); - } - verify(msg.Type() != McsMessage::KeyMsg2); - } - } - } + for (int p = 0; p < log_file_.users_size(); ++p) { + key_msg_index_[p].clear(); + + for (int i = start_index_[p]; i < msg_list_.size(); ++i) { + const auto &msg = msg_list_[i]; + if (msg.Sender() == p) { + if (!key_msg_index_[p].empty()) { + if (msg.Type() == McsMessage::MsgType::StartMsg) { + start_index_[p] = i + 1; + break; + } + } + + if (msg.Type() == McsMessage::MsgType::KeyMsg1) { + if (!key_msg_index_[p].empty()) { + verify(msg_list_[key_msg_index_[p].back()].FirstSeq() + 1 == msg.FirstSeq()); + } + key_msg_index_[p].emplace_back(i); + } + verify(msg.Type() != McsMessage::KeyMsg2); + } + } + } } void GdxsvBackendReplay::ProcessLbsMessage() { - if (state_ == State::Start) { - LbsMessage::SvNotice(LbsMessage::lbsReadyBattle).Serialize(recv_buf_); - recv_delay_ = 1; - state_ = State::LbsStartBattleFlow; - } - - LbsMessage msg; - if (lbs_tx_reader_.Read(msg)) { - // NOTICE_LOG(COMMON, "RECV cmd=%04x seq=%d", msg.command, msg.seq); - - if (state_ == State::Start) { - state_ = State::LbsStartBattleFlow; - } - - if (msg.command == LbsMessage::lbsLobbyMatchingEntry) { - LbsMessage::SvAnswer(msg).Serialize(recv_buf_); - LbsMessage::SvNotice(LbsMessage::lbsReadyBattle).Serialize(recv_buf_); - } - - if (msg.command == LbsMessage::lbsAskMatchingJoin) { - int n = log_file_.users_size(); - LbsMessage::SvAnswer(msg).Write8(n)->Serialize(recv_buf_); - } - - if (msg.command == LbsMessage::lbsAskPlayerSide) { - // camera player id - me_ = 0; - LbsMessage::SvAnswer(msg).Write8(me_ + 1)->Serialize(recv_buf_); - } - - if (msg.command == LbsMessage::lbsAskPlayerInfo) { - int pos = msg.Read8(); - const auto &user = log_file_.users(pos - 1); - NOTICE_LOG(COMMON, "pos=%d game_param.size=%d", pos, user.game_param().size()); - LbsMessage::SvAnswer(msg). - Write8(pos)-> - WriteString(user.user_id())-> - WriteBytes(user.user_name_sjis().data(), user.user_name_sjis().size())-> - WriteBytes(user.game_param().data(), user.game_param().size())-> - Write16(user.grade())-> - Write16(user.win_count())-> - Write16(user.lose_count())-> - Write16(0)-> - Write16(user.battle_count() - user.win_count() - user.lose_count())-> - Write16(0)-> - Write16(user.team())-> - Write16(0)-> - Serialize(recv_buf_); - } - - if (msg.command == LbsMessage::lbsAskRuleData) { - LbsMessage::SvAnswer(msg). - WriteBytes(log_file_.rule_bin().data(), log_file_.rule_bin().size())-> - Serialize(recv_buf_); - } - - if (msg.command == LbsMessage::lbsAskBattleCode) { - LbsMessage::SvAnswer(msg). - WriteBytes(log_file_.battle_code().data(), log_file_.battle_code().size())-> - Serialize(recv_buf_); - } - - if (msg.command == LbsMessage::lbsAskMcsVersion) { - LbsMessage::SvAnswer(msg). - Write8(10)->Serialize(recv_buf_); - } - - if (msg.command == LbsMessage::lbsAskMcsAddress) { - LbsMessage::SvAnswer(msg). - Write16(4)->Write8(127)->Write8(0)->Write8(0)->Write8(1)-> - Write16(2)->Write16(3333)->Serialize(recv_buf_); - } - - if (msg.command == LbsMessage::lbsLogout) { - state_ = State::McsWaitJoin; - } - - recv_delay_ = 1; - } + if (state_ == State::Start) { + LbsMessage::SvNotice(LbsMessage::lbsReadyBattle).Serialize(recv_buf_); + recv_delay_ = 1; + state_ = State::LbsStartBattleFlow; + } + + LbsMessage msg; + if (lbs_tx_reader_.Read(msg)) { + // NOTICE_LOG(COMMON, "RECV cmd=%04x seq=%d", msg.command, msg.seq); + + if (state_ == State::Start) { + state_ = State::LbsStartBattleFlow; + } + + if (msg.command == LbsMessage::lbsLobbyMatchingEntry) { + LbsMessage::SvAnswer(msg).Serialize(recv_buf_); + LbsMessage::SvNotice(LbsMessage::lbsReadyBattle).Serialize(recv_buf_); + } + + if (msg.command == LbsMessage::lbsAskMatchingJoin) { + int n = log_file_.users_size(); + LbsMessage::SvAnswer(msg).Write8(n)->Serialize(recv_buf_); + } + + if (msg.command == LbsMessage::lbsAskPlayerSide) { + // camera player id + me_ = 0; + LbsMessage::SvAnswer(msg).Write8(me_ + 1)->Serialize(recv_buf_); + } + + if (msg.command == LbsMessage::lbsAskPlayerInfo) { + int pos = msg.Read8(); + const auto &user = log_file_.users(pos - 1); + NOTICE_LOG(COMMON, "pos=%d game_param.size=%d", pos, user.game_param().size()); + LbsMessage::SvAnswer(msg) + .Write8(pos) + ->WriteString(user.user_id()) + ->WriteBytes(user.user_name_sjis().data(), user.user_name_sjis().size()) + ->WriteBytes(user.game_param().data(), user.game_param().size()) + ->Write16(user.grade()) + ->Write16(user.win_count()) + ->Write16(user.lose_count()) + ->Write16(0) + ->Write16(user.battle_count() - user.win_count() - user.lose_count()) + ->Write16(0) + ->Write16(user.team()) + ->Write16(0) + ->Serialize(recv_buf_); + } + + if (msg.command == LbsMessage::lbsAskRuleData) { + LbsMessage::SvAnswer(msg).WriteBytes(log_file_.rule_bin().data(), log_file_.rule_bin().size())->Serialize(recv_buf_); + } + + if (msg.command == LbsMessage::lbsAskBattleCode) { + LbsMessage::SvAnswer(msg).WriteBytes(log_file_.battle_code().data(), log_file_.battle_code().size())->Serialize(recv_buf_); + } + + if (msg.command == LbsMessage::lbsAskMcsVersion) { + LbsMessage::SvAnswer(msg).Write8(10)->Serialize(recv_buf_); + } + + if (msg.command == LbsMessage::lbsAskMcsAddress) { + LbsMessage::SvAnswer(msg).Write16(4)->Write8(127)->Write8(0)->Write8(0)->Write8(1)->Write16(2)->Write16(3333)->Serialize( + recv_buf_); + } + + if (msg.command == LbsMessage::lbsLogout) { + state_ = State::McsWaitJoin; + } + + recv_delay_ = 1; + } } void GdxsvBackendReplay::ProcessMcsMessage() { - McsMessage msg; - if (mcs_tx_reader_.Read(msg)) { - NOTICE_LOG(COMMON, "Read %s %s", McsMessage::MsgTypeName(msg.Type()), msg.ToHex().c_str()); - switch (msg.Type()) { - case McsMessage::MsgType::ConnectionIdMsg: - state_ = State::McsInBattle; - break; - case McsMessage::MsgType::IntroMsg: - for (int i = 0; i < log_file_.users_size(); ++i) { - if (i != me_) { - auto intro_msg = McsMessage::Create(McsMessage::MsgType::IntroMsg, i); - std::copy(intro_msg.body.begin(), intro_msg.body.end(), std::back_inserter(recv_buf_)); - } - } - break; - case McsMessage::MsgType::IntroMsgReturn: - for (int i = 0; i < log_file_.users_size(); ++i) { - if (i != me_) { - auto intro_msg = McsMessage::Create(McsMessage::MsgType::IntroMsgReturn, i); - std::copy(intro_msg.body.begin(), intro_msg.body.end(), std::back_inserter(recv_buf_)); - } - } - break; - case McsMessage::MsgType::PingMsg: - for (int i = 0; i < log_file_.users_size(); ++i) { - if (i != me_) { - auto pong_msg = McsMessage::Create(McsMessage::MsgType::PongMsg, i); - pong_msg.SetPongTo(me_); - pong_msg.PongCount(msg.PingCount()); - std::copy(pong_msg.body.begin(), pong_msg.body.end(), std::back_inserter(recv_buf_)); - } - } - break; - case McsMessage::MsgType::PongMsg: - break; - case McsMessage::MsgType::StartMsg: - for (int i = 0; i < log_file_.users_size(); ++i) { - if (i != me_) { - auto start_msg = McsMessage::Create(McsMessage::MsgType::StartMsg, i); - std::copy(start_msg.body.begin(), start_msg.body.end(), std::back_inserter(recv_buf_)); - } - } - PrepareKeyMsgIndex(); - break; - case McsMessage::MsgType::ForceMsg: - break; - case McsMessage::MsgType::KeyMsg1: { - // NOTICE_LOG(COMMON, "KeyMsg1:%s", msg.ToHex().c_str()); - for (int i = 0; i < log_file_.users_size(); ++i) { - if (msg.FirstSeq() < key_msg_index_[i].size()) { - auto key_msg = msg_list_[key_msg_index_[i][msg.FirstSeq()]]; - NOTICE_LOG(COMMON, "KeyMsg:%s", key_msg.ToHex().c_str()); - std::copy(key_msg.body.begin(), key_msg.body.end(), std::back_inserter(recv_buf_)); - } - } - break; - } - case McsMessage::MsgType::KeyMsg2: - verify(false); - break; - case McsMessage::MsgType::LoadStartMsg: - for (int i = 0; i < log_file_.users_size(); ++i) { - if (i != me_) { - auto load_start_msg = McsMessage::Create(McsMessage::MsgType::LoadStartMsg, i); - std::copy(load_start_msg.body.begin(), load_start_msg.body.end(), - std::back_inserter(recv_buf_)); - } - } - break; - case McsMessage::MsgType::LoadEndMsg: - for (int i = 0; i < log_file_.users_size(); ++i) { - if (i != me_) { - auto load_end_msg = McsMessage::Create(McsMessage::MsgType::LoadEndMsg, i); - std::copy(load_end_msg.body.begin(), load_end_msg.body.end(), - std::back_inserter(recv_buf_)); - } - } - break; - default: - WARN_LOG(COMMON, "unhandled mcs msg: %s", McsMessage::MsgTypeName(msg.Type())); - WARN_LOG(COMMON, "%s", msg.ToHex().c_str()); - break; - } - } + McsMessage msg; + if (mcs_tx_reader_.Read(msg)) { + NOTICE_LOG(COMMON, "Read %s %s", McsMessage::MsgTypeName(msg.Type()), msg.ToHex().c_str()); + switch (msg.Type()) { + case McsMessage::MsgType::ConnectionIdMsg: + state_ = State::McsInBattle; + break; + case McsMessage::MsgType::IntroMsg: + for (int i = 0; i < log_file_.users_size(); ++i) { + if (i != me_) { + auto intro_msg = McsMessage::Create(McsMessage::MsgType::IntroMsg, i); + std::copy(intro_msg.body.begin(), intro_msg.body.end(), std::back_inserter(recv_buf_)); + } + } + break; + case McsMessage::MsgType::IntroMsgReturn: + for (int i = 0; i < log_file_.users_size(); ++i) { + if (i != me_) { + auto intro_msg = McsMessage::Create(McsMessage::MsgType::IntroMsgReturn, i); + std::copy(intro_msg.body.begin(), intro_msg.body.end(), std::back_inserter(recv_buf_)); + } + } + break; + case McsMessage::MsgType::PingMsg: + for (int i = 0; i < log_file_.users_size(); ++i) { + if (i != me_) { + auto pong_msg = McsMessage::Create(McsMessage::MsgType::PongMsg, i); + pong_msg.SetPongTo(me_); + pong_msg.PongCount(msg.PingCount()); + std::copy(pong_msg.body.begin(), pong_msg.body.end(), std::back_inserter(recv_buf_)); + } + } + break; + case McsMessage::MsgType::PongMsg: + break; + case McsMessage::MsgType::StartMsg: + for (int i = 0; i < log_file_.users_size(); ++i) { + if (i != me_) { + auto start_msg = McsMessage::Create(McsMessage::MsgType::StartMsg, i); + std::copy(start_msg.body.begin(), start_msg.body.end(), std::back_inserter(recv_buf_)); + } + } + PrepareKeyMsgIndex(); + break; + case McsMessage::MsgType::ForceMsg: + break; + case McsMessage::MsgType::KeyMsg1: { + // NOTICE_LOG(COMMON, "KeyMsg1:%s", msg.ToHex().c_str()); + for (int i = 0; i < log_file_.users_size(); ++i) { + if (msg.FirstSeq() < key_msg_index_[i].size()) { + auto key_msg = msg_list_[key_msg_index_[i][msg.FirstSeq()]]; + NOTICE_LOG(COMMON, "KeyMsg:%s", key_msg.ToHex().c_str()); + std::copy(key_msg.body.begin(), key_msg.body.end(), std::back_inserter(recv_buf_)); + } + } + break; + } + case McsMessage::MsgType::KeyMsg2: + verify(false); + break; + case McsMessage::MsgType::LoadStartMsg: + for (int i = 0; i < log_file_.users_size(); ++i) { + if (i != me_) { + auto load_start_msg = McsMessage::Create(McsMessage::MsgType::LoadStartMsg, i); + std::copy(load_start_msg.body.begin(), load_start_msg.body.end(), std::back_inserter(recv_buf_)); + } + } + break; + case McsMessage::MsgType::LoadEndMsg: + for (int i = 0; i < log_file_.users_size(); ++i) { + if (i != me_) { + auto load_end_msg = McsMessage::Create(McsMessage::MsgType::LoadEndMsg, i); + std::copy(load_end_msg.body.begin(), load_end_msg.body.end(), std::back_inserter(recv_buf_)); + } + } + break; + default: + WARN_LOG(COMMON, "unhandled mcs msg: %s", McsMessage::MsgTypeName(msg.Type())); + WARN_LOG(COMMON, "%s", msg.ToHex().c_str()); + break; + } + } } void GdxsvBackendReplay::ApplyPatch(bool first_time) { - if (state_ == State::None || state_ == State::End) { - return; - } - - // Skip Key MsgPush - // TODO: disk1 - if (log_file_.game_disk() == "dc2") { - gdxsv_WriteMem16(0x8c045f64, 9); - gdxsv_WriteMem8(0x0c3abb90, 1); - } - if (log_file_.game_disk() == "ps2") { - gdxsv_WriteMem32(0x0037f5a0, 0); - gdxsv_WriteMem8(0x00580340, 1); - } - - // Online Patch - for (int i = 0; i < log_file_.patches_size(); ++i) { - if (log_file_.patches(i).write_once() && !first_time) { - continue; - } - - for (int j = 0; j < log_file_.patches(i).codes_size(); ++j) { - const auto &code = log_file_.patches(i).codes(j); - if (code.size() == 8) { - gdxsv_WriteMem8(code.address(), code.changed()); - } - if (code.size() == 16) { - gdxsv_WriteMem16(code.address(), code.changed()); - } - if (code.size() == 32) { - gdxsv_WriteMem32(code.address(), code.changed()); - } - } - } + if (state_ == State::None || state_ == State::End) { + return; + } + + // Skip Key MsgPush + // TODO: disk1 + if (log_file_.game_disk() == "dc2") { + gdxsv_WriteMem16(0x8c045f64, 9); + gdxsv_WriteMem8(0x0c3abb90, 1); + } + if (log_file_.game_disk() == "ps2") { + gdxsv_WriteMem32(0x0037f5a0, 0); + gdxsv_WriteMem8(0x00580340, 1); + } + + // Online Patch + for (int i = 0; i < log_file_.patches_size(); ++i) { + if (log_file_.patches(i).write_once() && !first_time) { + continue; + } + + for (int j = 0; j < log_file_.patches(i).codes_size(); ++j) { + const auto &code = log_file_.patches(i).codes(j); + if (code.size() == 8) { + gdxsv_WriteMem8(code.address(), code.changed()); + } + if (code.size() == 16) { + gdxsv_WriteMem16(code.address(), code.changed()); + } + if (code.size() == 32) { + gdxsv_WriteMem32(code.address(), code.changed()); + } + } + } } void GdxsvBackendReplay::RestorePatch() { - if (log_file_.game_disk() == "dc2") { - gdxsv_WriteMem16(0x8c045f64, 0x410b); - gdxsv_WriteMem8(0x0c3abb90, 2); - } - - if (log_file_.game_disk() == "ps2") { - gdxsv_WriteMem32(0x0037f5a0, 0x0c0e0be4); - gdxsv_WriteMem8(0x00580340, 2); - } - - // Online Patch - for (int i = 0; i < log_file_.patches_size(); ++i) { - for (int j = 0; j < log_file_.patches(i).codes_size(); ++j) { - const auto &code = log_file_.patches(i).codes(j); - if (code.size() == 8) { - gdxsv_WriteMem8(code.address(), code.original()); - } - if (code.size() == 16) { - gdxsv_WriteMem16(code.address(), code.original()); - } - if (code.size() == 32) { - gdxsv_WriteMem32(code.address(), code.original()); - } - } - } + if (log_file_.game_disk() == "dc2") { + gdxsv_WriteMem16(0x8c045f64, 0x410b); + gdxsv_WriteMem8(0x0c3abb90, 2); + } + + if (log_file_.game_disk() == "ps2") { + gdxsv_WriteMem32(0x0037f5a0, 0x0c0e0be4); + gdxsv_WriteMem8(0x00580340, 2); + } + + // Online Patch + for (int i = 0; i < log_file_.patches_size(); ++i) { + for (int j = 0; j < log_file_.patches(i).codes_size(); ++j) { + const auto &code = log_file_.patches(i).codes(j); + if (code.size() == 8) { + gdxsv_WriteMem8(code.address(), code.original()); + } + if (code.size() == 16) { + gdxsv_WriteMem16(code.address(), code.original()); + } + if (code.size() == 32) { + gdxsv_WriteMem32(code.address(), code.original()); + } + } + } } diff --git a/core/gdxsv/gdxsv_backend_replay.h b/core/gdxsv/gdxsv_backend_replay.h index 008bd5ddbd..a93cac9095 100644 --- a/core/gdxsv/gdxsv_backend_replay.h +++ b/core/gdxsv/gdxsv_backend_replay.h @@ -1,62 +1,60 @@ #pragma once +#include #include #include +#include #include -#include #include -#include -#include "types.h" #include "gdxsv.pb.h" -#include "mcs_message.h" #include "lbs_message.h" +#include "mcs_message.h" +#include "types.h" // Mock network implementation to replay local battle log class GdxsvBackendReplay { -public: - GdxsvBackendReplay(const std::map &symbols, std::atomic &maxlag) - : symbols_(symbols), maxlag_(maxlag) { - } + public: + GdxsvBackendReplay(const std::map &symbols, std::atomic &maxlag) : symbols_(symbols), maxlag_(maxlag) {} - enum class State { - None, - Start, - LbsStartBattleFlow, - McsWaitJoin, - McsSessionExchange, - McsInBattle, - End, - }; + enum class State { + None, + Start, + LbsStartBattleFlow, + McsWaitJoin, + McsSessionExchange, + McsInBattle, + End, + }; - void Reset(); - bool StartFile(const char *path); - bool StartBuffer(const char *buf, int size); - void Open(); - void Close(); - u32 OnSockWrite(u32 addr, u32 size); - u32 OnSockRead(u32 addr, u32 size); - u32 OnSockPoll(); + void Reset(); + bool StartFile(const char *path); + bool StartBuffer(const char *buf, int size); + void Open(); + void Close(); + u32 OnSockWrite(u32 addr, u32 size); + u32 OnSockRead(u32 addr, u32 size); + u32 OnSockPoll(); -private: - bool Start(); - void PrintDisconnectionSummary(); - void PrepareKeyMsgIndex(); - void ProcessLbsMessage(); - void ProcessMcsMessage(); - void ApplyPatch(bool first_time); - void RestorePatch(); + private: + bool Start(); + void PrintDisconnectionSummary(); + void PrepareKeyMsgIndex(); + void ProcessLbsMessage(); + void ProcessMcsMessage(); + void ApplyPatch(bool first_time); + void RestorePatch(); - const std::map &symbols_; - std::atomic &maxlag_; - State state_; - LbsMessageReader lbs_tx_reader_; - McsMessageReader mcs_tx_reader_; - proto::BattleLogFile log_file_; - std::deque recv_buf_; - int recv_delay_; - int me_; - std::vector msg_list_; - std::array start_index_; - std::array, 4> key_msg_index_; + const std::map &symbols_; + std::atomic &maxlag_; + State state_; + LbsMessageReader lbs_tx_reader_; + McsMessageReader mcs_tx_reader_; + proto::BattleLogFile log_file_; + std::deque recv_buf_; + int recv_delay_; + int me_; + std::vector msg_list_; + std::array start_index_; + std::array, 4> key_msg_index_; }; \ No newline at end of file diff --git a/core/gdxsv/gdxsv_backend_rollback.cpp b/core/gdxsv/gdxsv_backend_rollback.cpp index ebf064662b..95afcdfc07 100644 --- a/core/gdxsv/gdxsv_backend_rollback.cpp +++ b/core/gdxsv/gdxsv_backend_rollback.cpp @@ -21,12 +21,10 @@ #include "rend/transform_matrix.h" namespace { -u8 DummyGameParam[640] = {0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x02, 0x00, 0x05, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x76, 0x83, 0x8c, 0x83, 0x43, - 0x83, 0x84, 0x81, 0x5b, 0x82, 0x50, 0x00, 0x00, 0x00, 0x00, 0x07}; -u8 DummyRuleData[] = {0x03, 0x02, 0x03, 0x00, 0x00, 0x01, 0x58, 0x02, 0x58, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, - 0xff, 0x01, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f, 0x00}; +u8 DummyGameParam[640] = {0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x02, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, + 0x76, 0x83, 0x8c, 0x83, 0x43, 0x83, 0x84, 0x81, 0x5b, 0x82, 0x50, 0x00, 0x00, 0x00, 0x00, 0x07}; +u8 DummyRuleData[] = {0x03, 0x02, 0x03, 0x00, 0x00, 0x01, 0x58, 0x02, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0x3f, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0xff, 0x01, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0x3f, 0x00}; const u16 ExInputNone = 0; const u16 ExInputWaitStart = 1; @@ -34,284 +32,280 @@ const u16 ExInputWaitLoadEnd = 2; // maple input to mcs pad input u16 convertInput(MapleInputState input) { - u16 r = 0; - if (~input.kcode & 0x0004) r |= 0x4000; // A - if (~input.kcode & 0x0002) r |= 0x2000; // B - if (~input.kcode & 0x0400) r |= 0x0002; // X - if (~input.kcode & 0x0200) r |= 0x0001; // Y - if (~input.kcode & 0x0010) r |= 0x0020; // up - if (~input.kcode & 0x0020) r |= 0x0010; // down - if (~input.kcode & 0x0080) r |= 0x0004; // right - if (~input.kcode & 0x0040) r |= 0x0008; // left - if (~input.kcode & 0x0008) r |= 0x0080; // Start - if (~input.kcode & 0x00020000) r |= 0x8000; // LT - if (~input.kcode & 0x00040000) r |= 0x1000; // RT - - if (input.fullAxes[0] + 128 <= 128 - 0x20) r |= 0x0008; // left - if (input.fullAxes[0] + 128 >= 128 + 0x20) r |= 0x0004; // right - if (input.fullAxes[1] + 128 <= 128 - 0x20) r |= 0x0020; // up - if (input.fullAxes[1] + 128 >= 128 + 0x20) r |= 0x0010; // down - return r; + u16 r = 0; + if (~input.kcode & 0x0004) r |= 0x4000; // A + if (~input.kcode & 0x0002) r |= 0x2000; // B + if (~input.kcode & 0x0400) r |= 0x0002; // X + if (~input.kcode & 0x0200) r |= 0x0001; // Y + if (~input.kcode & 0x0010) r |= 0x0020; // up + if (~input.kcode & 0x0020) r |= 0x0010; // down + if (~input.kcode & 0x0080) r |= 0x0004; // right + if (~input.kcode & 0x0040) r |= 0x0008; // left + if (~input.kcode & 0x0008) r |= 0x0080; // Start + if (~input.kcode & 0x00020000) r |= 0x8000; // LT + if (~input.kcode & 0x00040000) r |= 0x1000; // RT + + if (input.fullAxes[0] + 128 <= 128 - 0x20) r |= 0x0008; // left + if (input.fullAxes[0] + 128 >= 128 + 0x20) r |= 0x0004; // right + if (input.fullAxes[1] + 128 <= 128 - 0x20) r |= 0x0020; // up + if (input.fullAxes[1] + 128 >= 128 + 0x20) r |= 0x0010; // down + return r; } void drawConnectionDiagram(int elapsed, const uint8_t matrix[4][4], const std::map& pos_to_id); } // namespace void GdxsvBackendRollback::DisplayOSD() { - const auto elapsed = ping_pong_.ElapsedMs(); - if (1550 < elapsed && elapsed < 6900) { - uint8_t matrix[4][4] = {}; - ping_pong_.GetRttMatrix(matrix); - std::map id_to_team, pos_to_id; - for (const auto& c : matching_.candidates()) { - id_to_team[c.peer_id()] = c.team(); - } - for (const auto& p : id_to_team) { - int pos = (p.second == 2 ? 2 : 0); - if (pos_to_id.find(pos) != pos_to_id.end()) pos++; - pos_to_id[pos] = p.first; - } - drawConnectionDiagram(elapsed, matrix, pos_to_id); - } + const auto elapsed = ping_pong_.ElapsedMs(); + if (1550 < elapsed && elapsed < 6900) { + uint8_t matrix[4][4] = {}; + ping_pong_.GetRttMatrix(matrix); + std::map id_to_team, pos_to_id; + for (const auto& c : matching_.candidates()) { + id_to_team[c.peer_id()] = c.team(); + } + for (const auto& p : id_to_team) { + int pos = (p.second == 2 ? 2 : 0); + if (pos_to_id.find(pos) != pos_to_id.end()) pos++; + pos_to_id[pos] = p.first; + } + drawConnectionDiagram(elapsed, matrix, pos_to_id); + } } void GdxsvBackendRollback::Reset() { - RestorePatch(); - state_ = State::None; - lbs_tx_reader_.Clear(); - recv_buf_.clear(); - recv_delay_ = 0; + RestorePatch(); + state_ = State::None; + lbs_tx_reader_.Clear(); + recv_buf_.clear(); + recv_delay_ = 0; } void GdxsvBackendRollback::OnMainUiLoop() { - const int COM_R_No0 = 0x0c391d79; // TODO:disk2 - const int ConnectionStatus = 0x0c3abb84; // TODO: disk2 - const int NetCountDown = 0x0c3ab942; // TODO: disk2 - - if (state_ == State::StopEmulator) { - emu.stop(); - state_ = State::WaitPingPong; - } - - if (state_ == State::WaitPingPong && !ping_pong_.Running()) { - state_ = State::StartGGPOSession; - } - - static auto session_start_time = std::chrono::high_resolution_clock::now(); - if (state_ == State::StartGGPOSession) { - bool ok = true; - std::vector ips(matching_.player_count()); - std::vector ports(matching_.player_count()); - float max_rtt = 0; - NOTICE_LOG(COMMON, "Peer count %d", matching_.player_count()); - for (int i = 0; i < matching_.player_count(); i++) { - if (i == matching_.peer_id()) { - NOTICE_LOG(COMMON, "Peer%d is self", i); - ips[i] = ""; - ports[i] = port_; - } else { - sockaddr_in addr; - float rtt; - if (ping_pong_.GetAvailableAddress(i, &addr, &rtt)) { - NOTICE_LOG(COMMON, "Peer%d rtt%.2f", i, rtt); - max_rtt = std::max(max_rtt, rtt); - char str[INET_ADDRSTRLEN] = {}; - inet_ntop(AF_INET, &(addr.sin_addr), str, INET_ADDRSTRLEN); - ips[i] = str; - ports[i] = ntohs(addr.sin_port); - } else { - NOTICE_LOG(COMMON, "No available address %d", i); - ok = false; - } - } - } - - if (ok) { - int delay = std::max(2, std::max(config::GdxMinDelay.get(), int(max_rtt / 2.0 / 16.66 + 0.5))); - NOTICE_LOG(COMMON, "max_rtt=%.2f delay=%d", max_rtt, delay); - config::GGPOEnable.override(1); - config::GGPODelay.override(delay); - start_network_ = ggpo::gdxsvStartNetwork(matching_.battle_code().c_str(), matching_.peer_id(), ips, ports); - ggpo::receiveChatMessages(nullptr); - session_start_time = std::chrono::high_resolution_clock::now(); - state_ = State::WaitGGPOSession; - } else { - gdxsv_WriteMem16(NetCountDown, 1); - emu.start(); - state_ = State::End; - } - } - - if (state_ == State::WaitGGPOSession) { - auto now = std::chrono::high_resolution_clock::now(); - auto timeout = 10000 <= std::chrono::duration_cast(now - session_start_time).count(); - - if (start_network_.valid() && start_network_.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) { - if (ggpo::active()) { - start_network_ = std::future(); - state_ = State::McsInBattle; - emu.start(); - } - else { - NOTICE_LOG(COMMON, "StartNetwork failure"); - state_ = State::End; - emu.start(); - } - } - else if (timeout) { - NOTICE_LOG(COMMON, "StartNetwork timeout"); - ggpo::stopSession(); - state_ = State::End; - emu.start(); - } - } - - // Rebattle end - if (gdxsv_ReadMem8(COM_R_No0) == 4 && gdxsv_ReadMem8(COM_R_No0 + 5) == 3 && ggpo::active() && !ggpo::rollbacking()) { - ggpo::disconnect(matching_.peer_id()); - state_ = State::CloseWait; - } - - // Friend save scene - if (gdxsv_ReadMem8(COM_R_No0) == 4 && gdxsv_ReadMem8(COM_R_No0 + 5) == 4 && ggpo::active() && !ggpo::rollbacking()) { - ggpo::stopSession(); - state_ = State::End; - } - - // Fast return to lobby on error - if (gdxsv_ReadMem16(ConnectionStatus) == 1 && - gdxsv_ReadMem16(ConnectionStatus + 4) == 10 && - 1 < gdxsv_ReadMem16(NetCountDown)) { - ggpo::stopSession(); - state_ = State::End; - gdxsv_WriteMem16(NetCountDown, 1); - } + const int COM_R_No0 = 0x0c391d79; // TODO:disk2 + const int ConnectionStatus = 0x0c3abb84; // TODO: disk2 + const int NetCountDown = 0x0c3ab942; // TODO: disk2 + + if (state_ == State::StopEmulator) { + emu.stop(); + state_ = State::WaitPingPong; + } + + if (state_ == State::WaitPingPong && !ping_pong_.Running()) { + state_ = State::StartGGPOSession; + } + + static auto session_start_time = std::chrono::high_resolution_clock::now(); + if (state_ == State::StartGGPOSession) { + bool ok = true; + std::vector ips(matching_.player_count()); + std::vector ports(matching_.player_count()); + float max_rtt = 0; + NOTICE_LOG(COMMON, "Peer count %d", matching_.player_count()); + for (int i = 0; i < matching_.player_count(); i++) { + if (i == matching_.peer_id()) { + NOTICE_LOG(COMMON, "Peer%d is self", i); + ips[i] = ""; + ports[i] = port_; + } else { + sockaddr_in addr; + float rtt; + if (ping_pong_.GetAvailableAddress(i, &addr, &rtt)) { + NOTICE_LOG(COMMON, "Peer%d rtt%.2f", i, rtt); + max_rtt = std::max(max_rtt, rtt); + char str[INET_ADDRSTRLEN] = {}; + inet_ntop(AF_INET, &(addr.sin_addr), str, INET_ADDRSTRLEN); + ips[i] = str; + ports[i] = ntohs(addr.sin_port); + } else { + NOTICE_LOG(COMMON, "No available address %d", i); + ok = false; + } + } + } + + if (ok) { + int delay = std::max(2, std::max(config::GdxMinDelay.get(), int(max_rtt / 2.0 / 16.66 + 0.5))); + NOTICE_LOG(COMMON, "max_rtt=%.2f delay=%d", max_rtt, delay); + config::GGPOEnable.override(1); + config::GGPODelay.override(delay); + start_network_ = ggpo::gdxsvStartNetwork(matching_.battle_code().c_str(), matching_.peer_id(), ips, ports); + ggpo::receiveChatMessages(nullptr); + session_start_time = std::chrono::high_resolution_clock::now(); + state_ = State::WaitGGPOSession; + } else { + gdxsv_WriteMem16(NetCountDown, 1); + emu.start(); + state_ = State::End; + } + } + + if (state_ == State::WaitGGPOSession) { + auto now = std::chrono::high_resolution_clock::now(); + auto timeout = 10000 <= std::chrono::duration_cast(now - session_start_time).count(); + + if (start_network_.valid() && start_network_.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) { + if (ggpo::active()) { + start_network_ = std::future(); + state_ = State::McsInBattle; + emu.start(); + } else { + NOTICE_LOG(COMMON, "StartNetwork failure"); + state_ = State::End; + emu.start(); + } + } else if (timeout) { + NOTICE_LOG(COMMON, "StartNetwork timeout"); + ggpo::stopSession(); + state_ = State::End; + emu.start(); + } + } + + // Rebattle end + if (gdxsv_ReadMem8(COM_R_No0) == 4 && gdxsv_ReadMem8(COM_R_No0 + 5) == 3 && ggpo::active() && !ggpo::rollbacking()) { + ggpo::disconnect(matching_.peer_id()); + state_ = State::CloseWait; + } + + // Friend save scene + if (gdxsv_ReadMem8(COM_R_No0) == 4 && gdxsv_ReadMem8(COM_R_No0 + 5) == 4 && ggpo::active() && !ggpo::rollbacking()) { + ggpo::stopSession(); + state_ = State::End; + } + + // Fast return to lobby on error + if (gdxsv_ReadMem16(ConnectionStatus) == 1 && gdxsv_ReadMem16(ConnectionStatus + 4) == 10 && 1 < gdxsv_ReadMem16(NetCountDown)) { + ggpo::stopSession(); + state_ = State::End; + gdxsv_WriteMem16(NetCountDown, 1); + } } -bool GdxsvBackendRollback::StartLocalTest(const char *param) { - auto args = std::string(param); - int me = 0; - int n = 4; - if (0 < args.size() && '1' <= args[0] && args[0] <= '4') { - me = args[0] - '1'; - } - if (2 < args.size() && args[1] == '/' && '1' <= args[2] && args[2] <= '4') { - n = args[2] - '0'; - } - Reset(); - DummyRuleData[6] = 1; - DummyRuleData[7] = 0; - DummyRuleData[8] = 1; - DummyRuleData[9] = 0; - state_ = State::StartLocalTest; - maxlag_ = 0; - matching_.set_battle_code("0123456"); - matching_.set_peer_id(me); - matching_.set_session_id(12345); - matching_.set_timeout_min_ms(1000); - matching_.set_timeout_max_ms(10000); - matching_.set_player_count(n); - for (int i = 0; i < n; i++) { - proto::PlayerAddress player{}; - player.set_ip("127.0.0.1"); - player.set_port(20010 + i); - player.set_user_id(std::to_string(i)); - player.set_peer_id(i); - player.set_team(i/2 + 1); - matching_.mutable_candidates()->Add(std::move(player)); - } - Prepare(matching_, 20010 + me); - NOTICE_LOG(COMMON, "RollbackNet StartLocalTest %d", me); - return true; +bool GdxsvBackendRollback::StartLocalTest(const char* param) { + auto args = std::string(param); + int me = 0; + int n = 4; + if (0 < args.size() && '1' <= args[0] && args[0] <= '4') { + me = args[0] - '1'; + } + if (2 < args.size() && args[1] == '/' && '1' <= args[2] && args[2] <= '4') { + n = args[2] - '0'; + } + Reset(); + DummyRuleData[6] = 1; + DummyRuleData[7] = 0; + DummyRuleData[8] = 1; + DummyRuleData[9] = 0; + state_ = State::StartLocalTest; + maxlag_ = 0; + matching_.set_battle_code("0123456"); + matching_.set_peer_id(me); + matching_.set_session_id(12345); + matching_.set_timeout_min_ms(1000); + matching_.set_timeout_max_ms(10000); + matching_.set_player_count(n); + for (int i = 0; i < n; i++) { + proto::PlayerAddress player{}; + player.set_ip("127.0.0.1"); + player.set_port(20010 + i); + player.set_user_id(std::to_string(i)); + player.set_peer_id(i); + player.set_team(i / 2 + 1); + matching_.mutable_candidates()->Add(std::move(player)); + } + Prepare(matching_, 20010 + me); + NOTICE_LOG(COMMON, "RollbackNet StartLocalTest %d", me); + return true; } -void GdxsvBackendRollback::Prepare(const proto::P2PMatching &matching, int port) { - matching_ = matching; - port_ = port; - - ping_pong_.Reset(); - for (const auto &c : matching.candidates()) { - if (c.peer_id() != matching_.peer_id()) { - ping_pong_.AddCandidate(c.user_id(), c.peer_id(), c.ip(), c.port()); - } - } - ping_pong_.Start(matching.session_id(), matching.peer_id(), port, matching.timeout_min_ms(), - matching.timeout_max_ms()); +void GdxsvBackendRollback::Prepare(const proto::P2PMatching& matching, int port) { + matching_ = matching; + port_ = port; + + ping_pong_.Reset(); + for (const auto& c : matching.candidates()) { + if (c.peer_id() != matching_.peer_id()) { + ping_pong_.AddCandidate(c.user_id(), c.peer_id(), c.ip(), c.port()); + } + } + ping_pong_.Start(matching.session_id(), matching.peer_id(), port, matching.timeout_min_ms(), matching.timeout_max_ms()); } void GdxsvBackendRollback::Open() { - NOTICE_LOG(COMMON, "GdxsvBackendRollback.Open"); - recv_buf_.assign({0x0e, 0x61, 0x00, 0x22, 0x10, 0x31, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd}); - state_ = State::McsSessionExchange; - maxlag_ = 0; - ApplyPatch(true); + NOTICE_LOG(COMMON, "GdxsvBackendRollback.Open"); + recv_buf_.assign({0x0e, 0x61, 0x00, 0x22, 0x10, 0x31, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd}); + state_ = State::McsSessionExchange; + maxlag_ = 0; + ApplyPatch(true); } void GdxsvBackendRollback::Close() { - ggpo::stopSession(); - RestorePatch(); - state_ = State::End; + ggpo::stopSession(); + RestorePatch(); + state_ = State::End; } u32 GdxsvBackendRollback::OnSockWrite(u32 addr, u32 size) { - if (state_ <= State::LbsStartBattleFlow) { - u8 buf[InetBufSize]; - for (int i = 0; i < size; ++i) { - buf[i] = gdxsv_ReadMem8(addr + i); - } - - lbs_tx_reader_.Write((const char *)buf, size); - ProcessLbsMessage(); - } - - ApplyPatch(false); - return size; + if (state_ <= State::LbsStartBattleFlow) { + u8 buf[InetBufSize]; + for (int i = 0; i < size; ++i) { + buf[i] = gdxsv_ReadMem8(addr + i); + } + + lbs_tx_reader_.Write((const char*)buf, size); + ProcessLbsMessage(); + } + + ApplyPatch(false); + return size; } u32 GdxsvBackendRollback::OnSockRead(u32 addr, u32 size) { - if (state_ <= State::LbsStartBattleFlow) { - ProcessLbsMessage(); - } else { - int frame = 0; - ggpo::getCurrentFrame(&frame); - const int COM_R_No0 = 0x0c391d79; // TODO:disk2 - const int InetBuf = 0x0c3ab984; // TODO: disk2 - const int ConnectionStatus = 0x0c3abb84; // TODO: disk2 - // NOTICE_LOG(COMMON, "[FRAME:%4d :RBK=%d] State=%d OnSockRead CONNECTION: %d %d", frame, ggpo::rollbacking(), state_, gdxsv_ReadMem16(ConnectionStatus), gdxsv_ReadMem16(ConnectionStatus + 4)); - - // Notify disconnect in game part if other player is disconnect on ggpo - if (gdxsv_ReadMem8(COM_R_No0) == 4 && gdxsv_ReadMem8(COM_R_No0 + 5) == 0 && gdxsv_ReadMem16(ConnectionStatus + 4) < 10) { - for (int i = 0; i < matching_.player_count(); ++i) { - if (!ggpo::isConnected(i)) { - gdxsv_WriteMem16(ConnectionStatus + 4, 0x0a); - ggpo::setExInput(ExInputNone); - break; - } - } - } - - auto inputState = mapleInputState; - auto memExInputAddr = symbols_.at("print_buf"); // TODO - - int msg_len = gdxsv_ReadMem8(InetBuf); - if (0 < msg_len) { - if (msg_len == 0x82) { - msg_len = 20; - } - McsMessage msg; - msg.body.resize(msg_len); - for (int i = 0; i < msg_len; i++) { - msg.body[i] = gdxsv_ReadMem8(InetBuf + i); - gdxsv_WriteMem8(InetBuf + i, 0); - } - - if (msg.Type() == McsMessage::ConnectionIdMsg) { + if (state_ <= State::LbsStartBattleFlow) { + ProcessLbsMessage(); + } else { + int frame = 0; + ggpo::getCurrentFrame(&frame); + const int COM_R_No0 = 0x0c391d79; // TODO:disk2 + const int InetBuf = 0x0c3ab984; // TODO: disk2 + const int ConnectionStatus = 0x0c3abb84; // TODO: disk2 + // NOTICE_LOG(COMMON, "[FRAME:%4d :RBK=%d] State=%d OnSockRead CONNECTION: %d %d", frame, ggpo::rollbacking(), state_, + // gdxsv_ReadMem16(ConnectionStatus), gdxsv_ReadMem16(ConnectionStatus + 4)); + + // Notify disconnect in game part if other player is disconnect on ggpo + if (gdxsv_ReadMem8(COM_R_No0) == 4 && gdxsv_ReadMem8(COM_R_No0 + 5) == 0 && gdxsv_ReadMem16(ConnectionStatus + 4) < 10) { + for (int i = 0; i < matching_.player_count(); ++i) { + if (!ggpo::isConnected(i)) { + gdxsv_WriteMem16(ConnectionStatus + 4, 0x0a); + ggpo::setExInput(ExInputNone); + break; + } + } + } + + auto inputState = mapleInputState; + auto memExInputAddr = symbols_.at("print_buf"); // TODO + + int msg_len = gdxsv_ReadMem8(InetBuf); + if (0 < msg_len) { + if (msg_len == 0x82) { + msg_len = 20; + } + McsMessage msg; + msg.body.resize(msg_len); + for (int i = 0; i < msg_len; i++) { + msg.body[i] = gdxsv_ReadMem8(InetBuf + i); + gdxsv_WriteMem8(InetBuf + i, 0); + } + + if (msg.Type() == McsMessage::ConnectionIdMsg) { state_ = State::StopEmulator; - } + } if (msg.Type() == McsMessage::IntroMsg) { for (int i = 0; i < matching_.player_count(); i++) { - if (i == matching_.peer_id()) continue; + if (i == matching_.peer_id()) continue; auto a = McsMessage::Create(McsMessage::IntroMsg, i); std::copy(a.body.begin(), a.body.end(), std::back_inserter(recv_buf_)); } @@ -319,31 +313,31 @@ u32 GdxsvBackendRollback::OnSockRead(u32 addr, u32 size) { if (msg.Type() == McsMessage::IntroMsgReturn) { for (int i = 0; i < matching_.player_count(); i++) { - if (i == matching_.peer_id()) continue; + if (i == matching_.peer_id()) continue; auto a = McsMessage::Create(McsMessage::IntroMsgReturn, i); std::copy(a.body.begin(), a.body.end(), std::back_inserter(recv_buf_)); } } - if (msg.Type() == McsMessage::PingMsg) { + if (msg.Type() == McsMessage::PingMsg) { for (int i = 0; i < matching_.player_count(); i++) { - if (i == matching_.peer_id()) continue; + if (i == matching_.peer_id()) continue; auto a = McsMessage::Create(McsMessage::PongMsg, i); a.SetPongTo(matching_.peer_id()); a.PongCount(msg.PingCount()); std::copy(a.body.begin(), a.body.end(), std::back_inserter(recv_buf_)); } - } + } - if (msg.Type() == McsMessage::StartMsg) { + if (msg.Type() == McsMessage::StartMsg) { gdxsv_WriteMem16(memExInputAddr, ExInputWaitStart); if (!ggpo::rollbacking()) { ggpo::setExInput(ExInputWaitStart); NOTICE_LOG(COMMON, "StartMsg KeyFrame:%d", frame); } - } + } - if (msg.Type() == McsMessage::KeyMsg1) { + if (msg.Type() == McsMessage::KeyMsg1) { for (int i = 0; i < matching_.player_count(); ++i) { auto a = McsMessage::Create(McsMessage::KeyMsg1, i); auto input = convertInput(inputState[i]); @@ -351,11 +345,11 @@ u32 GdxsvBackendRollback::OnSockRead(u32 addr, u32 size) { a.body[3] = input & 0xff; std::copy(a.body.begin(), a.body.end(), std::back_inserter(recv_buf_)); } - } + } - if (msg.Type() == McsMessage::LoadEndMsg) { + if (msg.Type() == McsMessage::LoadEndMsg) { for (int i = 0; i < matching_.player_count(); i++) { - if (i == matching_.peer_id()) continue; + if (i == matching_.peer_id()) continue; auto a = McsMessage::Create(McsMessage::LoadStartMsg, i); std::copy(a.body.begin(), a.body.end(), std::back_inserter(recv_buf_)); } @@ -365,152 +359,161 @@ u32 GdxsvBackendRollback::OnSockRead(u32 addr, u32 size) { ggpo::setExInput(ExInputWaitLoadEnd); NOTICE_LOG(COMMON, "LoadEndMsg KeyFrame:%d", frame); } - } - - verify(recv_buf_.size() <= size); - } - - if (gdxsv_ReadMem16(memExInputAddr) != ExInputNone) { - auto exInput = gdxsv_ReadMem16(memExInputAddr); - bool ok = true; - for (int i = 0; i < matching_.player_count(); i++) { - ok &= inputState[i].exInput == exInput; - } - - if (ok && exInput == ExInputWaitStart) { - NOTICE_LOG(COMMON, "StartMsg Join:%d", frame); - gdxsv_WriteMem16(memExInputAddr, ExInputNone); - if (!ggpo::rollbacking()) { - ggpo::setExInput(ExInputNone); - } - for (int i = 0; i < matching_.player_count(); i++) { - if (i == matching_.peer_id()) continue; + } + + verify(recv_buf_.size() <= size); + } + + if (gdxsv_ReadMem16(memExInputAddr) != ExInputNone) { + auto exInput = gdxsv_ReadMem16(memExInputAddr); + bool ok = true; + for (int i = 0; i < matching_.player_count(); i++) { + ok &= inputState[i].exInput == exInput; + } + + if (ok && exInput == ExInputWaitStart) { + NOTICE_LOG(COMMON, "StartMsg Join:%d", frame); + gdxsv_WriteMem16(memExInputAddr, ExInputNone); + if (!ggpo::rollbacking()) { + ggpo::setExInput(ExInputNone); + } + for (int i = 0; i < matching_.player_count(); i++) { + if (i == matching_.peer_id()) continue; auto a = McsMessage::Create(McsMessage::MsgType::StartMsg, i); std::copy(a.body.begin(), a.body.end(), std::back_inserter(recv_buf_)); - } - } - - if (ok && exInput == ExInputWaitLoadEnd) { - NOTICE_LOG(COMMON, "LoadEndMsg Join:%d", frame); - gdxsv_WriteMem16(memExInputAddr, ExInputNone); - if (!ggpo::rollbacking()) { - ggpo::setExInput(ExInputNone); - } - for (int i = 0; i < matching_.player_count(); i++) { - if (i == matching_.peer_id()) continue; + } + } + + if (ok && exInput == ExInputWaitLoadEnd) { + NOTICE_LOG(COMMON, "LoadEndMsg Join:%d", frame); + gdxsv_WriteMem16(memExInputAddr, ExInputNone); + if (!ggpo::rollbacking()) { + ggpo::setExInput(ExInputNone); + } + for (int i = 0; i < matching_.player_count(); i++) { + if (i == matching_.peer_id()) continue; auto a = McsMessage::Create(McsMessage::MsgType::LoadEndMsg, i); std::copy(a.body.begin(), a.body.end(), std::back_inserter(recv_buf_)); - } - } - } - verify(recv_buf_.size() <= size); - } - - if (recv_buf_.empty()) { - return 0; - } - - int n = std::min(recv_buf_.size(), size); - for (int i = 0; i < n; ++i) { - gdxsv_WriteMem8(addr + i, recv_buf_.front()); - recv_buf_.pop_front(); - } - return n; + } + } + } + verify(recv_buf_.size() <= size); + } + + if (recv_buf_.empty()) { + return 0; + } + + int n = std::min(recv_buf_.size(), size); + for (int i = 0; i < n; ++i) { + gdxsv_WriteMem8(addr + i, recv_buf_.front()); + recv_buf_.pop_front(); + } + return n; } u32 GdxsvBackendRollback::OnSockPoll() { - if (state_ <= State::LbsStartBattleFlow) { - ProcessLbsMessage(); - } - if (0 < recv_delay_) { - recv_delay_--; - return 0; - } - - return recv_buf_.size(); + if (state_ <= State::LbsStartBattleFlow) { + ProcessLbsMessage(); + } + if (0 < recv_delay_) { + recv_delay_--; + return 0; + } + + return recv_buf_.size(); } void GdxsvBackendRollback::ProcessLbsMessage() { - if (state_ == State::StartLocalTest) { - LbsMessage::SvNotice(LbsMessage::lbsReadyBattle).Serialize(recv_buf_); - recv_delay_ = 1; - state_ = State::LbsStartBattleFlow; - } - - LbsMessage msg; - if (lbs_tx_reader_.Read(msg)) { - if (state_ == State::StartLocalTest) { - state_ = State::LbsStartBattleFlow; - } - - if (msg.command == LbsMessage::lbsLobbyMatchingEntry) { - LbsMessage::SvAnswer(msg).Serialize(recv_buf_); - LbsMessage::SvNotice(LbsMessage::lbsReadyBattle).Serialize(recv_buf_); - } - - if (msg.command == LbsMessage::lbsAskMatchingJoin) { - LbsMessage::SvAnswer(msg).Write8(matching_.player_count())->Serialize(recv_buf_); - } - - if (msg.command == LbsMessage::lbsAskPlayerSide) { - LbsMessage::SvAnswer(msg).Write8(matching_.peer_id() + 1)->Serialize(recv_buf_); - } - - if (msg.command == LbsMessage::lbsAskPlayerInfo) { - int pos = msg.Read8(); - DummyGameParam[16] = '0' + pos; - DummyGameParam[17] = 0; - LbsMessage::SvAnswer(msg) - .Write8(pos)->WriteString("USER0" + std::to_string(pos))->WriteString("USER0" + std::to_string(pos)) - ->WriteBytes(reinterpret_cast(DummyGameParam), sizeof(DummyGameParam)) - ->Write16(1)->Write16(0)->Write16(0)->Write16(0)->Write16(0)->Write16(0) - ->Write16(1 + (pos - 1) / 2)->Write16(0) - ->Serialize(recv_buf_); - } - - if (msg.command == LbsMessage::lbsAskRuleData) { - LbsMessage::SvAnswer(msg).WriteBytes((char *)DummyRuleData, sizeof(DummyRuleData))->Serialize(recv_buf_); - } - - if (msg.command == LbsMessage::lbsAskBattleCode) { - LbsMessage::SvAnswer(msg).WriteString("012345")->Serialize(recv_buf_); - } - - if (msg.command == LbsMessage::lbsAskMcsVersion) { - LbsMessage::SvAnswer(msg).Write8(10)->Serialize(recv_buf_); - } - - if (msg.command == LbsMessage::lbsAskMcsAddress) { - LbsMessage::SvAnswer(msg).Write16(4)->Write8(255)->Write8(255)->Write8(255)->Write8(255)->Write16(2)->Write16(255)->Serialize(recv_buf_); - } - - if (msg.command == LbsMessage::lbsLogout) { - state_ = State::McsWaitJoin; - } - - recv_delay_ = 1; - } + if (state_ == State::StartLocalTest) { + LbsMessage::SvNotice(LbsMessage::lbsReadyBattle).Serialize(recv_buf_); + recv_delay_ = 1; + state_ = State::LbsStartBattleFlow; + } + + LbsMessage msg; + if (lbs_tx_reader_.Read(msg)) { + if (state_ == State::StartLocalTest) { + state_ = State::LbsStartBattleFlow; + } + + if (msg.command == LbsMessage::lbsLobbyMatchingEntry) { + LbsMessage::SvAnswer(msg).Serialize(recv_buf_); + LbsMessage::SvNotice(LbsMessage::lbsReadyBattle).Serialize(recv_buf_); + } + + if (msg.command == LbsMessage::lbsAskMatchingJoin) { + LbsMessage::SvAnswer(msg).Write8(matching_.player_count())->Serialize(recv_buf_); + } + + if (msg.command == LbsMessage::lbsAskPlayerSide) { + LbsMessage::SvAnswer(msg).Write8(matching_.peer_id() + 1)->Serialize(recv_buf_); + } + + if (msg.command == LbsMessage::lbsAskPlayerInfo) { + int pos = msg.Read8(); + DummyGameParam[16] = '0' + pos; + DummyGameParam[17] = 0; + LbsMessage::SvAnswer(msg) + .Write8(pos) + ->WriteString("USER0" + std::to_string(pos)) + ->WriteString("USER0" + std::to_string(pos)) + ->WriteBytes(reinterpret_cast(DummyGameParam), sizeof(DummyGameParam)) + ->Write16(1) + ->Write16(0) + ->Write16(0) + ->Write16(0) + ->Write16(0) + ->Write16(0) + ->Write16(1 + (pos - 1) / 2) + ->Write16(0) + ->Serialize(recv_buf_); + } + + if (msg.command == LbsMessage::lbsAskRuleData) { + LbsMessage::SvAnswer(msg).WriteBytes((char*)DummyRuleData, sizeof(DummyRuleData))->Serialize(recv_buf_); + } + + if (msg.command == LbsMessage::lbsAskBattleCode) { + LbsMessage::SvAnswer(msg).WriteString("012345")->Serialize(recv_buf_); + } + + if (msg.command == LbsMessage::lbsAskMcsVersion) { + LbsMessage::SvAnswer(msg).Write8(10)->Serialize(recv_buf_); + } + + if (msg.command == LbsMessage::lbsAskMcsAddress) { + LbsMessage::SvAnswer(msg).Write16(4)->Write8(255)->Write8(255)->Write8(255)->Write8(255)->Write16(2)->Write16(255)->Serialize( + recv_buf_); + } + + if (msg.command == LbsMessage::lbsLogout) { + state_ = State::McsWaitJoin; + } + + recv_delay_ = 1; + } } void GdxsvBackendRollback::ApplyPatch(bool first_time) { - if (state_ == State::None || state_ == State::End) { - return; - } + if (state_ == State::None || state_ == State::End) { + return; + } - gdxsv.WritePatch(); + gdxsv.WritePatch(); - if (gdxsv.Disk() == 2) { - // Skip Key MsgPush - gdxsv_WriteMem16(0x8c045f64, 9); - gdxsv_WriteMem8(0x0c3abb90, 1); - } + if (gdxsv.Disk() == 2) { + // Skip Key MsgPush + gdxsv_WriteMem16(0x8c045f64, 9); + gdxsv_WriteMem8(0x0c3abb90, 1); + } } void GdxsvBackendRollback::RestorePatch() { - if (gdxsv.Disk() == 2) { - gdxsv_WriteMem16(0x8c045f64, 0x410b); - gdxsv_WriteMem8(0x0c3abb90, 2); - } + if (gdxsv.Disk() == 2) { + gdxsv_WriteMem16(0x8c045f64, 0x410b); + gdxsv_WriteMem8(0x0c3abb90, 2); + } } namespace { @@ -566,9 +569,7 @@ ImColor barStep(int ms) { return 1; } -ImColor barColor(int ms, int elapsed) { - return fadeColor(barColor(ms), elapsed); -} +ImColor barColor(int ms, int elapsed) { return fadeColor(barColor(ms), elapsed); } void drawDot(ImDrawList* draw_list, ImVec2 center, ImColor c, float scale) { draw_list->AddCircleFilled(center, 6.5 * scale, ImColor(0, 0, 0, 128), 20); @@ -623,7 +624,8 @@ void drawRectWave(ImDrawList* draw_list, ImVec2 anchor, ImColor color, float sca for (int i = 0; i < 5; i++) { baseRect(points, 5, 3.5); auto c = color; - if (step <= i) c = ImColor(64, 64, 64); + if (step <= i) + c = ImColor(64, 64, 64); else if (i == (elapsed / 100 % 5)) { c.Value.x *= 2; c.Value.y *= 2; @@ -646,13 +648,12 @@ void drawConnectionDiagram(int elapsed, const uint8_t matrix[4][4], const std::m float scale = getScale(); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0); - ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x / 2.f, ImGui::GetIO().DisplaySize.y / 2.f), - ImGuiCond_Always, ImVec2(0.5f, 0.5f)); + ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x / 2.f, ImGui::GetIO().DisplaySize.y / 2.f), ImGuiCond_Always, + ImVec2(0.5f, 0.5f)); ImGui::SetNextWindowSize(ImVec2(W, H)); ImGui::SetNextWindowBgAlpha(0.0f); ImGui::Begin("##gdxsvosd", NULL, - ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | - ImGuiWindowFlags_NoInputs); + ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs); ImDrawList* draw_list = ImGui::GetWindowDrawList(); @@ -695,4 +696,4 @@ void drawConnectionDiagram(int elapsed, const uint8_t matrix[4][4], const std::m ImGui::PopStyleVar(); ImGui::PopStyleVar(); } -} +} // namespace diff --git a/core/gdxsv/gdxsv_backend_rollback.h b/core/gdxsv/gdxsv_backend_rollback.h index 521658087a..9127ec1b2a 100644 --- a/core/gdxsv/gdxsv_backend_rollback.h +++ b/core/gdxsv/gdxsv_backend_rollback.h @@ -9,49 +9,49 @@ class GdxsvBackendRollback { public: - GdxsvBackendRollback(const std::map &symbols, std::atomic &maxlag) - : state_(State::None), symbols_(symbols), maxlag_(maxlag), recv_delay_(0), port_(0) {} + GdxsvBackendRollback(const std::map &symbols, std::atomic &maxlag) + : state_(State::None), symbols_(symbols), maxlag_(maxlag), recv_delay_(0), port_(0) {} - enum class State { - None, - StartLocalTest, - LbsStartBattleFlow, - McsWaitJoin, + enum class State { + None, + StartLocalTest, + LbsStartBattleFlow, + McsWaitJoin, - McsSessionExchange, - StopEmulator, - WaitPingPong, - StartGGPOSession, - WaitGGPOSession, - McsInBattle, - CloseWait, - End, - }; + McsSessionExchange, + StopEmulator, + WaitPingPong, + StartGGPOSession, + WaitGGPOSession, + McsInBattle, + CloseWait, + End, + }; - void DisplayOSD(); - void Reset(); - void OnMainUiLoop(); - bool StartLocalTest(const char *param); - void Prepare(const proto::P2PMatching &matching, int port); - void Open(); - void Close(); - u32 OnSockWrite(u32 addr, u32 size); - u32 OnSockRead(u32 addr, u32 size); - u32 OnSockPoll(); + void DisplayOSD(); + void Reset(); + void OnMainUiLoop(); + bool StartLocalTest(const char *param); + void Prepare(const proto::P2PMatching &matching, int port); + void Open(); + void Close(); + u32 OnSockWrite(u32 addr, u32 size); + u32 OnSockRead(u32 addr, u32 size); + u32 OnSockPoll(); private: - void ApplyPatch(bool first_time); - void RestorePatch(); - void ProcessLbsMessage(); + void ApplyPatch(bool first_time); + void RestorePatch(); + void ProcessLbsMessage(); - State state_; - const std::map &symbols_; - std::atomic &maxlag_; - int recv_delay_; - int port_; - std::deque recv_buf_; - LbsMessageReader lbs_tx_reader_; - proto::P2PMatching matching_; - UdpPingPong ping_pong_; - std::future start_network_; + State state_; + const std::map &symbols_; + std::atomic &maxlag_; + int recv_delay_; + int port_; + std::deque recv_buf_; + LbsMessageReader lbs_tx_reader_; + proto::P2PMatching matching_; + UdpPingPong ping_pong_; + std::future start_network_; }; diff --git a/core/gdxsv/gdxsv_backend_tcp.h b/core/gdxsv/gdxsv_backend_tcp.h index 1d80d055ab..cb5a4171be 100644 --- a/core/gdxsv/gdxsv_backend_tcp.h +++ b/core/gdxsv/gdxsv_backend_tcp.h @@ -6,99 +6,82 @@ #include #include -#include "libs.h" #include "gdx_rpc.h" #include "gdxsv_network.h" #include "lbs_message.h" +#include "libs.h" class GdxsvBackendTcp { -public: - GdxsvBackendTcp(const std::map &symbols) : symbols_(symbols) { - } - - void Reset() { - tcp_client_.Close(); - lbs_msg_reader_.Clear(); - callback_lbs_packet_ = nullptr; - } - - bool Connect(const std::string &host, u16 port) { - bool ok = tcp_client_.Connect(host.c_str(), port); - if (!ok) { - WARN_LOG(COMMON, "Failed to connect with TCP %s:%d\n", host.c_str(), port); - return false; - } - - tcp_client_.SetNonBlocking(); - return true; - } - - bool IsConnected() const { - return tcp_client_.IsConnected(); - } - - void Close() { - tcp_client_.Close(); - } - - int Send(const std::vector &packet) { - return tcp_client_.Send((const char *) packet.data(), packet.size()); - } - - const std::string &LocalIP() const { - return tcp_client_.local_ip(); - } - - const std::string &RemoteHost() const { - return tcp_client_.host(); - } - - const int RemotePort() const { - return tcp_client_.port(); - } - - u32 OnSockRead(u32 addr, u32 size) { - u8 buf[InetBufSize]; - u32 n = std::min(tcp_client_.ReadableSize(), size); - if (n <= 0) { - return 0; - } - n = tcp_client_.Recv((char *) buf, n); - if (0 < n) { - for (int i = 0; i < n; ++i) { - gdxsv_WriteMem8(addr + i, buf[i]); - } - if (callback_lbs_packet_) { - lbs_msg_reader_.Write((char *) buf, n); - while (lbs_msg_reader_.Read(lbs_msg_)) { - callback_lbs_packet_(lbs_msg_); - } - } - } - return n; - } - - u32 OnSockWrite(u32 addr, u32 size) { - u8 buf[InetBufSize]; - u32 n = std::min(InetBufSize, size); - for (int i = 0; i < n; ++i) { - buf[i] = gdxsv_ReadMem8(addr + i); - } - return tcp_client_.Send((char *) buf, n); - } - - u32 OnSockPoll() { - return tcp_client_.ReadableSize(); - } - - void callback_lbs_packet(std::function callback) { - callback_lbs_packet_ = std::move(callback); - } - -private: - const std::map &symbols_; - TcpClient tcp_client_; - LbsMessage lbs_msg_; - LbsMessageReader lbs_msg_reader_; - std::function callback_lbs_packet_; + public: + GdxsvBackendTcp(const std::map &symbols) : symbols_(symbols) {} + + void Reset() { + tcp_client_.Close(); + lbs_msg_reader_.Clear(); + callback_lbs_packet_ = nullptr; + } + + bool Connect(const std::string &host, u16 port) { + bool ok = tcp_client_.Connect(host.c_str(), port); + if (!ok) { + WARN_LOG(COMMON, "Failed to connect with TCP %s:%d\n", host.c_str(), port); + return false; + } + + tcp_client_.SetNonBlocking(); + return true; + } + + bool IsConnected() const { return tcp_client_.IsConnected(); } + + void Close() { tcp_client_.Close(); } + + int Send(const std::vector &packet) { return tcp_client_.Send((const char *)packet.data(), packet.size()); } + + const std::string &LocalIP() const { return tcp_client_.local_ip(); } + + const std::string &RemoteHost() const { return tcp_client_.host(); } + + const int RemotePort() const { return tcp_client_.port(); } + + u32 OnSockRead(u32 addr, u32 size) { + u8 buf[InetBufSize]; + u32 n = std::min(tcp_client_.ReadableSize(), size); + if (n <= 0) { + return 0; + } + n = tcp_client_.Recv((char *)buf, n); + if (0 < n) { + for (int i = 0; i < n; ++i) { + gdxsv_WriteMem8(addr + i, buf[i]); + } + if (callback_lbs_packet_) { + lbs_msg_reader_.Write((char *)buf, n); + while (lbs_msg_reader_.Read(lbs_msg_)) { + callback_lbs_packet_(lbs_msg_); + } + } + } + return n; + } + + u32 OnSockWrite(u32 addr, u32 size) { + u8 buf[InetBufSize]; + u32 n = std::min(InetBufSize, size); + for (int i = 0; i < n; ++i) { + buf[i] = gdxsv_ReadMem8(addr + i); + } + return tcp_client_.Send((char *)buf, n); + } + + u32 OnSockPoll() { return tcp_client_.ReadableSize(); } + + void callback_lbs_packet(std::function callback) { callback_lbs_packet_ = std::move(callback); } + + private: + const std::map &symbols_; + TcpClient tcp_client_; + LbsMessage lbs_msg_; + LbsMessageReader lbs_msg_reader_; + std::function callback_lbs_packet_; }; \ No newline at end of file diff --git a/core/gdxsv/gdxsv_backend_udp.h b/core/gdxsv/gdxsv_backend_udp.h index 4965d1f7ae..c0c7f532ad 100644 --- a/core/gdxsv/gdxsv_backend_udp.h +++ b/core/gdxsv/gdxsv_backend_udp.h @@ -3,9 +3,9 @@ #pragma once #include -#include -#include #include +#include +#include #ifdef DC_PLATFORM_DREAMCAST @@ -13,311 +13,307 @@ #endif -#include "gdxsv_network.h" #include "gdx_rpc.h" +#include "gdxsv_network.h" class GdxsvBackendUdp { -public: - GdxsvBackendUdp(const std::map &symbols, std::atomic &maxlag) - : symbols_(symbols), maxlag_(maxlag) { - } - - ~GdxsvBackendUdp() { - CloseMcsRemoteWithReason("cl_hard_quit"); - net_terminate_ = true; - } - - void Reset() { - CloseMcsRemoteWithReason("cl_hard_reset"); - session_id_.clear(); - net_terminate_ = true; - } - - bool Connect(const std::string &host, u16 port) { - if (!udp_client_.Initialized()) { - bool ok = udp_client_.Bind(0); - if (!ok) { - WARN_LOG(COMMON, "Failed to Initialize Udp %s:%d", host.c_str(), port); - return false; - } - } - - CloseMcsRemoteWithReason("connect"); - bool ok = mcs_remote_.Open(host.c_str(), port); - if (!ok) { - WARN_LOG(COMMON, "Failed to open Udp %s:%d", host.c_str(), port); - return false; - } - - net_terminate_ = false; - std::thread([this]() { NetThreadLoop(); }).detach(); - return true; - } - - bool IsConnected() const { - return mcs_remote_.is_open(); - } - - void CloseMcsRemoteWithReason(const char *reason) { - if (udp_client_.Initialized() && mcs_remote_.is_open()) { - proto::Packet pkt; - pkt.Clear(); - pkt.set_type(proto::MessageType::Fin); - pkt.set_session_id(session_id_.c_str(), session_id_.size()); - pkt.mutable_fin_data()->set_detail(reason); - - char buf[1024]; - if (pkt.SerializePartialToArray((void *) buf, (int) sizeof(buf))) { - udp_client_.SendTo((const char *) buf, pkt.GetCachedSize(), mcs_remote_); - } else { - ERROR_LOG(COMMON, "packet serialize error"); - } - - mcs_remote_.Close(); - } - net_terminate_ = true; - } - - u32 OnSockRead(u32 addr, u32 size) { - std::lock_guard lock(recv_buf_mtx_); - u32 n = std::min(recv_buf_.size(), size); - for (int i = 0; i < n; ++i) { - gdxsv_WriteMem8(addr + i, recv_buf_.front()); - recv_buf_.pop_front(); - } - return n; - } - - u32 OnSockWrite(u32 addr, u32 size) { - std::lock_guard lock(send_buf_mtx_); - for (int i = 0; i < size; ++i) { - send_buf_.push_back(gdxsv_ReadMem8(addr + i)); - } - return size; - } - - u32 OnSockPoll() { - std::lock_guard lock(recv_buf_mtx_); - return recv_buf_.size(); - } - -private: - void NetThreadLoop() { - ClearBuffers(); - - const int kFirstMessageSize = 20; - int net_loop_count = 0; - int ping_send_count = 0; - int ping_recv_count = 0; - int rtt_sum = 0; - int udp_retransmit_countdown = 0; - std::string sender; - std::string user_id; - std::string session_id; - u8 buf[16 * 1024]; - proto::Packet pkt; - MessageBuffer msg_buf; - MessageFilter msg_filter; - - enum class State { - Start, - McsSessionExchange, - McsPingTest, - McsInBattle, - End, - }; - State state = State::Start; - - while (!net_terminate_) { - net_loop_count++; - - if (state == State::Start) { - ClearBuffers(); - recv_buf_mtx_.lock(); - recv_buf_.assign({0x0e, 0x61, 0x00, 0x22, 0x10, 0x31, 0x66, - 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd}); - recv_buf_mtx_.unlock(); - session_id_.clear(); - msg_buf.Clear(); - state = State::McsSessionExchange; - } - - if (state == State::McsSessionExchange) { - if (session_id.empty()) { - send_buf_mtx_.lock(); - if (kFirstMessageSize <= send_buf_.size()) { - for (int j = 12; j < kFirstMessageSize; ++j) { - session_id.push_back((char) send_buf_[j]); - } - NOTICE_LOG(COMMON, "session_id:%s", session_id.c_str()); - session_id_ = session_id; - msg_buf.SessionId(session_id); - send_buf_.clear(); - } - send_buf_mtx_.unlock(); - } else if (user_id.empty()) { - if (net_loop_count % 100 == 0) { - pkt.Clear(); - pkt.set_type(proto::MessageType::HelloServer); - pkt.set_session_id(session_id); - if (pkt.SerializeToArray((void *) buf, (int) sizeof(buf))) { - udp_client_.SendTo((const char *) buf, pkt.GetCachedSize(), mcs_remote_); - } else { - ERROR_LOG(COMMON, "packet serialize error"); - CloseMcsRemoteWithReason("cl_error"); - state = State::End; - } - } - } else { - state = State::McsPingTest; - } - } - - if (state == State::McsPingTest) { - if (ping_recv_count < 10) { - if (net_loop_count % 100 == 0 || ping_send_count == ping_recv_count) { - pkt.Clear(); - pkt.set_type(proto::MessageType::Ping); - pkt.set_session_id(session_id_.c_str(), session_id_.size()); - pkt.mutable_ping_data()->set_user_id(user_id); - pkt.mutable_ping_data()->set_timestamp(std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()).count()); - ping_send_count++; - if (pkt.SerializeToArray((void *) buf, (int) sizeof(buf))) { - udp_client_.SendTo((const char *) buf, pkt.GetCachedSize(), mcs_remote_); - } else { - ERROR_LOG(COMMON, "packet serialize error"); - CloseMcsRemoteWithReason("cl_error"); - state = State::End; - } - } - } else { - auto rtt = float(rtt_sum) / ping_recv_count; - NOTICE_LOG(COMMON, "PING AVG %.2f ms", rtt); - maxlag_ = std::min(0x7f, std::max(5, 4 + (int) std::floor(rtt / 16))); - NOTICE_LOG(COMMON, "set maxlag %d", (int) maxlag_); + public: + GdxsvBackendUdp(const std::map &symbols, std::atomic &maxlag) : symbols_(symbols), maxlag_(maxlag) {} + + ~GdxsvBackendUdp() { + CloseMcsRemoteWithReason("cl_hard_quit"); + net_terminate_ = true; + } + + void Reset() { + CloseMcsRemoteWithReason("cl_hard_reset"); + session_id_.clear(); + net_terminate_ = true; + } + + bool Connect(const std::string &host, u16 port) { + if (!udp_client_.Initialized()) { + bool ok = udp_client_.Bind(0); + if (!ok) { + WARN_LOG(COMMON, "Failed to Initialize Udp %s:%d", host.c_str(), port); + return false; + } + } + + CloseMcsRemoteWithReason("connect"); + bool ok = mcs_remote_.Open(host.c_str(), port); + if (!ok) { + WARN_LOG(COMMON, "Failed to open Udp %s:%d", host.c_str(), port); + return false; + } + + net_terminate_ = false; + std::thread([this]() { NetThreadLoop(); }).detach(); + return true; + } + + bool IsConnected() const { return mcs_remote_.is_open(); } + + void CloseMcsRemoteWithReason(const char *reason) { + if (udp_client_.Initialized() && mcs_remote_.is_open()) { + proto::Packet pkt; + pkt.Clear(); + pkt.set_type(proto::MessageType::Fin); + pkt.set_session_id(session_id_.c_str(), session_id_.size()); + pkt.mutable_fin_data()->set_detail(reason); + + char buf[1024]; + if (pkt.SerializePartialToArray((void *)buf, (int)sizeof(buf))) { + udp_client_.SendTo((const char *)buf, pkt.GetCachedSize(), mcs_remote_); + } else { + ERROR_LOG(COMMON, "packet serialize error"); + } + + mcs_remote_.Close(); + } + net_terminate_ = true; + } + + u32 OnSockRead(u32 addr, u32 size) { + std::lock_guard lock(recv_buf_mtx_); + u32 n = std::min(recv_buf_.size(), size); + for (int i = 0; i < n; ++i) { + gdxsv_WriteMem8(addr + i, recv_buf_.front()); + recv_buf_.pop_front(); + } + return n; + } + + u32 OnSockWrite(u32 addr, u32 size) { + std::lock_guard lock(send_buf_mtx_); + for (int i = 0; i < size; ++i) { + send_buf_.push_back(gdxsv_ReadMem8(addr + i)); + } + return size; + } + + u32 OnSockPoll() { + std::lock_guard lock(recv_buf_mtx_); + return recv_buf_.size(); + } + + private: + void NetThreadLoop() { + ClearBuffers(); + + const int kFirstMessageSize = 20; + int net_loop_count = 0; + int ping_send_count = 0; + int ping_recv_count = 0; + int rtt_sum = 0; + int udp_retransmit_countdown = 0; + std::string sender; + std::string user_id; + std::string session_id; + u8 buf[16 * 1024]; + proto::Packet pkt; + MessageBuffer msg_buf; + MessageFilter msg_filter; + + enum class State { + Start, + McsSessionExchange, + McsPingTest, + McsInBattle, + End, + }; + State state = State::Start; + + while (!net_terminate_) { + net_loop_count++; + + if (state == State::Start) { + ClearBuffers(); + recv_buf_mtx_.lock(); + recv_buf_.assign({0x0e, 0x61, 0x00, 0x22, 0x10, 0x31, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd}); + recv_buf_mtx_.unlock(); + session_id_.clear(); + msg_buf.Clear(); + state = State::McsSessionExchange; + } + + if (state == State::McsSessionExchange) { + if (session_id.empty()) { + send_buf_mtx_.lock(); + if (kFirstMessageSize <= send_buf_.size()) { + for (int j = 12; j < kFirstMessageSize; ++j) { + session_id.push_back((char)send_buf_[j]); + } + NOTICE_LOG(COMMON, "session_id:%s", session_id.c_str()); + session_id_ = session_id; + msg_buf.SessionId(session_id); + send_buf_.clear(); + } + send_buf_mtx_.unlock(); + } else if (user_id.empty()) { + if (net_loop_count % 100 == 0) { + pkt.Clear(); + pkt.set_type(proto::MessageType::HelloServer); + pkt.set_session_id(session_id); + if (pkt.SerializeToArray((void *)buf, (int)sizeof(buf))) { + udp_client_.SendTo((const char *)buf, pkt.GetCachedSize(), mcs_remote_); + } else { + ERROR_LOG(COMMON, "packet serialize error"); + CloseMcsRemoteWithReason("cl_error"); + state = State::End; + } + } + } else { + state = State::McsPingTest; + } + } + + if (state == State::McsPingTest) { + if (ping_recv_count < 10) { + if (net_loop_count % 100 == 0 || ping_send_count == ping_recv_count) { + pkt.Clear(); + pkt.set_type(proto::MessageType::Ping); + pkt.set_session_id(session_id_.c_str(), session_id_.size()); + pkt.mutable_ping_data()->set_user_id(user_id); + pkt.mutable_ping_data()->set_timestamp(std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count()); + ping_send_count++; + if (pkt.SerializeToArray((void *)buf, (int)sizeof(buf))) { + udp_client_.SendTo((const char *)buf, pkt.GetCachedSize(), mcs_remote_); + } else { + ERROR_LOG(COMMON, "packet serialize error"); + CloseMcsRemoteWithReason("cl_error"); + state = State::End; + } + } + } else { + auto rtt = float(rtt_sum) / ping_recv_count; + NOTICE_LOG(COMMON, "PING AVG %.2f ms", rtt); + maxlag_ = std::min(0x7f, std::max(5, 4 + (int)std::floor(rtt / 16))); + NOTICE_LOG(COMMON, "set maxlag %d", (int)maxlag_); #ifdef DC_PLATFORM_DREAMCAST - char osd_msg[128] = {}; - sprintf(osd_msg, "PING:%.0fms DELAY:%dfr", rtt, (int) maxlag_); - gui_display_notification(osd_msg, 3000); + char osd_msg[128] = {}; + sprintf(osd_msg, "PING:%.0fms DELAY:%dfr", rtt, (int)maxlag_); + gui_display_notification(osd_msg, 3000); #endif - state = State::McsInBattle; - } - } - - if (state == State::McsInBattle) { - std::lock_guard lock(send_buf_mtx_); - int n = send_buf_.size(); - if (0 < n || udp_retransmit_countdown-- == 0) { - if (0 < n && msg_buf.CanPush()) { - n = std::min(n, sizeof(buf)); - for (int i = 0; i < n; ++i) { - buf[i] = send_buf_.front(); - send_buf_.pop_front(); - } - msg_buf.PushBattleMessage(user_id, buf, n); - } - - if (msg_buf.Packet().SerializeToArray((void *) buf, (int) sizeof(buf))) { - if (udp_client_.SendTo((const char *) buf, msg_buf.Packet().GetCachedSize(), mcs_remote_)) { - udp_retransmit_countdown = 22; - } - } - } - } - - while (true) { - int n = udp_client_.ReadableSize(); - if (n <= 0) { - break; - } - - n = udp_client_.RecvFrom((char *) buf, std::min(n, sizeof(buf)), sender); - if (n <= 0) { - break; - } - - if (sender != mcs_remote_.str_addr()) { - continue; - } - - if (!pkt.ParseFromArray(buf, n)) { - ERROR_LOG(COMMON, "packet deserialize error"); - continue; - } - - switch (pkt.type()) { - case proto::MessageType::None: - break; - - case proto::MessageType::HelloServer: - if (state != State::McsSessionExchange) break; - if (pkt.hello_server_data().ok()) { - user_id = pkt.hello_server_data().user_id(); - NOTICE_LOG(COMMON, "user_id:%s", user_id.c_str()); - } - break; - - case proto::MessageType::Ping: - break; - - case proto::MessageType::Pong: { - if (state != State::McsPingTest) break; - auto t2 = std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()).count(); - auto rtt = static_cast(t2 - pkt.pong_data().timestamp()); - ping_recv_count++; - rtt_sum += rtt; - } - break; - - case proto::MessageType::Battle: - if (state != State::McsInBattle) break; - msg_buf.ApplySeqAck(pkt.seq(), pkt.ack()); - recv_buf_mtx_.lock(); - for (auto &msg : pkt.battle_data()) { - if (msg_filter.IsNextMessage(msg)) { - for (auto c : msg.body()) { - recv_buf_.push_back(c); - } - } - } - recv_buf_mtx_.unlock(); - break; - - case proto::Fin: - CloseMcsRemoteWithReason("cl_recv_fin"); - state = State::End; - break; - } - } - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - ClearBuffers(); - - NOTICE_LOG(COMMON, "NetThread finished"); - } - - void ClearBuffers() { - recv_buf_mtx_.lock(); - recv_buf_.clear(); - recv_buf_mtx_.unlock(); - - send_buf_mtx_.lock(); - send_buf_.clear(); - send_buf_mtx_.unlock(); - } - - const std::map &symbols_; - UdpRemote mcs_remote_; - UdpClient udp_client_; - - std::string session_id_; - std::atomic &maxlag_; - std::atomic net_terminate_; - std::mutex send_buf_mtx_; - std::mutex recv_buf_mtx_; - std::deque send_buf_; - std::deque recv_buf_; + state = State::McsInBattle; + } + } + + if (state == State::McsInBattle) { + std::lock_guard lock(send_buf_mtx_); + int n = send_buf_.size(); + if (0 < n || udp_retransmit_countdown-- == 0) { + if (0 < n && msg_buf.CanPush()) { + n = std::min(n, sizeof(buf)); + for (int i = 0; i < n; ++i) { + buf[i] = send_buf_.front(); + send_buf_.pop_front(); + } + msg_buf.PushBattleMessage(user_id, buf, n); + } + + if (msg_buf.Packet().SerializeToArray((void *)buf, (int)sizeof(buf))) { + if (udp_client_.SendTo((const char *)buf, msg_buf.Packet().GetCachedSize(), mcs_remote_)) { + udp_retransmit_countdown = 22; + } + } + } + } + + while (true) { + int n = udp_client_.ReadableSize(); + if (n <= 0) { + break; + } + + n = udp_client_.RecvFrom((char *)buf, std::min(n, sizeof(buf)), sender); + if (n <= 0) { + break; + } + + if (sender != mcs_remote_.str_addr()) { + continue; + } + + if (!pkt.ParseFromArray(buf, n)) { + ERROR_LOG(COMMON, "packet deserialize error"); + continue; + } + + switch (pkt.type()) { + case proto::MessageType::None: + break; + + case proto::MessageType::HelloServer: + if (state != State::McsSessionExchange) break; + if (pkt.hello_server_data().ok()) { + user_id = pkt.hello_server_data().user_id(); + NOTICE_LOG(COMMON, "user_id:%s", user_id.c_str()); + } + break; + + case proto::MessageType::Ping: + break; + + case proto::MessageType::Pong: { + if (state != State::McsPingTest) break; + auto t2 = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count(); + auto rtt = static_cast(t2 - pkt.pong_data().timestamp()); + ping_recv_count++; + rtt_sum += rtt; + } break; + + case proto::MessageType::Battle: + if (state != State::McsInBattle) break; + msg_buf.ApplySeqAck(pkt.seq(), pkt.ack()); + recv_buf_mtx_.lock(); + for (auto &msg : pkt.battle_data()) { + if (msg_filter.IsNextMessage(msg)) { + for (auto c : msg.body()) { + recv_buf_.push_back(c); + } + } + } + recv_buf_mtx_.unlock(); + break; + + case proto::Fin: + CloseMcsRemoteWithReason("cl_recv_fin"); + state = State::End; + break; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + ClearBuffers(); + + NOTICE_LOG(COMMON, "NetThread finished"); + } + + void ClearBuffers() { + recv_buf_mtx_.lock(); + recv_buf_.clear(); + recv_buf_mtx_.unlock(); + + send_buf_mtx_.lock(); + send_buf_.clear(); + send_buf_mtx_.unlock(); + } + + const std::map &symbols_; + UdpRemote mcs_remote_; + UdpClient udp_client_; + + std::string session_id_; + std::atomic &maxlag_; + std::atomic net_terminate_; + std::mutex send_buf_mtx_; + std::mutex recv_buf_mtx_; + std::deque send_buf_; + std::deque recv_buf_; }; diff --git a/core/gdxsv/gdxsv_emu_hooks.cpp b/core/gdxsv/gdxsv_emu_hooks.cpp index 13c2ebd68b..20ac0f30bf 100644 --- a/core/gdxsv/gdxsv_emu_hooks.cpp +++ b/core/gdxsv/gdxsv_emu_hooks.cpp @@ -1,171 +1,163 @@ #include "gdxsv_emu_hooks.h" -#include "gdxsv.h" #include -#include "version.h" -#include "oslib/oslib.h" + +#include "gdxsv.h" +#include "hw/maple/maple_if.h" #include "imgui/imgui.h" #include "imgui/imgui_internal.h" +#include "oslib/oslib.h" #include "rend/gui_util.h" -#include "hw/maple/maple_if.h" +#include "version.h" void gdxsv_latest_version_check(); static bool gdxsv_update_available = false; static std::string gdxsv_latest_version_tag; void gdxsv_emu_start() { - gdxsv.Reset(); - - if (gdxsv.Enabled()) { - auto replay = cfgLoadStr("gdxsv", "replay", ""); - if (!replay.empty()) { - dc_loadstate(99); - } - else if (!cfgLoadStr("gdxsv", "rbk_test", "").empty()) { - dc_loadstate(99); - } - else { - gdxsv.StartPingTest(); - } - - } + gdxsv.Reset(); + + if (gdxsv.Enabled()) { + auto replay = cfgLoadStr("gdxsv", "replay", ""); + if (!replay.empty()) { + dc_loadstate(99); + } else if (!cfgLoadStr("gdxsv", "rbk_test", "").empty()) { + dc_loadstate(99); + } else { + gdxsv.StartPingTest(); + } + } } -void gdxsv_emu_reset() { - gdxsv.Reset(); -} +void gdxsv_emu_reset() { gdxsv.Reset(); } void gdxsv_emu_update() { - if (gdxsv.Enabled()) { - gdxsv.Update(); - } + if (gdxsv.Enabled()) { + gdxsv.Update(); + } } void gdxsv_emu_rpc() { - if (gdxsv.Enabled()) { - gdxsv.HandleRPC(); - } + if (gdxsv.Enabled()) { + gdxsv.HandleRPC(); + } } void gdxsv_emu_savestate(int slot) { - if (gdxsv.Enabled()) { - gdxsv.RestoreOnlinePatch(); - } + if (gdxsv.Enabled()) { + gdxsv.RestoreOnlinePatch(); + } } void gdxsv_emu_loadstate(int slot) { - if (gdxsv.Enabled()) { - auto replay = cfgLoadStr("gdxsv", "replay", ""); - if (!replay.empty() && slot == 99) { - gdxsv.StartReplayFile(replay.c_str()); - } - - auto rbk_test = cfgLoadStr("gdxsv", "rbk_test", ""); - if (!rbk_test.empty() && slot == 99) { - gdxsv.StartRollbackTest(rbk_test.c_str()); - } - } + if (gdxsv.Enabled()) { + auto replay = cfgLoadStr("gdxsv", "replay", ""); + if (!replay.empty() && slot == 99) { + gdxsv.StartReplayFile(replay.c_str()); + } + + auto rbk_test = cfgLoadStr("gdxsv", "rbk_test", ""); + if (!rbk_test.empty() && slot == 99) { + gdxsv.StartRollbackTest(rbk_test.c_str()); + } + } } -bool gdxsv_emu_enabled() { - return gdxsv.Enabled(); -} +bool gdxsv_emu_enabled() { return gdxsv.Enabled(); } -bool gdxsv_emu_ingame() { - return gdxsv.InGame(); -} +bool gdxsv_emu_ingame() { return gdxsv.InGame(); } void gdxsv_update_popup() { - gdxsv_latest_version_check(); - bool no_popup_opened = !ImGui::IsPopupOpen(nullptr, ImGuiPopupFlags_AnyPopupId); - if (gdxsv_update_available && no_popup_opened) { - ImGui::OpenPopup("New version"); - gdxsv_update_available = false; - } - if (ImGui::BeginPopupModal("New version", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove)) { - ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 400.f * settings.display.uiScale); - ImGui::TextWrapped(" %s is available for download! ", gdxsv_latest_version_tag.c_str()); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(16 * settings.display.uiScale, 3 * settings.display.uiScale)); - float currentwidth = ImGui::GetContentRegionAvail().x; - ImGui::SetCursorPosX( - (currentwidth - 100.f * settings.display.uiScale) / 2.f + ImGui::GetStyle().WindowPadding.x - 55.f * settings.display.uiScale); - if (ImGui::Button("Download", ImVec2(100.f * settings.display.uiScale, 0.f))) { - gdxsv_update_available = false; - os_LaunchFromURL("https://github.com/inada-s/flycast/releases/latest/"); - ImGui::CloseCurrentPopup(); - } - ImGui::SameLine(); - ImGui::SetCursorPosX( - (currentwidth - 100.f * settings.display.uiScale) / 2.f + ImGui::GetStyle().WindowPadding.x + 55.f * settings.display.uiScale); - if (ImGui::Button("Cancel", ImVec2(100.f * settings.display.uiScale, 0.f))) { - gdxsv_update_available = false; - ImGui::CloseCurrentPopup(); - } - ImGui::SetItemDefaultFocus(); - ImGui::PopStyleVar(); - ImGui::EndPopup(); - } + gdxsv_latest_version_check(); + bool no_popup_opened = !ImGui::IsPopupOpen(nullptr, ImGuiPopupFlags_AnyPopupId); + if (gdxsv_update_available && no_popup_opened) { + ImGui::OpenPopup("New version"); + gdxsv_update_available = false; + } + if (ImGui::BeginPopupModal("New version", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove)) { + ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 400.f * settings.display.uiScale); + ImGui::TextWrapped(" %s is available for download! ", gdxsv_latest_version_tag.c_str()); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(16 * settings.display.uiScale, 3 * settings.display.uiScale)); + float currentwidth = ImGui::GetContentRegionAvail().x; + ImGui::SetCursorPosX((currentwidth - 100.f * settings.display.uiScale) / 2.f + ImGui::GetStyle().WindowPadding.x - + 55.f * settings.display.uiScale); + if (ImGui::Button("Download", ImVec2(100.f * settings.display.uiScale, 0.f))) { + gdxsv_update_available = false; + os_LaunchFromURL("https://github.com/inada-s/flycast/releases/latest/"); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + ImGui::SetCursorPosX((currentwidth - 100.f * settings.display.uiScale) / 2.f + ImGui::GetStyle().WindowPadding.x + + 55.f * settings.display.uiScale); + if (ImGui::Button("Cancel", ImVec2(100.f * settings.display.uiScale, 0.f))) { + gdxsv_update_available = false; + ImGui::CloseCurrentPopup(); + } + ImGui::SetItemDefaultFocus(); + ImGui::PopStyleVar(); + ImGui::EndPopup(); + } } inline static void gui_header(const char *title) { - ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.f, 0.5f)); // Left - ImGui::ButtonEx(title, ImVec2(-1, 0), ImGuiButtonFlags_Disabled); - ImGui::PopStyleVar(); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.f, 0.5f)); // Left + ImGui::ButtonEx(title, ImVec2(-1, 0), ImGuiButtonFlags_Disabled); + ImGui::PopStyleVar(); } void gdxsv_emu_settings() { - gui_header("gdxsv Settings"); - - ImGui::Columns(5, "gdxlang", false); - ImGui::SetColumnWidth(0, 135.0f * settings.display.uiScale); - ImGui::Text("Language mod:"); - ImGui::SameLine(); - ShowHelpMarker("Patch game language and texture, for DX only"); - ImGui::NextColumn(); - OptionRadioButton("Japanese", config::GdxLanguage, 0); - ImGui::NextColumn(); - OptionRadioButton("Cantonese", config::GdxLanguage, 1); - ImGui::NextColumn(); - OptionRadioButton("English", config::GdxLanguage, 2); - ImGui::NextColumn(); - OptionRadioButton("Disabled", config::GdxLanguage, 3); - ImGui::Columns(1, nullptr, false); - - if (ImGui::Button("Apply Recommended Settings", ImVec2(0, 40))) { - // Controls - config::MapleMainDevices[0].set(MapleDeviceType::MDT_SegaController); - config::MapleExpansionDevices[0][0].set(MDT_SegaVMU); - // Video - config::PerStripSorting = false; - config::AutoSkipFrame = 2; - config::VSync = true; - config::DelayFrameSwapping = false; + gui_header("gdxsv Settings"); + + ImGui::Columns(5, "gdxlang", false); + ImGui::SetColumnWidth(0, 135.0f * settings.display.uiScale); + ImGui::Text("Language mod:"); + ImGui::SameLine(); + ShowHelpMarker("Patch game language and texture, for DX only"); + ImGui::NextColumn(); + OptionRadioButton("Japanese", config::GdxLanguage, 0); + ImGui::NextColumn(); + OptionRadioButton("Cantonese", config::GdxLanguage, 1); + ImGui::NextColumn(); + OptionRadioButton("English", config::GdxLanguage, 2); + ImGui::NextColumn(); + OptionRadioButton("Disabled", config::GdxLanguage, 3); + ImGui::Columns(1, nullptr, false); + + if (ImGui::Button("Apply Recommended Settings", ImVec2(0, 40))) { + // Controls + config::MapleMainDevices[0].set(MapleDeviceType::MDT_SegaController); + config::MapleExpansionDevices[0][0].set(MDT_SegaVMU); + // Video + config::PerStripSorting = false; + config::AutoSkipFrame = 2; + config::VSync = true; + config::DelayFrameSwapping = false; #if defined(_WIN32) - config::RendererType.set(RenderType::DirectX11); + config::RendererType.set(RenderType::DirectX11); #else - config::RendererType.set(RenderType::OpenGL); + config::RendererType.set(RenderType::OpenGL); #endif - config::RenderResolution = 960; - config::SkipFrame = 0; - // Audio - config::DSPEnabled = false; - config::AudioVolume.set(50); - config::AudioVolume.calcDbPower(); - config::AudioBufferSize = 706; - // Others - config::DynarecEnabled = true; - config::DynarecIdleSkip = true; - config::ThreadedRendering = false; - - // Network - config::EnableUPnP = true; - config::GdxLocalPort = 0; - config::GdxMinDelay = 2; - - maple_ReconnectDevices(); - } - ImGui::SameLine(); - ShowHelpMarker(R"(Use gdxsv recommended settings: + config::RenderResolution = 960; + config::SkipFrame = 0; + // Audio + config::DSPEnabled = false; + config::AudioVolume.set(50); + config::AudioVolume.calcDbPower(); + config::AudioBufferSize = 706; + // Others + config::DynarecEnabled = true; + config::DynarecIdleSkip = true; + config::ThreadedRendering = false; + + // Network + config::EnableUPnP = true; + config::GdxLocalPort = 0; + config::GdxMinDelay = 2; + + maple_ReconnectDevices(); + } + ImGui::SameLine(); + ShowHelpMarker(R"(Use gdxsv recommended settings: Control: Device A: Sega Controller / Sega VMU @@ -193,97 +185,89 @@ void gdxsv_emu_settings() { Gdx Local UDP Port: 0 Gdx Minimum Delay: 2)"); - gui_header("Network Settings (P2P Lobby Only)"); + gui_header("Network Settings (P2P Lobby Only)"); OptionCheckbox("Enable UPnP", config::EnableUPnP, "Automatically configure your network router for netplay"); - char local_port[256]; - sprintf(local_port, "%d", (int)config::GdxLocalPort); - ImGui::InputText("Gdx UDP Port", local_port, sizeof(local_port), ImGuiInputTextFlags_CharsDecimal, nullptr, nullptr); - ImGui::SameLine(); - ShowHelpMarker("The local UDP Port. Set to 0 to automatically configure next time"); - config::GdxLocalPort.set(atoi(local_port)); - - static char upnp_result[256]; - if (config::GdxLocalPort == 0) { - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f); - } - if (ImGui::Button("UPnP Now")) { - auto upnp = gdxsv.UPnP(); - if (upnp.Init() && upnp.AddPortMapping(config::GdxLocalPort, false)) strcpy(upnp_result, "Success"); - else strcpy(upnp_result, upnp.getLastError()); - } - if (config::GdxLocalPort == 0) { - ImGui::PopItemFlag(); - ImGui::PopStyleVar(); - } - ImGui::SameLine(); - ImGui::Text(upnp_result); - - OptionSlider("Gdx Minimum Delay", config::GdxMinDelay, 2, 6, - "Minimum frame of input delay used for rollback communication.\nSmaller value reduces latency, but uses more CPU and introduces glitches."); - - ImGui::NewLine(); - gui_header("Flycast Settings"); + char local_port[256]; + sprintf(local_port, "%d", (int)config::GdxLocalPort); + ImGui::InputText("Gdx UDP Port", local_port, sizeof(local_port), ImGuiInputTextFlags_CharsDecimal, nullptr, nullptr); + ImGui::SameLine(); + ShowHelpMarker("The local UDP Port. Set to 0 to automatically configure next time"); + config::GdxLocalPort.set(atoi(local_port)); + + static char upnp_result[256]; + if (config::GdxLocalPort == 0) { + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f); + } + if (ImGui::Button("UPnP Now")) { + auto upnp = gdxsv.UPnP(); + if (upnp.Init() && upnp.AddPortMapping(config::GdxLocalPort, false)) + strcpy(upnp_result, "Success"); + else + strcpy(upnp_result, upnp.getLastError()); + } + if (config::GdxLocalPort == 0) { + ImGui::PopItemFlag(); + ImGui::PopStyleVar(); + } + ImGui::SameLine(); + ImGui::Text(upnp_result); + + OptionSlider("Gdx Minimum Delay", config::GdxMinDelay, 2, 6, + "Minimum frame of input delay used for rollback communication.\nSmaller value reduces latency, but uses more CPU and " + "introduces glitches."); + + ImGui::NewLine(); + gui_header("Flycast Settings"); } void gdxsv_handle_release_json(const std::string &json) { - const std::string version_prefix = "gdxsv-"; - const std::regex tag_name_regex(R"|#|("tag_name":"(.*?)")|#|"); - const std::regex semver_regex( - R"|#|(^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?$)|#|"); - - auto trim_prefix = [&version_prefix](const std::string &s) { - if (s.size() < version_prefix.size()) - return s; - if (version_prefix == s.substr(0, version_prefix.size())) - return s.substr(version_prefix.size()); - return s; - }; - - std::smatch match; - - auto current_version_str = trim_prefix(std::string(GIT_VERSION)); - if (!std::regex_match(current_version_str, match, semver_regex)) return; - - if (match.size() < 4) return; - auto current_version = std::tuple( - std::stoi(match.str(1)), std::stoi(match.str(2)), std::stoi(match.str(3))); - - if (!std::regex_search(json, match, tag_name_regex)) return; - if (match.size() < 2) return; - auto tag_name = match.str(1); - auto latest_version_str = trim_prefix(tag_name); - - if (!std::regex_match(latest_version_str, match, semver_regex)) return; - if (match.size() < 4) return; - auto latest_version = std::tuple( - std::stoi(match.str(1)), std::stoi(match.str(2)), std::stoi(match.str(3))); - - gdxsv_latest_version_tag = tag_name; - - if (current_version < latest_version) { - gdxsv_update_available = true; - } -} + const std::string version_prefix = "gdxsv-"; + const std::regex tag_name_regex(R"|#|("tag_name":"(.*?)")|#|"); + const std::regex semver_regex(R"|#|(^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?$)|#|"); -void gdxsv_latest_version_check() { - static std::once_flag once; - std::call_once(once, [] { - std::thread([]() { - const std::string json = os_FetchStringFromURL( - "https://api.github.com/repos/inada-s/flycast/releases/latest"); - if (json.empty()) return; - gdxsv_handle_release_json(json); - }).detach(); - }); + auto trim_prefix = [&version_prefix](const std::string &s) { + if (s.size() < version_prefix.size()) return s; + if (version_prefix == s.substr(0, version_prefix.size())) return s.substr(version_prefix.size()); + return s; + }; + + std::smatch match; + + auto current_version_str = trim_prefix(std::string(GIT_VERSION)); + if (!std::regex_match(current_version_str, match, semver_regex)) return; + + if (match.size() < 4) return; + auto current_version = std::tuple(std::stoi(match.str(1)), std::stoi(match.str(2)), std::stoi(match.str(3))); + + if (!std::regex_search(json, match, tag_name_regex)) return; + if (match.size() < 2) return; + auto tag_name = match.str(1); + auto latest_version_str = trim_prefix(tag_name); + + if (!std::regex_match(latest_version_str, match, semver_regex)) return; + if (match.size() < 4) return; + auto latest_version = std::tuple(std::stoi(match.str(1)), std::stoi(match.str(2)), std::stoi(match.str(3))); + + gdxsv_latest_version_tag = tag_name; + + if (current_version < latest_version) { + gdxsv_update_available = true; + } } -void gdxsv_mainui_loop() { - gdxsv.HookMainUiLoop(); +void gdxsv_latest_version_check() { + static std::once_flag once; + std::call_once(once, [] { + std::thread([]() { + const std::string json = os_FetchStringFromURL("https://api.github.com/repos/inada-s/flycast/releases/latest"); + if (json.empty()) return; + gdxsv_handle_release_json(json); + }).detach(); + }); } +void gdxsv_mainui_loop() { gdxsv.HookMainUiLoop(); } -void gdxsv_gui_display_osd() { - gdxsv.DisplayOSD(); -} +void gdxsv_gui_display_osd() { gdxsv.DisplayOSD(); } diff --git a/core/gdxsv/gdxsv_emu_hooks.h b/core/gdxsv/gdxsv_emu_hooks.h index b7af1fcdd2..388d1106b7 100644 --- a/core/gdxsv/gdxsv_emu_hooks.h +++ b/core/gdxsv/gdxsv_emu_hooks.h @@ -23,4 +23,3 @@ void gdxsv_emu_settings(); void gdxsv_mainui_loop(); void gdxsv_gui_display_osd(); - diff --git a/core/gdxsv/gdxsv_network.cpp b/core/gdxsv/gdxsv_network.cpp index d481fe3a19..834b1456b0 100644 --- a/core/gdxsv/gdxsv_network.cpp +++ b/core/gdxsv/gdxsv_network.cpp @@ -1,4 +1,5 @@ #include "gdxsv_network.h" + #include #ifndef _WIN32 @@ -6,594 +7,568 @@ #endif bool TcpClient::Connect(const char *host, int port) { - NOTICE_LOG(COMMON, "TCP Connect: %s:%d", host, port); - - sock_t new_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (new_sock == INVALID_SOCKET) { - WARN_LOG(COMMON, "Connect fail 1 %d", get_last_error()); - return false; - } - auto host_entry = gethostbyname(host); - if (host_entry == nullptr || host_entry->h_addr_list[0] == nullptr) { - WARN_LOG(COMMON, "Connect fail 2 gethostbyname"); - return false; - } - - sockaddr_in addr{}; - addr.sin_family = AF_INET; + NOTICE_LOG(COMMON, "TCP Connect: %s:%d", host, port); + + sock_t new_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (new_sock == INVALID_SOCKET) { + WARN_LOG(COMMON, "Connect fail 1 %d", get_last_error()); + return false; + } + auto host_entry = gethostbyname(host); + if (host_entry == nullptr || host_entry->h_addr_list[0] == nullptr) { + WARN_LOG(COMMON, "Connect fail 2 gethostbyname"); + return false; + } + + sockaddr_in addr{}; + addr.sin_family = AF_INET; #ifdef _WIN32 - addr.sin_addr = *((LPIN_ADDR) host_entry->h_addr_list[0]); + addr.sin_addr = *((LPIN_ADDR)host_entry->h_addr_list[0]); #else - memcpy(&addr.sin_addr, host_entry->h_addr_list[0], host_entry->h_length); + memcpy(&addr.sin_addr, host_entry->h_addr_list[0], host_entry->h_length); #endif - addr.sin_port = htons(port); + addr.sin_port = htons(port); - fd_set setW, setE; - struct timeval timeout = {5, 0}; - int res; + fd_set setW, setE; + struct timeval timeout = {5, 0}; + int res; - auto set_blocking_mode = [](const int &socket, bool is_blocking) { + auto set_blocking_mode = [](const int &socket, bool is_blocking) { #ifdef _WIN32 - u_long flags = is_blocking ? 0 : 1; - ioctlsocket(socket, FIONBIO, &flags); + u_long flags = is_blocking ? 0 : 1; + ioctlsocket(socket, FIONBIO, &flags); #else - const int flags = fcntl(socket, F_GETFL, 0); - fcntl(socket, F_SETFL, is_blocking ? flags ^ O_NONBLOCK : flags | O_NONBLOCK); + const int flags = fcntl(socket, F_GETFL, 0); + fcntl(socket, F_SETFL, is_blocking ? flags ^ O_NONBLOCK : flags | O_NONBLOCK); #endif - }; - - set_blocking_mode(new_sock, false); - - if (::connect(new_sock, (const sockaddr *) &addr, sizeof(addr)) != 0) { - if (get_last_error() != EINPROGRESS && get_last_error() != L_EWOULDBLOCK) { - WARN_LOG(COMMON, "Connect fail 2 %d", get_last_error()); - return false; - } else { - do { - FD_ZERO(&setW); - FD_SET(new_sock, &setW); - FD_ZERO(&setE); - FD_SET(new_sock, &setE); - - res = select(new_sock + 1, NULL, &setW, &setE, &timeout); - if (res < 0 && errno != EINTR) { - WARN_LOG(COMMON, "Connect fail 3 %d", get_last_error()); - return false; - } else if (res > 0) { - - int error; - socklen_t l = sizeof(int); + }; + + set_blocking_mode(new_sock, false); + + if (::connect(new_sock, (const sockaddr *)&addr, sizeof(addr)) != 0) { + if (get_last_error() != EINPROGRESS && get_last_error() != L_EWOULDBLOCK) { + WARN_LOG(COMMON, "Connect fail 2 %d", get_last_error()); + return false; + } else { + do { + FD_ZERO(&setW); + FD_SET(new_sock, &setW); + FD_ZERO(&setE); + FD_SET(new_sock, &setE); + + res = select(new_sock + 1, NULL, &setW, &setE, &timeout); + if (res < 0 && errno != EINTR) { + WARN_LOG(COMMON, "Connect fail 3 %d", get_last_error()); + return false; + } else if (res > 0) { + int error; + socklen_t l = sizeof(int); #ifdef _WIN32 - if (getsockopt(new_sock, SOL_SOCKET, SO_ERROR, (char *) &error, &l) < 0 || error) { + if (getsockopt(new_sock, SOL_SOCKET, SO_ERROR, (char *)&error, &l) < 0 || error) { #else - if (getsockopt(new_sock, SOL_SOCKET, SO_ERROR, &error, &l) < 0 || error) { + if (getsockopt(new_sock, SOL_SOCKET, SO_ERROR, &error, &l) < 0 || error) { #endif - WARN_LOG(COMMON, "Connect fail 4 %d", error); - return false; - } - - if (FD_ISSET(new_sock, &setE)) { - WARN_LOG(COMMON, "Connect fail 5 %d", get_last_error()); - return false; - } - - break; - } else { - WARN_LOG(COMMON, "Timeout in select() - Cancelling!"); - return false; - } - } while (1); - } - set_blocking_mode(new_sock, true); - } - - - if (sock_ != INVALID_SOCKET) { - closesocket(sock_); - } - - set_tcp_nodelay(new_sock); - - sock_ = new_sock; - host_ = std::string(host); - port_ = port; - - { - sockaddr_in name{}; - socklen_t namelen = sizeof(name); - if (getsockname(new_sock, reinterpret_cast(&name), &namelen) != 0) { - WARN_LOG(COMMON, "getsockname failed"); - } else { - char buf[INET_ADDRSTRLEN]; - local_ip_ = std::string(inet_ntop(AF_INET, &name.sin_addr, buf, INET_ADDRSTRLEN)); - } - } - - NOTICE_LOG(COMMON, "TCP Connect: %s:%d ok", host, port); - return true; -} - -int TcpClient::IsConnected() const { - return sock_ != INVALID_SOCKET; -} + WARN_LOG(COMMON, "Connect fail 4 %d", error); + return false; + } + + if (FD_ISSET(new_sock, &setE)) { + WARN_LOG(COMMON, "Connect fail 5 %d", get_last_error()); + return false; + } + + break; + } else { + WARN_LOG(COMMON, "Timeout in select() - Cancelling!"); + return false; + } + } while (1); + } + set_blocking_mode(new_sock, true); + } + + if (sock_ != INVALID_SOCKET) { + closesocket(sock_); + } + + set_tcp_nodelay(new_sock); + + sock_ = new_sock; + host_ = std::string(host); + port_ = port; + + { + sockaddr_in name{}; + socklen_t namelen = sizeof(name); + if (getsockname(new_sock, reinterpret_cast(&name), &namelen) != 0) { + WARN_LOG(COMMON, "getsockname failed"); + } else { + char buf[INET_ADDRSTRLEN]; + local_ip_ = std::string(inet_ntop(AF_INET, &name.sin_addr, buf, INET_ADDRSTRLEN)); + } + } + + NOTICE_LOG(COMMON, "TCP Connect: %s:%d ok", host, port); + return true; +} + +int TcpClient::IsConnected() const { return sock_ != INVALID_SOCKET; } void TcpClient::SetNonBlocking() { - set_recv_timeout(sock_, 1); - set_send_timeout(sock_, 1); - set_non_blocking(sock_); + set_recv_timeout(sock_, 1); + set_send_timeout(sock_, 1); + set_non_blocking(sock_); } int TcpClient::Recv(char *buf, int len) { - int n = ::recv(sock_, buf, len, 0); - if (n < 0 && get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK) { - WARN_LOG(COMMON, "TCP Recv failed. errno=%d", get_last_error()); - this->Close(); - } - if (n < 0) return 0; - return n; + int n = ::recv(sock_, buf, len, 0); + if (n < 0 && get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK) { + WARN_LOG(COMMON, "TCP Recv failed. errno=%d", get_last_error()); + this->Close(); + } + if (n < 0) return 0; + return n; } int TcpClient::Send(const char *buf, int len) { - int n = ::send(sock_, buf, len, 0); - if (n < 0 && get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK) { - WARN_LOG(COMMON, "TCP Send failed. errno=%d", get_last_error()); - this->Close(); - } - if (n < 0) return 0; - return n; + int n = ::send(sock_, buf, len, 0); + if (n < 0 && get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK) { + WARN_LOG(COMMON, "TCP Send failed. errno=%d", get_last_error()); + this->Close(); + } + if (n < 0) return 0; + return n; } u32 TcpClient::ReadableSize() const { - u_long n = 0; + u_long n = 0; #ifndef _WIN32 - ioctl(sock_, FIONREAD, &n); + ioctl(sock_, FIONREAD, &n); #else - ioctlsocket(sock_, FIONREAD, &n); + ioctlsocket(sock_, FIONREAD, &n); #endif - return u32(n); + return u32(n); } void TcpClient::Close() { - if (sock_ != INVALID_SOCKET) { - closesocket(sock_); - sock_ = INVALID_SOCKET; - } + if (sock_ != INVALID_SOCKET) { + closesocket(sock_); + sock_ = INVALID_SOCKET; + } } bool UdpRemote::Open(const char *host, int port) { - auto host_entry = gethostbyname(host); - if (host_entry == nullptr) { - WARN_LOG(COMMON, "UDP Remote::Initialize failed. gethostbyname %s", host); - return false; - } - - is_open_ = true; - str_addr_ = std::string(host) + ":" + std::to_string(port); - net_addr_.sin_family = AF_INET; - memcpy(&(net_addr_.sin_addr.s_addr), host_entry->h_addr, host_entry->h_length); - net_addr_.sin_port = htons(port); - return true; + auto host_entry = gethostbyname(host); + if (host_entry == nullptr) { + WARN_LOG(COMMON, "UDP Remote::Initialize failed. gethostbyname %s", host); + return false; + } + + is_open_ = true; + str_addr_ = std::string(host) + ":" + std::to_string(port); + net_addr_.sin_family = AF_INET; + memcpy(&(net_addr_.sin_addr.s_addr), host_entry->h_addr, host_entry->h_length); + net_addr_.sin_port = htons(port); + return true; } bool UdpRemote::Open(const std::string &addr) { - if (std::count(addr.begin(), addr.end(), ':') != 1) { - return false; - } - size_t colon_pos = addr.find_first_of(':'); - std::string host = addr.substr(0, colon_pos); - int port = std::stoi(addr.substr(colon_pos + 1)); - return Open(host.c_str(), port); + if (std::count(addr.begin(), addr.end(), ':') != 1) { + return false; + } + size_t colon_pos = addr.find_first_of(':'); + std::string host = addr.substr(0, colon_pos); + int port = std::stoi(addr.substr(colon_pos + 1)); + return Open(host.c_str(), port); } void UdpRemote::Close() { - is_open_ = false; - str_addr_.clear(); - memset(&net_addr_, 0, sizeof(net_addr_)); + is_open_ = false; + str_addr_.clear(); + memset(&net_addr_, 0, sizeof(net_addr_)); } -bool UdpRemote::is_open() const { - return is_open_; -} +bool UdpRemote::is_open() const { return is_open_; } -const std::string &UdpRemote::str_addr() const { - return str_addr_; -} +const std::string &UdpRemote::str_addr() const { return str_addr_; } -const sockaddr_in &UdpRemote::net_addr() const { - return net_addr_; -} +const sockaddr_in &UdpRemote::net_addr() const { return net_addr_; } bool UdpClient::Bind(int port) { - sock_t new_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (new_sock == INVALID_SOCKET) { - WARN_LOG(COMMON, "UDP Connect fail %d", get_last_error()); - return false; - } - - int optval = 0; - if (port != 0) { - setsockopt(new_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&optval, sizeof optval); - } - optval = 0; - setsockopt(new_sock, SOL_SOCKET, SO_LINGER, (const char*)&optval, sizeof optval); - - sockaddr_in recv_addr; - memset(&recv_addr, 0, sizeof(recv_addr)); - recv_addr.sin_port = htons(port); // if port == 0 then the system assign an available port. - recv_addr.sin_family = AF_INET; - recv_addr.sin_addr.s_addr = htonl(INADDR_ANY); - - if (::bind(new_sock, (struct sockaddr *) &recv_addr, sizeof(recv_addr)) < 0) { - ERROR_LOG(COMMON, "gdxsv: bind() failed. errno=%d", get_last_error()); - closesocket(new_sock); - return false; - } - - memset(&recv_addr, 0, sizeof(recv_addr)); - socklen_t addr_len = sizeof(recv_addr); - if (::getsockname(new_sock, (struct sockaddr *) &recv_addr, &addr_len) < 0) { - ERROR_LOG(COMMON, "gdxsv: getsockname() failed. errno=%d", get_last_error()); - closesocket(new_sock); - return false; - } - - set_recv_timeout(new_sock, 1); - set_send_timeout(new_sock, 1); - set_non_blocking(new_sock); - - sock_ = new_sock; - bind_ip_ = std::string(::inet_ntoa(recv_addr.sin_addr)); - bind_port_ = ntohs(recv_addr.sin_port); - NOTICE_LOG(COMMON, "UDP Initialize ok: %s:%d", bind_ip_.c_str(), bind_port_); - return true; -} - -bool UdpClient::Initialized() const { - return sock_ != INVALID_SOCKET; -} + sock_t new_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (new_sock == INVALID_SOCKET) { + WARN_LOG(COMMON, "UDP Connect fail %d", get_last_error()); + return false; + } + + int optval = 0; + if (port != 0) { + setsockopt(new_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof optval); + } + optval = 0; + setsockopt(new_sock, SOL_SOCKET, SO_LINGER, (const char *)&optval, sizeof optval); + + sockaddr_in recv_addr; + memset(&recv_addr, 0, sizeof(recv_addr)); + recv_addr.sin_port = htons(port); // if port == 0 then the system assign an available port. + recv_addr.sin_family = AF_INET; + recv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (::bind(new_sock, (struct sockaddr *)&recv_addr, sizeof(recv_addr)) < 0) { + ERROR_LOG(COMMON, "gdxsv: bind() failed. errno=%d", get_last_error()); + closesocket(new_sock); + return false; + } + + memset(&recv_addr, 0, sizeof(recv_addr)); + socklen_t addr_len = sizeof(recv_addr); + if (::getsockname(new_sock, (struct sockaddr *)&recv_addr, &addr_len) < 0) { + ERROR_LOG(COMMON, "gdxsv: getsockname() failed. errno=%d", get_last_error()); + closesocket(new_sock); + return false; + } + + set_recv_timeout(new_sock, 1); + set_send_timeout(new_sock, 1); + set_non_blocking(new_sock); + + sock_ = new_sock; + bind_ip_ = std::string(::inet_ntoa(recv_addr.sin_addr)); + bind_port_ = ntohs(recv_addr.sin_port); + NOTICE_LOG(COMMON, "UDP Initialize ok: %s:%d", bind_ip_.c_str(), bind_port_); + return true; +} + +bool UdpClient::Initialized() const { return sock_ != INVALID_SOCKET; } int UdpClient::RecvFrom(char *buf, int len, std::string &sender) { - sender.clear(); - sockaddr_in from_addr; - socklen_t addrlen = sizeof(from_addr); - memset(&from_addr, 0, sizeof(from_addr)); - - int n = ::recvfrom(sock_, buf, len, 0, (struct sockaddr *) &from_addr, &addrlen); - if (n < 0 && get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK) { - WARN_LOG(COMMON, "UDP Recv failed. errno=%d", get_last_error()); - return 0; - } - sender = std::string(::inet_ntoa(from_addr.sin_addr)) + ":" + std::to_string(ntohs(from_addr.sin_port)); - if (n < 0) return 0; - return n; + sender.clear(); + sockaddr_in from_addr; + socklen_t addrlen = sizeof(from_addr); + memset(&from_addr, 0, sizeof(from_addr)); + + int n = ::recvfrom(sock_, buf, len, 0, (struct sockaddr *)&from_addr, &addrlen); + if (n < 0 && get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK) { + WARN_LOG(COMMON, "UDP Recv failed. errno=%d", get_last_error()); + return 0; + } + sender = std::string(::inet_ntoa(from_addr.sin_addr)) + ":" + std::to_string(ntohs(from_addr.sin_port)); + if (n < 0) return 0; + return n; } int UdpClient::SendTo(const char *buf, int len, const UdpRemote &remote) { - int n = ::sendto(sock_, buf, len, 0, (const sockaddr *) &remote.net_addr(), sizeof(remote.net_addr())); - if (n < 0 && get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK) { - WARN_LOG(COMMON, "UDP Send failed. errno=%d", get_last_error()); - return 0; - } - if (n < 0) return 0; - return n; + int n = ::sendto(sock_, buf, len, 0, (const sockaddr *)&remote.net_addr(), sizeof(remote.net_addr())); + if (n < 0 && get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK) { + WARN_LOG(COMMON, "UDP Send failed. errno=%d", get_last_error()); + return 0; + } + if (n < 0) return 0; + return n; } u32 UdpClient::ReadableSize() const { - u_long n = 0; + u_long n = 0; #ifndef _WIN32 - ioctl(sock_, FIONREAD, &n); + ioctl(sock_, FIONREAD, &n); #else - ioctlsocket(sock_, FIONREAD, &n); + ioctlsocket(sock_, FIONREAD, &n); #endif - return u32(n); + return u32(n); } void UdpClient::Close() { - if (sock_ != INVALID_SOCKET) { - closesocket(sock_); - sock_ = INVALID_SOCKET; - } + if (sock_ != INVALID_SOCKET) { + closesocket(sock_); + sock_ = INVALID_SOCKET; + } } -MessageBuffer::MessageBuffer() { - Clear(); -} +MessageBuffer::MessageBuffer() { Clear(); } -bool MessageBuffer::CanPush() const { - return packet_.battle_data().size() < kBufSize; -} +bool MessageBuffer::CanPush() const { return packet_.battle_data().size() < kBufSize; } -void MessageBuffer::SessionId(const std::string &session_id) { - packet_.set_session_id(session_id.c_str()); -} +void MessageBuffer::SessionId(const std::string &session_id) { packet_.set_session_id(session_id.c_str()); } bool MessageBuffer::PushBattleMessage(const std::string &user_id, u8 *body, u32 body_length) { - if (!CanPush()) { - // buffer full - return false; - } - - auto msg = packet_.add_battle_data(); - msg->set_seq(msg_seq_); - msg->set_user_id(user_id); - msg->set_body(body, body_length); - packet_.set_seq(msg_seq_); - msg_seq_++; - return true; -} + if (!CanPush()) { + // buffer full + return false; + } -const proto::Packet &MessageBuffer::Packet() { - return packet_; + auto msg = packet_.add_battle_data(); + msg->set_seq(msg_seq_); + msg->set_user_id(user_id); + msg->set_body(body, body_length); + packet_.set_seq(msg_seq_); + msg_seq_++; + return true; } +const proto::Packet &MessageBuffer::Packet() { return packet_; } + void MessageBuffer::ApplySeqAck(u32 seq, u32 ack) { - if (snd_seq_ <= ack) { - packet_.mutable_battle_data()->DeleteSubrange(0, ack - snd_seq_ + 1); - snd_seq_ = ack + 1; - } - if (packet_.ack() < seq) { - packet_.set_ack(seq); - } + if (snd_seq_ <= ack) { + packet_.mutable_battle_data()->DeleteSubrange(0, ack - snd_seq_ + 1); + snd_seq_ = ack + 1; + } + if (packet_.ack() < seq) { + packet_.set_ack(seq); + } } void MessageBuffer::Clear() { - packet_.Clear(); - packet_.set_type(proto::MessageType::Battle); - msg_seq_ = 1; - snd_seq_ = 1; + packet_.Clear(); + packet_.set_type(proto::MessageType::Battle); + msg_seq_ = 1; + snd_seq_ = 1; } bool MessageFilter::IsNextMessage(const proto::BattleMessage &msg) { - auto last_seq = recv_seq[msg.user_id()]; - if (last_seq == 0 || msg.seq() == last_seq + 1) { - recv_seq[msg.user_id()] = msg.seq(); - return true; - } - return false; + auto last_seq = recv_seq[msg.user_id()]; + if (last_seq == 0 || msg.seq() == last_seq + 1) { + recv_seq[msg.user_id()] = msg.seq(); + return true; + } + return false; } -void MessageFilter::Clear() { - recv_seq.clear(); -} +void MessageFilter::Clear() { recv_seq.clear(); } void UdpPingPong::Start(uint32_t session_id, uint8_t peer_id, int port, int timeout_min_ms, int timeout_max_ms) { - if (running_) return; - verify(peer_id < N); - client_.Close(); - client_.Bind(port); - running_ = true; - int network_delay = 0; - const auto delay_option = std::getenv("GGPO_NETWORK_DELAY"); - if (delay_option != nullptr) { - network_delay = atoi(delay_option); - NOTICE_LOG(COMMON, "GGPO_NETWORK_DELAY is %d", network_delay); - } - - std::set peer_ids; - peer_ids.insert(peer_id); - for (const auto& c : candidates_) { - peer_ids.insert(c.peer_id); - } - int peer_count = peer_ids.size(); - - std::thread([this, session_id, peer_id, timeout_min_ms, timeout_max_ms, peer_count, network_delay]() { - WARN_LOG(COMMON, "Start UdpPingPong Thread"); - start_time_ = std::chrono::high_resolution_clock::now(); - std::string sender; - - for (int loop_count = 0; running_; loop_count++) { - auto now = std::chrono::high_resolution_clock::now(); - auto ms = std::chrono::duration_cast(now - start_time_).count(); - - while (true) { - Packet recv{}; - - int n = client_.ReadableSize(); - if (n <= 0) { - break; - } - - n = client_.RecvFrom(reinterpret_cast(&recv), sizeof(Packet), sender); - if (n <= 0) { - break; - } - - if (recv.magic != MAGIC) { - WARN_LOG(COMMON, "invalid magic"); - continue; - } - - if (recv.session_id != session_id) { - WARN_LOG(COMMON, "invalid session_id"); - continue; - } - - if (recv.to_peer_id != peer_id) { - WARN_LOG(COMMON, "invalid to_peer_id"); - continue; - } - - if (recv.from_peer_id == recv.to_peer_id) { - WARN_LOG(COMMON, "invalid peer_id"); - continue; - } - - if (recv.type == PING) { - NOTICE_LOG(COMMON, "Recv PING from %d", recv.from_peer_id); - std::lock_guard lock(mutex_); - - Packet p{}; - p.magic = MAGIC; - p.type = PONG; - p.session_id = session_id; - p.from_peer_id = peer_id; - p.to_peer_id = recv.from_peer_id; - p.send_timestamp = std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()).count(); - p.ping_timestamp = recv.send_timestamp; - p.ping_timestamp -= network_delay; - UdpRemote remote; - memcpy(p.rtt_matrix, rtt_matrix_, sizeof(rtt_matrix_)); - - auto it = std::find_if(candidates_.begin(), candidates_.end(), - [&recv, &sender](const Candidate& c) { - return c.peer_id == recv.from_peer_id && c.remote.str_addr() == sender; - }); - if (it != candidates_.end()) { - remote = it->remote; - } else { - Candidate c{}; - c.peer_id = recv.from_peer_id; - c.remote.Open(sender); - candidates_.push_back(c); - remote = c.remote; - } - if (remote.is_open()) { - client_.SendTo(reinterpret_cast(&p), sizeof(p), remote); - } - } - - if (recv.type == PONG) { - NOTICE_LOG(COMMON, "Recv PONG from %d", recv.from_peer_id); - const auto now = std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()).count(); - auto rtt = static_cast(now - recv.ping_timestamp); - if (rtt <= 0) rtt = 1; - - auto it = std::find_if(candidates_.begin(), candidates_.end(), - [&recv, &sender](const Candidate& c) { - return c.peer_id == recv.from_peer_id && c.remote.str_addr() == sender; - }); - if (it != candidates_.end()) { - it->rtt = float(it->pong_count * it->rtt + rtt) / float(it->pong_count + 1); - it->pong_count++; - rtt_matrix_[peer_id][recv.from_peer_id] = static_cast(std::min(255, (int)std::ceil(it->rtt))); - for (int j = 0; j < N; j++) { - rtt_matrix_[recv.from_peer_id][j] = recv.rtt_matrix[recv.from_peer_id][j]; - } - } else { - Candidate c{}; - c.peer_id = recv.from_peer_id; - c.remote.Open(sender); - candidates_.push_back(c); - } - } - } - - if (loop_count % 100 == 0) { - std::lock_guard lock(mutex_); - - for (auto& c : candidates_) { - NOTICE_LOG(COMMON, "Send PING to %d %s", c.peer_id, c.remote.str_addr().c_str()); - if (c.remote.is_open()) { - Packet p{}; - p.magic = MAGIC; - p.type = PING; - p.session_id = session_id; - p.from_peer_id = peer_id; - p.to_peer_id = c.peer_id; - p.send_timestamp = std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()).count(); - p.send_timestamp -= network_delay; - p.ping_timestamp = 0; - memcpy(p.rtt_matrix, rtt_matrix_, sizeof(rtt_matrix_)); - client_.SendTo(reinterpret_cast(&p), sizeof(p), c.remote); - c.ping_count++; - } - } - } - - if (loop_count % 500 == 0) { - bool matrix_ok = true; - std::lock_guard lock(mutex_); - - NOTICE_LOG(COMMON, "RTT MATRIX"); - NOTICE_LOG(COMMON, " %4d%4d%4d%4d", 0, 1, 2, 3); - for (int i = 0; i < 4; i++) { - NOTICE_LOG(COMMON, "%d>%4d%4d%4d%4d", - i, rtt_matrix_[i][0], rtt_matrix_[i][1], rtt_matrix_[i][2], rtt_matrix_[i][3]); - } - - NOTICE_LOG(COMMON, "peer_count%d", peer_count); - for (int i = 0; i < peer_count; i++) { - for (int j = 0; j < peer_count; j++) { - if (i != j) { - if (rtt_matrix_[i][j] == 0) { - matrix_ok = false; - } - } - } - } - - if (matrix_ok && timeout_min_ms < ms) { - NOTICE_LOG(COMMON, "UdpPingTest Finish ok"); - client_.Close(); - running_ = false; - break; - } - } - - if (timeout_max_ms < ms) { - NOTICE_LOG(COMMON, "UdpPingTest Finish timeout"); - client_.Close(); - running_ = false; - break; - } - - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - - WARN_LOG(COMMON, "End UdpPingPong Thread"); - }).detach(); -} - -void UdpPingPong::Stop() { - running_ = false; -} + if (running_) return; + verify(peer_id < N); + client_.Close(); + client_.Bind(port); + running_ = true; + int network_delay = 0; + const auto delay_option = std::getenv("GGPO_NETWORK_DELAY"); + if (delay_option != nullptr) { + network_delay = atoi(delay_option); + NOTICE_LOG(COMMON, "GGPO_NETWORK_DELAY is %d", network_delay); + } + + std::set peer_ids; + peer_ids.insert(peer_id); + for (const auto &c : candidates_) { + peer_ids.insert(c.peer_id); + } + int peer_count = peer_ids.size(); + + std::thread([this, session_id, peer_id, timeout_min_ms, timeout_max_ms, peer_count, network_delay]() { + WARN_LOG(COMMON, "Start UdpPingPong Thread"); + start_time_ = std::chrono::high_resolution_clock::now(); + std::string sender; + + for (int loop_count = 0; running_; loop_count++) { + auto now = std::chrono::high_resolution_clock::now(); + auto ms = std::chrono::duration_cast(now - start_time_).count(); + + while (true) { + Packet recv{}; + + int n = client_.ReadableSize(); + if (n <= 0) { + break; + } + + n = client_.RecvFrom(reinterpret_cast(&recv), sizeof(Packet), sender); + if (n <= 0) { + break; + } + + if (recv.magic != MAGIC) { + WARN_LOG(COMMON, "invalid magic"); + continue; + } + + if (recv.session_id != session_id) { + WARN_LOG(COMMON, "invalid session_id"); + continue; + } + + if (recv.to_peer_id != peer_id) { + WARN_LOG(COMMON, "invalid to_peer_id"); + continue; + } + + if (recv.from_peer_id == recv.to_peer_id) { + WARN_LOG(COMMON, "invalid peer_id"); + continue; + } + + if (recv.type == PING) { + NOTICE_LOG(COMMON, "Recv PING from %d", recv.from_peer_id); + std::lock_guard lock(mutex_); + + Packet p{}; + p.magic = MAGIC; + p.type = PONG; + p.session_id = session_id; + p.from_peer_id = peer_id; + p.to_peer_id = recv.from_peer_id; + p.send_timestamp = + std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()) + .count(); + p.ping_timestamp = recv.send_timestamp; + p.ping_timestamp -= network_delay; + UdpRemote remote; + memcpy(p.rtt_matrix, rtt_matrix_, sizeof(rtt_matrix_)); + + auto it = std::find_if(candidates_.begin(), candidates_.end(), [&recv, &sender](const Candidate &c) { + return c.peer_id == recv.from_peer_id && c.remote.str_addr() == sender; + }); + if (it != candidates_.end()) { + remote = it->remote; + } else { + Candidate c{}; + c.peer_id = recv.from_peer_id; + c.remote.Open(sender); + candidates_.push_back(c); + remote = c.remote; + } + if (remote.is_open()) { + client_.SendTo(reinterpret_cast(&p), sizeof(p), remote); + } + } + + if (recv.type == PONG) { + NOTICE_LOG(COMMON, "Recv PONG from %d", recv.from_peer_id); + const auto now = + std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()) + .count(); + auto rtt = static_cast(now - recv.ping_timestamp); + if (rtt <= 0) rtt = 1; + + auto it = std::find_if(candidates_.begin(), candidates_.end(), [&recv, &sender](const Candidate &c) { + return c.peer_id == recv.from_peer_id && c.remote.str_addr() == sender; + }); + if (it != candidates_.end()) { + it->rtt = float(it->pong_count * it->rtt + rtt) / float(it->pong_count + 1); + it->pong_count++; + rtt_matrix_[peer_id][recv.from_peer_id] = static_cast(std::min(255, (int)std::ceil(it->rtt))); + for (int j = 0; j < N; j++) { + rtt_matrix_[recv.from_peer_id][j] = recv.rtt_matrix[recv.from_peer_id][j]; + } + } else { + Candidate c{}; + c.peer_id = recv.from_peer_id; + c.remote.Open(sender); + candidates_.push_back(c); + } + } + } + + if (loop_count % 100 == 0) { + std::lock_guard lock(mutex_); + + for (auto &c : candidates_) { + NOTICE_LOG(COMMON, "Send PING to %d %s", c.peer_id, c.remote.str_addr().c_str()); + if (c.remote.is_open()) { + Packet p{}; + p.magic = MAGIC; + p.type = PING; + p.session_id = session_id; + p.from_peer_id = peer_id; + p.to_peer_id = c.peer_id; + p.send_timestamp = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count(); + p.send_timestamp -= network_delay; + p.ping_timestamp = 0; + memcpy(p.rtt_matrix, rtt_matrix_, sizeof(rtt_matrix_)); + client_.SendTo(reinterpret_cast(&p), sizeof(p), c.remote); + c.ping_count++; + } + } + } + + if (loop_count % 500 == 0) { + bool matrix_ok = true; + std::lock_guard lock(mutex_); + + NOTICE_LOG(COMMON, "RTT MATRIX"); + NOTICE_LOG(COMMON, " %4d%4d%4d%4d", 0, 1, 2, 3); + for (int i = 0; i < 4; i++) { + NOTICE_LOG(COMMON, "%d>%4d%4d%4d%4d", i, rtt_matrix_[i][0], rtt_matrix_[i][1], rtt_matrix_[i][2], rtt_matrix_[i][3]); + } + + NOTICE_LOG(COMMON, "peer_count%d", peer_count); + for (int i = 0; i < peer_count; i++) { + for (int j = 0; j < peer_count; j++) { + if (i != j) { + if (rtt_matrix_[i][j] == 0) { + matrix_ok = false; + } + } + } + } + + if (matrix_ok && timeout_min_ms < ms) { + NOTICE_LOG(COMMON, "UdpPingTest Finish ok"); + client_.Close(); + running_ = false; + break; + } + } + + if (timeout_max_ms < ms) { + NOTICE_LOG(COMMON, "UdpPingTest Finish timeout"); + client_.Close(); + running_ = false; + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + WARN_LOG(COMMON, "End UdpPingPong Thread"); + }).detach(); +} + +void UdpPingPong::Stop() { running_ = false; } void UdpPingPong::Reset() { - std::lock_guard lock(mutex_); - client_.Close(); - candidates_.clear(); + std::lock_guard lock(mutex_); + client_.Close(); + candidates_.clear(); } -bool UdpPingPong::Running() { - return running_; -} +bool UdpPingPong::Running() { return running_; } int UdpPingPong::ElapsedMs() { auto now = std::chrono::high_resolution_clock::now(); return std::chrono::duration_cast(now - start_time_).count(); } -void UdpPingPong::AddCandidate(const std::string& user_id, uint8_t peer_id, const std::string& ip, int port) { - std::lock_guard lock(mutex_); - Candidate c{}; - user_to_peer_[user_id] = peer_id; - peer_to_user_[peer_id] = user_id; - c.peer_id = peer_id; - c.remote.Open(ip.c_str(), port); - candidates_.push_back(c); -} - -bool UdpPingPong::GetAvailableAddress(uint8_t peer_id, sockaddr_in* dst, float* rtt) { - std::lock_guard lock(mutex_); - float min_rtt = 1000.0f; - bool found = false; - for (auto& c : candidates_) { - if (c.peer_id == peer_id && c.pong_count) { - if (c.rtt < min_rtt) { - min_rtt = c.rtt; - *dst = c.remote.net_addr(); - *rtt = c.rtt; - found = true; - } - } - } - return found; +void UdpPingPong::AddCandidate(const std::string &user_id, uint8_t peer_id, const std::string &ip, int port) { + std::lock_guard lock(mutex_); + Candidate c{}; + user_to_peer_[user_id] = peer_id; + peer_to_user_[peer_id] = user_id; + c.peer_id = peer_id; + c.remote.Open(ip.c_str(), port); + candidates_.push_back(c); +} + +bool UdpPingPong::GetAvailableAddress(uint8_t peer_id, sockaddr_in *dst, float *rtt) { + std::lock_guard lock(mutex_); + float min_rtt = 1000.0f; + bool found = false; + for (auto &c : candidates_) { + if (c.peer_id == peer_id && c.pong_count) { + if (c.rtt < min_rtt) { + min_rtt = c.rtt; + *dst = c.remote.net_addr(); + *rtt = c.rtt; + found = true; + } + } + } + return found; } void UdpPingPong::GetRttMatrix(uint8_t matrix[N][N]) { - std::lock_guard lock(mutex_); - memcpy(matrix, rtt_matrix_, sizeof(rtt_matrix_)); + std::lock_guard lock(mutex_); + memcpy(matrix, rtt_matrix_, sizeof(rtt_matrix_)); } diff --git a/core/gdxsv/gdxsv_network.h b/core/gdxsv/gdxsv_network.h index a26ffb9f74..7e0352ab0d 100644 --- a/core/gdxsv/gdxsv_network.h +++ b/core/gdxsv/gdxsv_network.h @@ -1,176 +1,173 @@ #pragma once -#include -#include #include #include +#include +#include -#include "types.h" -#include "network/net_platform.h" #include "gdxsv.pb.h" +#include "network/net_platform.h" +#include "types.h" class TcpClient { -public: - ~TcpClient() { - Close(); - } + public: + ~TcpClient() { Close(); } - bool Connect(const char *host, int port); + bool Connect(const char *host, int port); - void SetNonBlocking(); + void SetNonBlocking(); - int IsConnected() const; + int IsConnected() const; - int Recv(char *buf, int len); + int Recv(char *buf, int len); - int Send(const char *buf, int len); + int Send(const char *buf, int len); - void Close(); + void Close(); - u32 ReadableSize() const; + u32 ReadableSize() const; - const std::string &host() const { return host_; } + const std::string &host() const { return host_; } - const std::string &local_ip() const { return local_ip_; } + const std::string &local_ip() const { return local_ip_; } - int port() const { return port_; } + int port() const { return port_; } -private: - sock_t sock_ = INVALID_SOCKET; - std::string host_; - std::string local_ip_; - int port_; + private: + sock_t sock_ = INVALID_SOCKET; + std::string host_; + std::string local_ip_; + int port_; }; class MessageBuffer { -public: - static const int kBufSize = 50; + public: + static const int kBufSize = 50; - MessageBuffer(); + MessageBuffer(); - void SessionId(const std::string &session_id); + void SessionId(const std::string &session_id); - bool CanPush() const; + bool CanPush() const; - bool PushBattleMessage(const std::string &user_id, u8 *body, u32 body_length); + bool PushBattleMessage(const std::string &user_id, u8 *body, u32 body_length); - const proto::Packet &Packet(); + const proto::Packet &Packet(); - void ApplySeqAck(u32 seq, u32 ack); + void ApplySeqAck(u32 seq, u32 ack); - void Clear(); + void Clear(); -private: - u32 msg_seq_; - u32 snd_seq_; - proto::Packet packet_; + private: + u32 msg_seq_; + u32 snd_seq_; + proto::Packet packet_; }; class MessageFilter { -public: - bool IsNextMessage(const proto::BattleMessage &msg); + public: + bool IsNextMessage(const proto::BattleMessage &msg); - void Clear(); + void Clear(); -private: - std::map recv_seq; + private: + std::map recv_seq; }; - class UdpRemote { -public: - bool Open(const char *host, int port); + public: + bool Open(const char *host, int port); - bool Open(const std::string &addr); + bool Open(const std::string &addr); - void Close(); + void Close(); - bool is_open() const; + bool is_open() const; - const std::string &str_addr() const; + const std::string &str_addr() const; - const sockaddr_in &net_addr() const; + const sockaddr_in &net_addr() const; -private: - bool is_open_; - std::string str_addr_; - sockaddr_in net_addr_; + private: + bool is_open_; + std::string str_addr_; + sockaddr_in net_addr_; }; class UdpClient { -public: - bool Bind(int port); + public: + bool Bind(int port); - bool Initialized() const; + bool Initialized() const; - int RecvFrom(char *buf, int len, std::string &sender); + int RecvFrom(char *buf, int len, std::string &sender); - int SendTo(const char *buf, int len, const UdpRemote &remote); + int SendTo(const char *buf, int len, const UdpRemote &remote); - u32 ReadableSize() const; + u32 ReadableSize() const; - void Close(); + void Close(); - int bind_port() const { return bind_port_; } + int bind_port() const { return bind_port_; } -private: - sock_t sock_ = INVALID_SOCKET; - int bind_port_; - std::string bind_ip_; + private: + sock_t sock_ = INVALID_SOCKET; + int bind_port_; + std::string bind_ip_; }; class UdpPingPong { -public: - static const int N = 4; + public: + static const int N = 4; - void Start(uint32_t session_id, uint8_t peer_id, int port, int timeout_min_ms, int timeout_max_ms); + void Start(uint32_t session_id, uint8_t peer_id, int port, int timeout_min_ms, int timeout_max_ms); - void Stop(); + void Stop(); - void Reset(); + void Reset(); - bool Running(); + bool Running(); - int ElapsedMs(); + int ElapsedMs(); - void AddCandidate(const std::string& user_id, uint8_t peer_id, const std::string& ip, int port); + void AddCandidate(const std::string &user_id, uint8_t peer_id, const std::string &ip, int port); - bool GetAvailableAddress(uint8_t peer_id, sockaddr_in* dst, float* rtt); + bool GetAvailableAddress(uint8_t peer_id, sockaddr_in *dst, float *rtt); - void GetRttMatrix(uint8_t matrix[N][N]); + void GetRttMatrix(uint8_t matrix[N][N]); -private: - static const uint32_t MAGIC = 1434750950; - static const uint8_t PING = 1; - static const uint8_t PONG = 2; + private: + static const uint32_t MAGIC = 1434750950; + static const uint8_t PING = 1; + static const uint8_t PONG = 2; - struct Candidate { - uint8_t peer_id; - UdpRemote remote; - int ping_count; - int pong_count; - float rtt; - }; + struct Candidate { + uint8_t peer_id; + UdpRemote remote; + int ping_count; + int pong_count; + float rtt; + }; #pragma pack(1) - struct Packet { - uint32_t magic; - uint32_t session_id; - uint8_t type; - uint8_t from_peer_id; - uint8_t to_peer_id; - uint64_t send_timestamp; - uint64_t ping_timestamp; - uint8_t rtt_matrix[N][N]; - }; + struct Packet { + uint32_t magic; + uint32_t session_id; + uint8_t type; + uint8_t from_peer_id; + uint8_t to_peer_id; + uint64_t send_timestamp; + uint64_t ping_timestamp; + uint8_t rtt_matrix[N][N]; + }; #pragma pack() - std::atomic running_; - std::chrono::high_resolution_clock::time_point start_time_; - UdpClient client_ = UdpClient{}; - std::recursive_mutex mutex_; - uint8_t rtt_matrix_[N][N]; - std::vector candidates_; - std::map user_to_peer_; - std::map peer_to_user_; + std::atomic running_; + std::chrono::high_resolution_clock::time_point start_time_; + UdpClient client_ = UdpClient{}; + std::recursive_mutex mutex_; + uint8_t rtt_matrix_[N][N]; + std::vector candidates_; + std::map user_to_peer_; + std::map peer_to_user_; }; diff --git a/core/gdxsv/gdxsv_translation.cpp b/core/gdxsv/gdxsv_translation.cpp index be437d632e..088cc5ac1f 100644 --- a/core/gdxsv/gdxsv_translation.cpp +++ b/core/gdxsv/gdxsv_translation.cpp @@ -6,102 +6,104 @@ // Copyright © 2021 flycast. All rights reserved. // #ifdef _WIN32 -#define _AMD64_ // Fixing GitHub runner's winnt.h error +#define _AMD64_ // Fixing GitHub runner's winnt.h error #endif -#include "cfg/option.h" #include "gdxsv_translation.h" -const char * GdxsvTranslation::Text() const { - switch (GdxsvLanguage::Language()) { - case GdxsvLanguage::Lang::English: return english; - case GdxsvLanguage::Lang::Cantonese: return cantonese; - case GdxsvLanguage::Lang::Japanese: return japanese ? japanese : original; - } - return original; +#include "cfg/option.h" + +const char* GdxsvTranslation::Text() const { + switch (GdxsvLanguage::Language()) { + case GdxsvLanguage::Lang::English: + return english; + case GdxsvLanguage::Lang::Cantonese: + return cantonese; + case GdxsvLanguage::Lang::Japanese: + return japanese ? japanese : original; + } + return original; } GdxsvLanguage::Lang GdxsvLanguage::Language() { - Lang lang = static_cast(config::GdxLanguage.get()); - switch (lang) { - case Lang::Japanese: - case Lang::Cantonese: - case Lang::English: - case Lang::Disabled: - return lang; - case Lang::NOT_SET: - default: - lang = LanguageFromOS(); - config::GdxLanguage.set((int)lang); - return lang; - } + Lang lang = static_cast(config::GdxLanguage.get()); + switch (lang) { + case Lang::Japanese: + case Lang::Cantonese: + case Lang::English: + case Lang::Disabled: + return lang; + case Lang::NOT_SET: + default: + lang = LanguageFromOS(); + config::GdxLanguage.set((int)lang); + return lang; + } } std::string GdxsvLanguage::TextureDirectoryName() { - switch (Language()) { - case Lang::English: return "English"; - case Lang::Cantonese: return "Cantonese"; - default: return "Japanese"; - } + switch (Language()) { + case Lang::English: + return "English"; + case Lang::Cantonese: + return "Cantonese"; + default: + return "Japanese"; + } } #ifdef _WIN32 #include -#include + #include +#include #endif GdxsvLanguage::Lang GdxsvLanguage::LanguageFromOS() { #ifdef __APPLE__ - extern std::string os_Locale(); - std::string locale = os_Locale(); - - time_t ts = 0; - struct tm t; - char buf[16]; - localtime_r(&ts, &t); - strftime(buf, sizeof(buf), "%z%Z", &t); + extern std::string os_Locale(); + std::string locale = os_Locale(); + + time_t ts = 0; + struct tm t; + char buf[16]; + localtime_r(&ts, &t); + strftime(buf, sizeof(buf), "%z%Z", &t); #elif _WIN32 - std::string locale; - DWORD bufferLength = 0; - ULONG numberOfLanguages = 0; - std::wstring languagesWString; - GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, - &numberOfLanguages, - NULL, - &bufferLength); - languagesWString.resize(bufferLength); - BOOL result = GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, - &numberOfLanguages, - const_cast(languagesWString.data()), - &bufferLength); - if ( result ) { - std::wstring_convert> convert; - locale = convert.to_bytes(languagesWString); - } else { - locale = "en"; - } + std::string locale; + DWORD bufferLength = 0; + ULONG numberOfLanguages = 0; + std::wstring languagesWString; + GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &numberOfLanguages, NULL, &bufferLength); + languagesWString.resize(bufferLength); + BOOL result = + GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &numberOfLanguages, const_cast(languagesWString.data()), &bufferLength); + if (result) { + std::wstring_convert > convert; + locale = convert.to_bytes(languagesWString); + } else { + locale = "en"; + } #else - std::string locale = "en"; + std::string locale = "en"; #endif - if (locale.find("ja") == 0 + if (locale.find("ja") == 0 #ifdef __APPLE__ - || strcmp(buf, "+0900JST") == 0 + || strcmp(buf, "+0900JST") == 0 #elif _WIN32 - || strcmp(_tzname[0], "Tokyo Standard Time") == 0 + || strcmp(_tzname[0], "Tokyo Standard Time") == 0 #endif - ) - return Lang::Japanese; - else if (locale.find("yue") == 0 || locale.find("zh") == 0 //Chinese fallback + ) + return Lang::Japanese; + else if (locale.find("yue") == 0 || locale.find("zh") == 0 // Chinese fallback #ifdef __APPLE__ - || strcmp(buf, "+0800HKT") == 0 //Cantonese users love using English OS - || strcmp(buf, "+0800CST") == 0 + || strcmp(buf, "+0800HKT") == 0 // Cantonese users love using English OS + || strcmp(buf, "+0800CST") == 0 #elif _WIN32 - || strcmp(_tzname[0], "China Standard Time") == 0 - || strcmp(_tzname[0], "Taipei Standard Time") == 0 + || strcmp(_tzname[0], "China Standard Time") == 0 || strcmp(_tzname[0], "Taipei Standard Time") == 0 #endif - ) - return Lang::Cantonese; - else - return Lang::English; + ) + return Lang::Cantonese; + else + return Lang::English; } diff --git a/core/gdxsv/gdxsv_translation.h b/core/gdxsv/gdxsv_translation.h index 3bba287807..05e7d78ef1 100644 --- a/core/gdxsv/gdxsv_translation.h +++ b/core/gdxsv/gdxsv_translation.h @@ -12,53 +12,49 @@ #endif struct GdxsvTranslation { - u32 offset = 0; - const char* original = nullptr; - const char* cantonese = nullptr; - const char* english = nullptr; - const char* japanese = nullptr; - const char* Text() const; + u32 offset = 0; + const char* original = nullptr; + const char* cantonese = nullptr; + const char* english = nullptr; + const char* japanese = nullptr; + const char* Text() const; }; -template +template static GdxsvTranslation GdxsvTranslationWithMaxLength(u32 o, const char* orig, char const (&canto)[C_SIZE], char const (&eng)[E_SIZE]) { - static_assert(sizeof(canto) <= MAXSIZE, "canto is too big."); - static_assert(sizeof(eng) <= MAXSIZE, "eng is too big."); - GdxsvTranslation v; - v.offset = o; - v.original = orig; - v.cantonese = canto; - v.english = eng; - v.japanese = nullptr; - return v; + static_assert(sizeof(canto) <= MAXSIZE, "canto is too big."); + static_assert(sizeof(eng) <= MAXSIZE, "eng is too big."); + GdxsvTranslation v; + v.offset = o; + v.original = orig; + v.cantonese = canto; + v.english = eng; + v.japanese = nullptr; + return v; } -template -static GdxsvTranslation GdxsvTranslationWithMaxLength(u32 o, const char* orig, char const (&canto)[C_SIZE], char const (&eng)[E_SIZE], char const (&jap)[J_SIZE]) { - static_assert(sizeof(canto) <= MAXSIZE, "canto is too big."); - static_assert(sizeof(eng) <= MAXSIZE, "eng is too big."); - static_assert(sizeof(jap) <= MAXSIZE, "jap is too big."); - GdxsvTranslation v; - v.offset = o; - v.original = orig; - v.cantonese = canto; - v.english = eng; - v.japanese = jap; - return v; +template +static GdxsvTranslation GdxsvTranslationWithMaxLength(u32 o, const char* orig, char const (&canto)[C_SIZE], char const (&eng)[E_SIZE], + char const (&jap)[J_SIZE]) { + static_assert(sizeof(canto) <= MAXSIZE, "canto is too big."); + static_assert(sizeof(eng) <= MAXSIZE, "eng is too big."); + static_assert(sizeof(jap) <= MAXSIZE, "jap is too big."); + GdxsvTranslation v; + v.offset = o; + v.original = orig; + v.cantonese = canto; + v.english = eng; + v.japanese = jap; + return v; } class GdxsvLanguage { -public: - enum class Lang { - NOT_SET = -1, - Japanese, - Cantonese, - English, - Disabled - }; + public: + enum class Lang { NOT_SET = -1, Japanese, Cantonese, English, Disabled }; - static Lang Language(); - static std::string TextureDirectoryName(); -private: - static Lang LanguageFromOS(); + static Lang Language(); + static std::string TextureDirectoryName(); + + private: + static Lang LanguageFromOS(); }; diff --git a/core/gdxsv/lbs_message.h b/core/gdxsv/lbs_message.h index 1d50919f51..7799dd5b3e 100644 --- a/core/gdxsv/lbs_message.h +++ b/core/gdxsv/lbs_message.h @@ -1,211 +1,208 @@ #pragma once -#include "types.h" -#include #include +#include + +#include "types.h" class LbsMessage { -public: - u8 direction = 0; - u8 category = 0; - u16 command = 0; - u16 body_size = 0; - u16 seq = 0; - u32 status = 0; - std::vector body; - int reading = 0; - - static const int HeaderSize = 12; - static const u8 ServerToClient = 0x18; - static const u8 ClientToServer = 0x81; - static const u8 CategoryQuestion = 0x01; - static const u8 CategoryAnswer = 0x02; - static const u8 CategoryNotice = 0x10; - static const u8 CategoryCustom = 0xFF; - static const u32 StatusError = 0xFFFFFFFFu; - static const u32 StatusSuccess = 0x00FFFFFFu; - - static const u16 lbsLineCheck = 0x6001; - static const u16 lbsUserRegist = 0x6112; - static const u16 lbsUserDecide = 0x6113; - - static const u16 lbsLobbyMatchingEntry = 0x640e; - static const u16 lbsReadyBattle = 0x6910; - static const u16 lbsAskMatchingJoin = 0x6911; - static const u16 lbsAskPlayerSide = 0x6912; - static const u16 lbsAskPlayerInfo = 0x6913; - static const u16 lbsAskRuleData = 0x6914; - static const u16 lbsAskBattleCode = 0x6915; - static const u16 lbsAskMcsAddress = 0x6916; - static const u16 lbsAskMcsVersion = 0x6917; - static const u16 lbsLogout = 0x6002; - static const u16 lbsShutdown = 0x6003; - static const u16 lbsGamePatch = 0x9960; - static const u16 lbsP2PMatching = 0x9961; - static const u16 lbsBattleUserCount = 0x9965; - - int Serialize(std::deque &buf) const { - buf.push_back(direction); - buf.push_back(category); - buf.push_back((command >> 8) & 0xffu); - buf.push_back((command) & 0xffu); - buf.push_back((body.size() >> 8) & 0xffu); - buf.push_back((body.size()) & 0xffu); - buf.push_back((seq >> 8) & 0xffu); - buf.push_back((seq & 0xffu)); - buf.push_back((status >> 24) & 0xffu); - buf.push_back((status >> 16) & 0xffu); - buf.push_back((status >> 8) & 0xffu); - buf.push_back((status & 0xffu)); - std::copy(std::begin(body), std::end(body), std::back_inserter(buf)); - return int(HeaderSize) + int(body.size()); - } - - int Deserialize(const std::deque &buf) { - if (buf.size() < HeaderSize) { - return 0; - } - - int pkt_size = HeaderSize + ((int(buf[4]) << 8) | int(buf[5])); - if (buf.size() < pkt_size) { - return 0; - } - - direction = buf[0]; - category = buf[1]; - command = u16(buf[2]) << 8 | u16(buf[3]); - body_size = u16(buf[4] << 8) | u16(buf[5]); - seq = u16(buf[6] << 8) | u16(buf[7]); - status = u32(buf[8]) << 24 | u32(buf[9]) << 16 | u32(buf[10]) << 8 | u32(buf[11]); - body.clear(); - for (int i = HeaderSize; i < HeaderSize + body_size; ++i) { - body.push_back(buf[i]); - } - reading = 0; - return pkt_size; - } - - static int MessageSize(const char *buf, int size) { //TODO - if (size < HeaderSize) { - return 0; - } - - int pkt_size = HeaderSize + (int(buf[4] << 8) | int(buf[5])); - if (size < pkt_size) { - return 0; - } - - return pkt_size; - } - - static LbsMessage SvAnswer(const LbsMessage &q) { - LbsMessage msg; - msg.direction = ServerToClient; - msg.category = CategoryAnswer; - msg.command = q.command; - msg.seq = q.seq; - msg.status = StatusSuccess; - return msg; - } - - static LbsMessage SvNotice(u16 cmd) { - LbsMessage msg; - msg.direction = ServerToClient; - msg.category = CategoryNotice; - msg.command = cmd; - msg.seq = 1; - msg.status = StatusSuccess; - return msg; - } - - LbsMessage *Write8(u8 v) { - body.push_back(v); - return this; - } - - LbsMessage *Write16(u16 v) { - body.push_back(u8((v >> 8) & 0xffu)); - body.push_back(u8(v & 0xffu)); - return this; - } - - LbsMessage *Write32(u32 v) { - body.push_back(u8((v >> 24) & 0xff)); - body.push_back(u8((v >> 16) & 0xff)); - body.push_back(u8((v >> 8) & 0xff)); - body.push_back(u8((v) & 0xff)); - return this; - } - - LbsMessage *WriteBytes(const std::string &v) { - u16 size = v.size(); - Write16(size); - std::copy(std::begin(v), std::end(v), std::back_inserter(body)); - return this; - } - - LbsMessage *WriteBytes(const char *buf, int size) { - Write16(size); - std::copy(buf, buf + size, std::back_inserter(body)); - return this; - } - - LbsMessage *WriteString(const std::string &s) { - // TODO utf8 -> sjis - return WriteBytes(s); - } - - u8 Read8() { - u8 v = body[reading]; - reading++; - return v; - } - - u16 Read16() { - u16 v = u16(body[reading]) << 8 | u16(body[reading]); - reading += 2; - return v; - } - - u32 Read32() { - u32 v = u32(body[reading]) << 24 | u32(body[reading]) << 16 | u32(body[reading]) << 8 | body[reading]; - reading += 4; - return v; - } - - std::string to_hex() const { - std::deque tmp; - Serialize(tmp); - - std::string ret(tmp.size() * 2, ' '); - for (int i = 0; i < tmp.size(); i++) { - std::sprintf(&ret[0] + i * 2, "%02x", tmp[i]); - } - return ret; - } + public: + u8 direction = 0; + u8 category = 0; + u16 command = 0; + u16 body_size = 0; + u16 seq = 0; + u32 status = 0; + std::vector body; + int reading = 0; + + static const int HeaderSize = 12; + static const u8 ServerToClient = 0x18; + static const u8 ClientToServer = 0x81; + static const u8 CategoryQuestion = 0x01; + static const u8 CategoryAnswer = 0x02; + static const u8 CategoryNotice = 0x10; + static const u8 CategoryCustom = 0xFF; + static const u32 StatusError = 0xFFFFFFFFu; + static const u32 StatusSuccess = 0x00FFFFFFu; + + static const u16 lbsLineCheck = 0x6001; + static const u16 lbsUserRegist = 0x6112; + static const u16 lbsUserDecide = 0x6113; + + static const u16 lbsLobbyMatchingEntry = 0x640e; + static const u16 lbsReadyBattle = 0x6910; + static const u16 lbsAskMatchingJoin = 0x6911; + static const u16 lbsAskPlayerSide = 0x6912; + static const u16 lbsAskPlayerInfo = 0x6913; + static const u16 lbsAskRuleData = 0x6914; + static const u16 lbsAskBattleCode = 0x6915; + static const u16 lbsAskMcsAddress = 0x6916; + static const u16 lbsAskMcsVersion = 0x6917; + static const u16 lbsLogout = 0x6002; + static const u16 lbsShutdown = 0x6003; + static const u16 lbsGamePatch = 0x9960; + static const u16 lbsP2PMatching = 0x9961; + static const u16 lbsBattleUserCount = 0x9965; + + int Serialize(std::deque &buf) const { + buf.push_back(direction); + buf.push_back(category); + buf.push_back((command >> 8) & 0xffu); + buf.push_back((command)&0xffu); + buf.push_back((body.size() >> 8) & 0xffu); + buf.push_back((body.size()) & 0xffu); + buf.push_back((seq >> 8) & 0xffu); + buf.push_back((seq & 0xffu)); + buf.push_back((status >> 24) & 0xffu); + buf.push_back((status >> 16) & 0xffu); + buf.push_back((status >> 8) & 0xffu); + buf.push_back((status & 0xffu)); + std::copy(std::begin(body), std::end(body), std::back_inserter(buf)); + return int(HeaderSize) + int(body.size()); + } + + int Deserialize(const std::deque &buf) { + if (buf.size() < HeaderSize) { + return 0; + } + + int pkt_size = HeaderSize + ((int(buf[4]) << 8) | int(buf[5])); + if (buf.size() < pkt_size) { + return 0; + } + + direction = buf[0]; + category = buf[1]; + command = u16(buf[2]) << 8 | u16(buf[3]); + body_size = u16(buf[4] << 8) | u16(buf[5]); + seq = u16(buf[6] << 8) | u16(buf[7]); + status = u32(buf[8]) << 24 | u32(buf[9]) << 16 | u32(buf[10]) << 8 | u32(buf[11]); + body.clear(); + for (int i = HeaderSize; i < HeaderSize + body_size; ++i) { + body.push_back(buf[i]); + } + reading = 0; + return pkt_size; + } + + static int MessageSize(const char *buf, int size) { // TODO + if (size < HeaderSize) { + return 0; + } + + int pkt_size = HeaderSize + (int(buf[4] << 8) | int(buf[5])); + if (size < pkt_size) { + return 0; + } + + return pkt_size; + } + + static LbsMessage SvAnswer(const LbsMessage &q) { + LbsMessage msg; + msg.direction = ServerToClient; + msg.category = CategoryAnswer; + msg.command = q.command; + msg.seq = q.seq; + msg.status = StatusSuccess; + return msg; + } + + static LbsMessage SvNotice(u16 cmd) { + LbsMessage msg; + msg.direction = ServerToClient; + msg.category = CategoryNotice; + msg.command = cmd; + msg.seq = 1; + msg.status = StatusSuccess; + return msg; + } + + LbsMessage *Write8(u8 v) { + body.push_back(v); + return this; + } + + LbsMessage *Write16(u16 v) { + body.push_back(u8((v >> 8) & 0xffu)); + body.push_back(u8(v & 0xffu)); + return this; + } + + LbsMessage *Write32(u32 v) { + body.push_back(u8((v >> 24) & 0xff)); + body.push_back(u8((v >> 16) & 0xff)); + body.push_back(u8((v >> 8) & 0xff)); + body.push_back(u8((v)&0xff)); + return this; + } + + LbsMessage *WriteBytes(const std::string &v) { + u16 size = v.size(); + Write16(size); + std::copy(std::begin(v), std::end(v), std::back_inserter(body)); + return this; + } + + LbsMessage *WriteBytes(const char *buf, int size) { + Write16(size); + std::copy(buf, buf + size, std::back_inserter(body)); + return this; + } + + LbsMessage *WriteString(const std::string &s) { + // TODO utf8 -> sjis + return WriteBytes(s); + } + + u8 Read8() { + u8 v = body[reading]; + reading++; + return v; + } + + u16 Read16() { + u16 v = u16(body[reading]) << 8 | u16(body[reading]); + reading += 2; + return v; + } + + u32 Read32() { + u32 v = u32(body[reading]) << 24 | u32(body[reading]) << 16 | u32(body[reading]) << 8 | body[reading]; + reading += 4; + return v; + } + + std::string to_hex() const { + std::deque tmp; + Serialize(tmp); + + std::string ret(tmp.size() * 2, ' '); + for (int i = 0; i < tmp.size(); i++) { + std::sprintf(&ret[0] + i * 2, "%02x", tmp[i]); + } + return ret; + } }; class LbsMessageReader { -public: - void Write(const char *buf, int size) { - std::copy(buf, buf + size, std::back_inserter(buf_)); - } - - bool Read(LbsMessage &msg) { - int size = msg.Deserialize(buf_); - if (size == 0) { - return false; - } - for (int i = 0; i < size; ++i) { - buf_.pop_front(); - } - return true; - } - - void Clear() { - buf_.clear(); - } - -private: - std::deque buf_; + public: + void Write(const char *buf, int size) { std::copy(buf, buf + size, std::back_inserter(buf_)); } + + bool Read(LbsMessage &msg) { + int size = msg.Deserialize(buf_); + if (size == 0) { + return false; + } + for (int i = 0; i < size; ++i) { + buf_.pop_front(); + } + return true; + } + + void Clear() { buf_.clear(); } + + private: + std::deque buf_; }; \ No newline at end of file diff --git a/core/gdxsv/mcs_message.h b/core/gdxsv/mcs_message.h index fb05536c0d..5b31aa48c9 100644 --- a/core/gdxsv/mcs_message.h +++ b/core/gdxsv/mcs_message.h @@ -1,232 +1,215 @@ #pragma once -#include "types.h" - -#include #include +#include + +#include "types.h" class McsMessage { -public: - enum MsgType { - ConnectionIdMsg, - IntroMsg, - IntroMsgReturn, - PingMsg, - PongMsg, - StartMsg, - ForceMsg, - KeyMsg1, - KeyMsg2, - LoadStartMsg, - LoadEndMsg, - LagControlTestMsg, - UnknownMsg, - }; - - static const char *MsgTypeName(MsgType m) { - if (m == ConnectionIdMsg) return "ConnectionIdMsg"; - if (m == StartMsg) return "StartMsg"; - if (m == IntroMsg) return "IntroMsg"; - if (m == IntroMsgReturn) return "IntroMsgReturn"; - if (m == KeyMsg1) return "KeyMsg1"; - if (m == KeyMsg2) return "KeyMsg2"; - if (m == PingMsg) return "PingMsg"; - if (m == PongMsg) return "PongMsg"; - if (m == LoadStartMsg) return "LoadStartMsg"; - if (m == LoadEndMsg) return "LoadEndMsg"; - if (m == LagControlTestMsg) return "LagControlTestMsg"; - if (m == ForceMsg) return "ForceMsg"; - if (m == UnknownMsg) return "UnknownMsg"; - return "UnknownDefault"; - } - - MsgType Type() const { - if (body.size() < 4) return UnknownMsg; - if (body[0] == 0x82 && body[1] == 0x02) return ConnectionIdMsg; - - const int n = body[0]; - const int k = (body[1] & 0xf0) >> 4; - const int p = body[2]; - - if (k == 1 && p == 0) return IntroMsg; - if (k == 1 && p == 1) return IntroMsgReturn; - if (n == 0x0a && k == 2) return KeyMsg1; - if (n == 0x12 && k == 2) return KeyMsg2; - if (k == 3 && p == 0) return PingMsg; - if (k == 3 && p == 1) return PongMsg; - if (k == 4) return StartMsg; - if (k == 5 && p == 0) return LoadStartMsg; - if (k == 5 && p == 1) return LoadEndMsg; - if (k == 7) return ForceMsg; - if (k == 9) return LagControlTestMsg; - return UnknownMsg; - } - - template - int Deserialize(const T &buf) { - if (buf.size() < 4) { - return 0; - } - - if (buf[0] == 0x82 && buf[1] == 0x02) { - const int n = 20; - body.clear(); - std::copy_n(buf.begin(), n, std::back_inserter(body)); - return n; - } - - const int n = buf[0]; - if (buf.size() < n) { - return 0; - } - - body.clear(); - std::copy_n(buf.begin(), n, std::back_inserter(body)); - return n; - } - - static McsMessage Create(MsgType type, u8 p) { - McsMessage msg; - if (type == IntroMsg) { - msg.body.assign({0x04, 0x10, 0x00, 0x00}); - } else if (type == IntroMsgReturn) { - msg.body.assign({0x04, 0x10, 0x01, 0x00}); - } else if (type == StartMsg) { - msg.body.assign({0x04, 0x40, 0x00, 0x00}); - } else if (type == KeyMsg1) { - msg.body.assign( - {0x0a, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); - } else if (type == KeyMsg2) { - msg.body.assign( - {0x12, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); - } else if (type == PingMsg) { - // TODO unknown detail - msg.body.assign( - {0x14, 0x30, - 0x00, 0x00, - 0x04, 0x12, 0x01, 0x00, - 0x04, 0x12, 0x01, 0x00, - 0x00, 0x36, 0x36, 0x38, 0x39, 0x31, 0x32, 0x32}); - } else if (type == PongMsg) { - // TODO unknown detail - msg.body.assign({0x06, 0x30, 0x01, 0x00, 0x02, 0x00}); - } else if (type == LoadStartMsg) { - msg.body.assign({0x04, 0x50, 0x00, 0x00}); - } else if (type == LoadEndMsg) { - msg.body.assign({0x04, 0x50, 0x01, 0x00}); - } else if (type == LagControlTestMsg) { - msg.body.assign({0x04, 0x90, 0x00, 0x00}); - } else if (type == ForceMsg) { - msg.body.assign({0x04, 0x70, 0x00, 0x00}); - } else if (type == ConnectionIdMsg) { - verify(false); - } else if (type == UnknownMsg) { - verify(false); - } - - if (2 <= msg.body.size()) { - msg.body[1] |= p; - } - return msg; - } - - std::string ToHex() const { - std::string ret(body.size() * 2, ' '); - for (int i = 0; i < body.size(); i++) { - std::sprintf(&ret[0] + i * 2, "%02x", body[i]); - } - return ret; - } - - McsMessage *SetPongTo(int id) { - verify(Type() == MsgType::PongMsg); - body[4] = id; - return this; - } - - int PingCount() const { - verify(Type() == MsgType::PingMsg); - return body[4]; - } - - void PongCount(int n) { - verify(Type() == MsgType::PongMsg); - body[3] = n; - } - - int FirstSwCrnt() const { - verify(Type() == MsgType::KeyMsg1 || Type() == MsgType::KeyMsg2); - return int(body[7]) << 8 | int(body[6]); - } - - int SecondSwCrnt() const { - verify(Type() == MsgType::KeyMsg2); - return int(body[15]) << 8 | int(body[14]); - } - - int FirstSeq() const { - verify(Type() == MsgType::KeyMsg1 || Type() == MsgType::KeyMsg2); - return int(body[9]) << 8 | int(body[8]); - } - - int SecondSeq() const { - verify(Type() == MsgType::KeyMsg2); - return int(body[17]) << 8 | int(body[16]); - } - - McsMessage FirstKeyMsg() const { - verify(Type() == MsgType::KeyMsg2); - auto ret = McsMessage::Create(MsgType::KeyMsg1, Sender()); - for (int i = 0; i < 8; ++i) { - ret.body[2 + i] = body[2 + i]; - } - return ret; - } - - McsMessage SecondKeyMsg() const { - verify(Type() == MsgType::KeyMsg2); - auto ret = McsMessage::Create(MsgType::KeyMsg1, Sender()); - for (int i = 0; i < 8; ++i) { - ret.body[2 + i] = body[10 + i]; - } - return ret; - } - - int Sender() const { - return body[1] & 0x0f; - } - - void Sender(int p) { - body[1] = (body[1] & 0xf0) | p; - } - - std::vector body; + public: + enum MsgType { + ConnectionIdMsg, + IntroMsg, + IntroMsgReturn, + PingMsg, + PongMsg, + StartMsg, + ForceMsg, + KeyMsg1, + KeyMsg2, + LoadStartMsg, + LoadEndMsg, + LagControlTestMsg, + UnknownMsg, + }; + + static const char *MsgTypeName(MsgType m) { + if (m == ConnectionIdMsg) return "ConnectionIdMsg"; + if (m == StartMsg) return "StartMsg"; + if (m == IntroMsg) return "IntroMsg"; + if (m == IntroMsgReturn) return "IntroMsgReturn"; + if (m == KeyMsg1) return "KeyMsg1"; + if (m == KeyMsg2) return "KeyMsg2"; + if (m == PingMsg) return "PingMsg"; + if (m == PongMsg) return "PongMsg"; + if (m == LoadStartMsg) return "LoadStartMsg"; + if (m == LoadEndMsg) return "LoadEndMsg"; + if (m == LagControlTestMsg) return "LagControlTestMsg"; + if (m == ForceMsg) return "ForceMsg"; + if (m == UnknownMsg) return "UnknownMsg"; + return "UnknownDefault"; + } + + MsgType Type() const { + if (body.size() < 4) return UnknownMsg; + if (body[0] == 0x82 && body[1] == 0x02) return ConnectionIdMsg; + + const int n = body[0]; + const int k = (body[1] & 0xf0) >> 4; + const int p = body[2]; + + if (k == 1 && p == 0) return IntroMsg; + if (k == 1 && p == 1) return IntroMsgReturn; + if (n == 0x0a && k == 2) return KeyMsg1; + if (n == 0x12 && k == 2) return KeyMsg2; + if (k == 3 && p == 0) return PingMsg; + if (k == 3 && p == 1) return PongMsg; + if (k == 4) return StartMsg; + if (k == 5 && p == 0) return LoadStartMsg; + if (k == 5 && p == 1) return LoadEndMsg; + if (k == 7) return ForceMsg; + if (k == 9) return LagControlTestMsg; + return UnknownMsg; + } + + template + int Deserialize(const T &buf) { + if (buf.size() < 4) { + return 0; + } + + if (buf[0] == 0x82 && buf[1] == 0x02) { + const int n = 20; + body.clear(); + std::copy_n(buf.begin(), n, std::back_inserter(body)); + return n; + } + + const int n = buf[0]; + if (buf.size() < n) { + return 0; + } + + body.clear(); + std::copy_n(buf.begin(), n, std::back_inserter(body)); + return n; + } + + static McsMessage Create(MsgType type, u8 p) { + McsMessage msg; + if (type == IntroMsg) { + msg.body.assign({0x04, 0x10, 0x00, 0x00}); + } else if (type == IntroMsgReturn) { + msg.body.assign({0x04, 0x10, 0x01, 0x00}); + } else if (type == StartMsg) { + msg.body.assign({0x04, 0x40, 0x00, 0x00}); + } else if (type == KeyMsg1) { + msg.body.assign({0x0a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); + } else if (type == KeyMsg2) { + msg.body.assign({0x12, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); + } else if (type == PingMsg) { + // TODO unknown detail + msg.body.assign( + {0x14, 0x30, 0x00, 0x00, 0x04, 0x12, 0x01, 0x00, 0x04, 0x12, 0x01, 0x00, 0x00, 0x36, 0x36, 0x38, 0x39, 0x31, 0x32, 0x32}); + } else if (type == PongMsg) { + // TODO unknown detail + msg.body.assign({0x06, 0x30, 0x01, 0x00, 0x02, 0x00}); + } else if (type == LoadStartMsg) { + msg.body.assign({0x04, 0x50, 0x00, 0x00}); + } else if (type == LoadEndMsg) { + msg.body.assign({0x04, 0x50, 0x01, 0x00}); + } else if (type == LagControlTestMsg) { + msg.body.assign({0x04, 0x90, 0x00, 0x00}); + } else if (type == ForceMsg) { + msg.body.assign({0x04, 0x70, 0x00, 0x00}); + } else if (type == ConnectionIdMsg) { + verify(false); + } else if (type == UnknownMsg) { + verify(false); + } + + if (2 <= msg.body.size()) { + msg.body[1] |= p; + } + return msg; + } + + std::string ToHex() const { + std::string ret(body.size() * 2, ' '); + for (int i = 0; i < body.size(); i++) { + std::sprintf(&ret[0] + i * 2, "%02x", body[i]); + } + return ret; + } + + McsMessage *SetPongTo(int id) { + verify(Type() == MsgType::PongMsg); + body[4] = id; + return this; + } + + int PingCount() const { + verify(Type() == MsgType::PingMsg); + return body[4]; + } + + void PongCount(int n) { + verify(Type() == MsgType::PongMsg); + body[3] = n; + } + + int FirstSwCrnt() const { + verify(Type() == MsgType::KeyMsg1 || Type() == MsgType::KeyMsg2); + return int(body[7]) << 8 | int(body[6]); + } + + int SecondSwCrnt() const { + verify(Type() == MsgType::KeyMsg2); + return int(body[15]) << 8 | int(body[14]); + } + + int FirstSeq() const { + verify(Type() == MsgType::KeyMsg1 || Type() == MsgType::KeyMsg2); + return int(body[9]) << 8 | int(body[8]); + } + + int SecondSeq() const { + verify(Type() == MsgType::KeyMsg2); + return int(body[17]) << 8 | int(body[16]); + } + + McsMessage FirstKeyMsg() const { + verify(Type() == MsgType::KeyMsg2); + auto ret = McsMessage::Create(MsgType::KeyMsg1, Sender()); + for (int i = 0; i < 8; ++i) { + ret.body[2 + i] = body[2 + i]; + } + return ret; + } + + McsMessage SecondKeyMsg() const { + verify(Type() == MsgType::KeyMsg2); + auto ret = McsMessage::Create(MsgType::KeyMsg1, Sender()); + for (int i = 0; i < 8; ++i) { + ret.body[2 + i] = body[10 + i]; + } + return ret; + } + + int Sender() const { return body[1] & 0x0f; } + + void Sender(int p) { body[1] = (body[1] & 0xf0) | p; } + + std::vector body; }; class McsMessageReader { -public: - void Write(const char *buf, int size) { - std::copy(buf, buf + size, std::back_inserter(buf_)); - } - - bool Read(McsMessage &msg) { - int size = msg.Deserialize(buf_); - if (size == 0) { - return false; - } - for (int i = 0; i < size; ++i) { - buf_.pop_front(); - } - return true; - } - - void Clear() { - buf_.clear(); - } - -private: - std::deque buf_; + public: + void Write(const char *buf, int size) { std::copy(buf, buf + size, std::back_inserter(buf_)); } + + bool Read(McsMessage &msg) { + int size = msg.Deserialize(buf_); + if (size == 0) { + return false; + } + for (int i = 0; i < size; ++i) { + buf_.pop_front(); + } + return true; + } + + void Clear() { buf_.clear(); } + + private: + std::deque buf_; }; \ No newline at end of file diff --git a/gdxsv-format.sh b/gdxsv-format.sh new file mode 100644 index 0000000000..c284006cd5 --- /dev/null +++ b/gdxsv-format.sh @@ -0,0 +1,2 @@ +FILES=$(find core/gdxsv -type f -name "*.cpp" -or -name "*.h" -not -name "*.pb.h") +clang-format -style='{BasedOnStyle: google, UseTab: Always, IndentWidth: 4, TabWidth: 4, ColumnLimit: 140}' -i ${FILES} From 3e0efab857790002e3e3c4b9f7db3661a748858d Mon Sep 17 00:00:00 2001 From: Shingo INADA Date: Wed, 5 Oct 2022 21:25:38 +0900 Subject: [PATCH 2/2] fix include --- core/network/net_platform.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/network/net_platform.h b/core/network/net_platform.h index a665620156..4d1ae7570f 100644 --- a/core/network/net_platform.h +++ b/core/network/net_platform.h @@ -19,6 +19,8 @@ along with flycast. If not, see . */ #pragma once +#include "types.h" + #ifndef _WIN32 #include #include