Files
psxrecomp/runtime/tests/test_netplay_local_view.c
Shokunin 465f926ecf netplay: confirm against every peer, shed the own view when behind, progress-based stall timeout
Four-peer rollback desynced under load (R4, host behind): the guests
predicted the slow host's pad, hash-confirmed the tick among themselves
through the one shared peer ring, then promoted the host's real pad into
history only (hc-silent promote) while the host simulated it. The host
stayed forked; its recovery episode was then refused as a "zombie load"
behind the guests' (wrongly) agreed tip.

- Hash confirm is a quorum over every occupied peer seat
  (rnet_hc_set_peer_mask / note_peer_from, recomp-net) for three or more
  seats; two seats keep the one-peer chain.
- A peer more than 4 ticks behind the others' inputs sheds its own view
  (psx_mod_render_local_view status FAST_FORWARD, held 60 ticks), so the
  presentation-only pass goes before the simulation slips.
  PSX_NET_LOCAL_VIEW_SHED=0 keeps it.
- The admit-stall watchdog counts time without progress: a remote input tip
  that advances restarts it, so a slow peer is waited for.
  PSX_NET_ADMIT_STALL_MS (20000) and PSX_NET_LIVENESS_MS (1500) configure it.
- Pins recomp-net fix/rollback-span-stall.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:59:03 -07:00

74 lines
3.5 KiB
C

#include "netplay_local_view.h"
#undef NDEBUG
#include <assert.h>
#include <string.h>
int main(void)
{
PsxNetplayLocalView view;
uint32_t x = 0, y = 0, w = 0, h = 0;
psx_netplay_local_view_reset(&view);
assert(!psx_netplay_local_view_get(&view, 10, 320, 240, &x, &y, &w, &h));
/* Empty and out-of-VRAM rectangles are refused. */
assert(!psx_netplay_local_view_set(&view, 10, 0, 0, 0, 120));
assert(!psx_netplay_local_view_set(&view, 10, 1000, 0, 160, 120));
assert(!view.valid);
assert(psx_netplay_local_view_set(&view, 100, 160, 120, 160, 120));
assert(psx_netplay_local_view_get(&view, 100, 320, 240, &x, &y, &w, &h));
assert(x == 160 && y == 120 && w == 160 && h == 120);
/* Held for PSX_NETPLAY_LOCAL_VIEW_HOLD ticks, then lapses. */
assert(psx_netplay_local_view_get(&view, 100 + PSX_NETPLAY_LOCAL_VIEW_HOLD,
320, 240, NULL, NULL, NULL, NULL));
assert(!psx_netplay_local_view_get(&view,
101 + PSX_NETPLAY_LOCAL_VIEW_HOLD,
320, 240, NULL, NULL, NULL, NULL));
/* A display smaller than the rectangle presents the full frame. */
assert(!psx_netplay_local_view_get(&view, 100, 256, 240,
NULL, NULL, NULL, NULL));
/* Resimulating an older tick renews the rectangle, not the stamp. */
assert(psx_netplay_local_view_set(&view, 96, 0, 0, 160, 120));
assert(view.tick == 100);
assert(psx_netplay_local_view_get(&view, 104, 320, 240, &x, &y, &w, &h));
assert(x == 0 && y == 0);
/* A present slightly behind a resimulated request still uses it. */
assert(psx_netplay_local_view_get(&view, 98, 320, 240,
NULL, NULL, NULL, NULL));
/* A load far behind restarts the stamp. */
assert(psx_netplay_local_view_set(&view, 10, 0, 0, 160, 120));
assert(view.tick == 10);
assert(!psx_netplay_local_view_get(&view, 200, 320, 240,
NULL, NULL, NULL, NULL));
psx_netplay_local_view_reset(&view);
assert(!psx_netplay_local_view_get(&view, 10, 320, 240,
NULL, NULL, NULL, NULL));
/* Load shedding: D=6, so remote_lead 6 is on pace. */
{
PsxNetplayLocalViewShed shed;
memset(&shed, 0, sizeof shed);
assert(!psx_netplay_local_view_shed_step(&shed, 1000, 6, 6));
assert(!psx_netplay_local_view_shed_step(&shed, 1001, 9, 6));
/* Four ticks behind: shed. */
assert(psx_netplay_local_view_shed_step(&shed, 1002, 10, 6));
/* Caught up at once, but held for the minimum window. */
assert(psx_netplay_local_view_shed_step(&shed, 1003, 6, 6));
assert(psx_netplay_local_view_shed_step(
&shed, 1001 + PSX_NETPLAY_LOCAL_VIEW_SHED_HOLD, 6, 6));
/* Past the window but still 2 behind: keep shedding. */
assert(psx_netplay_local_view_shed_step(
&shed, 1002 + PSX_NETPLAY_LOCAL_VIEW_SHED_HOLD, 8, 6));
/* Within one tick of pace again: the own view returns. */
assert(!psx_netplay_local_view_shed_step(
&shed, 1003 + PSX_NETPLAY_LOCAL_VIEW_SHED_HOLD, 7, 6));
/* A rollback/load far back does not leave it shed for ages. */
assert(psx_netplay_local_view_shed_step(&shed, 5000, 20, 6));
assert(psx_netplay_local_view_shed_step(&shed, 100, 6, 6));
assert(shed.until == 100 + PSX_NETPLAY_LOCAL_VIEW_SHED_HOLD);
}
return 0;
}