From 6db66748929b20e6669949db0cdf6023de8102ca Mon Sep 17 00:00:00 2001 From: MiTHRAL Date: Fri, 28 Aug 2026 22:23:34 -0400 Subject: [PATCH] netcode-revival: fix the EA-connect gate - XNetGetConnectStatus returns 2 The party state machine (sub_82A1B310 state 5) reads the RETURN VALUE of XNetGetConnectStatus and only advances to 'EA connected' (party[423]=1, which unlocks session advertise/join) when it equals 2. rexglue returned 0, so the game stalled on 'Cannot Connect to EA Servers' forever despite the pointer write. Also: - XNetDnsLookup now actually resolves hostnames (getaddrinfo) into XNDNS (status=0, cina, aina[0..]) instead of always erroring - the EA resolver (sub_8298A460) busy-waits on it. - XNetServerToInAddr ABI fixed: the game calls (caller, server_id, service_id, out), not (caller, XNADDR*, out, flags); returns the virtual IP (the party flow fakes EA connectivity via Connect/GetConnectStatus). --- src/kernel/xam/xam_net.cpp | 71 ++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 14 deletions(-) diff --git a/src/kernel/xam/xam_net.cpp b/src/kernel/xam/xam_net.cpp index 15feab0..a4db09c 100644 --- a/src/kernel/xam/xam_net.cpp +++ b/src/kernel/xam/xam_net.cpp @@ -37,6 +37,7 @@ #include // NOLINT(build/include_order) #elif REX_PLATFORM_LINUX || REX_PLATFORM_MAC #include +#include #include #include #include @@ -602,13 +603,42 @@ u32 NetDll_XNetGetEthernetLinkStatus_entry(u32 caller) { } u32 NetDll_XNetDnsLookup_entry(u32 caller, mapped_string host, u32 event_handle, mapped_u32 pdns) { - // TODO(gibbed): actually implement this - if (pdns) { - auto dns_guest = REX_KERNEL_MEMORY()->SystemHeapAlloc(sizeof(XNDNS)); - auto dns = REX_KERNEL_MEMORY()->TranslateVirtual(dns_guest); - dns->status = 1; // non-zero = error - *pdns = dns_guest; + // netcode-revival: actually resolve the hostname so the EA Nation resolver + // (sub_8298A460) and any DNS consumer get a valid address instead of the + // old always-error (status=1) result. The game polls status until it is + // not XNET_STATUS_PENDING (10036), then requires status==0 && cina>0 and + // uses aina[0]. + auto dns_guest = REX_KERNEL_MEMORY()->SystemHeapAlloc(sizeof(XNDNS)); + auto dns = REX_KERNEL_MEMORY()->TranslateVirtual(dns_guest); + dns->status = 1; + dns->cina = 0; + if (host) { + addrinfo hints = {}; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + addrinfo* res = nullptr; + if (getaddrinfo(host, nullptr, &hints, &res) == 0 && res != nullptr) { + uint32_t n = 0; + for (addrinfo* p = res; p != nullptr && n < 8; p = p->ai_next) { + auto* sin = reinterpret_cast(p->ai_addr); + if (sin->sin_family == AF_INET) { + dns->aina[n++] = sin->sin_addr; // network order + } + } + freeaddrinfo(res); + dns->status = 0; + dns->cina = n; + if (XnetTrace()) { + REXLOG_INFO("xnet trace: XNetDnsLookup('{}') -> {} address(es)", + static_cast(host), n); + } + } else { + REXLOG_WARN("xnet trace: XNetDnsLookup('{}') failed to resolve", + static_cast(host)); + } } + *pdns = dns_guest; if (event_handle) { auto ev = REX_KERNEL_OBJECTS()->LookupObject(event_handle); assert_not_null(ev); @@ -784,6 +814,10 @@ u32 NetDll_XNetConnect_entry(u32 caller, ppc_ptr_t xn_addr) { // https://learn.microsoft.com/en-us/previous-versions/windows/desktop/xna/xnetgetconnectstatus // INT XNetGetConnectStatus(const XNADDR* pxna, INT* pxncs) u32 NetDll_XNetGetConnectStatus_entry(u32 caller, ppc_ptr_t xn_addr, mapped_void status_ptr) { + // netcode-revival: the party state machine (sub_82A1B310 state 5) reads the + // RETURN VALUE and only advances to 'EA connected' when it equals 2 + // (XNET_CONNECT_STATUS_CONNECTED). Returning 0 stalled it on "Cannot + // Connect to EA Servers" forever. if (XnetTrace()) { REXLOG_INFO("xnet trace: XNetGetConnectStatus(caller={}) -> CONNECTED", caller); } @@ -792,18 +826,27 @@ u32 NetDll_XNetGetConnectStatus_entry(u32 caller, ppc_ptr_t xn_addr, map const uint32_t connected = 2; // XNET_CONNECT_STATUS_CONNECTED std::memcpy(status_ptr, &connected, sizeof(connected)); } - return 0; + return 2; } -// INT XNetServerToInAddr(const XNADDR* pxn, IN_ADDR* pina, DWORD dwFlags) -// netcode-revival: our server addresses also carry the virtual IP in ina. -u32 NetDll_XNetServerToInAddr_entry(u32 caller, ppc_ptr_t pxn, mapped_void pina, - u32 dwFlags) { - (void)dwFlags; - if (!pxn || !pina) { +// The game calls XNetServerToInAddr(caller=1, server_id, service_id, out) +// (sub_82F38580 maps r3/r4/r5 accordingly). netcode-revival: return our +// virtual IP (the machine's online identity); the party flow fakes EA +// connectivity via XNetConnect/XNetGetConnectStatus, so any stable address +// works here. +u32 NetDll_XNetServerToInAddr_entry(u32 caller, u32 server_id, u32 service_id, + mapped_void pina) { + (void)caller; + (void)server_id; + (void)service_id; + if (!pina) { return X_ERROR_INVALID_PARAMETER; } - uint32_t ip = pxn->ina.s_addr; + if (XnetTrace()) { + REXLOG_INFO("xnet trace: XNetServerToInAddr(server={}, service=0x{:08X}) -> virtual ip", + server_id, service_id); + } + const uint32_t ip = htonl(rex::system::XNetTransport::LocalVirtualIp()); std::memcpy(pina, &ip, sizeof(ip)); return 0; }