Accepted - 2026-06-09
After porting netcode, reliable, and serialize, three features remain unimplemented:
| Feature | Gap | Source reference |
|---|---|---|
| Reliable-ordered messages + aggressive resend | No message channel layer above ReliableEndpoint |
yojimbo_reliable_ordered_channel.h/.cpp |
| Large data blocks on reliable messages | No block fragmentation at message layer | yojimbo_reliable_ordered_channel.h SendBlockData / ReceiveBlockData |
| Jitter estimation per-connection | ReliableEndpoint tracks RTT but not jitter |
yojimbo_network_info.h NetworkInfo.RTT |
flowchart TD
App[Application] -->|sendMessage / sendBlock| ROC[ReliableOrderedChannel]
ROC -->|generatePacketData - WriteStream| Conn[Connection]
Conn -->|sendPacket| RE[ReliableEndpoint]
RE -->|transmitHandler| Transport[UdpTransport]
Transport -->|recv| RE2[ReliableEndpoint]
RE2 -->|processHandler - receivePacket| Conn2[Connection]
Conn2 -->|processPacketData - ReadStream| ROC2[ReliableOrderedChannel]
ROC2 -->|receiveMessage| App2[Application]
RE2 -->|getAcks| Conn3[Connection]
Conn3 -->|processAck| ROC3[ReliableOrderedChannel]
Application
| sendMessage(msgType, data, off, len)
| sendBlock(msgType, blockData, off, len)
| receiveMessage(...) -> msgType or -1
v
ReliableOrderedChannel <- this plan
| generatePacketData(WriteStream, packetSeq, availBits) -> bitsWritten
| processPacketData(ReadStream, packetSeq)
| processAck(packetSeq)
| advanceTime(time)
v
Connection <- this plan (thin glue)
| sendPendingData() -> calls endpoint.sendPacket()
| onPacketReceived(data, off, len, packetSeq)
| onAcksReceived(acks, numAcks)
v
ReliableEndpoint <- already exists; add jitter
Yojimbo uses reference-counted Message* objects allocated per send. This is hostile to the
allocation-free constraint. The Java port uses:
| Yojimbo | Java port |
|---|---|
Message* ref-counted objects |
Raw byte[] per slot (pre-allocated at construction) |
MessageFactory creating typed objects |
msgType int tag; caller serializes before enqueue |
BlockMessage with attached heap block |
Dedicated pre-allocated byte[] sendBlockData (single in-flight block) |
BitArray class for fragment ack tracking |
long[] bit arrays (256 fragments max = 4 longs) |
Dynamic sentPacket->messageIds alloc |
Flat pre-allocated short[] sentMsgIds[sentCap * maxPerPacket] |
Channel data is written into the CONNECTION_PAYLOAD plaintext using WriteStream / ReadStream.
It sits between the reliable header (added by ReliableEndpoint) and the UDP packet boundary.
numMessages : 8 bits (0..maxMessagesPerPacket)
for each message i:
messageId : 16 bits (uint16, wrapping)
messageType: 16 bits
messageLen : 16 bits (byte count, 0..maxMessageSize)
align to byte boundary
messageData: messageLen bytes
messageId : 16 bits (message id the block is attached to)
fragmentId : 16 bits (0..numFragments-1)
numFragments : 16 bits
messageType : 16 bits
fragmentSize : 16 bits (bytes in this fragment)
hasMsgData : 1 bit (1 only when fragmentId == 0)
if hasMsgData:
msgDataLen : 16 bits (header bytes, 0..maxMessageSize)
align to byte boundary
msgData : msgDataLen bytes
align to byte boundary
fragmentData : fragmentSize bytes
for each channel c in [0, numChannels):
hasData : 1 bit
if hasData:
isBlock: 1 bit
... message-mode or block-mode data above ...
int[] sendSeq; // 0xFFFF_FFFF = empty; else uint32 of msgId
double[] sendTimeLastSent;
boolean[] sendIsBlock;
int[] sendMsgType;
int[] sendMsgLen;
byte[][] sendData; // [sendCap][maxMessageSize] - non-block header bytes or full messageint[] recvSeq;
int[] recvMsgType;
int[] recvMsgLen;
boolean[] recvIsBlock;
byte[][] recvData; // [recvCap][maxMessageSize] - non-block messagesint[] sentPktSeq; // 0xFFFF_FFFF = empty
boolean[] sentPktIsBlock;
int[] sentPktNumMsgIds;
short[] sentPktMsgIds; // [sentCap * maxMessagesPerPacket]
short[] sentPktBlockMsgId;
short[] sentPktBlockFragId;boolean sendBlockActive;
int sendBlockMsgId; // uint16
int sendBlockSize;
int sendBlockNumFrags;
int sendBlockNumAckedFrags;
long[] sendBlockAcked; // bit array, 256 max fragments = 4 longs
double[] sendBlockFragTime; // last send time per fragment
byte[] sendBlockData; // pre-alloc maxBlockSize bytes
int sendBlockMsgType;boolean recvBlockActive;
int recvBlockMsgId;
int recvBlockNumFrags;
int recvBlockNumRecvFrags;
int recvBlockSize;
int recvBlockMsgType;
long[] recvBlockReceived; // bit array
byte[] recvBlockData; // pre-alloc maxBlockSize bytesAdd jitter (float, ms) to ReliableEndpoint. Computed in processAckBits() using
mean absolute deviation from RTT:
rttDeviation = |rttSample - smoothedRtt|
jitter += (rttDeviation - jitter) * rttSmoothingFactor
API addition: ReliableEndpoint.jitter() returns float in ms.
- All buffer sizes MUST be power-of-two (enforced by constructor assertion)
- Only ONE block message can be in-flight at a time (send side); returns error otherwise
- Only ONE block can be in reassembly at a time (recv side); mismatched messageId -> desync
maxBlockFragments = maxBlockSize / blockFragmentSize- MUST be <= 256sentPktMsgIdsflat array: index =(packetSeq & sentMask) * maxMessagesPerPacket + i- Message ID arithmetic uses wrapping uint16:
(id & 0xFFFF) - Sequence comparisons use
sequenceGreaterThan(half-space rule, same as reliable)
New package: net.ztrust.channel
net.ztrust.channel/
ChannelConfig.java - configuration (all public fields, defaults from yojimbo)
ChannelError.java - error level constants
ReliableOrderedChannel.java - main class: send queue, recv queue, block send/recv
Connection.java - glues ReliableOrderedChannel[] to ReliableEndpoint
New tests: src/test/java/.../channel/
ReliableOrderedChannelTest.java - send/recv, resend, ack, block send/recv
ConnectionTest.java - end-to-end round trip
| Operation | Target |
|---|---|
sendMessage (enqueue) |
< 50 ns |
generatePacketData (64 messages, no block) |
< 2 us |
processPacketData (64 messages) |
< 2 us |
processAck |
< 200 ns |
sendBlock fragment selection |
< 300 ns |
| Allocation on any hot-path call | 0 bytes |
ChannelConfig.javaChannelError.javaReliableOrderedChannel.javaConnection.java- Patch
ReliableEndpoint.java- add jitter ReliableOrderedChannelTest.javaConnectionTest.java