From 7b6322b3170be047005efd760051008ca60163ea Mon Sep 17 00:00:00 2001 From: MiTHRAL Date: Fri, 28 Aug 2026 14:20:58 -0400 Subject: [PATCH] netcode-revival: trace the online-path XNet/Xam calls Adds an 'xnet trace' log to the online-path functions (XNetGetTitleXnAddr, XNetGetEthernetLinkStatus, QosListen/QosLookup, CreateKey, Connect, GetConnectStatus, XamUserGetSigninState/Info, XamShowSigninUI, privilege) gated on the xnet_trace cvar (default on during bring-up, rate-limited for the polling signin-state calls). Running the game and clicking the online flow now shows exactly which gate the party/EA path hits, so we can fix the real check instead of guessing at the reverse-engineered globals. --- src/kernel/xam/xam_net.cpp | 40 +++++++++++++++++++++++++++++++++++++ src/kernel/xam/xam_user.cpp | 19 ++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/kernel/xam/xam_net.cpp b/src/kernel/xam/xam_net.cpp index 050600a..7b8de3c 100644 --- a/src/kernel/xam/xam_net.cpp +++ b/src/kernel/xam/xam_net.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -48,6 +49,15 @@ namespace xam { using namespace rex::system; using namespace rex::system::xam; +// netcode-revival: trace the online-path calls so a run shows exactly which +// XNet/Xam gates the game hits (party/Ea Nation debugging). Default on during +// the revival bring-up; disable with xnet_trace=false before any release. +REXCVAR_DEFINE_BOOL(xnet_trace, true, "Kernel", + "Log online-path XNet/Xam calls (netcode debugging)"); +bool XnetTrace() { + return REXCVAR_GET(xnet_trace); +} + // https://github.com/G91/TitanOffLine/blob/1e692d9bb9dfac386d08045ccdadf4ae3227bb5e/xkelib/xam/xamNet.h enum { XNCALLER_INVALID = 0x0, @@ -498,6 +508,10 @@ u32 NetDll_XNetGetTitleXnAddr_entry(u32 caller, ppc_ptr_t addr_ptr) { std::memset(addr_ptr->abOnline, 0, 20); // 0x60 = the game's "online" status bits (not the standard 0-3 enum; the // game checks (status & 0x60) everywhere it means "connected to LIVE"). + if (XnetTrace()) { + REXLOG_INFO("xnet trace: XNetGetTitleXnAddr(caller={}) -> status 0x60 (online)", + caller); + } return 0x60; } @@ -576,6 +590,10 @@ u32 NetDll_XNetGetEthernetLinkStatus_entry(u32 caller) { // machine only reaches the EA-Nation/online path when the link is up // (sub_82A1AAE8 returns the online server IP only if this is non-zero), // so returning 0 left it frozen on "connecting to EA Nation" forever. + if (XnetTrace()) { + REXLOG_INFO("xnet trace: XNetGetEthernetLinkStatus(caller={}) -> active 100Mbps", + caller); + } return XEthernetStatus::XNET_ETHERNET_LINK_ACTIVE | XEthernetStatus::XNET_ETHERNET_LINK_100MBPS; } @@ -639,6 +657,10 @@ u32 NetDll_XNetQosListen_entry(u32 caller, mapped_void id, mapped_void data, u32 (void)data_size; (void)r7; (void)flags; + if (XnetTrace()) { + REXLOG_INFO("xnet trace: XNetQosListen(caller={}, flags={}) -> ok (advertise)", + caller, flags); + } return 0; } @@ -696,6 +718,11 @@ u32 NetDll_XNetQosLookup_entry(u32 caller, u32 cxna, mapped_void apxna, mapped_v } qos->count = qos->count_pending = n; + if (XnetTrace()) { + REXLOG_INFO("xnet trace: XNetQosLookup(caller={}) -> {} session(s) found", + caller, n); + } + if (apxnqosResults) { *apxnqosResults = qos_guest; } @@ -713,6 +740,9 @@ u32 NetDll_XNetCreateKey_entry(u32 caller, mapped_void key) { if (!key) { return X_ERROR_INVALID_PARAMETER; } + if (XnetTrace()) { + REXLOG_INFO("xnet trace: XNetCreateKey(caller={})", caller); + } uint8_t* out = static_cast(key); uint64_t r = XnetNextRand(); for (int i = 0; i < 16; ++i) { @@ -726,6 +756,9 @@ u32 NetDll_XNetCreateKey_entry(u32 caller, mapped_void key) { // netcode-revival: accept + (Phase 1) register the session key. u32 NetDll_XNetRegisterKey_entry(u32 caller, mapped_void key) { + if (XnetTrace()) { + REXLOG_INFO("xnet trace: XNetRegisterKey(caller={})", caller); + } (void)key; return 0; } @@ -737,6 +770,10 @@ u32 NetDll_XNetUnregisterKey_entry(u32 caller, mapped_void key) { // netcode-revival: connecting to a peer is reported as established. u32 NetDll_XNetConnect_entry(u32 caller, ppc_ptr_t xn_addr) { + if (XnetTrace()) { + REXLOG_INFO("xnet trace: XNetConnect(caller={}, peer=10.80.0.{})", + caller, xn_addr ? (ntohl(xn_addr->ina.s_addr) & 0xFFu) : 0); + } (void)xn_addr; return 0; } @@ -744,6 +781,9 @@ 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) { + if (XnetTrace()) { + REXLOG_INFO("xnet trace: XNetGetConnectStatus(caller={}) -> CONNECTED", caller); + } (void)xn_addr; if (status_ptr) { const uint32_t connected = 2; // XNET_CONNECT_STATUS_CONNECTED diff --git a/src/kernel/xam/xam_user.cpp b/src/kernel/xam/xam_user.cpp index f91a65d..e3b9c71 100644 --- a/src/kernel/xam/xam_user.cpp +++ b/src/kernel/xam/xam_user.cpp @@ -43,6 +43,8 @@ using namespace rex::system::xam; extern std::atomic xam_dialogs_shown_; +REXCVAR_DECLARE(bool, xnet_trace); + i32 XamUserGetXUID_entry(u32 user_index, u32 type_mask, mapped_u64 xuid_ptr) { assert_true(type_mask == 1 || type_mask == 2 || type_mask == 3 || type_mask == 4 || type_mask == 7); @@ -82,6 +84,17 @@ u32 XamUserGetSigninState_entry(u32 user_index) { signin_state = user_profile->signin_state(); } } + if (REXCVAR_GET(xnet_trace)) { + // Rate-limited: the frontend polls this frequently. + static double last_log = 0.0; + const double now = + double(std::chrono::steady_clock::now().time_since_epoch().count()) / 1e9; + if (now - last_log >= 0.5) { + last_log = now; + REXLOG_INFO("xam trace: XamUserGetSigninState(user={}) -> {}", user_index, + signin_state); + } + } return signin_state; } @@ -113,6 +126,10 @@ i32 XamUserGetSigninInfo_entry(u32 user_index, u32 flags, ppc_ptr_txuid = user_profile->xuid(); info->signin_state = user_profile->signin_state(); rex::string::util_copy_truncating(info->name, user_profile->name(), rex::countof(info->name)); + if (REXCVAR_GET(xnet_trace)) { + REXLOG_INFO("xam trace: XamUserGetSigninInfo(user={}, flags={}) -> signin_state={}", + user_index, flags, user_profile->signin_state()); + } return X_E_SUCCESS; } @@ -538,6 +555,8 @@ u32 XamUserAreUsersFriends_entry(u32 user_index, u32 unk1, u32 unk2, mapped_u32 } u32 XamShowSigninUI_entry(u32 unk, u32 unk_mask) { + REXLOG_INFO("xam trace: XamShowSigninUI(unk={}, mask={}) - game asked for sign-in", + unk, unk_mask); // Mask values vary. Probably matching user types? Local/remote? KernelState* kernel_state = REX_KERNEL_STATE();