mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
Lobby: honour transport "ice" launches (same-NAT peers no longer dial themselves)
The lobby server starts a 2-player room the host relay cannot carry on ICE (recomp-net-server start_transport, launch transport "ice"). This client knew only "host" and the legacy server relay. It inferred a relay from equal host/guest endpoints -- which is exactly what two peers behind one NAT advertise -- and each peer dialled its own public address: the match stayed at "linking" forever. - psx_lobby_launch_transport.h: one pure decision (host / ice / relay / direct) with a unit test covering ICE, same-NAT endpoints, host relay and the legacy relay cases. - PsxLobbyJoinInfo.transport_ice; the launch handler records it and never turns it into a relay; launch fill and resolve_use_ice start ICE p2p (signalled over the lobby) for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -53,6 +53,9 @@ if(BUILD_TESTING)
|
||||
target_include_directories(netplay_sim_pad_cache_test PRIVATE include)
|
||||
target_compile_definitions(netplay_sim_pad_cache_test PRIVATE PSX_MAX_PLAYERS=4)
|
||||
add_test(NAME netplay_sim_pad_cache_test COMMAND netplay_sim_pad_cache_test)
|
||||
add_executable(lobby_launch_transport_test tests/test_lobby_launch_transport.c)
|
||||
target_include_directories(lobby_launch_transport_test PRIVATE include)
|
||||
add_test(NAME lobby_launch_transport_test COMMAND lobby_launch_transport_test)
|
||||
add_executable(netplay_local_view_test tests/test_netplay_local_view.c)
|
||||
target_include_directories(netplay_local_view_test PRIVATE include)
|
||||
add_test(NAME netplay_local_view_test COMMAND netplay_local_view_test)
|
||||
|
||||
@@ -272,6 +272,10 @@ typedef struct PsxLobbyJoinInfo {
|
||||
* force_input_relay is 0. The host binds bind_hostport and accepts the
|
||||
* guest (2 seats) or hubs (3+). */
|
||||
int transport_host;
|
||||
/* Launch: transport "ice" -- a 2-player room started on ICE
|
||||
* (peer-to-peer, signalled over the lobby). No endpoint is dialled and
|
||||
* force_input_relay is 0 (psx_lobby_launch_transport.h). */
|
||||
int transport_ice;
|
||||
/* A server code (need_password | bad_password | …) or, from
|
||||
* psx_lobby_set_last_error, one sentence the launcher shows as is. */
|
||||
char last_error[192];
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/* psx_lobby_launch_transport.h - how a lobby `launch` is carried.
|
||||
*
|
||||
* The lobby server picks the transport at `start` (recomp-net-server
|
||||
* start_transport, WS_LOBBY.md "Host relay"):
|
||||
* "host" -- the host carries the match on host_endpoint; guests dial it.
|
||||
* "ice" -- a 2-player room the host relay cannot carry starts on ICE:
|
||||
* peer-to-peer over STUN/TURN, signalled over the lobby socket.
|
||||
* The endpoints are not dialled at all.
|
||||
* a relay_endpoint, or equal host/guest endpoints from a server that
|
||||
* rewrote both to its relay advertise address -- legacy server SFU.
|
||||
*
|
||||
* The last rule used to win over "ice": two peers behind one NAT advertise
|
||||
* the same public endpoint, so an ICE launch between them looked like a relay
|
||||
* and each peer dialled its own address -- the match never linked. */
|
||||
#ifndef PSX_LOBBY_LAUNCH_TRANSPORT_H
|
||||
#define PSX_LOBBY_LAUNCH_TRANSPORT_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
enum {
|
||||
PSX_LAUNCH_DIRECT = 0, /* dial the peer endpoint (legacy p2p UDP) */
|
||||
PSX_LAUNCH_HOST = 1, /* host relay: host binds, guests dial host_endpoint */
|
||||
PSX_LAUNCH_ICE = 2, /* ICE peer-to-peer, signalled over the lobby */
|
||||
PSX_LAUNCH_RELAY = 3 /* server input relay (SFU) */
|
||||
};
|
||||
|
||||
static inline int psx_lobby_endpoint_usable(const char *ep)
|
||||
{
|
||||
const char *colon;
|
||||
if (!ep || !ep[0]) return 0;
|
||||
colon = strrchr(ep, ':');
|
||||
if (!colon || !colon[1]) return 0;
|
||||
return strtoul(colon + 1, NULL, 10) != 0;
|
||||
}
|
||||
|
||||
/* transport: the launch's "transport" field ("" when absent).
|
||||
* relay_endpoint: the launch's relay_endpoint ("" when absent).
|
||||
* caps_force_relay: the host's match_caps asked for the server relay.
|
||||
* my_bind: this peer's own advertised bind ("" when unknown). */
|
||||
static inline int psx_lobby_launch_transport(const char *transport,
|
||||
const char *relay_endpoint,
|
||||
int caps_force_relay,
|
||||
const char *host_ep,
|
||||
const char *guest_ep,
|
||||
const char *my_bind)
|
||||
{
|
||||
if (transport && strcmp(transport, "host") == 0) return PSX_LAUNCH_HOST;
|
||||
if (transport && strcmp(transport, "ice") == 0) return PSX_LAUNCH_ICE;
|
||||
if (psx_lobby_endpoint_usable(relay_endpoint) || caps_force_relay)
|
||||
return PSX_LAUNCH_RELAY;
|
||||
if (psx_lobby_endpoint_usable(host_ep) && psx_lobby_endpoint_usable(guest_ep) &&
|
||||
strcmp(host_ep, guest_ep) == 0 &&
|
||||
(!my_bind || !my_bind[0] || strcmp(host_ep, my_bind) != 0))
|
||||
return PSX_LAUNCH_RELAY;
|
||||
return PSX_LAUNCH_DIRECT;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -15236,13 +15236,14 @@ namespace {
|
||||
* §108's "always SFU"). Prefer caps.force_input_relay from the
|
||||
* relay_endpoint rewrite; also infer when host==guest advertise. */
|
||||
out->force_input_relay =
|
||||
(g_lnch_hosting_lan || g_lnch_joined_lan || ji->transport_host)
|
||||
(g_lnch_hosting_lan || g_lnch_joined_lan || ji->transport_host || ji->transport_ice)
|
||||
? 0
|
||||
: (caps->force_input_relay ? 1 : 0);
|
||||
#if defined(RECOMP_LAUNCHER_HAS_HOST_RELAY)
|
||||
out->transport_host = ji->transport_host ? 1 : 0;
|
||||
#endif
|
||||
if (!out->force_input_relay && !ji->transport_host && !g_lnch_hosting_lan &&
|
||||
if (!out->force_input_relay && !ji->transport_host && !ji->transport_ice &&
|
||||
!g_lnch_hosting_lan &&
|
||||
!g_lnch_joined_lan && ji->host_endpoint[0] &&
|
||||
ji->guest_endpoint[0] &&
|
||||
std::strcmp(ji->host_endpoint, ji->guest_endpoint) == 0) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "psx_lobby_client.h"
|
||||
#include "psx_lobby_launch_transport.h"
|
||||
#include "netplay_bios_settle.h"
|
||||
|
||||
#include <ctype.h>
|
||||
@@ -1910,16 +1911,12 @@ static void copy_first_usable_endpoint(char *dst, size_t dst_len, const char *a,
|
||||
|
||||
static int using_server_input_relay(const PsxLobbyJoinInfo *j)
|
||||
{
|
||||
if (g_lc.match_caps.valid && g_lc.match_caps.force_input_relay)
|
||||
return 1;
|
||||
/* Server rewrote both endpoints to the same relay advertise address. */
|
||||
if (j && j->host_endpoint[0] && j->guest_endpoint[0] &&
|
||||
!endpoint_port_is_zero(j->host_endpoint) &&
|
||||
!endpoint_port_is_zero(j->guest_endpoint) &&
|
||||
strcmp(j->host_endpoint, j->guest_endpoint) == 0 &&
|
||||
(!g_lc.my_bind[0] || strcmp(j->host_endpoint, g_lc.my_bind) != 0))
|
||||
return 1;
|
||||
return 0;
|
||||
if (!j)
|
||||
return g_lc.match_caps.valid && g_lc.match_caps.force_input_relay;
|
||||
return psx_lobby_launch_transport(j->transport_ice ? "ice" : j->transport_host ? "host" : "",
|
||||
"", g_lc.match_caps.valid && g_lc.match_caps.force_input_relay,
|
||||
j->host_endpoint, j->guest_endpoint,
|
||||
g_lc.my_bind) == PSX_LAUNCH_RELAY;
|
||||
}
|
||||
|
||||
static void fill_peer_bind_from_join(void)
|
||||
@@ -3275,6 +3272,7 @@ static void handle_server_json(const char *json)
|
||||
transport_kind[0] = '\0';
|
||||
json_get_str(json, "transport", transport_kind, sizeof(transport_kind));
|
||||
g_lc.join.transport_host = strcmp(transport_kind, "host") == 0 ? 1 : 0;
|
||||
g_lc.join.transport_ice = strcmp(transport_kind, "ice") == 0 ? 1 : 0;
|
||||
rnet_host_relay_release_port(g_host_relay);
|
||||
g_lc.join.player_count = json_get_int(json, "player_count", g_lc.join.player_count);
|
||||
g_lc.join.max_slots = json_get_int(json, "max_slots", g_lc.join.max_slots);
|
||||
@@ -3283,7 +3281,10 @@ static void handle_server_json(const char *json)
|
||||
/* Prefer explicit relay_endpoint when the server opened input relay.
|
||||
* Apply after caps ingest: omitted force_input_relay must not leave
|
||||
* hosts on the hub path while guests dial the relay. */
|
||||
if (relay_endpoint[0] && !endpoint_port_is_zero(relay_endpoint)) {
|
||||
if (g_lc.join.transport_ice) {
|
||||
/* ICE: nothing is dialled; a stale relay ask must not survive. */
|
||||
if (g_lc.match_caps.valid) g_lc.match_caps.force_input_relay = 0;
|
||||
} else if (relay_endpoint[0] && !endpoint_port_is_zero(relay_endpoint)) {
|
||||
char relay_raw[PSX_LOBBY_ENDPOINT_LEN];
|
||||
strncpy(relay_raw, relay_endpoint, sizeof(relay_raw) - 1);
|
||||
relay_raw[sizeof(relay_raw) - 1] = '\0';
|
||||
@@ -3325,7 +3326,11 @@ static void handle_server_json(const char *json)
|
||||
json_get_str(json, "transport", transport, sizeof(transport));
|
||||
if (strcmp(transport, "ice_p2p") == 0)
|
||||
ice_p2p = 1;
|
||||
if (force_relay) {
|
||||
if (g_lc.join.transport_ice) {
|
||||
fprintf(stderr, "psx_lobby: launch transport=ice (peer-to-peer, "
|
||||
"signalled over the lobby)\n");
|
||||
fflush(stderr);
|
||||
} else if (force_relay) {
|
||||
if (peer_bad) {
|
||||
strncpy(g_lc.join.last_error, "missing_endpoints",
|
||||
sizeof(g_lc.join.last_error) - 1);
|
||||
|
||||
@@ -3800,6 +3800,19 @@ static int resolve_use_ice(const PsxNetplayConfig *cfg)
|
||||
fflush(stderr);
|
||||
return 0;
|
||||
}
|
||||
#if defined(PSX_HAS_LOBBY_CLIENT) && defined(RNET_ENABLE_ICE)
|
||||
/* The server started this room on ICE (2 players the host relay cannot
|
||||
* carry): peer-to-peer over STUN/TURN, signalled over the lobby socket.
|
||||
* Same-NAT peers advertise equal endpoints; that is not a relay. */
|
||||
if (in_motk_room && !cfg->force_input_relay) {
|
||||
const PsxLobbyJoinInfo *ji = psx_lobby_join_info();
|
||||
if (ji && ji->transport_ice) {
|
||||
fprintf(stderr, "psx_netplay: lobby launch transport=ice — ICE peer-to-peer\n");
|
||||
fflush(stderr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (cfg->force_input_relay || in_motk_room) {
|
||||
if (!cfg->peer_hostport || !cfg->peer_hostport[0]) {
|
||||
fprintf(stderr,
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "psx_lobby_launch_transport.h"
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* ICE launch between two peers behind one NAT: same public endpoint on
|
||||
* both sides must stay ICE, not turn into a relay to ourselves. */
|
||||
assert(psx_lobby_launch_transport("ice", "", 0, "136.25.29.198:7778",
|
||||
"136.25.29.198:7778", "") == PSX_LAUNCH_ICE);
|
||||
assert(psx_lobby_launch_transport("ice", "", 0, "1.2.3.4:7777", "5.6.7.8:7777", "")
|
||||
== PSX_LAUNCH_ICE);
|
||||
/* Host relay is unchanged, even with equal endpoints. */
|
||||
assert(psx_lobby_launch_transport("host", "", 0, "1.2.3.4:7777", "1.2.3.4:7777", "")
|
||||
== PSX_LAUNCH_HOST);
|
||||
/* Legacy server relay: explicit relay_endpoint, caps, or equal rewritten
|
||||
* endpoints with no transport field. */
|
||||
assert(psx_lobby_launch_transport("", "9.9.9.9:8777", 0, "", "", "") == PSX_LAUNCH_RELAY);
|
||||
assert(psx_lobby_launch_transport("", "", 1, "1.2.3.4:7777", "5.6.7.8:7777", "")
|
||||
== PSX_LAUNCH_RELAY);
|
||||
assert(psx_lobby_launch_transport("", "", 0, "9.9.9.9:8777", "9.9.9.9:8777", "")
|
||||
== PSX_LAUNCH_RELAY);
|
||||
/* ...but not our own bind echoed back, and not unusable ports. */
|
||||
assert(psx_lobby_launch_transport("", "", 0, "1.2.3.4:7777", "1.2.3.4:7777",
|
||||
"1.2.3.4:7777") == PSX_LAUNCH_DIRECT);
|
||||
assert(psx_lobby_launch_transport("", "", 0, "1.2.3.4:0", "1.2.3.4:0", "")
|
||||
== PSX_LAUNCH_DIRECT);
|
||||
assert(psx_lobby_launch_transport("", "", 0, "1.2.3.4:7777", "5.6.7.8:7777", "")
|
||||
== PSX_LAUNCH_DIRECT);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user