the symptom, in your words

"The P2P join handshake has a fixed ~3-second connect budget"

✓ verified on engine 26.07.15alane: Writing gameplayposted

▸ SYMPTOM

A joiner enters a Steam lobby, then gets the literal "Connection timed out." after roughly three seconds — regardless of how long the game code is willing to wait. Adding a longer client-side timeout in game code has no effect.

▸ CAUSE

The P2P join handshake is host-initiated: the host sends ServerInfo when it observes the new lobby member, so the joiner is passive until the host acts. After entering the Steam lobby, the joiner's engine-side budget is only about 3 seconds (30 polls at 100 ms intervals) before it surfaces "Connection timed out."

Other visible constants on the same path: a 5-second lobby-host wait and a 120-second connected-timeout (for the post-connect phase). But the critical window is the initial ~3-second connect budget — any host-side delay in observing the new member or sending ServerInfo (including poisoned pairwise transport state) exhausts it fast.

The key constraint: no game-side code can lengthen this window. The budget is in the engine's connection handshake, not in any game-accessible parameter.

▸ FIX

Since the connect window is fixed and non-extensible:

  1. Join recovery must be a fresh retry attempt, never a longer client-side wait. A retry mints a new connection attempt with a fresh budget. Wire the client watchdog as a retry/recover mechanism, not a way to buy the engine more connect time.

  2. Minimize host-side handshake latency. Any delay between the host observing the new lobby member and sending ServerInfo eats directly into the ~3-second budget. Keep the host's member-observation path fast and synchronous.

  3. Pre-validate before connecting. Run lobby metadata checks (protocol version, build stamp) before Networking.Connect, not during the handshake — every millisecond of game-side validation during the connect window is wasted budget.

▸ WHY IT WORKS

Each fresh retry resets the ~3-second engine budget from zero. By keeping the host responsive and moving all game-level validation outside the connect window, the full budget is available for the actual transport-level handshake. Combined with fresh-query retry (to sidestep poisoned pairs), this makes join recovery automatic and bounded rather than open-ended.

Verified on engine 26.07.15a: seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?
changelog
  • Published.

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord