netcode-revival: fix the EA-connect gate - XNetGetConnectStatus returns 2
Some checks are pending
Lint & Format Check / Format Check (push) Waiting to run
Some checks are pending
Lint & Format Check / Format Check (push) Waiting to run
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).
This commit is contained in:
parent
3016f14fe8
commit
6db6674892
1 changed files with 57 additions and 14 deletions
|
|
@ -37,6 +37,7 @@
|
|||
#include <winsock2.h> // NOLINT(build/include_order)
|
||||
#elif REX_PLATFORM_LINUX || REX_PLATFORM_MAC
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <sys/select.h>
|
||||
|
|
@ -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) {
|
||||
// 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<XNDNS*>(dns_guest);
|
||||
dns->status = 1; // non-zero = error
|
||||
*pdns = 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<sockaddr_in*>(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<const char*>(host), n);
|
||||
}
|
||||
} else {
|
||||
REXLOG_WARN("xnet trace: XNetDnsLookup('{}') failed to resolve",
|
||||
static_cast<const char*>(host));
|
||||
}
|
||||
}
|
||||
*pdns = dns_guest;
|
||||
if (event_handle) {
|
||||
auto ev = REX_KERNEL_OBJECTS()->LookupObject<XEvent>(event_handle);
|
||||
assert_not_null(ev);
|
||||
|
|
@ -784,6 +814,10 @@ u32 NetDll_XNetConnect_entry(u32 caller, ppc_ptr_t<XNADDR> 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<XNADDR> 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<XNADDR> 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<XNADDR> 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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue