diff --git a/include/rex/system/xnet_transport.h b/include/rex/system/xnet_transport.h index cf089bd..bb32ab5 100644 --- a/include/rex/system/xnet_transport.h +++ b/include/rex/system/xnet_transport.h @@ -138,7 +138,10 @@ class XNetTransport { XNetTransport& operator=(const XNetTransport&) = delete; void Reconnect(); - void StopLocked(); + // Stop the receive thread and join it WITHOUT holding mu_ (the Run thread + // needs mu_ to observe running_ and exit; joining under the lock deadlocks + // once recvfrom times out and the thread tries to lock mu_). + void StopThread(); void Run(); void HandlePacket(const uint8_t* data, int size); diff --git a/src/kernel/xam/xam_user.cpp b/src/kernel/xam/xam_user.cpp index 578533b..f91a65d 100644 --- a/src/kernel/xam/xam_user.cpp +++ b/src/kernel/xam/xam_user.cpp @@ -430,8 +430,13 @@ u32 XamUserCheckPrivilege_entry(u32 user_index, u32 mask, mapped_u32 out_value) } } - // If we deny everything, games should hopefully not try to do stuff. - *out_value = 0; + // netcode-revival: the game gates its party/session flow on + // XamUserCheckPrivilege (e.g. MULTIPLAYER_SESSIONS); denying everything + // (the old default) made "create a party" refuse. Grant requested online + // privileges so the P2P session flow opens. + *out_value = 1; + REXLOG_INFO("xam: XamUserCheckPrivilege(user={}, mask=0x{:08X}) -> granted", + user_index, mask); return X_ERROR_SUCCESS; } diff --git a/src/system/xnet_transport.cpp b/src/system/xnet_transport.cpp index cc60f6f..a81d24a 100644 --- a/src/system/xnet_transport.cpp +++ b/src/system/xnet_transport.cpp @@ -118,8 +118,16 @@ XNetTransport& XNetTransport::Instance() { } XNetTransport::~XNetTransport() { + StopThread(); // join outside the lock (see StopThread) std::lock_guard lock(mu_); - StopLocked(); + if (sock_ != -1) { + SocketClose(sock_); + sock_ = -1; + } + active_ = false; + joined_ = false; + sessions_.clear(); + sockets_.clear(); } uint32_t XNetTransport::LocalVirtualIp() { @@ -156,17 +164,29 @@ void XNetTransport::SetRelay(const char* host, uint16_t port, const char* room) host_ = h; port_ = port; room_ = r; - StopLocked(); } + StopThread(); // join the old thread OUTSIDE the lock Reconnect(); } -void XNetTransport::StopLocked() { - if (thread_.joinable()) { +void XNetTransport::StopThread() { + std::thread old; + { + std::lock_guard lock(mu_); running_ = false; - thread_.join(); + old = std::move(thread_); thread_ = {}; } + if (old.joinable()) { + old.join(); + } +} + +void XNetTransport::Reconnect() { + std::lock_guard lock(mu_); + if (host_.empty() || port_ == 0) { + return; + } if (sock_ != -1) { SocketClose(sock_); sock_ = -1; @@ -174,13 +194,7 @@ void XNetTransport::StopLocked() { active_ = false; joined_ = false; sessions_.clear(); -} - -void XNetTransport::Reconnect() { - std::lock_guard lock(mu_); - if (host_.empty() || port_ == 0) { - return; - } + relay_resolved_ = false; sockaddr_in relay_addr = {}; if (!Resolve(host_, port_, &relay_addr)) { REXLOG_WARN("xnet transport: cannot resolve relay '{}:{}'", host_, port_);