DEV Community

Kohan Mathers
Kohan Mathers

Posted on

Stop Reinventing Multiplayer Netcode — Here's How I Built a Universal Protocol

TL;DR: I got tired of every game needing custom netcode, so I built Project Neon — a minimal UDP protocol that works for any multiplayer game. 8-byte header, zero assumptions about your game logic, and you define your own packets. MIT licensed, works with Unity/Unreal/Godot via JNI.


The Problem Nobody Talks About

You want to add multiplayer to your game. Simple, right?

Option 1: Use a library (Photon, Mirror, Netcode for GameObjects)

  • ✅ Fast to get started
  • ❌ Vendor lock-in
  • ❌ Black box you can't modify
  • ❌ Doesn't work with your custom engine

Option 2: Roll your own

  • ✅ Full control
  • ❌ You're implementing TCP handshakes in 2026
  • ❌ NAT traversal is still a nightmare
  • ❌ You just reinvented the same connection management everyone else has

There's no middle ground. Either you get a bloated framework with RPCs and state sync you don't need, or you're writing raw socket code and debugging why packets disappear behind your router.

What If We Just... Didn't?

Here's the insight: Every multiplayer game needs connection management. Zero games need the same game logic.

So I built Project Neon with one rule: The protocol should know nothing about your game.

record NeonPacket(
    PacketHeader header,  // 8 bytes - that's it
    PacketPayload payload // Your game, your rules
) {}
Enter fullscreen mode Exit fullscreen mode

The header:

  • Magic number (are you even talking Project Neon?)
  • Version (protocol compatibility)
  • Packet type (0x01-0x0F reserved, 0x10+ yours)
  • Sequence number (for ordering/reliability)
  • Client ID + Destination (who's talking, who's listening)

That's it. The relay doesn't parse your payloads. It doesn't care if you're sending player positions, chess moves, or Minecraft blocks. It just routes bytes.

Show Me Code

Client connects to a session:

NeonClient client = new NeonClient("PlayerName");

if (client.connect(12345, "relay.example.com:7777")) {
    System.out.println("Connected! My ID: " + client.getClientId());

    // Game loop
    while (gameRunning) {
        client.processPackets(); // Handle incoming
        updateGame();            // Your logic
        sendGameState(client);   // Your packets
    }
}
Enter fullscreen mode Exit fullscreen mode

Define your own packet:

// You decide what this looks like
record PlayerMovement(
    int playerId,
    float x, float y, float z,
    float velocityX, float velocityY, float velocityZ
) {
    byte[] serialize() {
        ByteBuffer buf = ByteBuffer.allocate(28);
        buf.putInt(playerId);
        buf.putFloat(x).putFloat(y).putFloat(z);
        buf.putFloat(velocityX).putFloat(velocityY).putFloat(velocityZ);
        return buf.array();
    }
}

// Send it
client.sendGamePacket((byte) 0x10, 0, movement.serialize()); // 0 = broadcast
Enter fullscreen mode Exit fullscreen mode

That's the entire API. No inheritance, no component system, no "NetworkBehaviour" base class. Just packets.

The Architecture (It's Actually Simple)

┌────────┐         ┌───────┐         ┌────────┐
│ Client │ ─────→  │ Relay │  ─────→ │  Host  │
└────────┘         └───────┘         └────────┘
                       │
                       ↓
              (broadcasts to others)
Enter fullscreen mode Exit fullscreen mode

Why a relay? NAT traversal. Your game works behind routers without port forwarding. The relay is dumb and fast — it validates the 8-byte header and forwards raw bytes.

Client-Server or Peer-to-Peer? Yes. The "host" is just another client with special privileges. You decide if it's authoritative or if clients trust each other.

Real Talk: What This Doesn't Do

Project Neon is not a game networking framework. It's infrastructure.

You still need to implement:

  • ❌ Player synchronization (positions, animations)
  • ❌ Client-side prediction / server reconciliation
  • ❌ Lag compensation
  • ❌ Authentication (there's no passwords — you add that)

What you get:

  • ✅ Connection management (connect, disconnect, reconnect)
  • ✅ Session handling (relay routes everything)
  • ✅ Packet ordering (sequence numbers in header)
  • ✅ Optional reliability (ACK/retry utility for critical packets)
  • ✅ Rate limiting (relay blocks floods)
  • ✅ Metrics (latency tracking, packet counts)

Think of it like Express.js for game networking. It handles the boring parts (sockets, routing, lifecycle) so you can focus on your game.

Cross-Engine? Yeah.

Java core with JNI bindings for C/C++. Works with:

  • Unity (call via JNI or run Java directly)
  • Unreal Engine (link libneon_jni.so as ThirdParty)
  • Godot (GDNative/GDExtension)
  • Custom C++ engines
  • Literally anything that can call C functions
// C API example
NeonClientHandle* client = neon_client_new("PlayerName");
neon_client_connect(client, 12345, "127.0.0.1:7777");

while (game_running) {
    neon_client_process_packets(client);
    // your game loop
}

neon_client_free(client);
Enter fullscreen mode Exit fullscreen mode

Security: Let's Be Honest

Project Neon has no encryption. It's plaintext UDP.

Why? Because encryption adds latency, and for LAN parties or trusted networks, you don't need it. For internet deployment, you should:

  1. Run the relay behind a VPN/firewall
  2. Implement app-layer encryption (AES-GCM) for sensitive data
  3. Add authentication in your game code (use the gameIdentifier field)

There's a 3000-word SECURITY.md that breaks down every threat and mitigation. Read it before deploying to the internet.

UDP is also unreliable (packets get lost). You can:

  • Use the built-in ReliablePacketManager for critical events
  • Send redundantly (position updates every frame don't need ACKs)
  • Implement your own ACK logic with sequence numbers

Performance

Latency budget:

  • Network RTT: 20-50ms (LAN) / 50-200ms (internet)
  • Relay forwarding: <1ms
  • Packet serialization: <0.1ms
  • Total added overhead: ~1ms

Throughput:
Example with 32 clients, 30 packets/sec, 200 bytes/packet:

  • Per-client: 6 KB/s
  • Relay total: ~6 MB/s (with broadcast)

The relay is single-threaded and handles 1000+ packets/sec easily. For higher loads, run multiple relays with session-based load balancing.

Getting Started

# Clone and build
git clone https://github.com/Quiet-Terminal-interactive/ProjectNeon
cd ProjectNeon
mvn clean package

# Run relay
java -jar target/neon-relay.jar

# Run example host (new terminal)
java -jar target/neon-host.jar 12345

# Run example client (new terminal)
java -jar target/neon-client.jar
Enter fullscreen mode Exit fullscreen mode

Maven integration:

<dependency>
    <groupId>com.quietterminal</groupId>
    <artifactId>project-neon</artifactId>
    <version>1.1.0</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Full docs: github.com/Quiet-Terminal-interactive/ProjectNeon

What's Next

This is v1.1.0 (Beta). The roadmap includes:

  • [ ] Session persistence (survive relay restarts)
  • [ ] DTLS support (optional encryption)
  • [ ] NAT traversal (STUN/TURN integration)
  • [ ] WebSocket relay variant (browser games)
  • [ ] Prometheus metrics endpoint

But the core is stable. I've been using it for a Minecraft multiplayer plugin and it just works.

Why I Built This

I got tired of seeing the same connection logic rewritten in every game. TCP handshakes, UDP reliability layers, session management — it's solved problems, yet every engine makes you start from scratch.

Project Neon is my answer: Minimal infrastructure so you can build games, not protocols.

If you're building a multiplayer game and don't want vendor lock-in or to reinvent sockets, give it a shot. It's MIT licensed, well-documented, and designed to get out of your way.


GitHub: https://github.com/Quiet-Terminal-interactive/ProjectNeon

Docs: README | Architecture | Security

Questions? Open an issue or email kohanmathersmcgonnell@gmail.com

Top comments (0)