createWebSocketSendRate
Send high-rate state, such as aim, cursor or position, at a rate that adapts to the connection. Messages are coalesced per key between ticks, and the rate drops when the link is congested.
Available from both react-websocket and react-websocket/core.
Usage
Send as often as you like, for example on every pointer move. At most maxRate messages per key go out each second, fewer on a congested link, and each one is the newest:
The guide is Real-time games.
Parameters
Returns
WebSocketSendRate<Out>:
SendRate
Show it in a HUD with useSyncExternalStore(stream.subscribe, stream.getSnapshot).
Behavior
- Coalescing. Between ticks, only the newest message per key is kept. A replaced message moves to the end, so keys go out in the order they were last sent. Every message needs a key: send one-shot actions such as fire or jump with
client.sendinstead, afterflush()if they must follow the latest state. - Ticks. A tick runs only while the socket is open and something is waiting. The first message after a pause goes out on the next task, and later ones at most
ratetimes a second. Messages are sent withvolatile. - Adapting. At each tick, if more than
maxBufferedbytes are waiting in the send buffer, nothing is sent and the rate halves. Otherwise everything waiting is sent and the rate climbs by one. Withlatency, each new measurement whoserttis more thanmaxRttIncreaseabove the lowest one on this connection halves the rate too, and stops it climbing until the round trip comes back down. The rate stays betweenminRateandmaxRate. From 5 back to 30 takes about two seconds. - Steady high ping is fine. Only a round trip that rises counts, so a player who is far away but on a good link keeps the full rate.
- Reconnects. Messages waiting during a reconnect are kept and go out after it, after
openlisteners and the client's own queue.close()and giving up drop them. The lowest round trip is measured afresh on each connection, and the rate carries over. - Doesn't connect. It doesn't open or hold the connection.
- Not for inputs you predict. Coalescing drops messages, and an input dropped this way would never be confirmed. For timeline inputs, read
getSnapshot().rateto decide how often to calltimeline.input(), and put the steps in between into one input. The rate only adapts on ticks, so this works while you also stream something, such as aim, throughsend().