From 0a5a235f5e7813d4d6f6dae804352bc450d3173c Mon Sep 17 00:00:00 2001 From: Brendan Turner Date: Sun, 11 Oct 2026 11:23:04 -0500 Subject: [PATCH] Ask STUN and the brokers again after the p2p thread was stopped When the process is suspended (a phone app in the background) or the machine sleeps (a laptop), the p2p thread's wait lasts far longer than it asked for. The NAT's mapping of the tunnel may have lapsed and the brokers' TCP connections may be gone, but nothing notices until each one's silence timeout, so joins and listings fail for a while after the game resumes. When a wait lasts longer than its length plus PEER_TIMEOUT, the thread now discards what the sockets reported, asks the STUN servers for the public address again, and has every broker close and connect again at once (p2p_signal_reconnect). A wait of normal length changes nothing. Nothing sent over the network changes, so HALO_PORT_NETWORK_VERSION stays. Checked: the Android guest and the web build compile with no new warnings. The same change, together with an iOS host fix for the sockets iOS takes from a suspended app, made server browser joins work on an iPad Pro after about five minutes in the background (they failed until a relaunch without both). Not run on Linux, Windows or Android. --- port/linux/src/p2p.c | 29 +++++++++++++++++++++++++++++ port/linux/src/p2p_internal.h | 4 ++++ port/linux/src/p2p_signal.c | 17 +++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/port/linux/src/p2p.c b/port/linux/src/p2p.c index 170028e3..ba6c89c4 100644 --- a/port/linux/src/p2p.c +++ b/port/linux/src/p2p.c @@ -1406,6 +1406,26 @@ static int stun_settled(void) return 1; } +/* the p2p thread was stopped for longer than a peer lasts: the process was +suspended (a phone app in the background) or the machine slept (a laptop). +The NAT's mapping of the tunnel may have lapsed, so this machine's public +address is asked again before an offer names it, and the brokers' +connections, which the network may have dropped, are made anew rather than +at their silence timeout */ +static void network_resumed(unsigned long stopped) +{ + int index; + + for (index = 0; index < p2p.stun_count; index++) + { + p2p.stun[index].has_mapped = 0; + p2p.stun[index].attempts = 0; + } + p2p_signal_reconnect(); + platform_log("Internet play: the game was stopped for %lu seconds; its public address is asked again and " + "the brokers reconnected", stopped / 1000); +} + /* ---------- stand-ins for peers' ports */ static void close_socket(int *socket) @@ -2942,6 +2962,7 @@ static void *p2p_thread(void *unused) streams; otherwise the thread can sleep longer */ int wait = webrtc_busy ? LOOP_INTERVAL : LOOP_INTERVAL * 5; int index, asked; + unsigned long waited; /* what to wait for */ read_owners[read_count] = _owner_tunnel; @@ -3002,6 +3023,7 @@ static void *p2p_thread(void *unused) memcpy(asked_write, write, sizeof(*write) * (size_t)write_count); pthread_mutex_unlock(&p2p_lock); + waited = p2p_now(); if (posix_socket_select(read, &read_count, write, &write_count, NULL, &error_count, 0, wait * 1000, 0) < 0) { @@ -3011,6 +3033,13 @@ static void *p2p_thread(void *unused) #ifdef HALO_PROFILE profile_trace_begin(p2p_profile_names.pass); #endif + /* the wait lasted far longer than asked (the process was suspended + or the machine slept): what the sockets report is stale */ + if (elapsed(waited, (unsigned long)wait + PEER_TIMEOUT)) + { + network_resumed(p2p_now() - waited); + read_count = write_count = 0; + } /* what is ready (the lists now hold only ready sockets, in the order asked, so each one's owner is found going along both); a socket diff --git a/port/linux/src/p2p_internal.h b/port/linux/src/p2p_internal.h index b3e54d6a..dfd59bc8 100644 --- a/port/linux/src/p2p_internal.h +++ b/port/linux/src/p2p_internal.h @@ -130,6 +130,10 @@ void p2p_signal_join(const unsigned char *host_hash, const unsigned char *token) void p2p_signal_stop_joining(void); /* whether any broker is connected */ int p2p_signal_connected(void); +/* every broker's connection made anew at once: after the p2p thread was +stopped (the process was suspended, or the machine slept), the old ones +may carry nothing, and would be noticed only at their silence timeout */ +void p2p_signal_reconnect(void); /* the server browser's topics: the own slot and the queries (a listed game), and every slot (browsing) */ void p2p_signal_lobby_topics(int listed, int browsing); diff --git a/port/linux/src/p2p_signal.c b/port/linux/src/p2p_signal.c index efd1259e..c004af00 100644 --- a/port/linux/src/p2p_signal.c +++ b/port/linux/src/p2p_signal.c @@ -1844,6 +1844,23 @@ int p2p_signal_connected(void) return 0; } +void p2p_signal_reconnect(void) +{ + int index; + + for (index = 0; index < signalling.broker_count; index++) + { + struct broker *broker = &signalling.brokers[index]; + + if (broker->state == _broker_idle && broker->socket < 0 && !broker->failures) + continue; + broker_close(broker, 0); + broker->failures = 0; + /* (again at once) */ + broker->state_time = p2p_now() - MAXIMUM_RETRY_INTERVAL; + } +} + static void sync_all_topics(void) { int index;