Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ jobs:
cd ..
nimby install -g "${{ github.event.repository.name }}/${{ github.event.repository.name }}.nimble"
- run: nim r tests/tests.nim
- run: nim r tests/test_hardening.nim
- run: nim r tests/test_timeseries.nim
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@

## About

Netty is a reliable connection over UDP aimed at games. Normally UDP packets can get duplicated, dropped, or come out of order. Netty makes sure packets are not duplicated, re-sends them if they get dropped, and all packets come in order. UDP packets might also get split if they are above 512 bytes and also can fail to be sent if they are bigger than 1-2k. Netty breaks up big packets and sends them in pieces making sure each piece comes reliably in order. Finally sometimes it's impossible for two clients to communicate direclty with TCP because of NATs, but Netty provides hole punching which allows them to connect.
Netty is a reliable connection over UDP aimed at games. Normally UDP packets can get duplicated, dropped, or come out of order. Netty makes sure packets are not duplicated, re-sends them if they get dropped, and all packets come in order. UDP packets might also get split if they are above 512 bytes and also can fail to be sent if they are bigger than 1-2k. Netty breaks up big packets and sends them in pieces making sure each piece comes reliably in order. Finally sometimes it is impossible for two clients to communicate directly with TCP because of NATs, but Netty can send hole punch probes to help them connect.

### Documentation

API reference: https://treeform.github.io/netty

## Is Netty a implementation of TCP?
## Is Netty an implementation of TCP?

TCP is really bad for short latency sensitive messages. TCP was designed for throughput (downloading files) not latency (games). Netty will resend stuff faster than TCP, Netty will not buffer and you also get nat punch-through (which TCP does not have). Netty is basically "like TCP but for games". You should not be using Netty if you are will be sending large mount of data. By default Netty is capped at 250K of data in flight.
TCP is really bad for short latency sensitive messages. TCP was designed for throughput (downloading files) not latency (games). Netty will resend stuff faster than TCP, Netty will not buffer and you also get NAT punch-through probes (which TCP does not have). Netty is basically "like TCP but for games". You should not be using Netty if you will be sending large amounts of data. By default Netty is capped at 250KB of data in flight (`reactor.maxInFlight`, configurable).

## Features:

Expand All @@ -30,10 +30,11 @@ TCP is really bad for short latency sensitive messages. TCP was designed for thr
| packet ordering | yes | no | yes |
| packet splitting | yes | no | yes |
| packet retry | yes | no | yes |
| packet reduplication | yes | no | yes |
| packet deduplication | yes | no | yes |
| hole punch through | no | yes | yes |
| connection handling | yes | no | yes |
| congestion control | yes | no | yes |
| congestion control | yes | no | no |
| fixed in-flight byte cap | no | no | yes |


# Echo Server/Client example
Expand All @@ -45,7 +46,7 @@ import netty

# listen for a connection on localhost port 1999
var server = newReactor("127.0.0.1", 1999)
echo "Listenting for UDP on 127.0.0.1:1999"
echo "Listening for UDP on 127.0.0.1:1999"
# main loop
while true:
# must call tick to both read and write
Expand Down Expand Up @@ -87,7 +88,7 @@ while true:
import netty

var server = newReactor("127.0.0.1", 2001)
echo "Listenting for UDP on 127.0.0.1:2001"
echo "Listening for UDP on 127.0.0.1:2001"
while true:
server.tick()
for connection in server.newConnections:
Expand Down
48 changes: 48 additions & 0 deletions docs/bench-baseline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Reactor bench baseline

Target: **10k connections** — Istrolid1 peaked around 3k concurrent players;
Istrolid2 should handle that easily, with 10k as headroom.

```
nim r -d:release -d:nettyBench tests/bench_reactor.nim
# default scale is 10000
```

## Environment

- Host: macOS darwin 24.1.0 (arm64)
- Nim: 2.2.4
- Flags: `-d:release -d:nettyBench`
- Default scale: 10000

## Current baseline (post perf work, scale=10000)

| scenario | conns | ticks | msgs | bytes | msgs/s | mean tick us | p99 tick us | occupied MB |
|---|---:|---:|---:|---:|---:|---:|---:|---:|
| many-idle | 10000 | 200 | 0 | 0 | 0 | 215.5 | 282 | 40.0 |
| many-active | 5000 | 200 | 819800 | 3279200 | 838653 | 4887.6 | 5631 | 39.4 |
| fan-in-large | 4 | 200 | 800 | 6400000 | 12748 | 313.8 | 462 | 0.1 |
| churn | 216 | 2000 | 1937 | 9685 | 91847 | 10.5 | 18 | 0.0 |

## Reference at scale=1000 (smoke)

| scenario | conns | mean tick us | msgs/s |
|---|---:|---:|---:|
| many-idle | 1000 | ~19 | 0 |
| many-active | 500 | ~439 | ~1.1M |

## Reading the 10k numbers for Istrolid2

- **3k players** is below this bench's idle=10k / active=5k load.
- **many-active ~4.9ms mean tick** at 5k sending every tick is a stress ceiling, not a typical frame (games traffic is far sparser per conn).
- **many-idle ~0.22ms** at 10k is the "connected but quiet" cost — still scans every connection each tick; next win if needed.
- Prefer improving **10k** numbers over chasing 100k (establish is too slow/flaky on localhost UDP).

## What already landed

- `Table[uint32, Connection]` for O(1) `getConn`
- Per-sequence receive window (no O(n) inserts)
- `Deque` send queue + part pool
- ACK bundling (`AckBundleMagic`)
- Reused `outBuf`, `MaxUdpRecv`, `MaxPartsPerTick`
- `DefaultMaxConnections = 10_000`
Loading
Loading