Skip to content

[1/4] NetherNet signaling reliability - #7

Open
SendableMetatype wants to merge 3 commits into
Kas-tle:masterfrom
SendableMetatype:nethernet-improvements
Open

[1/4] NetherNet signaling reliability#7
SendableMetatype wants to merge 3 commits into
Kas-tle:masterfrom
SendableMetatype:nethernet-improvements

Conversation

@SendableMetatype

@SendableMetatypeSendableMetatype commented Jul 2, 2026

Copy link
Copy Markdown

A set of improvements from running the NetherNet transport on a production Geyser deployment, where frequent signaling drops at player counts above 30 motivated most of this.

  • Make signaling WebSockets reconnectable in place: reconnect(freshToken) swaps only the socket while handlers, the server channel, factories, and live peer connections survive, so a signaling drop no longer requires tearing the transport down. The signaling channels also gain a pollable liveness API: isChannelAlive with a max silence window and getMillisSinceLastMessage, which consumers can watchdog against; WebSocket protocol pings every 15 seconds guarantee inbound pongs on a healthy socket, so the silence check stays reliable even when no signaling traffic is flowing and catches silently half open TCP that channel.isActive misses. Per channel scheduled tasks are cancelled on channel inactive, TURN credential pushes are applied for the lifetime of the socket, the JSON RPC endpoint refreshes credentials every 30 minutes so late joining peers never receive expired relay credentials, and the frame aggregator limit is raised to 128 KB for batched RPC frames.
  • Scope pending RPC request failures to their own socket: each in flight request records the WebSocket channel it was written to, so during a reconnect the old channel's inactive event fails only its own requests, not those already written to the replacement socket.
  • Fix array form params delivery: the object or array check introduced with the params support tested the message envelope rather than the params element, so array form params, which appear in batched frames, were being dropped.

First PR of a stack rebuilding the transport for high player counts: this one, then the transport core behind a backend seam, then HTTP signaling, then RTT exposure. This layer is standalone: it compiles against released webrtc-java 1.0.3 and merges on its own. Everything here has run in production through Geyser for about a month and is confirmed working; the array params fix is newer, from a pre submission review.

@Kas-tle

Copy link
Copy Markdown
Owner

Thank you for the PR. I will review this weekend.

@SendableMetatypeSendableMetatype changed the title NetherNet transport improvements from production useNetherNet signaling reliabilityAug 2, 2026
@SendableMetatypeSendableMetatype changed the title NetherNet signaling reliability[1/4] NetherNet signaling reliabilityAug 2, 2026
reconnect(freshToken) replaces only the socket to the signaling service.
The signaling instance, its handlers, and everything built on it (server
channel, WebRTC factories, live peer connections) survive, so a
signaling drop no longer requires tearing the transport down.
Liveness is now detectable on idle servers: a WebSocket protocol ping
every 15 seconds guarantees inbound pongs on a healthy socket, so
isChannelAlive(maxSilence) also catches silently half open TCP.
Per channel scheduled tasks are tracked and cancelled on channel
inactive, so reconnects no longer leak ping loops. TURN credential
pushes are applied for the lifetime of the socket instead of only
during connect, and the JSON RPC endpoint refreshes credentials every
30 minutes, so late joining peers no longer receive expired relay
credentials. Pending RPC requests fail fast when the socket dies. The
frame aggregator limit is raised to 128 KB for batched RPC frames.
Each in flight JSON RPC request now records the WebSocket channel it
was written to. When a socket dies, onChannelInactive fails only the
requests that were sent on that socket: during a reconnect the old
channel's inactive event can no longer fail requests already written
to the replacement socket, which previously left those callers with a
spurious ClosedChannelException while the reply was still on its way.
The object or array check introduced with the params support tested
the message envelope, which is always a JSON object, so the array
branch could never run: array form params fell into the object branch,
where getAsJsonObject throws on an array and the delivery was lost.
The check now tests the params element itself. Batched frames are
where array form params appear, so those deliveries were being
dropped.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@SendableMetatype@Kas-tle