netcode-revival: fix SetRelay deadlock + grant multiplayer privileges
Some checks are pending
Lint & Format Check / Format Check (push) Waiting to run

- Deadlock: SetRelay held the transport mutex while join()ing the receive
  thread, which needs the mutex to observe running_ and exit. With the 200ms
  recv timeout the thread woke and hit the lock -> a room/server change froze
  the game (every room keystroke reconnects the transport). StopThread now
  detaches the thread under the lock and joins WITHOUT it.
- XamUserCheckPrivilege denied everything, which is the gate the game's
  'create a party' flow checks (multiplayer sessions); grant requested
  privileges so the P2P session flow opens, and log the mask.
This commit is contained in:
MiTHRAL 2026-08-28 13:52:20 -04:00
parent 3229aa3632
commit 6fcc96de18
3 changed files with 37 additions and 15 deletions

View file

@ -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);

View file

@ -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;
}

View file

@ -118,8 +118,16 @@ XNetTransport& XNetTransport::Instance() {
}
XNetTransport::~XNetTransport() {
StopThread(); // join outside the lock (see StopThread)
std::lock_guard<std::mutex> 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<std::mutex> lock(mu_);
running_ = false;
thread_.join();
old = std::move(thread_);
thread_ = {};
}
if (old.joinable()) {
old.join();
}
}
void XNetTransport::Reconnect() {
std::lock_guard<std::mutex> 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<std::mutex> 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_);