Skip to content

Transport Layer

Hytale primarily uses QUIC over UDP for client-server communication, with TCP as a fallback option. Both transports use the same packet protocol on top.

QUIC (Quick UDP Internet Connections) is the primary transport protocol, providing:

  • Low latency: 0-RTT or 1-RTT connection establishment
  • Multiplexing: Multiple streams without head-of-line blocking
  • Built-in encryption: TLS 1.3 integrated into the protocol
  • Connection migration: Survives IP/port changes
  • Congestion control: BBR algorithm for optimal throughput

The Application-Layer Protocol Negotiation identifier is:

hytale/1
SettingValueNotes
Client AuthRequiredMutual TLS (mTLS)
Early Data (0-RTT)DisabledSecurity measure
Min TLS Version1.3Via QUIC

The server generates a self-signed certificate at startup. The certificate’s SHA-256 fingerprint is sent to the session service during authentication and used by clients to verify the server’s identity.

TCP is available as a fallback for environments that block UDP. It provides:

  • Compatibility with restrictive firewalls
  • Reliable ordered delivery
  • Standard TLS encryption

TCP lacks QUIC’s multiplexing and connection migration benefits.

Both IPv4 and IPv6 are supported. The server binds to both protocol families with:

OptionValuePurpose
SO_REUSEADDRtrueAllow port reuse after restart
IP_DONTFRAGMENTtruePrevent IP-level fragmentation (IPv4)
PhaseTimeoutBehavior
Auth grant30sDisconnect if no response
Auth token30sDisconnect if client doesn’t respond
Server token exchange15sDisconnect on failure
Idle (playing)ConfigurablePing/pong keeps alive

Connections can be closed gracefully or forcefully:

TypeValueDescription
Disconnect0Normal disconnect with reason message
Crash1Client crashed

A graceful disconnect sends a Disconnect packet (ID 1) with a reason string before closing the connection.

The server uses a Netty channel pipeline with these handlers:

Inbound: Network -> timeout -> decoder -> handler
Outbound: handler -> encoder -> Network
HandlerClassPurpose
timeoutReadTimeoutHandlerDisconnects idle connections
decoderPacketDecoderParses framed bytes to Packet objects
encoderPacketEncoderSerializes Packet objects to framed bytes
handlerPacketHandlerProtocol logic (auth, game, etc.)

Mods can insert custom handlers into the pipeline to intercept, modify, or inject packets.

Hytale provides a PacketAdapters API for intercepting packets without modifying the pipeline directly:

public class PacketFilterPlugin extends PluginBase {
private PacketFilter filter;
@Override
public void setup() {
// register inbound packet filter
filter = PacketAdapters.registerInbound((handler, packet) -> {
// return true to DROP the packet, false to allow
if (packet instanceof SomePacket p) {
return !isValid(p); // drop invalid packets
}
return false;
});
}
@Override
public void shutdown() {
// clean up on plugin unload
if (filter != null) {
PacketAdapters.deregisterInbound(filter);
}
}
}

For player-specific filtering:

PacketAdapters.registerInbound((PlayerPacketFilter) (player, packet) -> {
// access player context
if (player.hasPermission("bypass.filter")) {
return false; // allow
}
return shouldDrop(packet);
});

Filter types:

  • PacketFilter - Filter any packet
  • PacketWatcher - Observe packets without filtering
  • PlayerPacketFilter - Filter with player context
  • PlayerPacketWatcher - Observe with player context

Directions:

  • PacketAdapters.registerInbound(...) - client to server packets
  • PacketAdapters.registerOutbound(...) - server to client packets