---
title: Networking
type: docs
weight: 6
---

The Hytale server uses a custom networking system for client-server communication. Understanding how data is synchronized and transmitted is essential for creating responsive and efficient plugins.

{{< cards cols="2" >}}
  {{< card link="packets" title="Packets" subtitle="Network packet reference and usage" icon="server" >}}
  {{< card link="sync" title="Synchronization" subtitle="Entity and world state synchronization" icon="refresh" >}}
{{< /cards >}}

---

## Overview

The networking system in Hytale handles:

- **Client-Server Communication**: Bidirectional packet transmission
- **Entity Synchronization**: Keeping entity states consistent across clients
- **World Updates**: Broadcasting block changes and world events
- **Player Actions**: Processing and validating player inputs

{{< callout type="info" >}}
Most networking operations are handled automatically by the server. Plugins typically interact with the high-level APIs rather than raw packets.
{{< /callout >}}

---

## Architecture

```
┌─────────────────────────────────────────────────────────────┐
│                         Server                               │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │
│  │   World     │  │   Entity    │  │      Plugin         │  │
│  │   State     │  │   Manager   │  │      Events         │  │
│  └──────┬──────┘  └──────┬──────┘  └──────────┬──────────┘  │
│         │                │                     │             │
│         └────────────────┼─────────────────────┘             │
│                          │                                   │
│                    ┌─────┴─────┐                             │
│                    │  Network  │                             │
│                    │   Layer   │                             │
│                    └─────┬─────┘                             │
└──────────────────────────┼──────────────────────────────────┘
                           │
              ┌────────────┼────────────┐
              │            │            │
         ┌────┴────┐  ┌────┴────┐  ┌────┴────┐
         │ Client  │  │ Client  │  │ Client  │
         │    1    │  │    2    │  │    N    │
         └─────────┘  └─────────┘  └─────────┘
```

---

## Key Concepts

### Broadcast Distance

Different types of data have different broadcast distances:

| Data Type | Default Distance | Description |
|-----------|------------------|-------------|
| Particles | 75 blocks | `DEFAULT_PARTICLE_DISTANCE` |
| Entities | View distance | Based on server configuration |
| Sounds | 16 blocks | Default sound range |
| Block changes | Chunk-based | Sent to players with loaded chunks |

### Network Outdated Pattern

The server uses a "network outdated" pattern to efficiently synchronize state:

```java
// Components implement this pattern for network synchronization
public interface NetworkSyncable {
    boolean isNetworkOutdated();
    void markNetworkOutdated();
    void clearNetworkOutdated();
}
```

When a component's state changes:
1. The component marks itself as `networkOutdated`
2. The sync system detects outdated components
3. Updates are batched and sent to relevant clients
4. The outdated flag is cleared after sending

---

## Common Patterns

### Sending Messages to Players

```java
// Send to a specific player
player.sendMessage(Message.raw("Hello!"));

// Send to all players in a world
for (Player p : world.getPlayers()) {
    p.sendMessage(Message.raw("World announcement"));
}

// Send to all online players
for (Player p : server.getOnlinePlayers()) {
    p.sendMessage(Message.raw("Server broadcast"));
}
```

### Spawning Particles (Network Broadcast)

```java
// Spawn particles visible to nearby players
world.spawnParticle(particleSystem, position);

// The server handles:
// 1. Finding players within DEFAULT_PARTICLE_DISTANCE (75 blocks)
// 2. Sending SpawnParticleSystem packet (ID: 152) to those players
// 3. Client-side rendering
```

### Entity Position Updates

```java
// When you modify an entity's position
entity.setPosition(newPosition);

// The server automatically:
// 1. Marks the TransformComponent as network outdated
// 2. Batches position updates
// 3. Sends updates to players tracking this entity
```

---

## Best Practices

{{< tabs items="Performance,Security,Reliability" >}}
{{< tab >}}

**Minimize Network Traffic:**

```java
// BAD: Sending updates every tick
@Subscribe
public void onTick(TickEvent event) {
    for (Player p : server.getOnlinePlayers()) {
        p.sendMessage(Message.raw("Tick!")); // Don't do this!
    }
}

// GOOD: Batch updates and use intervals
private int tickCounter = 0;

@Subscribe
public void onTick(TickEvent event) {
    tickCounter++;
    if (tickCounter >= 20) { // Every second
        tickCounter = 0;
        broadcastScoreboard();
    }
}
```

**Use Appropriate Broadcast Distances:**

```java
// Only notify nearby players for local events
Vector3d eventPos = event.getPosition();
double range = 50.0;

for (Player p : world.getPlayers()) {
    if (p.getPosition().distanceTo(eventPos) <= range) {
        p.sendMessage(Message.raw("Something happened nearby!"));
    }
}
```

{{< /tab >}}
{{< tab >}}

**Validate Client Input:**

```java
// Note: PlayerInteractEvent is deprecated. For block interactions, use UseBlockEvent.
getEventRegistry().register(PlayerInteractEvent.class, event -> {
    Player player = event.getPlayer();
    Vector3i targetBlock = event.getTargetBlock();

    if (targetBlock != null) {
        // Validate distance
        Vector3d targetPos = new Vector3d(targetBlock.x(), targetBlock.y(), targetBlock.z());
        double distance = player.getPosition().distance(targetPos);
        if (distance > MAX_INTERACTION_DISTANCE) {
            event.setCancelled(true);
            return;
        }
    }
});
```

**Rate Limit Actions:**

```java
private final Map<UUID, Long> lastAction = new HashMap<>();
private static final long COOLDOWN_MS = 1000;

public boolean canPerformAction(Player player) {
    long now = System.currentTimeMillis();
    Long last = lastAction.get(player.getUuid());

    if (last != null && now - last < COOLDOWN_MS) {
        return false;
    }

    lastAction.put(player.getUuid(), now);
    return true;
}
```

{{< /tab >}}
{{< tab >}}

**Handle Disconnections:**

```java
@Subscribe
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
    UUID playerId = event.getPlayerRef().getUuid();

    // Clean up player-specific data
    playerData.remove(playerId);
    pendingOperations.removeIf(op -> op.getPlayerId().equals(playerId));
}
```

**Use PlayerRef for Async Operations:**

```java
// PlayerRef is safe to use across async boundaries
PlayerRef playerRef = event.getPlayerRef();

CompletableFuture.runAsync(() -> {
    // Some async operation
    processData();
}).thenAccept(result -> {
    // Player might have disconnected
    Player player = playerRef.getPlayer();
    if (player != null) {
        player.sendMessage(Message.raw("Operation complete!"));
    }
});
```

{{< /tab >}}
{{< /tabs >}}

---

## Related Topics

- [Player API]({{< relref "/world/entities/player-api" >}}) - Player networking methods
- [Events]({{< relref "/core-concepts/events" >}}) - Network-related events
- [Entity Components]({{< relref "/world/entities/entity-components" >}}) - Synchronized component data
