mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
1633ecf1d0
Two rollback bookkeeping bugs that only show with more than two seats over a real link: - g_peer_resolved_through took the highest RESOLVED from any peer. With three seats the fastest follower stood in for the others, so the host opened loads past the slower followers' frontiers (3P WAN: load 2400 vs frontiers 2344 and 2319); every follower refused, episodes aborted and the session diverged at 2496 before the race started. Adverts are now kept per seat (netplay_peer_frontier.h) and the lowest across the peer roster is the frontier. - The §55 bisect cap (next load < L after a baseline mismatch at L) was only cleared by an episode commit. An abort followed by a Live heal left it armed for the rest of the session; once its loads left the snapshot ring every episode was refused with "no confirmed snap" (4P WAN: cap 1928, agreed 3136, ring 2656..3166) and a later fork at 3150 was never repaired. A live hash-confirmed agreed ADVANCE at or past the cap now retires it. Bumps lib/recomp-net for RB_RESOLVED sender attribution and rb_* queue retransmit coalescing (RetroPortingToolKit/recomp-net fix/rb-ctrl-queue-dedupe). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
3.0 KiB
C
84 lines
3.0 KiB
C
/* Per-seat peer RESOLVED frontiers for rollback episodes.
|
|
*
|
|
* A follower can only replay from a snapshot at or below the frontier it
|
|
* advertised. With two seats there is one follower and one number. With more,
|
|
* the initiator must open at or below the SLOWEST follower's frontier: taking
|
|
* the highest advert (the old single watermark) let the fastest peer stand in
|
|
* for the others, and on a 3-seat WAN race the host opened load=2400 against
|
|
* followers at 2344 and 2319 -- every follower refused, the episodes aborted
|
|
* and the session diverged before the race started.
|
|
*
|
|
* Header-only and free of runtime state so the bookkeeping is unit-tested
|
|
* (tests/test_netplay_peer_frontier.c). */
|
|
#ifndef NETPLAY_PEER_FRONTIER_H
|
|
#define NETPLAY_PEER_FRONTIER_H
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#define NETPLAY_PEER_FRONTIER_SEATS 16u
|
|
|
|
typedef struct NetplayPeerFrontier {
|
|
uint32_t through[NETPLAY_PEER_FRONTIER_SEATS];
|
|
} NetplayPeerFrontier;
|
|
|
|
static inline void netplay_peer_frontier_reset(NetplayPeerFrontier *f)
|
|
{
|
|
if (f)
|
|
memset(f, 0, sizeof(*f));
|
|
}
|
|
|
|
/* A RESOLVED advert from `seat`. Watermark: never lowers. */
|
|
static inline void netplay_peer_frontier_note(NetplayPeerFrontier *f, int seat,
|
|
uint32_t through)
|
|
{
|
|
if (!f || seat < 0 || (uint32_t)seat >= NETPLAY_PEER_FRONTIER_SEATS)
|
|
return;
|
|
if (through > f->through[seat])
|
|
f->through[seat] = through;
|
|
}
|
|
|
|
/* Evidence above `tick` was invalidated (realign) or a follower proved it
|
|
* cannot follow above it (NACK): no seat may stay above it. */
|
|
static inline void netplay_peer_frontier_clamp(NetplayPeerFrontier *f, uint32_t tick)
|
|
{
|
|
uint32_t i;
|
|
if (!f)
|
|
return;
|
|
for (i = 0; i < NETPLAY_PEER_FRONTIER_SEATS; ++i)
|
|
if (f->through[i] > tick)
|
|
f->through[i] = tick;
|
|
}
|
|
|
|
/* Frontier every seat in `seats` has reached: the lowest, and 0 while any of
|
|
* them has not advertised yet. */
|
|
static inline uint32_t netplay_peer_frontier_min(const NetplayPeerFrontier *f,
|
|
uint32_t seats)
|
|
{
|
|
uint32_t i, lo = 0xffffffffu;
|
|
int any = 0;
|
|
if (!f)
|
|
return 0u;
|
|
for (i = 0; i < NETPLAY_PEER_FRONTIER_SEATS; ++i) {
|
|
if (!(seats & (1u << i)))
|
|
continue;
|
|
any = 1;
|
|
if (f->through[i] < lo)
|
|
lo = f->through[i];
|
|
}
|
|
return any ? lo : 0u;
|
|
}
|
|
|
|
/* §55 bisect cap: after a baseline mismatch at load L the next load must be
|
|
* below L. Once every peer has hash-confirmed a tick at or past L (agreed
|
|
* ADVANCE from a live confirm) the fork is healed and the cap is stale; left
|
|
* armed it refused every later episode, because the only loads it allowed had
|
|
* long left the snapshot ring (4-seat race: cap 1928, agreed 3136, ring
|
|
* 2656..3166 -> "no confirmed snap" and a permanent fork from 3168). */
|
|
static inline int netplay_fork_cap_retired(uint32_t fork_cap, uint32_t agreed_through)
|
|
{
|
|
return fork_cap > 0u && agreed_through >= fork_cap;
|
|
}
|
|
|
|
#endif /* NETPLAY_PEER_FRONTIER_H */
|