From a2e3d3fa35acb47592a4a5c4fcaee1eda0c602fb Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 23 Aug 2026 18:54:18 +0200 Subject: [PATCH] Bind reliable windows to the RUDP ACK horizon --- docs/cultnet-transport-parity.md | 2 +- packages/cultcache-py/src/cultnet_py/transport.py | 2 +- .../src/main/kotlin/org/gamecult/cultmesh/CultMesh.kt | 2 +- packages/cultnet-rs/src/rudp.rs | 2 +- packages/cultnet-rs/tests/cultnet.rs | 11 ++++++++++- packages/cultnet-ts/src/rudp.ts | 2 +- src/GameCult.Networking/CultNetTransport.cs | 2 +- 7 files changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/cultnet-transport-parity.md b/docs/cultnet-transport-parity.md index e9fd0ef8..35e33400 100644 --- a/docs/cultnet-transport-parity.md +++ b/docs/cultnet-transport-parity.md @@ -258,7 +258,7 @@ Current progress: echo against C#/Kotlin/Python/Rust UDP peers; local runtime tests prove timeout state transitions. - TypeScript, C#, Rust, Python, and Kotlin now share bounded reliable-send - backpressure for RUDP sessions. At most 64 reliable packets are admitted to + backpressure for RUDP sessions. At most 32 reliable packets are admitted to the wire at once; later fragments remain in a session-owned FIFO and exact acknowledgements promote the next packets. This keeps the send frontier well inside the 4,096-sequence receive history instead of blasting an entire large diff --git a/packages/cultcache-py/src/cultnet_py/transport.py b/packages/cultcache-py/src/cultnet_py/transport.py index ca877a2a..70af8f48 100644 --- a/packages/cultcache-py/src/cultnet_py/transport.py +++ b/packages/cultcache-py/src/cultnet_py/transport.py @@ -396,7 +396,7 @@ def create_rudp_transport_profile( class CultNetRudpSession: - RELIABLE_SEND_WINDOW_PACKETS = 64 + RELIABLE_SEND_WINDOW_PACKETS = 32 RECEIVED_SEQUENCE_WINDOW = 4_096 def __init__(self, options: CultNetRudpSessionOptions) -> None: diff --git a/packages/cultmesh-kotlin/src/main/kotlin/org/gamecult/cultmesh/CultMesh.kt b/packages/cultmesh-kotlin/src/main/kotlin/org/gamecult/cultmesh/CultMesh.kt index a7f90610..bd0f269f 100644 --- a/packages/cultmesh-kotlin/src/main/kotlin/org/gamecult/cultmesh/CultMesh.kt +++ b/packages/cultmesh-kotlin/src/main/kotlin/org/gamecult/cultmesh/CultMesh.kt @@ -2621,7 +2621,7 @@ private data class FragmentBuffer( class CultNetRudpSession(options: CultNetRudpSessionOptions) { companion object { - const val ReliableSendWindowPackets = 64 + const val ReliableSendWindowPackets = 32 private const val ReceivedSequenceWindow = 4_096 } diff --git a/packages/cultnet-rs/src/rudp.rs b/packages/cultnet-rs/src/rudp.rs index f14bf648..f84d8cae 100644 --- a/packages/cultnet-rs/src/rudp.rs +++ b/packages/cultnet-rs/src/rudp.rs @@ -29,7 +29,7 @@ const RUDP_MAGIC: [u8; 4] = [0x43, 0x4e, 0x52, 0x30]; const RUDP_VERSION: u8 = 0; const RUDP_FIXED_HEADER_BYTES: usize = 36; pub const CULTNET_RUDP_DEFAULT_MAX_PAYLOAD_BYTES: usize = 16 * 1024 * 1024; -pub const CULTNET_RUDP_RELIABLE_SEND_WINDOW_PACKETS: usize = 64; +pub const CULTNET_RUDP_RELIABLE_SEND_WINDOW_PACKETS: usize = 32; const RUDP_RECEIVED_SEQUENCE_WINDOW: usize = 4_096; #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/packages/cultnet-rs/tests/cultnet.rs b/packages/cultnet-rs/tests/cultnet.rs index 32e643d3..1669fd0e 100644 --- a/packages/cultnet-rs/tests/cultnet.rs +++ b/packages/cultnet-rs/tests/cultnet.rs @@ -68,6 +68,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration as StdDuration; +use std::time::Instant as StdInstant; const TS_HELLO_FRAME: &[u8] = include_bytes!("fixtures/cultnet-ts-hello.frame"); const TS_LEGACY_LOGIN_FRAME: &[u8] = include_bytes!("fixtures/cultnet-ts-legacy-login.frame"); @@ -1370,7 +1371,15 @@ fn rudp_socket_flush_waits_for_large_fragment_delivery() -> Result<()> { CULTNET_RUDP_RELIABLE_SEND_WINDOW_PACKETS + 17 ); - let server_worker = thread::spawn(move || receive_rudp_frame(&mut server)); + let server_worker = thread::spawn(move || -> Result { + let delivered = receive_rudp_frame(&mut server)?; + let linger_deadline = StdInstant::now() + StdDuration::from_millis(250); + while StdInstant::now() < linger_deadline { + let _ = server.receive_once()?; + server.poll_resends()?; + } + Ok(delivered) + }); client.flush_reliable(StdDuration::from_secs(5))?; let delivered = server_worker .join() diff --git a/packages/cultnet-ts/src/rudp.ts b/packages/cultnet-ts/src/rudp.ts index 7a1a9e17..6c69d1df 100644 --- a/packages/cultnet-ts/src/rudp.ts +++ b/packages/cultnet-ts/src/rudp.ts @@ -16,7 +16,7 @@ const RUDP_MAGIC = [0x43, 0x4e, 0x52, 0x30] as const; // CNR0 const RUDP_VERSION = 0; const RUDP_FIXED_HEADER_BYTES = 36; const MAX_CHANNEL_ID_BYTES = 255; -export const CULTNET_RUDP_RELIABLE_SEND_WINDOW_PACKETS = 64; +export const CULTNET_RUDP_RELIABLE_SEND_WINDOW_PACKETS = 32; const RUDP_RECEIVED_SEQUENCE_WINDOW = 4_096; export type CultNetRudpPacketType = diff --git a/src/GameCult.Networking/CultNetTransport.cs b/src/GameCult.Networking/CultNetTransport.cs index b64399b0..e65eb966 100644 --- a/src/GameCult.Networking/CultNetTransport.cs +++ b/src/GameCult.Networking/CultNetTransport.cs @@ -870,7 +870,7 @@ namespace GameCult.Networking public sealed class CultNetRudpSession { /// Maximum reliable packets admitted to the wire before acknowledgements advance the window. - public const int ReliableSendWindowPackets = 64; + public const int ReliableSendWindowPackets = 32; private const int ReceivedSequenceWindow = 4096; private sealed class PendingReliablePacket {