Network visibility¶
Milestone 10 (network half). The honest version, the same as the chunk engine: the heavy lifting is already done upstream, so this adds sight, not a rewrite.
1. Existing Paper behaviour¶
Vanilla and Paper already avoid redundant entity packets. Entity data is
dirty-tracked: ServerEntity.sendDirtyEntityData sends only the values that
changed, getNonDefaultValues seeds a new tracker with only what differs from
default, movement packets are sent on an interval and skipped when nothing moved
enough, and Paper adds allocation and item-frame-visibility optimisations on top.
There is no pile of duplicate metadata to remove; "packet de-duplication" would
be reimplementing what the dirty-tracking already does. Not done.
2. Gap¶
What is missing is not an optimisation but a measurement. When a server's downstream bandwidth is high, or a particular area lags only for the players in it, there is no built-in way to see the shape of the outbound traffic: how much is the entity tracker (moves, metadata), how much is chunk and light data, how much is sound and particle spam from a plugin. The profiler shows where tick time goes; nothing showed where the bytes go.
3. Change¶
A time-boxed outbound sample, off by default.
/ember netstat start [seconds]turns the sampler on for a window (default 15s, capped at 600). While it runs, each clientbound packet is classified by type and its estimated size added to a per-category tally./ember netstatprints the result: packets and bytes per category, the per-second rates, and each category's share, sorted by bytes, with a total. Categories are the ones that matter for diagnosis: entity move, metadata, velocity, equipment, spawn; chunk, light, block; sound, particle, player-info./ember netstat stopends the window early. The counts stay readable until the next start.
4. Zero cost when off¶
The send path checks a single volatile boolean. When no sample is running that
read returns immediately and nothing else happens: no classification, no size
estimate, no allocation. The packet's class name and size estimate are only
touched while a sample is active, which is a deliberate, bounded diagnostic
window an operator chose to open. Nothing about what is sent is changed; the
sampler only counts.
5. Thread safety¶
Packets are sent from more than one thread (the main thread and Netty threads),
so the tallies are AtomicLongArray and the on/off flag is volatile. The
sampler touches no world or entity state.
6. Diagnostics and honesty¶
The read-out ends with the plain statement that metadata is already deduplicated upstream and nothing is being dropped. This is a window into traffic the server was always sending, not a claim to have reduced it. If a category looks surprisingly large (a plugin flooding particles, an entity farm's metadata), that is a lead for the operator to act on, at the plugin or design level.
7. How it is measured¶
OutboundCategoryTest pins the classifier: entity move vs metadata vs velocity
vs equipment are distinct, spawns classify before the generic entity bucket,
chunk/light/block are separated, and the noisy extras and unknowns land where
expected. The tallies and the window timing are read on the box with
/ember netstat during normal play and under a spawned entity load.
8. Measurements¶
Unit tests pass. Live outbound sample recorded in BENCHMARKS.md when run on the box.