mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 10:19:47 -04:00
a069f1ca0c
With three or more seats two guests can mispredict the same tick and both BEGIN. The tie-break (lower initiator slot wins) ran only on initiators, so a follower followed whichever BEGIN arrived first and stashed the rest. On a 4-seat WAN race the host followed slot 3's epoch while slot 1's won; the seats split across epochs, each waited for baselines that never came, and the match ended after 20 s at rb_baseline. The epoch id names its initiator, so a follower now applies the same rule to the episode it follows (netplay_rb_concurrent_begin): yield during the handshake and follow the winner, or drop the loser's BEGIN. A follower seeing a new epoch from the initiator it already follows (abort and reopen, the only case two seats can produce) is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
79 lines
3.7 KiB
C
79 lines
3.7 KiB
C
#include "netplay_peer_frontier.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
static int failures;
|
|
#define CHECK(cond, msg) \
|
|
do { \
|
|
if (!(cond)) { \
|
|
printf("FAIL: %s\n", msg); \
|
|
failures++; \
|
|
} else { \
|
|
printf("ok: %s\n", msg); \
|
|
} \
|
|
} while (0)
|
|
|
|
int main(void)
|
|
{
|
|
NetplayPeerFrontier f;
|
|
const uint32_t two = 1u << 1; /* host + seat 1 */
|
|
const uint32_t three = (1u << 1) | (1u << 2); /* host + seats 1, 2 */
|
|
const uint32_t four = three | (1u << 3);
|
|
|
|
netplay_peer_frontier_reset(&f);
|
|
CHECK(netplay_peer_frontier_min(&f, three) == 0u, "nothing advertised -> 0");
|
|
|
|
netplay_peer_frontier_note(&f, 1, 752u);
|
|
CHECK(netplay_peer_frontier_min(&f, two) == 752u, "two seats: the single peer's frontier");
|
|
CHECK(netplay_peer_frontier_min(&f, three) == 0u,
|
|
"three seats: one advert does not speak for the silent seat");
|
|
|
|
/* The 3-seat WAN failure: host had 2400 from the fast peer only. */
|
|
netplay_peer_frontier_reset(&f);
|
|
netplay_peer_frontier_note(&f, 1, 2344u);
|
|
netplay_peer_frontier_note(&f, 2, 2319u);
|
|
netplay_peer_frontier_note(&f, 1, 2400u);
|
|
CHECK(netplay_peer_frontier_min(&f, three) == 2319u, "slowest follower bounds the load");
|
|
|
|
netplay_peer_frontier_note(&f, 2, 2300u);
|
|
CHECK(netplay_peer_frontier_min(&f, three) == 2319u, "reordered older advert never lowers");
|
|
|
|
netplay_peer_frontier_note(&f, 3, 5000u);
|
|
CHECK(netplay_peer_frontier_min(&f, four) == 2319u, "four seats: still the slowest");
|
|
|
|
netplay_peer_frontier_clamp(&f, 2320u);
|
|
CHECK(f.through[1] == 2320u && f.through[2] == 2319u && f.through[3] == 2320u,
|
|
"clamp lowers every seat above the tick, leaves the rest");
|
|
|
|
netplay_peer_frontier_note(&f, -1, 9u);
|
|
netplay_peer_frontier_note(&f, 99, 9u);
|
|
CHECK(netplay_peer_frontier_min(&f, four) == 2319u, "out-of-range seats ignored");
|
|
CHECK(netplay_peer_frontier_min(&f, 0u) == 0u, "no peer seats -> 0");
|
|
|
|
CHECK(!netplay_fork_cap_retired(0u, 3136u), "no cap: nothing to retire");
|
|
CHECK(!netplay_fork_cap_retired(1928u, 1920u), "agreed below the cap keeps it");
|
|
CHECK(netplay_fork_cap_retired(1928u, 1928u), "agreed at the cap retires it");
|
|
CHECK(netplay_fork_cap_retired(1928u, 3136u), "agreed past the cap retires it (4-seat race)");
|
|
|
|
/* Epoch ids: (counter << 3) | initiator slot. */
|
|
#define EP(n, slot) (((uint32_t)(n) << 3) | (uint32_t)(slot))
|
|
CHECK(netplay_rb_concurrent_begin(EP(5, 1), EP(5, 1), 0, 7u) == 0, "same epoch: no contest");
|
|
CHECK(netplay_rb_concurrent_begin(EP(5, 2), EP(5, 1), 0, 7u) == 1,
|
|
"initiator: lower peer slot wins, we yield");
|
|
CHECK(netplay_rb_concurrent_begin(EP(5, 1), EP(5, 2), 0, 7u) == -1,
|
|
"initiator: higher peer slot loses, drop it");
|
|
CHECK(netplay_rb_concurrent_begin(EP(5, 3), EP(5, 1), 1, 7u) == 1,
|
|
"follower of slot 3 switches to slot 1 (4-seat WAN split)");
|
|
CHECK(netplay_rb_concurrent_begin(EP(5, 1), EP(5, 3), 1, 7u) == -1,
|
|
"follower of slot 1 drops slot 3's BEGIN");
|
|
CHECK(netplay_rb_concurrent_begin(EP(5, 0), EP(6, 0), 1, 7u) == 0,
|
|
"follower: initiator reopened -> not a contest (two-seat path)");
|
|
|
|
if (failures) {
|
|
printf("%d failure(s)\n", failures);
|
|
return 1;
|
|
}
|
|
printf("ALL PASS\n");
|
|
return 0;
|
|
}
|