Source: https://shadowfrag.com/blog/a-smaller-language-for-combat/

# Teaching the network to say less

By the SHADOWFRAG developer · Netcode · Oct 21, 2026 · 3 min read

I was sending appearance data and empty fields more often than the game needed them. Removing that repetition reduced traffic while preserving the facts used to decide a hit.

[Ironworks, captured in SHADOWFRAG's Vulkan renderer](https://shadowfrag.com/_astro/3a1e54580da629b51969f3a6577914226177d7e0d58d793db6a6b7a324829631.CCAwZTaR.png)

Actual engine render · Development build · 22 October 2026

**Update, 26–27 October 2026.** The byte counts below belong to the 20–21 October compact-wire work. The build at this update uses [wire protocol 37](https://shadowfrag.com/glossary/#term-wire-protocol), the shared network message format, with [PicoQUIC](https://shadowfrag.com/glossary/#term-picoquic) as the default [transport](https://shadowfrag.com/glossary/#term-transport-backend) implementation and [CUBIC](https://shadowfrag.com/glossary/#term-cubic) controlling how its sending rate responds to congestion. [See the current implementation and dated archives](https://shadowfrag.com/netcode/).

Looking through the traffic, I kept finding information that did not need to be sent again. A suit does not change with every movement update. A bow action does not need all the fields used by a rail shot. An input field that contains its default value can often be represented more briefly.

I tackled those cases separately, with correctness checks for each. Fewer bytes would not help if losing a [packet](https://shadowfrag.com/glossary/#term-network-packet) made the next message impossible to understand, or if a new player joining a reused slot changed the appearance of an arrow already in flight.

## Sending appearance when it changes

I moved appearance into a reliably delivered roster, tied to the particular player occupying a slot, and locked the chosen look for the round. A new preference takes effect next round. Disconnecting ends that [occupant identity](https://shadowfrag.com/glossary/#term-life-warp-identity), so someone joining the same slot is still a different person as far as existing events are concerned.

A score update can then leave out the name and appearance the viewer already has. A newly connected viewer still receives the complete starting information. Shots and [projectiles](https://shadowfrag.com/glossary/#term-projectile) also retain the appearance data they need after their owner leaves; an old arrow must not borrow the next occupant's look.

For input and combat messages, I used short indicators for fields at their exact default values and smaller records for each kind of action. Actions can share identity information from the message carrying them, but only after that enclosing message has been validated. A bow release or cancellation still refers to the draw it belongs to. Rail and [melee](https://shadowfrag.com/glossary/#term-melee) actions still carry the view information needed to check the attack. Aim precision and timing deadlines stayed unchanged.

## Measuring the bytes saved

The archived comparison used **32 clients on Ironworks**: an original 20-second sample and three later samples of 20 seconds each, compared as traffic rates. The server's outgoing game-data [payload](https://shadowfrag.com/glossary/#term-application-payload), added across all clients, fell from about **4.249 [MB/s](https://shadowfrag.com/glossary/#term-data-rate-units) to 2.842 [MB/s](https://shadowfrag.com/glossary/#term-data-rate-units)**, or **33.1%**. This is the application's data; the extra bytes used to [transport](https://shadowfrag.com/glossary/#term-transport-backend) it are counted separately.

That reduction applies to this workload. It does not establish a matching reduction in processor use or [latency](https://shadowfrag.com/glossary/#term-latency), or a matching increase in player capacity. [Compact encoding](https://shadowfrag.com/glossary/#term-compact-encoding) can itself take work to produce and decode. The later [64-player limit](https://shadowfrag.com/glossary/#term-player-cap) came from separate tests on physical machines.

The useful change was to send information at the point it was needed. Clients already have the models, trails and sounds, so server-confirmed events can tell them what happened without repeatedly sending the details of how it looks. [The traffic breakdown](https://shadowfrag.com/netcode/#traffic) preserves the categories and historical measurements behind the comparison.

[Back to blog](https://shadowfrag.com/blog/)[Browse the glossary](https://shadowfrag.com/glossary/)

## Request an _invite code._

SHADOWFRAG is opening by invitation. Request a code and receive development updates. [Shadow Council](https://shadowfrag.com/glossary/#term-shadow-council) contributors receive priority when invitations begin.

Enable JavaScript to request an invite code and confirm your email address.

Email confirmation required. Codes will be issued later; a request does not guarantee access. Unsubscribe at any time. [Privacy](https://shadowfrag.com/privacy/)

Confirm your email to join the invitation list. Codes will be issued in a future round; a request does not guarantee access. Unsubscribe at any time.

Anyone can request a code. For [Shadow Council](https://shadowfrag.com/shadow-council/) priority, introduce yourself on Discord with your role, experience, region and time zone. Include examples of your maps or content if you have them.

[Join Discord](https://discord.gg/EHj7rZDNj)
