---
title: Packets
type: docs
weight: 1
---

Hytale uses a binary packet protocol for efficient client-server communication. While most networking is handled internally, understanding the packet system helps when debugging or implementing advanced features.

{{< callout type="warning" >}}
Direct packet manipulation is generally not recommended. Use the high-level APIs provided by the plugin system whenever possible.
{{< /callout >}}

---

## Packet Structure

All packets follow a common structure:

```
┌──────────────┬───────────────┬─────────────────────┐
│  Packet ID   │  Payload Size │      Payload        │
│   (varint)   │   (varint)    │  (variable bytes)   │
└──────────────┴───────────────┴─────────────────────┘
```

| Field | Type | Description |
|-------|------|-------------|
| Packet ID | VarInt | Unique identifier for packet type |
| Payload Size | VarInt | Size of payload in bytes |
| Payload | bytes | Packet-specific data |

---

## Known Packet IDs

{{< tabs items="Client-bound,Server-bound" >}}
{{< tab >}}

Packets sent from server to client:

| ID | Name | Description |
|----|------|-------------|
| 152 | SpawnParticleSystem | Spawns particles at a location |
| -- | EntitySpawn | Spawns an entity on the client |
| -- | EntityDespawn | Removes an entity from the client |
| -- | EntityMove | Updates entity position |
| -- | BlockChange | Updates a single block |
| -- | ChunkData | Sends chunk block data |
| -- | ChatMessage | Sends chat/system message |
| -- | PlaySound | Plays a sound effect |
| -- | SetWeather | Changes weather state |
| -- | TimeUpdate | Synchronizes world time |
| -- | PlayerInfo | Updates player list data |

{{< callout type="info" >}}
Packet IDs marked with `--` are internal and subject to change between versions.
{{< /callout >}}

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

Packets sent from client to server:

| ID | Name | Description |
|----|------|-------------|
| -- | PlayerPosition | Player movement update |
| -- | PlayerAction | Player action (attack, use, etc.) |
| -- | ChatMessage | Chat message from player |
| -- | BlockBreak | Block breaking progress |
| -- | BlockPlace | Block placement request |
| -- | UseItem | Item use request |
| -- | Interaction | Entity/block interaction |
| -- | MouseMotion | Mouse movement data |
| -- | MouseButton | Mouse button press/release |

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

---

## Particle System Packet

The particle system packet (ID: 152) is used to spawn visual effects:

```java
// Internal packet structure (for reference only)
public class SpawnParticleSystemPacket {
    private ParticleSystem particleSystem;
    private Vector3d position;
    private Vector3f rotation;      // Optional
    private Vector3f scale;         // Optional
    private Entity attachedEntity;  // Optional
}
```

**Broadcast Distance:** `DEFAULT_PARTICLE_DISTANCE = 75` blocks

### Spawning Particles via API

```java
// Simple particle spawn
world.spawnParticle(particleSystem, position);

// Particle with rotation
world.spawnParticle(particleSystem, position, rotation);

// Particle attached to entity
world.spawnParticle(particleSystem, entity);
```

---

## Entity Packets

### Entity Spawn

When an entity enters a player's view distance, the server sends entity spawn data:

```java
// Automatic - when entity is created in world
Entity entity = world.spawnEntity(entityType, position);
// Server automatically notifies nearby clients

// Manual control via entity visibility
entity.setVisibleTo(player, true);  // Force show to specific player
entity.setVisibleTo(player, false); // Hide from specific player
```

### Entity Updates

Entity state changes are batched and sent periodically:

| Component | Update Frequency | Priority |
|-----------|------------------|----------|
| TransformComponent | Every tick if changed | High |
| Velocity | Every tick if changed | High |
| Health | On change | Medium |
| DisplayName | On change | Low |
| Equipment | On change | Medium |
| Effects | On change | Low |

```java
// Position updates are automatic
entity.setPosition(newPos);

// Velocity updates are automatic
entity.setVelocity(newVelocity);

// Display name updates
entity.setComponent(DisplayNameComponent.class, new DisplayNameComponent("New Name"));
```

---

## Block Packets

### Single Block Change

```java
// Server-side API
world.setBlock(position, blockType);
// Automatically sends BlockChange to players with loaded chunk

// With block state
world.setBlock(position, blockType, blockState);
```

### Multi-Block Change

For bulk updates, changes are batched by chunk:

```java
// Multiple blocks in same chunk are batched
for (int x = 0; x < 16; x++) {
    for (int z = 0; z < 16; z++) {
        world.setBlock(new Vector3i(chunkX * 16 + x, 64, chunkZ * 16 + z), blockType);
    }
}
// Server batches these into a single multi-block update packet
```

---

## Sound Packets

### Playing Sounds

```java
// Play sound at position
world.playSound(soundEvent, position, volume, pitch);

// Play sound to specific player
player.playSound(soundEvent, position, volume, pitch);

// Play sound from entity
entity.playSound(soundEvent, volume, pitch);
```

### Sound Categories

| Category | Description | Default Volume |
|----------|-------------|----------------|
| MASTER | Overall volume | 1.0 |
| MUSIC | Background music | 0.5 |
| AMBIENT | Environmental sounds | 0.8 |
| BLOCKS | Block interaction sounds | 1.0 |
| ENTITIES | Entity sounds | 1.0 |
| PLAYERS | Player-made sounds | 1.0 |
| UI | User interface sounds | 1.0 |

---

## Chat/Message Packets

### Message Types

```java
// Chat message (appears in chat)
player.sendMessage(Message.raw("Hello!"));

// Translation-based message
player.sendMessage(Message.translation("my.translation.key")
    .param("player", player.getDisplayName()));
```

### Rich Text Formatting

Hytale's Message class supports styling through method chaining:

```java
// Raw text message
Message message = Message.raw("Hello, World!");

// Colored message (using java.awt.Color)
Message colored = Message.raw("Important!")
    .color(Color.RED);

// Message with parameters (for translations)
Message parameterized = Message.translation("quest.completed")
    .param("player", playerName)
    .param("reward", "100 XP");

// Bold/italic formatting
Message styled = Message.raw("Notice!")
    .bold(true)
    .italic(false);

// Inserting multiple messages
Message combined = Message.empty()
    .insert(Message.raw("Part 1"))
    .insert(Message.raw(" - "))
    .insert(Message.raw("Part 2"));
```

---

## Network Events

### Intercepting Network Activity

```java
public class NetworkMonitorPlugin extends ServerPlugin {

    @Subscribe
    public void onPlayerConnect(PlayerConnectEvent event) {
        // New connection established
        log.info("Player connected: " + event.getUsername());
    }

    @Subscribe
    public void onPlayerSetupConnect(PlayerSetupConnectEvent event) {
        // Before connection is fully established
        // Can be cancelled
        if (isBlacklisted(event.getUuid())) {
            event.setCancelled(true);
        }
    }

    @Subscribe
    public void onPlayerDisconnect(PlayerDisconnectEvent event) {
        // Connection closed
        log.info("Player disconnected: " + event.getPlayerRef().getUuid());
    }
}
```

---

## Performance Considerations

{{< tabs items="Optimization,Debugging" >}}
{{< tab >}}

### Reducing Network Load

**1. Batch Updates:**
```java
// Instead of sending many small updates
world.setBlock(pos1, block);
world.setBlock(pos2, block);
world.setBlock(pos3, block);

// The server automatically batches these per tick
// No manual batching needed
```

**2. Use Appropriate Update Rates:**
```java
// For frequently changing data, consider throttling
private long lastUpdate = 0;
private static final long UPDATE_INTERVAL_MS = 50; // 20 updates/sec

public void updateDisplay() {
    long now = System.currentTimeMillis();
    if (now - lastUpdate >= UPDATE_INTERVAL_MS) {
        lastUpdate = now;
        sendDisplayUpdate();
    }
}
```

**3. Limit Broadcast Scope:**
```java
// Only send to players who need the update
double maxDistance = 100.0;
Vector3d sourcePos = event.getPosition();

// Note: Player doesn't have getPosition() directly
// Use PlayerRef to get position via getTransform()
for (PlayerRef playerRef : world.getPlayerRefs()) {
    Transform transform = playerRef.getTransform();
    if (transform != null) {
        double distance = transform.getPosition().distance(sourcePos);
        if (distance <= maxDistance) {
            playerRef.sendMessage(notification);
        }
    }
}
```

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

### Debugging Network Issues

**1. Log Network Activity:**
```java
@Subscribe
public void onPlayerConnect(PlayerConnectEvent event) {
    log.debug("Connect from {} ({})",
        event.getUsername(),
        event.getUuid());
}

@Subscribe
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
    log.debug("Disconnect: {}",
        event.getPlayerRef().getUuid());
}
```

**2. Monitor Packet Timing:**
```java
// Track message round-trip time
private final Map<UUID, Long> pingTimes = new ConcurrentHashMap<>();

public void sendPing(Player player) {
    pingTimes.put(player.getUuid(), System.currentTimeMillis());
    player.sendMessage(Message.raw("ping"));
}

// In start() method:
getEventRegistry().register(PlayerChatEvent.class, event -> {
    if (event.getContent().equals("pong")) {
        Long sent = pingTimes.remove(event.getSender().getUuid());
        if (sent != null) {
            long rtt = System.currentTimeMillis() - sent;
            getLogger().at(Level.INFO).log("RTT for " + event.getSender().getUsername() + ": " + rtt + "ms");
        }
    }
});
```

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

---

## Security Notes

{{< callout type="error" >}}
**Never trust client data!** Always validate:
- Positions (within valid range, not too far from player)
- Actions (player has permission, cooldown respected)
- Resources (item counts, block placements)
{{< /callout >}}

```java
// Example: Validating a block placement
getEventRegistry().register(PlaceBlockEvent.class, event -> {
    Vector3i targetPos = event.getTargetBlock();

    // Validate world bounds
    // Note: Vector3i uses getY(), NOT y()
    if (targetPos.getY() < 0 || targetPos.getY() > 255) {
        event.setCancelled(true);
        return;
    }

    // Note: PlaceBlockEvent is an ECS event - player access is through
    // the component system, not direct getPlayer() calls.
    // For player-specific validation, consider using entity components.
});
```
