This commit is contained in:
2026-01-20 20:33:59 +01:00
commit b16a40e431
583 changed files with 87339 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
---
title: World
type: docs
weight: 4
---
World management in Hytale covers universes, worlds, chunks, and blocks.
{{< cards >}}
{{< card link="universe-and-worlds" title="Universe & Worlds" subtitle="World hierarchy and management" >}}
{{< card link="chunks" title="Chunks" subtitle="Chunk loading and storage" >}}
{{< card link="blocks" title="Blocks" subtitle="Block manipulation" >}}
{{< card link="player-refs" title="PlayerRef" subtitle="Cross-world player references" >}}
{{< card link="worldgen" title="World Generation" subtitle="Terrain, biomes, and structures" icon="globe" >}}
{{< card link="portals" title="Portals" subtitle="Inter-world travel and teleportation" icon="link" >}}
{{< card link="entities" title="Entities" subtitle="Players, creatures, and ECS components" icon="user" >}}
{{< card link="interactions" title="Interactions" subtitle="Block and entity interactions" icon="cursor-click" >}}
{{< /cards >}}
## World Hierarchy
```
Universe (singleton)
└── World (multiple)
└── Chunk (many)
└── Block (many)
```
## Quick Access
```java
Universe universe = Universe.get();
World world = universe.getWorld("default");
Collection<World> allWorlds = universe.getWorlds().values();
```

View File

@@ -0,0 +1,35 @@
---
title: Monde
type: docs
weight: 4
---
La gestion du monde dans Hytale couvre les univers, mondes, chunks et blocs.
{{< cards >}}
{{< card link="universe-and-worlds" title="Univers & Mondes" subtitle="Hiérarchie et gestion des mondes" >}}
{{< card link="chunks" title="Chunks" subtitle="Chargement et stockage des chunks" >}}
{{< card link="blocks" title="Blocs" subtitle="Manipulation des blocs" >}}
{{< card link="player-refs" title="PlayerRef" subtitle="Références joueur inter-mondes" >}}
{{< card link="worldgen" title="Génération de Monde" subtitle="Terrain, biomes et structures" icon="globe" >}}
{{< card link="portals" title="Portails" subtitle="Voyage inter-mondes et téléportation" icon="link" >}}
{{< card link="entities" title="Entités" subtitle="Joueurs, créatures et composants ECS" icon="user" >}}
{{< card link="interactions" title="Interactions" subtitle="Interactions blocs et entités" icon="cursor-click" >}}
{{< /cards >}}
## Hiérarchie du Monde
```
Universe (singleton)
└── World (multiple)
└── Chunk (many)
└── Block (many)
```
## Accès Rapide
```java
Universe universe = Universe.get();
World world = universe.getWorld("default");
Collection<World> allWorlds = universe.getWorlds().values();
```

329
content/world/blocks.en.md Normal file
View File

@@ -0,0 +1,329 @@
---
title: Blocks
type: docs
weight: 3
---
Block manipulation is a core part of world modification in Hytale plugins. The `World` class provides block access through the `IChunkAccessorSync` interface, while `WorldChunk` implements the full `BlockAccessor` interface for rotation-aware placement.
{{< callout type="warning" >}}
**Important:** `World` does NOT implement `BlockAccessor` directly. For `placeBlock()` with rotation, you must get the chunk first using `world.getChunk()`.
{{< /callout >}}
## Getting Blocks
```java
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.math.vector.Vector3i;
World world = Universe.get().getWorld("default");
// Get block ID (int index) at position
int blockId = world.getBlock(x, y, z);
int blockId = world.getBlock(new Vector3i(x, y, z));
// Get BlockType at position
BlockType blockType = world.getBlockType(x, y, z);
BlockType blockType = world.getBlockType(new Vector3i(x, y, z));
// Check if block is empty
if (blockType == BlockType.EMPTY) {
// Air or empty space
}
```
## Setting Blocks
The `setBlock` method takes a **String block type key**, not a BlockType object or int ID:
```java
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
World world = player.getWorld();
// Set block by block type key (string ID)
world.setBlock(x, y, z, "stone");
world.setBlock(x, y, z, "oak_planks", 0); // with settings
// Using BlockType - get the ID string first
BlockType stone = BlockType.getAssetMap().getAsset("stone");
if (stone != null) {
world.setBlock(x, y, z, stone.getId()); // Use getId() to get the string key
}
// Block ID index (for internal use)
int stoneIndex = BlockType.getAssetMap().getIndex("stone");
// Note: setBlock takes String, not int - use the key directly
```
## Block Placement with Rotation
{{< callout type="info" >}}
**placeBlock() is on WorldChunk, not World!** You must get the chunk first to place blocks with rotation.
{{< /callout >}}
```java
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;
import com.hypixel.hytale.math.util.ChunkUtil;
// Get the chunk containing this block position
WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(x, z));
if (chunk != null) {
// Place block with rotation
// Rotation values: None, Ninety, OneEighty, TwoSeventy
chunk.placeBlock(x, y, z, "oak_log",
Rotation.Ninety, // yaw (90 degrees)
Rotation.None, // pitch
Rotation.None // roll
);
// Place with rotation tuple and options
chunk.placeBlock(x, y, z, "oak_log",
RotationTuple.of(Rotation.Ninety, Rotation.None, Rotation.None),
0, // settings
true // validatePlacement
);
}
```
### Rotation Values
The `Rotation` enum has these values (NOT cardinal directions):
| Rotation | Degrees |
|----------|---------|
| `Rotation.None` | 0° |
| `Rotation.Ninety` | 90° |
| `Rotation.OneEighty` | 180° |
| `Rotation.TwoSeventy` | 270° |
## Breaking Blocks
```java
// Break block at position (requires settings parameter)
world.breakBlock(x, y, z, 0); // 0 = default settings
```
## BlockType Assets
BlockType assets define block properties:
```java
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.protocol.BlockMaterial;
// Get block type by ID
BlockType blockType = BlockType.getAssetMap().getAsset("stone");
if (blockType != null) {
// Get block ID string
String blockId = blockType.getId();
// Get material
BlockMaterial material = blockType.getMaterial();
}
// Get block ID index (for internal use)
int blockIndex = BlockType.getAssetMap().getIndex("stone");
// Get block from index
BlockType fromIndex = BlockType.getAssetMap().getAsset(blockIndex);
```
## Block Events
### BreakBlockEvent
Fired when a block is broken:
```java
import com.hypixel.hytale.server.core.event.events.ecs.BreakBlockEvent;
import com.hypixel.hytale.math.vector.Vector3i;
import java.util.logging.Level;
getEventRegistry().register(BreakBlockEvent.class, event -> {
// Get break info
Vector3i position = event.getTargetBlock();
BlockType blockType = event.getBlockType();
getLogger().at(Level.INFO).log("Block broken at " + position);
// Cancel if protected
event.setCancelled(true);
});
```
### PlaceBlockEvent
Fired when a block is placed:
```java
import com.hypixel.hytale.server.core.event.events.ecs.PlaceBlockEvent;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;
getEventRegistry().register(PlaceBlockEvent.class, event -> {
// Get place info
Vector3i position = event.getTargetBlock();
RotationTuple rotation = event.getRotation();
getLogger().at(Level.INFO).log("Block placed at " + position);
// Cancel if not allowed
event.setCancelled(true);
});
```
### DamageBlockEvent
Fired when a block is damaged (before breaking):
```java
import com.hypixel.hytale.server.core.event.events.ecs.DamageBlockEvent;
getEventRegistry().register(DamageBlockEvent.class, event -> {
Vector3i position = event.getTargetBlock();
BlockType blockType = event.getBlockType();
// Cancel to prevent damage
event.setCancelled(true);
});
```
## Block Interaction State
Blocks can have interaction states (like open/closed doors):
```java
BlockType currentBlock = world.getBlockType(x, y, z);
if (currentBlock != null) {
// Set block to a different state
world.setBlockInteractionState(
new Vector3i(x, y, z),
currentBlock,
"open" // state name
);
}
```
## Working with Blocks
### Check Area for Block Type
```java
public boolean containsBlockType(World world, Vector3i min, Vector3i max, String blockTypeKey) {
// Note: Vector3i uses getX(), getY(), getZ() - NOT x(), y(), z()
for (int x = min.getX(); x <= max.getX(); x++) {
for (int y = min.getY(); y <= max.getY(); y++) {
for (int z = min.getZ(); z <= max.getZ(); z++) {
BlockType block = world.getBlockType(x, y, z);
if (block != null && block.getId().equals(blockTypeKey)) {
return true;
}
}
}
}
return false;
}
```
### Fill Area
```java
public void fillArea(World world, Vector3i min, Vector3i max, String blockTypeKey) {
for (int x = min.getX(); x <= max.getX(); x++) {
for (int y = min.getY(); y <= max.getY(); y++) {
for (int z = min.getZ(); z <= max.getZ(); z++) {
world.setBlock(x, y, z, blockTypeKey);
}
}
}
}
```
### Replace Blocks
```java
public void replaceBlocks(World world, Vector3i min, Vector3i max,
String fromBlock, String toBlock) {
for (int x = min.getX(); x <= max.getX(); x++) {
for (int y = min.getY(); y <= max.getY(); y++) {
for (int z = min.getZ(); z <= max.getZ(); z++) {
BlockType current = world.getBlockType(x, y, z);
if (current != null && current.getId().equals(fromBlock)) {
world.setBlock(x, y, z, toBlock);
}
}
}
}
}
```
## Block Positions
```java
import com.hypixel.hytale.math.vector.Vector3i;
// Vector3i for integer block positions
Vector3i blockPos = new Vector3i(100, 64, 200);
// Get individual coordinates - use getX(), getY(), getZ()
int x = blockPos.getX();
int y = blockPos.getY();
int z = blockPos.getZ();
// Adjacent positions
Vector3i above = new Vector3i(x, y + 1, z);
Vector3i below = new Vector3i(x, y - 1, z);
Vector3i north = new Vector3i(x, y, z - 1);
Vector3i south = new Vector3i(x, y, z + 1);
Vector3i east = new Vector3i(x + 1, y, z);
Vector3i west = new Vector3i(x - 1, y, z);
```
## Best Practices
{{< callout type="info" >}}
**Block Manipulation Tips:**
- Block operations must run on the world's ticking thread
- Use `getBlockType()` for checking block types, `getBlock()` for raw IDs
- Check `BlockType.EMPTY` for air/empty blocks
- Events extend `CancellableEcsEvent` - use `setCancelled(true)` to prevent
- Consider chunk boundaries when processing large areas
- For `placeBlock()` with rotation, get the chunk first
{{< /callout >}}
```java
// Safe block modification from async context
world.execute(() -> {
world.setBlock(x, y, z, "stone");
});
```
## API Reference
### World (IChunkAccessorSync) Methods
| Method | Returns | Description |
|--------|---------|-------------|
| `getBlock(int, int, int)` | `int` | Get block ID at position |
| `getBlock(Vector3i)` | `int` | Get block ID at position |
| `getBlockType(int, int, int)` | `BlockType?` | Get BlockType at position |
| `getBlockType(Vector3i)` | `BlockType?` | Get BlockType at position |
| `setBlock(x, y, z, String)` | `void` | Set block by type key |
| `setBlock(x, y, z, String, int)` | `void` | Set block with settings |
| `breakBlock(x, y, z, int)` | `boolean` | Break block with settings |
| `setBlockInteractionState(...)` | `void` | Set block state |
### WorldChunk (BlockAccessor) Methods
| Method | Returns | Description |
|--------|---------|-------------|
| `placeBlock(x, y, z, String, Rotation, Rotation, Rotation)` | `boolean` | Place with rotation |
| `placeBlock(x, y, z, String, RotationTuple, int, boolean)` | `boolean` | Place with options |
| `testPlaceBlock(...)` | `boolean` | Test if placement valid |

329
content/world/blocks.fr.md Normal file
View File

@@ -0,0 +1,329 @@
---
title: Blocs
type: docs
weight: 3
---
La manipulation des blocs est une partie centrale de la modification du monde dans les plugins Hytale. La classe `World` fournit l'accès aux blocs via l'interface `IChunkAccessorSync`, tandis que `WorldChunk` implémente l'interface complète `BlockAccessor` pour le placement avec rotation.
{{< callout type="warning" >}}
**Important :** `World` n'implémente PAS `BlockAccessor` directement. Pour `placeBlock()` avec rotation, vous devez d'abord obtenir le chunk avec `world.getChunk()`.
{{< /callout >}}
## Obtenir des Blocs
```java
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.math.vector.Vector3i;
World world = Universe.get().getWorld("default");
// Obtenir l'ID du bloc (index int) à une position
int blockId = world.getBlock(x, y, z);
int blockId = world.getBlock(new Vector3i(x, y, z));
// Obtenir le BlockType à une position
BlockType blockType = world.getBlockType(x, y, z);
BlockType blockType = world.getBlockType(new Vector3i(x, y, z));
// Vérifier si le bloc est vide
if (blockType == BlockType.EMPTY) {
// Air ou espace vide
}
```
## Définir des Blocs
La méthode `setBlock` prend une **clé de type bloc String**, pas un objet BlockType ou un int ID :
```java
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
World world = player.getWorld();
// Définir un bloc par clé de type (ID string)
world.setBlock(x, y, z, "stone");
world.setBlock(x, y, z, "oak_planks", 0); // avec settings
// Utiliser BlockType - obtenir d'abord la chaîne ID
BlockType stone = BlockType.getAssetMap().getAsset("stone");
if (stone != null) {
world.setBlock(x, y, z, stone.getId()); // Utiliser getId() pour obtenir la clé string
}
// Index d'ID de bloc (pour usage interne)
int stoneIndex = BlockType.getAssetMap().getIndex("stone");
// Note : setBlock prend String, pas int - utilisez la clé directement
```
## Placement de Blocs avec Rotation
{{< callout type="info" >}}
**placeBlock() est sur WorldChunk, pas World !** Vous devez d'abord obtenir le chunk pour placer des blocs avec rotation.
{{< /callout >}}
```java
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;
import com.hypixel.hytale.math.util.ChunkUtil;
// Obtenir le chunk contenant cette position de bloc
WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(x, z));
if (chunk != null) {
// Placer un bloc avec rotation
// Valeurs de Rotation : None, Ninety, OneEighty, TwoSeventy
chunk.placeBlock(x, y, z, "oak_log",
Rotation.Ninety, // yaw (90 degrés)
Rotation.None, // pitch
Rotation.None // roll
);
// Placer avec tuple de rotation et options
chunk.placeBlock(x, y, z, "oak_log",
RotationTuple.of(Rotation.Ninety, Rotation.None, Rotation.None),
0, // settings
true // validatePlacement
);
}
```
### Valeurs de Rotation
L'enum `Rotation` a ces valeurs (PAS les directions cardinales) :
| Rotation | Degrés |
|----------|--------|
| `Rotation.None` | 0° |
| `Rotation.Ninety` | 90° |
| `Rotation.OneEighty` | 180° |
| `Rotation.TwoSeventy` | 270° |
## Casser des Blocs
```java
// Casser un bloc à une position (nécessite le paramètre settings)
world.breakBlock(x, y, z, 0); // 0 = settings par défaut
```
## Assets BlockType
Les assets BlockType définissent les propriétés des blocs :
```java
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.protocol.BlockMaterial;
// Obtenir le type de bloc par ID
BlockType blockType = BlockType.getAssetMap().getAsset("stone");
if (blockType != null) {
// Obtenir la chaîne ID du bloc
String blockId = blockType.getId();
// Obtenir le matériau
BlockMaterial material = blockType.getMaterial();
}
// Obtenir l'index d'ID du bloc (pour usage interne)
int blockIndex = BlockType.getAssetMap().getIndex("stone");
// Obtenir le bloc depuis l'index
BlockType fromIndex = BlockType.getAssetMap().getAsset(blockIndex);
```
## Événements de Blocs
### BreakBlockEvent
Déclenché quand un bloc est cassé :
```java
import com.hypixel.hytale.server.core.event.events.ecs.BreakBlockEvent;
import com.hypixel.hytale.math.vector.Vector3i;
import java.util.logging.Level;
getEventRegistry().register(BreakBlockEvent.class, event -> {
// Obtenir les infos de destruction
Vector3i position = event.getTargetBlock();
BlockType blockType = event.getBlockType();
getLogger().at(Level.INFO).log("Bloc cassé à " + position);
// Annuler si protégé
event.setCancelled(true);
});
```
### PlaceBlockEvent
Déclenché quand un bloc est placé :
```java
import com.hypixel.hytale.server.core.event.events.ecs.PlaceBlockEvent;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;
getEventRegistry().register(PlaceBlockEvent.class, event -> {
// Obtenir les infos de placement
Vector3i position = event.getTargetBlock();
RotationTuple rotation = event.getRotation();
getLogger().at(Level.INFO).log("Bloc placé à " + position);
// Annuler si non autorisé
event.setCancelled(true);
});
```
### DamageBlockEvent
Déclenché quand un bloc est endommagé (avant d'être cassé) :
```java
import com.hypixel.hytale.server.core.event.events.ecs.DamageBlockEvent;
getEventRegistry().register(DamageBlockEvent.class, event -> {
Vector3i position = event.getTargetBlock();
BlockType blockType = event.getBlockType();
// Annuler pour empêcher les dégâts
event.setCancelled(true);
});
```
## État d'Interaction de Bloc
Les blocs peuvent avoir des états d'interaction (comme portes ouvertes/fermées) :
```java
BlockType currentBlock = world.getBlockType(x, y, z);
if (currentBlock != null) {
// Définir le bloc à un état différent
world.setBlockInteractionState(
new Vector3i(x, y, z),
currentBlock,
"open" // nom de l'état
);
}
```
## Travailler avec les Blocs
### Vérifier une Zone pour un Type de Bloc
```java
public boolean containsBlockType(World world, Vector3i min, Vector3i max, String blockTypeKey) {
// Note : Vector3i utilise getX(), getY(), getZ() - PAS x(), y(), z()
for (int x = min.getX(); x <= max.getX(); x++) {
for (int y = min.getY(); y <= max.getY(); y++) {
for (int z = min.getZ(); z <= max.getZ(); z++) {
BlockType block = world.getBlockType(x, y, z);
if (block != null && block.getId().equals(blockTypeKey)) {
return true;
}
}
}
}
return false;
}
```
### Remplir une Zone
```java
public void fillArea(World world, Vector3i min, Vector3i max, String blockTypeKey) {
for (int x = min.getX(); x <= max.getX(); x++) {
for (int y = min.getY(); y <= max.getY(); y++) {
for (int z = min.getZ(); z <= max.getZ(); z++) {
world.setBlock(x, y, z, blockTypeKey);
}
}
}
}
```
### Remplacer des Blocs
```java
public void replaceBlocks(World world, Vector3i min, Vector3i max,
String fromBlock, String toBlock) {
for (int x = min.getX(); x <= max.getX(); x++) {
for (int y = min.getY(); y <= max.getY(); y++) {
for (int z = min.getZ(); z <= max.getZ(); z++) {
BlockType current = world.getBlockType(x, y, z);
if (current != null && current.getId().equals(fromBlock)) {
world.setBlock(x, y, z, toBlock);
}
}
}
}
}
```
## Positions de Blocs
```java
import com.hypixel.hytale.math.vector.Vector3i;
// Vector3i pour les positions de blocs entières
Vector3i blockPos = new Vector3i(100, 64, 200);
// Obtenir les coordonnées individuelles - utiliser getX(), getY(), getZ()
int x = blockPos.getX();
int y = blockPos.getY();
int z = blockPos.getZ();
// Positions adjacentes
Vector3i above = new Vector3i(x, y + 1, z);
Vector3i below = new Vector3i(x, y - 1, z);
Vector3i north = new Vector3i(x, y, z - 1);
Vector3i south = new Vector3i(x, y, z + 1);
Vector3i east = new Vector3i(x + 1, y, z);
Vector3i west = new Vector3i(x - 1, y, z);
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Conseils pour la Manipulation de Blocs :**
- Les opérations sur les blocs doivent s'exécuter sur le thread de tick du monde
- Utilisez `getBlockType()` pour vérifier les types, `getBlock()` pour les IDs bruts
- Vérifiez `BlockType.EMPTY` pour les blocs air/vides
- Les événements étendent `CancellableEcsEvent` - utilisez `setCancelled(true)` pour empêcher
- Considérez les limites de chunks lors du traitement de grandes zones
- Pour `placeBlock()` avec rotation, obtenez d'abord le chunk
{{< /callout >}}
```java
// Modification sécurisée de bloc depuis un contexte asynchrone
world.execute(() -> {
world.setBlock(x, y, z, "stone");
});
```
## Référence API
### Méthodes World (IChunkAccessorSync)
| Méthode | Retour | Description |
|--------|--------|-------------|
| `getBlock(int, int, int)` | `int` | Obtenir l'ID du bloc à la position |
| `getBlock(Vector3i)` | `int` | Obtenir l'ID du bloc à la position |
| `getBlockType(int, int, int)` | `BlockType?` | Obtenir le BlockType à la position |
| `getBlockType(Vector3i)` | `BlockType?` | Obtenir le BlockType à la position |
| `setBlock(x, y, z, String)` | `void` | Définir bloc par clé de type |
| `setBlock(x, y, z, String, int)` | `void` | Définir bloc avec settings |
| `breakBlock(x, y, z, int)` | `boolean` | Casser bloc avec settings |
| `setBlockInteractionState(...)` | `void` | Définir état du bloc |
### Méthodes WorldChunk (BlockAccessor)
| Méthode | Retour | Description |
|--------|--------|-------------|
| `placeBlock(x, y, z, String, Rotation, Rotation, Rotation)` | `boolean` | Placer avec rotation |
| `placeBlock(x, y, z, String, RotationTuple, int, boolean)` | `boolean` | Placer avec options |
| `testPlaceBlock(...)` | `boolean` | Tester si placement valide |

172
content/world/chunks.en.md Normal file
View File

@@ -0,0 +1,172 @@
---
title: Chunks
type: docs
weight: 2
---
Chunks are the fundamental unit of world storage and loading in Hytale.
## Chunk Basics
A chunk represents a fixed-size section of the world containing blocks:
```java
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
World world = Universe.get().getWorld("default");
// Chunks are loaded automatically when needed
// Access blocks within a chunk through the World API
BlockType blockType = world.getBlockType(x, y, z);
```
## Chunk Coordinates
Chunks use a separate coordinate system from block coordinates:
```java
// Convert block coordinates to chunk coordinates
int chunkX = blockX >> 4; // Divide by 16
int chunkZ = blockZ >> 4;
// Convert chunk coordinates to block coordinates (corner)
int blockX = chunkX << 4; // Multiply by 16
int blockZ = chunkZ << 4;
```
## ChunkStore
The ChunkStore manages chunk persistence:
```java
// ChunkStore handles saving and loading chunks
// This is typically managed internally by the server
```
## Chunk Loading
Chunks are loaded dynamically based on player positions:
```java
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Transform;
import java.util.logging.Level;
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef playerRef = event.getPlayerRef();
// Get position from PlayerRef.getTransform()
// Note: Player.getPosition() does NOT exist!
Transform transform = playerRef.getTransform();
if (transform != null) {
Vector3d pos = transform.getPosition();
// Vector3d uses getX(), getZ() - NOT x(), z()
int chunkX = (int) pos.getX() >> 4;
int chunkZ = (int) pos.getZ() >> 4;
getLogger().at(Level.INFO).log("Player at chunk: " + chunkX + ", " + chunkZ);
}
});
```
## Working with Chunks
### Iterating Blocks in a Chunk
```java
// Iterate through all blocks in a chunk area
int chunkX = 0;
int chunkZ = 0;
int startX = chunkX << 4;
int startZ = chunkZ << 4;
for (int x = startX; x < startX + 16; x++) {
for (int z = startZ; z < startZ + 16; z++) {
for (int y = 0; y < 256; y++) {
BlockType blockType = world.getBlockType(x, y, z);
// Process block...
}
}
}
```
### Chunk-based Operations
```java
// Perform operations on a chunk basis for efficiency
public void processChunk(World world, int chunkX, int chunkZ) {
int baseX = chunkX << 4;
int baseZ = chunkZ << 4;
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
// Process relative to chunk origin
int worldX = baseX + x;
int worldZ = baseZ + z;
// ...
}
}
}
```
## Performance Considerations
{{< callout type="warning" >}}
Avoid loading or processing many chunks simultaneously as this can impact server performance. Use async operations for heavy chunk processing.
{{< /callout >}}
```java
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.math.vector.Vector3i;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.CompletableFuture;
// Process chunks asynchronously
World world = player.getWorld();
PlayerRef playerRef = player.getPlayerRef();
CompletableFuture.runAsync(() -> {
// Heavy chunk processing here
Map<Vector3i, String> results = new HashMap<>();
// ... process
// Return to world thread to apply results
world.execute(() -> {
for (var entry : results.entrySet()) {
Vector3i pos = entry.getKey();
// Vector3i uses getX(), getY(), getZ() - NOT x(), y(), z()
world.setBlock(pos.getX(), pos.getY(), pos.getZ(), entry.getValue());
}
playerRef.sendMessage(Message.raw("Chunk processing complete!"));
});
});
```
## Chunk Management
{{< callout type="info" >}}
**Note:** Hytale handles chunk loading and unloading internally. There are no direct chunk load/unload events exposed to plugins. Chunks are automatically managed based on player positions and view distance.
{{< /callout >}}
```java
import com.hypixel.hytale.math.vector.Vector2i;
// Calculate chunk coordinates from a player position
public Vector2i getChunkCoords(PlayerRef playerRef) {
Transform transform = playerRef.getTransform();
if (transform == null) {
return new Vector2i(0, 0);
}
Vector3d pos = transform.getPosition();
// Vector3d uses getX(), getZ() - NOT x(), z()
int chunkX = (int) pos.getX() >> 4;
int chunkZ = (int) pos.getZ() >> 4;
return new Vector2i(chunkX, chunkZ);
}
```

172
content/world/chunks.fr.md Normal file
View File

@@ -0,0 +1,172 @@
---
title: Chunks
type: docs
weight: 2
---
Les chunks sont l'unité fondamentale de stockage et de chargement du monde dans Hytale.
## Bases des Chunks
Un chunk représente une section de taille fixe du monde contenant des blocs :
```java
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
World world = Universe.get().getWorld("default");
// Les chunks sont chargés automatiquement quand nécessaire
// Accédez aux blocs via l'API World
BlockType blockType = world.getBlockType(x, y, z);
```
## Coordonnées des Chunks
Les chunks utilisent un système de coordonnées séparé des coordonnées de blocs :
```java
// Convertir les coordonnées de bloc en coordonnées de chunk
int chunkX = blockX >> 4; // Diviser par 16
int chunkZ = blockZ >> 4;
// Convertir les coordonnées de chunk en coordonnées de bloc (coin)
int blockX = chunkX << 4; // Multiplier par 16
int blockZ = chunkZ << 4;
```
## ChunkStore
Le ChunkStore gère la persistance des chunks :
```java
// ChunkStore gère la sauvegarde et le chargement des chunks
// Ceci est généralement géré en interne par le serveur
```
## Chargement des Chunks
Les chunks sont chargés dynamiquement en fonction des positions des joueurs :
```java
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Transform;
import java.util.logging.Level;
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef playerRef = event.getPlayerRef();
// Obtenir la position depuis PlayerRef.getTransform()
// Note : Player.getPosition() n'existe PAS !
Transform transform = playerRef.getTransform();
if (transform != null) {
Vector3d pos = transform.getPosition();
// Vector3d utilise getX(), getZ() - PAS x(), z()
int chunkX = (int) pos.getX() >> 4;
int chunkZ = (int) pos.getZ() >> 4;
getLogger().at(Level.INFO).log("Joueur au chunk : " + chunkX + ", " + chunkZ);
}
});
```
## Travailler avec les Chunks
### Itérer les Blocs d'un Chunk
```java
// Itérer tous les blocs dans une zone de chunk
int chunkX = 0;
int chunkZ = 0;
int startX = chunkX << 4;
int startZ = chunkZ << 4;
for (int x = startX; x < startX + 16; x++) {
for (int z = startZ; z < startZ + 16; z++) {
for (int y = 0; y < 256; y++) {
BlockType blockType = world.getBlockType(x, y, z);
// Traiter le bloc...
}
}
}
```
### Opérations par Chunk
```java
// Effectuer des opérations par chunk pour l'efficacité
public void processChunk(World world, int chunkX, int chunkZ) {
int baseX = chunkX << 4;
int baseZ = chunkZ << 4;
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
// Traiter relatif à l'origine du chunk
int worldX = baseX + x;
int worldZ = baseZ + z;
// ...
}
}
}
```
## Considérations de Performance
{{< callout type="warning" >}}
Évitez de charger ou traiter beaucoup de chunks simultanément car cela peut impacter les performances du serveur. Utilisez des opérations asynchrones pour le traitement lourd de chunks.
{{< /callout >}}
```java
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.math.vector.Vector3i;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.CompletableFuture;
// Traiter les chunks de manière asynchrone
World world = player.getWorld();
PlayerRef playerRef = player.getPlayerRef();
CompletableFuture.runAsync(() -> {
// Traitement lourd de chunks ici
Map<Vector3i, String> results = new HashMap<>();
// ... traiter
// Retourner au thread du monde pour appliquer les résultats
world.execute(() -> {
for (var entry : results.entrySet()) {
Vector3i pos = entry.getKey();
// Vector3i utilise getX(), getY(), getZ() - PAS x(), y(), z()
world.setBlock(pos.getX(), pos.getY(), pos.getZ(), entry.getValue());
}
playerRef.sendMessage(Message.raw("Traitement des chunks terminé !"));
});
});
```
## Gestion des Chunks
{{< callout type="info" >}}
**Note :** Hytale gère le chargement et déchargement des chunks en interne. Il n'y a pas d'événements de chargement/déchargement de chunks exposés aux plugins. Les chunks sont automatiquement gérés en fonction des positions des joueurs et de la distance de vue.
{{< /callout >}}
```java
import com.hypixel.hytale.math.vector.Vector2i;
// Calculer les coordonnées de chunk depuis une position de joueur
public Vector2i getChunkCoords(PlayerRef playerRef) {
Transform transform = playerRef.getTransform();
if (transform == null) {
return new Vector2i(0, 0);
}
Vector3d pos = transform.getPosition();
// Vector3d utilise getX(), getZ() - PAS x(), z()
int chunkX = (int) pos.getX() >> 4;
int chunkZ = (int) pos.getZ() >> 4;
return new Vector2i(chunkX, chunkZ);
}
```

View File

@@ -0,0 +1,53 @@
---
title: Entities
type: docs
weight: 7
---
Entities are all dynamic objects in the game world - players, creatures, items, and more. Hytale uses an Entity Component System (ECS) architecture.
{{< cards >}}
{{< card link="entity-hierarchy" title="Entity Hierarchy" subtitle="Entity → LivingEntity → Player" >}}
{{< card link="entity-components" title="Entity Components" subtitle="Transform, BoundingBox, and more" >}}
{{< card link="spawning-entities" title="Spawning Entities" subtitle="Create and spawn custom entities" >}}
{{< card link="player-api" title="Player API" subtitle="Player-specific methods and data" >}}
{{< card link="npc" title="NPC System" subtitle="AI, navigation, and behaviors" >}}
{{< card link="spawning" title="Spawning System" subtitle="Markers, beacons, and suppression" >}}
{{< card link="flocking-behavior" title="Flocking Behavior" subtitle="NPC group coordination" >}}
{{< card link="mounts" title="Mounts" subtitle="Riding entities and vehicles" >}}
{{< card link="inventory" title="Inventory" subtitle="Items, containers, and transactions" icon="archive" >}}
{{< /cards >}}
## Entity Hierarchy
```
Entity
├── LivingEntity
│ ├── Player
│ └── Creature
└── BlockEntity
```
## Quick Example
```java
getEventRegistry().register(PlayerConnectEvent.class, event -> {
Player player = event.getPlayer();
if (player != null) {
// Get position via TransformComponent
TransformComponent transform = player.getTransformComponent();
Vector3d pos = transform.getPosition();
// Get world
World world = player.getWorld();
// Send message (requires Message object)
player.sendMessage(Message.raw("Welcome at " + pos.toString()));
}
});
```
{{< callout type="info" >}}
`event.getPlayer()` may return null if the player hasn't fully loaded yet. Always check for null. For position access, `getTransformComponent()` is deprecated - prefer ECS component access in new code.
{{< /callout >}}

View File

@@ -0,0 +1,53 @@
---
title: Entités
type: docs
weight: 7
---
Les entités sont tous les objets dynamiques dans le monde du jeu - joueurs, créatures, items, et plus. Hytale utilise une architecture Entity Component System (ECS).
{{< cards >}}
{{< card link="entity-hierarchy" title="Hiérarchie des Entités" subtitle="Entity → LivingEntity → Player" >}}
{{< card link="entity-components" title="Composants d'Entité" subtitle="Transform, BoundingBox, et plus" >}}
{{< card link="spawning-entities" title="Faire Apparaître des Entités" subtitle="Créer et spawner des entités personnalisées" >}}
{{< card link="player-api" title="API Joueur" subtitle="Méthodes et données spécifiques aux joueurs" >}}
{{< card link="npc" title="Système NPC" subtitle="IA, navigation, et comportements" >}}
{{< card link="spawning" title="Système de Spawning" subtitle="Marqueurs, beacons, et suppression" >}}
{{< card link="flocking-behavior" title="Comportement de Groupe" subtitle="Coordination de groupe NPC" >}}
{{< card link="mounts" title="Montures" subtitle="Chevaucher entites et vehicules" >}}
{{< card link="inventory" title="Inventaire" subtitle="Items, conteneurs et transactions" icon="archive" >}}
{{< /cards >}}
## Hiérarchie des Entités
```
Entity
├── LivingEntity
│ ├── Player
│ └── Creature
└── BlockEntity
```
## Exemple Rapide
```java
getEventRegistry().register(PlayerConnectEvent.class, event -> {
Player player = event.getPlayer();
if (player != null) {
// Obtenir la position via TransformComponent
TransformComponent transform = player.getTransformComponent();
Vector3d pos = transform.getPosition();
// Obtenir le monde
World world = player.getWorld();
// Envoyer un message (nécessite un objet Message)
player.sendMessage(Message.raw("Bienvenue à " + pos.toString()));
}
});
```
{{< callout type="info" >}}
`event.getPlayer()` peut retourner null si le joueur n'est pas encore complètement chargé. Vérifiez toujours la valeur null. Pour l'accès à la position, `getTransformComponent()` est déprécié - préférez l'accès aux composants ECS dans le nouveau code.
{{< /callout >}}

View File

@@ -0,0 +1,828 @@
---
title: Entity Components
type: docs
weight: 2
---
Hytale uses an Entity Component System (ECS) where entities are composed of modular components. Each component adds specific functionality or data to an entity.
## Component Categories
{{< cards cols="3" >}}
{{< card link="#transform--position" title="Transform & Position" subtitle="Location, rotation, collision" >}}
{{< card link="#display--visual" title="Display & Visual" subtitle="Models, animations, effects" >}}
{{< card link="#behavior-markers" title="Behavior Markers" subtitle="Interaction flags" >}}
{{< card link="#physics" title="Physics" subtitle="Position data, collision" >}}
{{< card link="#audio" title="Audio" subtitle="Movement sounds" >}}
{{< card link="#custom-components" title="Custom Components" subtitle="Plugin-defined components" >}}
{{< /cards >}}
---
## Component Access Pattern
Before diving into specific components, understand how to access components in Hytale's ECS:
```java
// Get component type from the component class
ComponentType<EntityStore, TransformComponent> transformType =
TransformComponent.getComponentType();
// Get Ref<EntityStore> from PlayerRef or other source
Ref<EntityStore> ref = playerRef.getReference();
if (ref == null || !ref.isValid()) {
return;
}
// Get Store from Ref, then access component via Store
Store<EntityStore> store = ref.getStore();
TransformComponent transform = store.getComponent(ref, transformType);
// Check if entity has a component using Archetype
Archetype<EntityStore> archetype = store.getArchetype(ref);
boolean hasComponent = archetype.contains(transformType);
```
{{< callout type="warning" >}}
**Important:** `Ref<EntityStore>` does NOT have `getComponent()` or `hasComponent()` methods directly. You must get the `Store` first via `ref.getStore()`, then call `store.getComponent(ref, componentType)`.
{{< /callout >}}
{{< callout type="info" >}}
All entity components implement `Component<EntityStore>` and provide a static `getComponentType()` method to retrieve the `ComponentType` for component access.
{{< /callout >}}
---
## Transform & Position
### TransformComponent
Core component for entity position, rotation, and chunk reference.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| position | `Vector3d` | World position (x, y, z) |
| rotation | `Vector3f` | Rotation angles (x, y, z) |
| chunkRef | `Ref<ChunkStore>` | Reference to containing chunk |
{{< /tab >}}
{{< tab >}}
- `getPosition()` - Returns world position as Vector3d
- `setPosition(Vector3d)` - Sets world position (uses `assign()`)
- `getRotation()` - Returns rotation angles
- `setRotation(Vector3f)` - Sets rotation angles (uses `assign()`)
- `teleportPosition(Vector3d)` - Teleports entity to position
- `getTransform()` - Returns Transform object combining position/rotation
- `getChunkRef()` - Returns chunk reference (`Ref<ChunkStore>`)
- `getChunk()` - **Deprecated** - Returns WorldChunk directly
{{< callout type="warning" >}}
`getChunk()` is deprecated and marked for removal. Use `getChunkRef()` instead.
{{< /callout >}}
{{< /tab >}}
{{< tab >}}
```java
// Get store from ref
Store<EntityStore> store = ref.getStore();
ComponentType<EntityStore, TransformComponent> transformType =
TransformComponent.getComponentType();
TransformComponent transform = store.getComponent(ref, transformType);
if (transform != null) {
// Get current position
Vector3d pos = transform.getPosition();
getLogger().at(Level.INFO).log("Entity at: " + pos.x + ", " + pos.y + ", " + pos.z);
// Get rotation
Vector3f rot = transform.getRotation();
// Get chunk reference (recommended)
Ref<ChunkStore> chunkRef = transform.getChunkRef();
}
```
{{< /tab >}}
{{< /tabs >}}
---
### BoundingBox
Defines the collision bounds of an entity using a `Box` object.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| boundingBox | `Box` | The collision box with min/max corners |
| detailBoxes | `Map<String, DetailBox[]>` | Named detail boxes for precise collision |
{{< /tab >}}
{{< tab >}}
- `getBoundingBox()` - Returns the `Box` object
- `setBoundingBox(Box)` - Sets the bounding box
- `getDetailBoxes()` - Returns map of detail boxes (can be null)
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, BoundingBox> boundsType = BoundingBox.getComponentType();
BoundingBox bounds = store.getComponent(ref, boundsType);
// Access the Box object
Box box = bounds.getBoundingBox();
// Box provides min/max as Vector3d
Vector3d min = box.getMin();
Vector3d max = box.getMax();
// Box utility methods
double width = box.width(); // max.x - min.x
double height = box.height(); // max.y - min.y
double depth = box.depth(); // max.z - min.z
// Center point
double centerX = box.middleX();
double centerY = box.middleY();
double centerZ = box.middleZ();
// Check intersection with another box
Box otherBox = otherBounds.getBoundingBox();
if (box.isIntersecting(otherBox)) {
handleCollision();
}
// Modify box (mutates in place)
box.offset(1.0, 0, 0); // Move box by offset
box.scale(2.0f); // Scale box
// Access detail boxes for precise collision
Map<String, DetailBox[]> details = bounds.getDetailBoxes();
if (details != null) {
for (Map.Entry<String, DetailBox[]> entry : details.entrySet()) {
// Process named detail boxes
}
}
```
{{< /tab >}}
{{< /tabs >}}
#### The Box Class
The `Box` class (`com.hypixel.hytale.math.shape.Box`) is a fundamental shape class:
| Method | Description |
|--------|-------------|
| `width()` | Returns width (X axis) |
| `height()` | Returns height (Y axis) |
| `depth()` | Returns depth (Z axis) |
| `middleX/Y/Z()` | Returns center coordinates |
| `getVolume()` | Returns total volume |
| `isIntersecting(Box)` | Checks intersection with another box |
| `containsPosition(x,y,z)` | Checks if point is inside |
| `offset(x,y,z)` | Moves box by offset (mutates) |
| `scale(float)` | Scales box (mutates) |
| `union(Box)` | Expands to contain another box |
| `clone()` | Creates a copy |
---
### CollisionResultComponent
Stores collision detection results from the physics system.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| collisionResult | `CollisionResult` | Full collision computation result |
| collisionStartPosition | `Vector3d` | Position where collision started |
| collisionPositionOffset | `Vector3d` | Offset from collision |
| pendingCollisionCheck | `boolean` | Whether collision check is pending |
{{< /tab >}}
{{< tab >}}
- `getCollisionResult()` - Returns the `CollisionResult` object
- `isPendingCollisionCheck()` - Returns true if collision needs recomputation
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, CollisionResultComponent> collisionType =
CollisionResultComponent.getComponentType();
CollisionResultComponent collision = store.getComponent(ref,collisionType);
// Get collision result object
CollisionResult result = collision.getCollisionResult();
// Check for block collisions
int blockCollisionCount = result.getBlockCollisionCount();
if (blockCollisionCount > 0) {
BlockCollisionData firstCollision = result.getFirstBlockCollision();
// Process collision
}
// Check if sliding (on ground)
if (result.isSliding) {
// Entity is sliding on a surface
double slideStart = result.slideStart;
double slideEnd = result.slideEnd;
}
// Check character collisions (if enabled)
int charCollisionCount = result.getCharacterCollisionCount();
CharacterCollisionData charCollision = result.getFirstCharacterCollision();
// Access trigger blocks (collision triggers)
CollisionDataArray<BlockCollisionData> triggers = result.getTriggerBlocks();
```
{{< /tab >}}
{{< /tabs >}}
{{< callout type="info" >}}
The `CollisionResult` class is complex and handles block collisions, character collisions, slides, and trigger blocks. It's primarily used internally by the collision system.
{{< /callout >}}
---
### PositionDataComponent
Tracks block type IDs at entity's position.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| insideBlockTypeId | `int` | Block type ID entity is inside |
| standingOnBlockTypeId | `int` | Block type ID entity is standing on |
{{< /tab >}}
{{< tab >}}
- `getInsideBlockTypeId()` - Returns block type ID entity is inside
- `setInsideBlockTypeId(int)` - Sets inside block type ID
- `getStandingOnBlockTypeId()` - Returns block type ID beneath entity
- `setStandingOnBlockTypeId(int)` - Sets standing-on block type ID
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, PositionDataComponent> posDataType =
PositionDataComponent.getComponentType();
PositionDataComponent posData = store.getComponent(ref,posDataType);
// Check what block entity is standing on
int standingOn = posData.getStandingOnBlockTypeId();
// Check what block entity is inside
int insideBlock = posData.getInsideBlockTypeId();
// Use block type registry to get actual BlockType
BlockType blockType = BlockType.getAssetMap().getAsset(standingOn);
if (blockType != null) {
getLogger().at(Level.INFO).log("Standing on: " + blockType.getId());
}
```
{{< /tab >}}
{{< /tabs >}}
---
## Display & Visual
### DisplayNameComponent
Controls the display name shown above an entity.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| displayName | `Message` | The displayed name as a Message object |
{{< /tab >}}
{{< tab >}}
- `getDisplayName()` - Returns the `Message` display name
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, DisplayNameComponent> nameType =
DisplayNameComponent.getComponentType();
DisplayNameComponent nameComp = store.getComponent(ref,nameType);
// Get current display name
Message name = nameComp.getDisplayName();
// Message is the formatted text object used throughout Hytale
// Use Message.raw() or Message formatting methods to create new names
```
{{< /tab >}}
{{< /tabs >}}
{{< callout type="info" >}}
**Message Type:** Display names use Hytale's `Message` class, not plain strings. The `Message` class supports formatted text, colors, and localization.
{{< /callout >}}
---
### ModelComponent
Links an entity to its visual 3D model.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| model | `Model` | Reference to the model |
| isNetworkOutdated | `boolean` | Network sync flag |
{{< /tab >}}
{{< tab >}}
- `getModel()` - Returns the Model
- `setModel(Model)` - Sets the model
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, ModelComponent> modelType = ModelComponent.getComponentType();
ModelComponent model = store.getComponent(ref,modelType);
// Get current model
Model currentModel = model.getModel();
// Set new model
model.setModel(newModel);
```
{{< /tab >}}
{{< /tabs >}}
---
### EntityScaleComponent
Controls the scale/size of an entity.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| scale | `float` | Uniform scale factor (default: 1.0) |
| isNetworkOutdated | `boolean` | Network sync flag |
{{< /tab >}}
{{< tab >}}
- `getScale()` - Returns the scale factor (float)
- `setScale(float)` - Sets the uniform scale factor
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, EntityScaleComponent> scaleType =
EntityScaleComponent.getComponentType();
EntityScaleComponent scale = store.getComponent(ref,scaleType);
// Get current scale
float currentScale = scale.getScale(); // Default: 1.0
// Make entity twice as large
scale.setScale(2.0f);
// Make entity half size
scale.setScale(0.5f);
// Giant boss spawn effect - track growth in tick handler
private final Map<Ref<EntityStore>, Float> growingBosses = new ConcurrentHashMap<>();
public void startGrowBoss(Ref<EntityStore> bossRef) {
EntityScaleComponent scale = store.getComponent(bossRef, scaleType);
scale.setScale(0.1f); // Start small
growingBosses.put(bossRef, 0.1f);
}
// Call this from your tick handler (every 5 ticks)
public void onTick() {
growingBosses.entrySet().removeIf(entry -> {
Ref<EntityStore> bossRef = entry.getKey();
float currentScale = entry.getValue();
if (currentScale >= 1.0f) {
return true; // Remove from map
}
EntityScaleComponent scale = store.getComponent(bossRef, scaleType);
if (scale != null) {
float newScale = currentScale + 0.1f;
scale.setScale(newScale);
entry.setValue(newScale);
}
return false; // Keep in map
});
}
```
{{< /tab >}}
{{< /tabs >}}
{{< callout type="warning" >}}
**Uniform Scale Only:** Hytale's EntityScaleComponent uses a single `float` for uniform scaling on all axes, not per-axis Vector3f scaling.
{{< /callout >}}
---
### ActiveAnimationComponent
Manages currently playing animations per animation slot.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| activeAnimations | `String[]` | Animation names per slot |
| isNetworkOutdated | `boolean` | Network sync flag |
{{< /tab >}}
{{< tab >}}
- `getActiveAnimations()` - Returns array of animation names indexed by `AnimationSlot`
- `setPlayingAnimation(AnimationSlot, String)` - Sets animation for a specific slot
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, ActiveAnimationComponent> animType =
ActiveAnimationComponent.getComponentType();
ActiveAnimationComponent anim = store.getComponent(ref,animType);
// Get all active animations
String[] animations = anim.getActiveAnimations();
// Set animation for specific slot
anim.setPlayingAnimation(AnimationSlot.Action, "attack_swing");
anim.setPlayingAnimation(AnimationSlot.Movement, "walk");
// Clear animation on a slot
anim.setPlayingAnimation(AnimationSlot.Emote, null);
```
{{< /tab >}}
{{< /tabs >}}
#### AnimationSlot Enum
Hytale uses animation slots to layer animations:
| Slot | Value | Purpose |
|------|-------|---------|
| `Movement` | 0 | Locomotion (walk, run, jump) |
| `Status` | 1 | Status effects |
| `Action` | 2 | Actions (attack, use item) |
| `Face` | 3 | Facial expressions |
| `Emote` | 4 | Social emotes |
---
### DynamicLight
Emits dynamic light from an entity using a `ColorLight` structure.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| colorLight | `ColorLight` | Light data (radius, RGB) |
| isNetworkOutdated | `boolean` | Network sync flag |
{{< /tab >}}
{{< tab >}}
- `getColorLight()` - Returns the `ColorLight` object
- `setColorLight(ColorLight)` - Sets the light configuration
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, DynamicLight> lightType = DynamicLight.getComponentType();
DynamicLight light = store.getComponent(ref,lightType);
// Get current light
ColorLight currentLight = light.getColorLight();
// Create new light configuration
// ColorLight has public fields: radius, red, green, blue (all bytes)
ColorLight torchLight = new ColorLight();
torchLight.radius = 8; // Light radius
torchLight.red = (byte)255; // Warm orange color
torchLight.green = (byte)200;
torchLight.blue = (byte)100;
light.setColorLight(torchLight);
// Or use constructor
ColorLight blueLight = new ColorLight(
(byte)12, // radius
(byte)50, // red
(byte)100, // green
(byte)255 // blue
);
light.setColorLight(blueLight);
```
{{< /tab >}}
{{< /tabs >}}
#### ColorLight Structure
The `ColorLight` class (`com.hypixel.hytale.protocol.ColorLight`) has public fields:
| Field | Type | Description |
|-------|------|-------------|
| `radius` | `byte` | Light radius |
| `red` | `byte` | Red component (0-255) |
| `green` | `byte` | Green component (0-255) |
| `blue` | `byte` | Blue component (0-255) |
---
### HeadRotation
Controls head rotation independently from body.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| rotation | `Vector3f` | Head rotation (x, y, z) |
{{< /tab >}}
{{< tab >}}
- `getRotation()` - Returns head rotation as Vector3f
- `setRotation(Vector3f)` - Sets head rotation (uses `assign()`)
- `getDirection()` - Returns look direction as Vector3d
- `getAxisDirection()` - Returns axis-aligned direction as Vector3i
- `teleportRotation(Vector3f)` - Teleports head rotation
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, HeadRotation> headType = HeadRotation.getComponentType();
HeadRotation head = store.getComponent(ref,headType);
// Get current head rotation
Vector3f rot = head.getRotation();
// Set head rotation (pitch, yaw, roll)
head.setRotation(new Vector3f(-30, 45, 0)); // Look up-right
// Get look direction as normalized vector
Vector3d direction = head.getDirection();
// Get axis direction (discrete: -1, 0, or 1 per axis)
Vector3i axisDir = head.getAxisDirection();
// Teleport rotation (instant, no interpolation)
head.teleportRotation(new Vector3f(0, 180, 0)); // Face backward
```
{{< /tab >}}
{{< /tabs >}}
---
## Behavior Markers
Singleton components that act as flags modifying entity behavior. They use the singleton pattern with an `INSTANCE` field.
### Interactable
{{< badge "Singleton" >}}
Marks an entity as interactable by players.
```java
ComponentType<EntityStore, Interactable> type = Interactable.getComponentType();
// Check if entity has component using archetype
Store<EntityStore> store = ref.getStore();
Archetype<EntityStore> archetype = store.getArchetype(ref);
if (archetype.contains(type)) {
// Entity is interactable
}
// Component is singleton - use INSTANCE
Interactable component = Interactable.INSTANCE;
```
---
### Invulnerable
{{< badge "Singleton" >}}
Makes an entity immune to damage.
```java
ComponentType<EntityStore, Invulnerable> type = Invulnerable.getComponentType();
// Check invulnerability in damage handler
getEventRegistry().register(EntityDamageEvent.class, event -> {
Ref<EntityStore> targetRef = event.getTarget();
if (targetRef != null && targetRef.isValid()) {
Store<EntityStore> store = targetRef.getStore();
Archetype<EntityStore> archetype = store.getArchetype(targetRef);
if (archetype.contains(type)) {
event.setCancelled(true);
}
}
});
```
---
### Intangible
{{< badge "Singleton" >}}
Makes an entity pass through other entities and blocks (no collision).
```java
ComponentType<EntityStore, Intangible> type = Intangible.getComponentType();
// Ghost entity - no collision
Store<EntityStore> store = ref.getStore();
Archetype<EntityStore> archetype = store.getArchetype(ref);
if (archetype.contains(type)) {
// Entity passes through obstacles
}
```
---
## Audio
### MovementAudioComponent
Handles movement-related sounds with a predicate system.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| shouldHearPredicate | `ShouldHearPredicate` | Predicate for who hears sounds |
| lastInsideBlockTypeId | `int` | Last block entity was inside |
| nextMoveInRepeat | `float` | Timer for movement sound repeat |
{{< /tab >}}
{{< tab >}}
- `getShouldHearPredicate(Ref<EntityStore>)` - Returns predicate for sound hearing
- `getLastInsideBlockTypeId()` - Returns last inside block ID
- `setLastInsideBlockTypeId(int)` - Sets last inside block ID
- `canMoveInRepeat()` - Returns true if repeat timer is active
- `tickMoveInRepeat(float)` - Advances repeat timer, returns true if elapsed
- `setNextMoveInRepeat(float)` - Sets next repeat interval
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, MovementAudioComponent> audioType =
MovementAudioComponent.getComponentType();
MovementAudioComponent movementAudio = store.getComponent(ref,audioType);
// Get predicate for who should hear movement sounds
MovementAudioComponent.ShouldHearPredicate predicate =
movementAudio.getShouldHearPredicate(entityRef);
// The predicate returns true for entities that should hear the sound
// (excludes the owner by default)
boolean shouldHear = predicate.test(otherEntityRef);
// Check move-in repeat timer
if (movementAudio.canMoveInRepeat()) {
float dt = 1.0f / 20.0f; // Delta time
if (movementAudio.tickMoveInRepeat(dt)) {
// Timer elapsed, play repeat sound
}
}
// Disable repeat sounds
movementAudio.setNextMoveInRepeat(MovementAudioComponent.NO_REPEAT);
```
{{< /tab >}}
{{< /tabs >}}
---
## Custom Components
### Creating Custom Components
Create custom components by implementing `Component<EntityStore>`:
```java
public class CustomDataComponent implements Component<EntityStore> {
// Static component type reference (set during registration)
private static ComponentType<EntityStore, CustomDataComponent> TYPE;
// Component data
private int level = 1;
private String faction = "neutral";
public static ComponentType<EntityStore, CustomDataComponent> getComponentType() {
return TYPE;
}
public int getLevel() { return level; }
public void setLevel(int level) { this.level = level; }
public String getFaction() { return faction; }
public void setFaction(String faction) { this.faction = faction; }
@Override
@Nonnull
public Component<EntityStore> clone() {
CustomDataComponent copy = new CustomDataComponent();
copy.level = this.level;
copy.faction = this.faction;
return copy;
}
}
```
### Registering Components
Register components during plugin start:
```java
@Override
public void start() {
// Register with EntityStoreRegistry
ComponentType<EntityStore, CustomDataComponent> type =
getEntityStoreRegistry().register(
"custom_data",
CustomDataComponent.class,
CustomDataComponent::new
);
// Store type reference for static access
CustomDataComponent.TYPE = type;
}
```
### Using Custom Components
```java
// Get component type
ComponentType<EntityStore, CustomDataComponent> type =
CustomDataComponent.getComponentType();
// Get store from ref
Store<EntityStore> store = ref.getStore();
// Check if entity has component
Archetype<EntityStore> archetype = store.getArchetype(ref);
if (archetype.contains(type)) {
CustomDataComponent data = store.getComponent(ref, type);
if (data.getLevel() > 10) {
// High level entity logic
}
}
```
---
## Best Practices
{{< callout type="info" >}}
**Component Guidelines:**
- Use `getComponentType()` static method to get the `ComponentType` for access
- Always check if a component exists using `hasComponent()` before accessing
- Register custom components in `start()`, after all plugins have completed setup
- Implement `clone()` for components that need copying
- Use singleton pattern (static `INSTANCE`) for marker components
{{< /callout >}}
{{< callout type="warning" >}}
**Network Sync:** Many components have an `isNetworkOutdated` flag. This is managed automatically by the network system - avoid setting it manually unless you understand the networking layer.
{{< /callout >}}
{{< callout type="error" >}}
**Thread Safety:** Component access should be done on the world's ticking thread. Use `world.isInThread()` to verify, or use the task system for cross-thread operations.
{{< /callout >}}
---
## Component Type Reference
| Component | Package | Type | Purpose |
|-----------|---------|------|---------|
| TransformComponent | entity.component | Data | Position, rotation, chunk |
| BoundingBox | entity.component | Data | Collision bounds |
| CollisionResultComponent | entity.component | Data | Collision results |
| PositionDataComponent | entity.component | Data | Block context |
| DisplayNameComponent | entity.component | Data | Visible name |
| ModelComponent | entity.component | Data | 3D model |
| EntityScaleComponent | entity.component | Data | Size scaling |
| ActiveAnimationComponent | entity.component | Data | Animations |
| DynamicLight | entity.component | Data | Light emission |
| HeadRotation | entity.component | Data | Head direction |
| MovementAudioComponent | entity.component | Data | Movement sounds |
| Interactable | entity.component | Singleton | Can be interacted |
| Invulnerable | entity.component | Singleton | Cannot be damaged |
| Intangible | entity.component | Singleton | No collision |

View File

@@ -0,0 +1,828 @@
---
title: Entity Components
type: docs
weight: 2
---
Hytale uses an Entity Component System (ECS) where entities are composed of modular components. Each component adds specific functionality or data to an entity.
## Component Categories
{{< cards cols="3" >}}
{{< card link="#transform--position" title="Transform & Position" subtitle="Location, rotation, collision" >}}
{{< card link="#display--visual" title="Display & Visual" subtitle="Models, animations, effects" >}}
{{< card link="#behavior-markers" title="Behavior Markers" subtitle="Interaction flags" >}}
{{< card link="#physics" title="Physics" subtitle="Position data, collision" >}}
{{< card link="#audio" title="Audio" subtitle="Movement sounds" >}}
{{< card link="#custom-components" title="Custom Components" subtitle="Plugin-defined components" >}}
{{< /cards >}}
---
## Component Access Pattern
Before diving into specific components, understand how to access components in Hytale's ECS:
```java
// Get component type from the component class
ComponentType<EntityStore, TransformComponent> transformType =
TransformComponent.getComponentType();
// Get Ref<EntityStore> from PlayerRef or other source
Ref<EntityStore> ref = playerRef.getReference();
if (ref == null || !ref.isValid()) {
return;
}
// Get Store from Ref, then access component via Store
Store<EntityStore> store = ref.getStore();
TransformComponent transform = store.getComponent(ref, transformType);
// Check if entity has a component using Archetype
Archetype<EntityStore> archetype = store.getArchetype(ref);
boolean hasComponent = archetype.contains(transformType);
```
{{< callout type="warning" >}}
**Important:** `Ref<EntityStore>` does NOT have `getComponent()` or `hasComponent()` methods directly. You must get the `Store` first via `ref.getStore()`, then call `store.getComponent(ref, componentType)`.
{{< /callout >}}
{{< callout type="info" >}}
All entity components implement `Component<EntityStore>` and provide a static `getComponentType()` method to retrieve the `ComponentType` for component access.
{{< /callout >}}
---
## Transform & Position
### TransformComponent
Core component for entity position, rotation, and chunk reference.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| position | `Vector3d` | World position (x, y, z) |
| rotation | `Vector3f` | Rotation angles (x, y, z) |
| chunkRef | `Ref<ChunkStore>` | Reference to containing chunk |
{{< /tab >}}
{{< tab >}}
- `getPosition()` - Returns world position as Vector3d
- `setPosition(Vector3d)` - Sets world position (uses `assign()`)
- `getRotation()` - Returns rotation angles
- `setRotation(Vector3f)` - Sets rotation angles (uses `assign()`)
- `teleportPosition(Vector3d)` - Teleports entity to position
- `getTransform()` - Returns Transform object combining position/rotation
- `getChunkRef()` - Returns chunk reference (`Ref<ChunkStore>`)
- `getChunk()` - **Deprecated** - Returns WorldChunk directly
{{< callout type="warning" >}}
`getChunk()` is deprecated and marked for removal. Use `getChunkRef()` instead.
{{< /callout >}}
{{< /tab >}}
{{< tab >}}
```java
// Get store from ref
Store<EntityStore> store = ref.getStore();
ComponentType<EntityStore, TransformComponent> transformType =
TransformComponent.getComponentType();
TransformComponent transform = store.getComponent(ref, transformType);
if (transform != null) {
// Get current position
Vector3d pos = transform.getPosition();
getLogger().at(Level.INFO).log("Entity at: " + pos.x + ", " + pos.y + ", " + pos.z);
// Get rotation
Vector3f rot = transform.getRotation();
// Get chunk reference (recommended)
Ref<ChunkStore> chunkRef = transform.getChunkRef();
}
```
{{< /tab >}}
{{< /tabs >}}
---
### BoundingBox
Defines the collision bounds of an entity using a `Box` object.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| boundingBox | `Box` | The collision box with min/max corners |
| detailBoxes | `Map<String, DetailBox[]>` | Named detail boxes for precise collision |
{{< /tab >}}
{{< tab >}}
- `getBoundingBox()` - Returns the `Box` object
- `setBoundingBox(Box)` - Sets the bounding box
- `getDetailBoxes()` - Returns map of detail boxes (can be null)
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, BoundingBox> boundsType = BoundingBox.getComponentType();
BoundingBox bounds = store.getComponent(ref, boundsType);
// Access the Box object
Box box = bounds.getBoundingBox();
// Box provides min/max as Vector3d
Vector3d min = box.getMin();
Vector3d max = box.getMax();
// Box utility methods
double width = box.width(); // max.x - min.x
double height = box.height(); // max.y - min.y
double depth = box.depth(); // max.z - min.z
// Center point
double centerX = box.middleX();
double centerY = box.middleY();
double centerZ = box.middleZ();
// Check intersection with another box
Box otherBox = otherBounds.getBoundingBox();
if (box.isIntersecting(otherBox)) {
handleCollision();
}
// Modify box (mutates in place)
box.offset(1.0, 0, 0); // Move box by offset
box.scale(2.0f); // Scale box
// Access detail boxes for precise collision
Map<String, DetailBox[]> details = bounds.getDetailBoxes();
if (details != null) {
for (Map.Entry<String, DetailBox[]> entry : details.entrySet()) {
// Process named detail boxes
}
}
```
{{< /tab >}}
{{< /tabs >}}
#### The Box Class
The `Box` class (`com.hypixel.hytale.math.shape.Box`) is a fundamental shape class:
| Method | Description |
|--------|-------------|
| `width()` | Returns width (X axis) |
| `height()` | Returns height (Y axis) |
| `depth()` | Returns depth (Z axis) |
| `middleX/Y/Z()` | Returns center coordinates |
| `getVolume()` | Returns total volume |
| `isIntersecting(Box)` | Checks intersection with another box |
| `containsPosition(x,y,z)` | Checks if point is inside |
| `offset(x,y,z)` | Moves box by offset (mutates) |
| `scale(float)` | Scales box (mutates) |
| `union(Box)` | Expands to contain another box |
| `clone()` | Creates a copy |
---
### CollisionResultComponent
Stores collision detection results from the physics system.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| collisionResult | `CollisionResult` | Full collision computation result |
| collisionStartPosition | `Vector3d` | Position where collision started |
| collisionPositionOffset | `Vector3d` | Offset from collision |
| pendingCollisionCheck | `boolean` | Whether collision check is pending |
{{< /tab >}}
{{< tab >}}
- `getCollisionResult()` - Returns the `CollisionResult` object
- `isPendingCollisionCheck()` - Returns true if collision needs recomputation
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, CollisionResultComponent> collisionType =
CollisionResultComponent.getComponentType();
CollisionResultComponent collision = store.getComponent(ref,collisionType);
// Get collision result object
CollisionResult result = collision.getCollisionResult();
// Check for block collisions
int blockCollisionCount = result.getBlockCollisionCount();
if (blockCollisionCount > 0) {
BlockCollisionData firstCollision = result.getFirstBlockCollision();
// Process collision
}
// Check if sliding (on ground)
if (result.isSliding) {
// Entity is sliding on a surface
double slideStart = result.slideStart;
double slideEnd = result.slideEnd;
}
// Check character collisions (if enabled)
int charCollisionCount = result.getCharacterCollisionCount();
CharacterCollisionData charCollision = result.getFirstCharacterCollision();
// Access trigger blocks (collision triggers)
CollisionDataArray<BlockCollisionData> triggers = result.getTriggerBlocks();
```
{{< /tab >}}
{{< /tabs >}}
{{< callout type="info" >}}
The `CollisionResult` class is complex and handles block collisions, character collisions, slides, and trigger blocks. It's primarily used internally by the collision system.
{{< /callout >}}
---
### PositionDataComponent
Tracks block type IDs at entity's position.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| insideBlockTypeId | `int` | Block type ID entity is inside |
| standingOnBlockTypeId | `int` | Block type ID entity is standing on |
{{< /tab >}}
{{< tab >}}
- `getInsideBlockTypeId()` - Returns block type ID entity is inside
- `setInsideBlockTypeId(int)` - Sets inside block type ID
- `getStandingOnBlockTypeId()` - Returns block type ID beneath entity
- `setStandingOnBlockTypeId(int)` - Sets standing-on block type ID
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, PositionDataComponent> posDataType =
PositionDataComponent.getComponentType();
PositionDataComponent posData = store.getComponent(ref,posDataType);
// Check what block entity is standing on
int standingOn = posData.getStandingOnBlockTypeId();
// Check what block entity is inside
int insideBlock = posData.getInsideBlockTypeId();
// Use block type registry to get actual BlockType
BlockType blockType = BlockType.getAssetMap().getAsset(standingOn);
if (blockType != null) {
getLogger().at(Level.INFO).log("Standing on: " + blockType.getId());
}
```
{{< /tab >}}
{{< /tabs >}}
---
## Display & Visual
### DisplayNameComponent
Controls the display name shown above an entity.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| displayName | `Message` | The displayed name as a Message object |
{{< /tab >}}
{{< tab >}}
- `getDisplayName()` - Returns the `Message` display name
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, DisplayNameComponent> nameType =
DisplayNameComponent.getComponentType();
DisplayNameComponent nameComp = store.getComponent(ref,nameType);
// Get current display name
Message name = nameComp.getDisplayName();
// Message is the formatted text object used throughout Hytale
// Use Message.raw() or Message formatting methods to create new names
```
{{< /tab >}}
{{< /tabs >}}
{{< callout type="info" >}}
**Message Type:** Display names use Hytale's `Message` class, not plain strings. The `Message` class supports formatted text, colors, and localization.
{{< /callout >}}
---
### ModelComponent
Links an entity to its visual 3D model.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| model | `Model` | Reference to the model |
| isNetworkOutdated | `boolean` | Network sync flag |
{{< /tab >}}
{{< tab >}}
- `getModel()` - Returns the Model
- `setModel(Model)` - Sets the model
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, ModelComponent> modelType = ModelComponent.getComponentType();
ModelComponent model = store.getComponent(ref,modelType);
// Get current model
Model currentModel = model.getModel();
// Set new model
model.setModel(newModel);
```
{{< /tab >}}
{{< /tabs >}}
---
### EntityScaleComponent
Controls the scale/size of an entity.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| scale | `float` | Uniform scale factor (default: 1.0) |
| isNetworkOutdated | `boolean` | Network sync flag |
{{< /tab >}}
{{< tab >}}
- `getScale()` - Returns the scale factor (float)
- `setScale(float)` - Sets the uniform scale factor
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, EntityScaleComponent> scaleType =
EntityScaleComponent.getComponentType();
EntityScaleComponent scale = store.getComponent(ref,scaleType);
// Get current scale
float currentScale = scale.getScale(); // Default: 1.0
// Make entity twice as large
scale.setScale(2.0f);
// Make entity half size
scale.setScale(0.5f);
// Giant boss spawn effect - track growth in tick handler
private final Map<Ref<EntityStore>, Float> growingBosses = new ConcurrentHashMap<>();
public void startGrowBoss(Ref<EntityStore> bossRef) {
EntityScaleComponent scale = store.getComponent(bossRef, scaleType);
scale.setScale(0.1f); // Start small
growingBosses.put(bossRef, 0.1f);
}
// Call this from your tick handler (every 5 ticks)
public void onTick() {
growingBosses.entrySet().removeIf(entry -> {
Ref<EntityStore> bossRef = entry.getKey();
float currentScale = entry.getValue();
if (currentScale >= 1.0f) {
return true; // Remove from map
}
EntityScaleComponent scale = store.getComponent(bossRef, scaleType);
if (scale != null) {
float newScale = currentScale + 0.1f;
scale.setScale(newScale);
entry.setValue(newScale);
}
return false; // Keep in map
});
}
```
{{< /tab >}}
{{< /tabs >}}
{{< callout type="warning" >}}
**Uniform Scale Only:** Hytale's EntityScaleComponent uses a single `float` for uniform scaling on all axes, not per-axis Vector3f scaling.
{{< /callout >}}
---
### ActiveAnimationComponent
Manages currently playing animations per animation slot.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| activeAnimations | `String[]` | Animation names per slot |
| isNetworkOutdated | `boolean` | Network sync flag |
{{< /tab >}}
{{< tab >}}
- `getActiveAnimations()` - Returns array of animation names indexed by `AnimationSlot`
- `setPlayingAnimation(AnimationSlot, String)` - Sets animation for a specific slot
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, ActiveAnimationComponent> animType =
ActiveAnimationComponent.getComponentType();
ActiveAnimationComponent anim = store.getComponent(ref,animType);
// Get all active animations
String[] animations = anim.getActiveAnimations();
// Set animation for specific slot
anim.setPlayingAnimation(AnimationSlot.Action, "attack_swing");
anim.setPlayingAnimation(AnimationSlot.Movement, "walk");
// Clear animation on a slot
anim.setPlayingAnimation(AnimationSlot.Emote, null);
```
{{< /tab >}}
{{< /tabs >}}
#### AnimationSlot Enum
Hytale uses animation slots to layer animations:
| Slot | Value | Purpose |
|------|-------|---------|
| `Movement` | 0 | Locomotion (walk, run, jump) |
| `Status` | 1 | Status effects |
| `Action` | 2 | Actions (attack, use item) |
| `Face` | 3 | Facial expressions |
| `Emote` | 4 | Social emotes |
---
### DynamicLight
Emits dynamic light from an entity using a `ColorLight` structure.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| colorLight | `ColorLight` | Light data (radius, RGB) |
| isNetworkOutdated | `boolean` | Network sync flag |
{{< /tab >}}
{{< tab >}}
- `getColorLight()` - Returns the `ColorLight` object
- `setColorLight(ColorLight)` - Sets the light configuration
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, DynamicLight> lightType = DynamicLight.getComponentType();
DynamicLight light = store.getComponent(ref,lightType);
// Get current light
ColorLight currentLight = light.getColorLight();
// Create new light configuration
// ColorLight has public fields: radius, red, green, blue (all bytes)
ColorLight torchLight = new ColorLight();
torchLight.radius = 8; // Light radius
torchLight.red = (byte)255; // Warm orange color
torchLight.green = (byte)200;
torchLight.blue = (byte)100;
light.setColorLight(torchLight);
// Or use constructor
ColorLight blueLight = new ColorLight(
(byte)12, // radius
(byte)50, // red
(byte)100, // green
(byte)255 // blue
);
light.setColorLight(blueLight);
```
{{< /tab >}}
{{< /tabs >}}
#### ColorLight Structure
The `ColorLight` class (`com.hypixel.hytale.protocol.ColorLight`) has public fields:
| Field | Type | Description |
|-------|------|-------------|
| `radius` | `byte` | Light radius |
| `red` | `byte` | Red component (0-255) |
| `green` | `byte` | Green component (0-255) |
| `blue` | `byte` | Blue component (0-255) |
---
### HeadRotation
Controls head rotation independently from body.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| rotation | `Vector3f` | Head rotation (x, y, z) |
{{< /tab >}}
{{< tab >}}
- `getRotation()` - Returns head rotation as Vector3f
- `setRotation(Vector3f)` - Sets head rotation (uses `assign()`)
- `getDirection()` - Returns look direction as Vector3d
- `getAxisDirection()` - Returns axis-aligned direction as Vector3i
- `teleportRotation(Vector3f)` - Teleports head rotation
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, HeadRotation> headType = HeadRotation.getComponentType();
HeadRotation head = store.getComponent(ref,headType);
// Get current head rotation
Vector3f rot = head.getRotation();
// Set head rotation (pitch, yaw, roll)
head.setRotation(new Vector3f(-30, 45, 0)); // Look up-right
// Get look direction as normalized vector
Vector3d direction = head.getDirection();
// Get axis direction (discrete: -1, 0, or 1 per axis)
Vector3i axisDir = head.getAxisDirection();
// Teleport rotation (instant, no interpolation)
head.teleportRotation(new Vector3f(0, 180, 0)); // Face backward
```
{{< /tab >}}
{{< /tabs >}}
---
## Behavior Markers
Singleton components that act as flags modifying entity behavior. They use the singleton pattern with an `INSTANCE` field.
### Interactable
{{< badge "Singleton" >}}
Marks an entity as interactable by players.
```java
ComponentType<EntityStore, Interactable> type = Interactable.getComponentType();
// Check if entity has component using archetype
Store<EntityStore> store = ref.getStore();
Archetype<EntityStore> archetype = store.getArchetype(ref);
if (archetype.contains(type)) {
// Entity is interactable
}
// Component is singleton - use INSTANCE
Interactable component = Interactable.INSTANCE;
```
---
### Invulnerable
{{< badge "Singleton" >}}
Makes an entity immune to damage.
```java
ComponentType<EntityStore, Invulnerable> type = Invulnerable.getComponentType();
// Check invulnerability in damage handler
getEventRegistry().register(EntityDamageEvent.class, event -> {
Ref<EntityStore> targetRef = event.getTarget();
if (targetRef != null && targetRef.isValid()) {
Store<EntityStore> store = targetRef.getStore();
Archetype<EntityStore> archetype = store.getArchetype(targetRef);
if (archetype.contains(type)) {
event.setCancelled(true);
}
}
});
```
---
### Intangible
{{< badge "Singleton" >}}
Makes an entity pass through other entities and blocks (no collision).
```java
ComponentType<EntityStore, Intangible> type = Intangible.getComponentType();
// Ghost entity - no collision
Store<EntityStore> store = ref.getStore();
Archetype<EntityStore> archetype = store.getArchetype(ref);
if (archetype.contains(type)) {
// Entity passes through obstacles
}
```
---
## Audio
### MovementAudioComponent
Handles movement-related sounds with a predicate system.
**Package:** `com.hypixel.hytale.server.core.modules.entity.component`
{{< tabs items="Fields,Methods,Example" >}}
{{< tab >}}
| Field | Type | Description |
|-------|------|-------------|
| shouldHearPredicate | `ShouldHearPredicate` | Predicate for who hears sounds |
| lastInsideBlockTypeId | `int` | Last block entity was inside |
| nextMoveInRepeat | `float` | Timer for movement sound repeat |
{{< /tab >}}
{{< tab >}}
- `getShouldHearPredicate(Ref<EntityStore>)` - Returns predicate for sound hearing
- `getLastInsideBlockTypeId()` - Returns last inside block ID
- `setLastInsideBlockTypeId(int)` - Sets last inside block ID
- `canMoveInRepeat()` - Returns true if repeat timer is active
- `tickMoveInRepeat(float)` - Advances repeat timer, returns true if elapsed
- `setNextMoveInRepeat(float)` - Sets next repeat interval
{{< /tab >}}
{{< tab >}}
```java
ComponentType<EntityStore, MovementAudioComponent> audioType =
MovementAudioComponent.getComponentType();
MovementAudioComponent movementAudio = store.getComponent(ref,audioType);
// Get predicate for who should hear movement sounds
MovementAudioComponent.ShouldHearPredicate predicate =
movementAudio.getShouldHearPredicate(entityRef);
// The predicate returns true for entities that should hear the sound
// (excludes the owner by default)
boolean shouldHear = predicate.test(otherEntityRef);
// Check move-in repeat timer
if (movementAudio.canMoveInRepeat()) {
float dt = 1.0f / 20.0f; // Delta time
if (movementAudio.tickMoveInRepeat(dt)) {
// Timer elapsed, play repeat sound
}
}
// Disable repeat sounds
movementAudio.setNextMoveInRepeat(MovementAudioComponent.NO_REPEAT);
```
{{< /tab >}}
{{< /tabs >}}
---
## Custom Components
### Creating Custom Components
Create custom components by implementing `Component<EntityStore>`:
```java
public class CustomDataComponent implements Component<EntityStore> {
// Static component type reference (set during registration)
private static ComponentType<EntityStore, CustomDataComponent> TYPE;
// Component data
private int level = 1;
private String faction = "neutral";
public static ComponentType<EntityStore, CustomDataComponent> getComponentType() {
return TYPE;
}
public int getLevel() { return level; }
public void setLevel(int level) { this.level = level; }
public String getFaction() { return faction; }
public void setFaction(String faction) { this.faction = faction; }
@Override
@Nonnull
public Component<EntityStore> clone() {
CustomDataComponent copy = new CustomDataComponent();
copy.level = this.level;
copy.faction = this.faction;
return copy;
}
}
```
### Registering Components
Register components during plugin start:
```java
@Override
public void start() {
// Register with EntityStoreRegistry
ComponentType<EntityStore, CustomDataComponent> type =
getEntityStoreRegistry().register(
"custom_data",
CustomDataComponent.class,
CustomDataComponent::new
);
// Store type reference for static access
CustomDataComponent.TYPE = type;
}
```
### Using Custom Components
```java
// Get component type
ComponentType<EntityStore, CustomDataComponent> type =
CustomDataComponent.getComponentType();
// Get store from ref
Store<EntityStore> store = ref.getStore();
// Check if entity has component
Archetype<EntityStore> archetype = store.getArchetype(ref);
if (archetype.contains(type)) {
CustomDataComponent data = store.getComponent(ref, type);
if (data.getLevel() > 10) {
// High level entity logic
}
}
```
---
## Best Practices
{{< callout type="info" >}}
**Component Guidelines:**
- Use `getComponentType()` static method to get the `ComponentType` for access
- Always check if a component exists using `hasComponent()` before accessing
- Register custom components in `start()`, after all plugins have completed setup
- Implement `clone()` for components that need copying
- Use singleton pattern (static `INSTANCE`) for marker components
{{< /callout >}}
{{< callout type="warning" >}}
**Network Sync:** Many components have an `isNetworkOutdated` flag. This is managed automatically by the network system - avoid setting it manually unless you understand the networking layer.
{{< /callout >}}
{{< callout type="error" >}}
**Thread Safety:** Component access should be done on the world's ticking thread. Use `world.isInThread()` to verify, or use the task system for cross-thread operations.
{{< /callout >}}
---
## Component Type Reference
| Component | Package | Type | Purpose |
|-----------|---------|------|---------|
| TransformComponent | entity.component | Data | Position, rotation, chunk |
| BoundingBox | entity.component | Data | Collision bounds |
| CollisionResultComponent | entity.component | Data | Collision results |
| PositionDataComponent | entity.component | Data | Block context |
| DisplayNameComponent | entity.component | Data | Visible name |
| ModelComponent | entity.component | Data | 3D model |
| EntityScaleComponent | entity.component | Data | Size scaling |
| ActiveAnimationComponent | entity.component | Data | Animations |
| DynamicLight | entity.component | Data | Light emission |
| HeadRotation | entity.component | Data | Head direction |
| MovementAudioComponent | entity.component | Data | Movement sounds |
| Interactable | entity.component | Singleton | Can be interacted |
| Invulnerable | entity.component | Singleton | Cannot be damaged |
| Intangible | entity.component | Singleton | No collision |

View File

@@ -0,0 +1,259 @@
---
title: Entity Hierarchy
type: docs
weight: 1
---
Hytale uses an **Entity Component System (ECS)** architecture. Unlike traditional OOP where entities have methods directly on them, Hytale entities are composed of components stored in an `EntityStore`.
{{< callout type="warning" >}}
**ECS Architecture:** Entity classes like `Player` and `LivingEntity` are themselves `Component<EntityStore>` instances. Data is accessed through component lookup, not direct method calls on entities.
{{< /callout >}}
## Class Hierarchy Overview
{{< filetree/container >}}
{{< filetree/folder name="Entity (Component)" state="open" >}}
{{< filetree/folder name="LivingEntity" state="open" >}}
{{< filetree/file name="Player" >}}
{{< /filetree/folder >}}
{{< /filetree/folder >}}
{{< /filetree/container >}}
---
## Entity (Base Class)
**Package:** `com.hypixel.hytale.server.core.entity`
The base class for all entities. `Entity` implements `Component<EntityStore>` - it is stored as a component in the ECS system.
### Available Methods
| Method | Return Type | Description | Notes |
|--------|-------------|-------------|-------|
| `getWorld()` | `World` | The world this entity is in | |
| `getUuid()` | `UUID` | Unique identifier | Deprecated |
| `wasRemoved()` | `boolean` | Whether entity was removed | |
| `remove()` | `boolean` | Remove entity from world | |
| `getNetworkId()` | `int` | Network ID for this entity | Deprecated |
| `getTransformComponent()` | `TransformComponent` | Position/rotation data | Deprecated |
| `getReference()` | `Ref<EntityStore>` | ECS reference | |
```java
// Example: Accessing entity data
Entity entity = ...; // obtained from event or lookup
if (!entity.wasRemoved()) {
World world = entity.getWorld();
// Position access (deprecated but works)
TransformComponent transform = entity.getTransformComponent();
Vector3d position = transform.getPosition();
// ECS reference for component access
Ref<EntityStore> ref = entity.getReference();
if (ref != null && ref.isValid()) {
Store<EntityStore> store = ref.getStore();
// Access other components via store
}
}
```
---
## LivingEntity
**Package:** `com.hypixel.hytale.server.core.entity`
Extends Entity with inventory capabilities. Base class for entities that can hold items.
### Available Methods
| Method | Return Type | Description |
|--------|-------------|-------------|
| `getInventory()` | `Inventory` | Entity's inventory |
| `setInventory(Inventory)` | `Inventory` | Set inventory |
| `getCurrentFallDistance()` | `double` | Current fall distance |
| `getStatModifiersManager()` | `StatModifiersManager` | Stat modifiers |
```java
LivingEntity living = (LivingEntity) entity;
// Inventory access
Inventory inventory = living.getInventory();
// Fall distance tracking
double fallDistance = living.getCurrentFallDistance();
// Stat modifiers
StatModifiersManager stats = living.getStatModifiersManager();
```
{{< callout type="info" >}}
Health, damage, and effects are managed through the ECS system, not directly on LivingEntity. See [Entity Components]({{< relref "entity-components" >}}) for component-based data access.
{{< /callout >}}
---
## Player
**Package:** `com.hypixel.hytale.server.core.entity.entities`
Extends LivingEntity with player-specific features.
### Available Methods
| Method | Return Type | Description |
|--------|-------------|-------------|
| `sendMessage(Message)` | `void` | Send message to player |
| `hasPermission(String)` | `boolean` | Check permission |
| `hasPermission(String, boolean)` | `boolean` | Check with default |
| `getDisplayName()` | `String` | Player display name |
| `getGameMode()` | `GameMode` | Current game mode |
| `getInventory()` | `Inventory` | Player inventory |
| `getPlayerRef()` | `PlayerRef` | Thread-safe reference (deprecated) |
| `getWindowManager()` | `WindowManager` | Window management |
| `getPageManager()` | `PageManager` | Page management |
| `getHudManager()` | `HudManager` | HUD management |
| `getHotbarManager()` | `HotbarManager` | Hotbar management |
```java
Player player = event.getPlayer();
if (player != null) {
// Send messages (requires Message object)
player.sendMessage(Message.raw("Welcome!"));
player.sendMessage(Message.translation("greeting.key")
.param("name", player.getDisplayName()));
// Check permissions
if (player.hasPermission("admin.teleport")) {
// Has permission
}
// Get display name
String name = player.getDisplayName();
// Get inventory
Inventory inventory = player.getInventory();
// Get position via TransformComponent (deprecated)
TransformComponent transform = player.getTransformComponent();
Vector3d position = transform.getPosition();
}
```
---
## PlayerRef (Thread-Safe Reference)
**Package:** `com.hypixel.hytale.server.core.universe`
`PlayerRef` provides thread-safe access to player data. Use it for async operations or storing references.
### Available Methods
| Method | Return Type | Description |
|--------|-------------|-------------|
| `getUsername()` | `String` | Player username |
| `getUuid()` | `UUID` | Player UUID |
| `getWorldUuid()` | `UUID` | Current world UUID |
| `sendMessage(Message)` | `void` | Send message |
| `getReference()` | `Ref<EntityStore>` | ECS reference (may be null) |
| `getPacketHandler()` | `PacketHandler` | Network handler |
```java
// Get PlayerRef from event
PlayerRef playerRef = event.getPlayerRef();
// Thread-safe access to player data
String username = playerRef.getUsername();
UUID uuid = playerRef.getUuid();
// Send message (thread-safe)
playerRef.sendMessage(Message.raw("Hello, " + username + "!"));
// Get ECS reference for component access
Ref<EntityStore> ref = playerRef.getReference();
if (ref != null && ref.isValid()) {
Store<EntityStore> store = ref.getStore();
// Access Player component
Player player = store.getComponent(ref, Player.getComponentType());
// Access TransformComponent for position
TransformComponent transform = store.getComponent(ref, TransformComponent.getComponentType());
if (transform != null) {
Vector3d position = transform.getPosition();
}
}
```
---
## ECS Component Access Pattern
The correct way to access entity data in Hytale's ECS:
```java
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef playerRef = event.getPlayerRef();
// Get ECS reference
Ref<EntityStore> ref = playerRef.getReference();
if (ref == null || !ref.isValid()) {
return;
}
// Get store for component access
Store<EntityStore> store = ref.getStore();
// Access various components
Player player = store.getComponent(ref, Player.getComponentType());
TransformComponent transform = store.getComponent(ref, TransformComponent.getComponentType());
if (player != null && transform != null) {
Vector3d position = transform.getPosition();
player.sendMessage(Message.raw("You are at: " + position.toString()));
}
});
```
---
## Type Checking & Casting
Use pattern matching for safe type checking:
```java
public void handleEntity(Entity entity) {
if (entity instanceof Player player) {
player.sendMessage(Message.raw("You're a player!"));
} else if (entity instanceof LivingEntity living) {
Inventory inv = living.getInventory();
}
}
```
---
## Best Practices
{{< callout type="info" >}}
**Entity Handling Guidelines:**
- Always check `wasRemoved()` before using stored entity references
- Use `PlayerRef` for storing player references across ticks
- Access position via `TransformComponent` (deprecated methods work but ECS is preferred)
- Check `ref.isValid()` before accessing ECS components
{{< /callout >}}
{{< callout type="warning" >}}
**Thread Safety:** Entity objects are not thread-safe. Use `PlayerRef` for async operations. Never store direct entity references in long-lived data structures without proper cleanup.
{{< /callout >}}
{{< callout type="error" >}}
**Critical:** Never store `Entity` or `Player` references in static fields or maps without proper cleanup. Use UUIDs and look up entities when needed.
{{< /callout >}}

View File

@@ -0,0 +1,259 @@
---
title: Hiérarchie des Entités
type: docs
weight: 1
---
Hytale utilise une architecture **Entity Component System (ECS)**. Contrairement à la POO traditionnelle où les entités ont des méthodes directement sur elles, les entités Hytale sont composées de composants stockés dans un `EntityStore`.
{{< callout type="warning" >}}
**Architecture ECS:** Les classes d'entités comme `Player` et `LivingEntity` sont elles-mêmes des instances de `Component<EntityStore>`. Les données sont accessibles via la recherche de composants, pas via des appels de méthodes directs sur les entités.
{{< /callout >}}
## Aperçu de la Hiérarchie de Classes
{{< filetree/container >}}
{{< filetree/folder name="Entity (Component)" state="open" >}}
{{< filetree/folder name="LivingEntity" state="open" >}}
{{< filetree/file name="Player" >}}
{{< /filetree/folder >}}
{{< /filetree/folder >}}
{{< /filetree/container >}}
---
## Entity (Classe de Base)
**Package:** `com.hypixel.hytale.server.core.entity`
La classe de base pour toutes les entités. `Entity` implémente `Component<EntityStore>` - elle est stockée comme composant dans le système ECS.
### Méthodes Disponibles
| Méthode | Type de Retour | Description | Notes |
|---------|----------------|-------------|-------|
| `getWorld()` | `World` | Le monde où se trouve l'entité | |
| `getUuid()` | `UUID` | Identifiant unique | Dépréciée |
| `wasRemoved()` | `boolean` | Si l'entité a été supprimée | |
| `remove()` | `boolean` | Supprimer l'entité du monde | |
| `getNetworkId()` | `int` | ID réseau de l'entité | Dépréciée |
| `getTransformComponent()` | `TransformComponent` | Données de position/rotation | Dépréciée |
| `getReference()` | `Ref<EntityStore>` | Référence ECS | |
```java
// Exemple: Accéder aux données d'entité
Entity entity = ...; // obtenue depuis un événement ou une recherche
if (!entity.wasRemoved()) {
World world = entity.getWorld();
// Accès à la position (dépréciée mais fonctionne)
TransformComponent transform = entity.getTransformComponent();
Vector3d position = transform.getPosition();
// Référence ECS pour l'accès aux composants
Ref<EntityStore> ref = entity.getReference();
if (ref != null && ref.isValid()) {
Store<EntityStore> store = ref.getStore();
// Accéder à d'autres composants via le store
}
}
```
---
## LivingEntity
**Package:** `com.hypixel.hytale.server.core.entity`
Étend Entity avec des capacités d'inventaire. Classe de base pour les entités qui peuvent porter des objets.
### Méthodes Disponibles
| Méthode | Type de Retour | Description |
|---------|----------------|-------------|
| `getInventory()` | `Inventory` | Inventaire de l'entité |
| `setInventory(Inventory)` | `Inventory` | Définir l'inventaire |
| `getCurrentFallDistance()` | `double` | Distance de chute actuelle |
| `getStatModifiersManager()` | `StatModifiersManager` | Modificateurs de stats |
```java
LivingEntity living = (LivingEntity) entity;
// Accès à l'inventaire
Inventory inventory = living.getInventory();
// Suivi de la distance de chute
double fallDistance = living.getCurrentFallDistance();
// Modificateurs de stats
StatModifiersManager stats = living.getStatModifiersManager();
```
{{< callout type="info" >}}
La santé, les dégâts et les effets sont gérés via le système ECS, pas directement sur LivingEntity. Voir [Composants d'Entité]({{< relref "entity-components" >}}) pour l'accès aux données basé sur les composants.
{{< /callout >}}
---
## Player
**Package:** `com.hypixel.hytale.server.core.entity.entities`
Étend LivingEntity avec des fonctionnalités spécifiques aux joueurs.
### Méthodes Disponibles
| Méthode | Type de Retour | Description |
|---------|----------------|-------------|
| `sendMessage(Message)` | `void` | Envoyer un message au joueur |
| `hasPermission(String)` | `boolean` | Vérifier une permission |
| `hasPermission(String, boolean)` | `boolean` | Vérifier avec valeur par défaut |
| `getDisplayName()` | `String` | Nom d'affichage du joueur |
| `getGameMode()` | `GameMode` | Mode de jeu actuel |
| `getInventory()` | `Inventory` | Inventaire du joueur |
| `getPlayerRef()` | `PlayerRef` | Référence thread-safe (dépréciée) |
| `getWindowManager()` | `WindowManager` | Gestion des fenêtres |
| `getPageManager()` | `PageManager` | Gestion des pages |
| `getHudManager()` | `HudManager` | Gestion du HUD |
| `getHotbarManager()` | `HotbarManager` | Gestion de la barre d'action |
```java
Player player = event.getPlayer();
if (player != null) {
// Envoyer des messages (nécessite un objet Message)
player.sendMessage(Message.raw("Bienvenue !"));
player.sendMessage(Message.translation("greeting.key")
.param("name", player.getDisplayName()));
// Vérifier les permissions
if (player.hasPermission("admin.teleport")) {
// A la permission
}
// Obtenir le nom d'affichage
String name = player.getDisplayName();
// Obtenir l'inventaire
Inventory inventory = player.getInventory();
// Obtenir la position via TransformComponent (dépréciée)
TransformComponent transform = player.getTransformComponent();
Vector3d position = transform.getPosition();
}
```
---
## PlayerRef (Référence Thread-Safe)
**Package:** `com.hypixel.hytale.server.core.universe`
`PlayerRef` fournit un accès thread-safe aux données du joueur. Utilisez-le pour les opérations async ou pour stocker des références.
### Méthodes Disponibles
| Méthode | Type de Retour | Description |
|---------|----------------|-------------|
| `getUsername()` | `String` | Nom d'utilisateur du joueur |
| `getUuid()` | `UUID` | UUID du joueur |
| `getWorldUuid()` | `UUID` | UUID du monde actuel |
| `sendMessage(Message)` | `void` | Envoyer un message |
| `getReference()` | `Ref<EntityStore>` | Référence ECS (peut être null) |
| `getPacketHandler()` | `PacketHandler` | Gestionnaire réseau |
```java
// Obtenir PlayerRef depuis l'événement
PlayerRef playerRef = event.getPlayerRef();
// Accès thread-safe aux données du joueur
String username = playerRef.getUsername();
UUID uuid = playerRef.getUuid();
// Envoyer un message (thread-safe)
playerRef.sendMessage(Message.raw("Bonjour, " + username + " !"));
// Obtenir la référence ECS pour l'accès aux composants
Ref<EntityStore> ref = playerRef.getReference();
if (ref != null && ref.isValid()) {
Store<EntityStore> store = ref.getStore();
// Accéder au composant Player
Player player = store.getComponent(ref, Player.getComponentType());
// Accéder au TransformComponent pour la position
TransformComponent transform = store.getComponent(ref, TransformComponent.getComponentType());
if (transform != null) {
Vector3d position = transform.getPosition();
}
}
```
---
## Pattern d'Accès aux Composants ECS
La bonne façon d'accéder aux données d'entité dans l'ECS de Hytale:
```java
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef playerRef = event.getPlayerRef();
// Obtenir la référence ECS
Ref<EntityStore> ref = playerRef.getReference();
if (ref == null || !ref.isValid()) {
return;
}
// Obtenir le store pour l'accès aux composants
Store<EntityStore> store = ref.getStore();
// Accéder à divers composants
Player player = store.getComponent(ref, Player.getComponentType());
TransformComponent transform = store.getComponent(ref, TransformComponent.getComponentType());
if (player != null && transform != null) {
Vector3d position = transform.getPosition();
player.sendMessage(Message.raw("Vous êtes à : " + position.toString()));
}
});
```
---
## Vérification de Type & Cast
Utilisez le pattern matching pour une vérification de type sûre:
```java
public void handleEntity(Entity entity) {
if (entity instanceof Player player) {
player.sendMessage(Message.raw("Vous êtes un joueur !"));
} else if (entity instanceof LivingEntity living) {
Inventory inv = living.getInventory();
}
}
```
---
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives de Gestion des Entités:**
- Toujours vérifier `wasRemoved()` avant d'utiliser des références d'entités stockées
- Utiliser `PlayerRef` pour stocker des références de joueurs entre les ticks
- Accéder à la position via `TransformComponent` (les méthodes dépréciées fonctionnent mais l'ECS est préféré)
- Vérifier `ref.isValid()` avant d'accéder aux composants ECS
{{< /callout >}}
{{< callout type="warning" >}}
**Sécurité des Threads:** Les objets Entity ne sont pas thread-safe. Utilisez `PlayerRef` pour les opérations async. Ne stockez jamais de références directes d'entités dans des structures de données à longue durée de vie sans nettoyage approprié.
{{< /callout >}}
{{< callout type="error" >}}
**Critique:** Ne stockez jamais de références `Entity` ou `Player` dans des champs statiques ou des maps sans nettoyage approprié. Utilisez les UUID et recherchez les entités quand nécessaire.
{{< /callout >}}

View File

@@ -0,0 +1,302 @@
---
title: Flocking Behavior
type: docs
weight: 10
---
The flock system enables NPCs to form and behave as coordinated groups.
**Package:** `com.hypixel.hytale.server.flock`
## Overview
Flocks are entity-based groups that allow NPCs to:
- Move and act as a coordinated unit
- Share damage information across members
- Spawn in configurable group sizes
- Have designated leaders
## Architecture
```
Flock System
├── Components
│ ├── Flock - Group entity component
│ ├── FlockMembership - Member reference
│ └── PersistentFlockData - Saved flock data
├── Assets
│ ├── FlockAsset - Flock configuration
│ ├── RangeSizeFlockAsset - Range-based size
│ └── WeightedSizeFlockAsset - Weighted size
├── Systems
│ ├── FlockSystems - Core flock logic
│ ├── FlockMembershipSystems - Member management
│ └── FlockDeathSystems - Death handling
└── NPC Components
├── BodyMotionFlock - Movement behavior
├── ActionFlockJoin/Leave - Join/leave actions
└── SensorFlockLeader - Leader detection
```
## Flock Component
The flock entity holds shared group state:
```java
public class Flock implements Component<EntityStore> {
// Shared damage data for combat coordination
private DamageData currentDamageData;
private DamageData nextDamageData;
// Leader-specific damage data
private DamageData currentLeaderDamageData;
private DamageData nextLeaderDamageData;
// Persistent group configuration
private PersistentFlockData flockData;
// Removal status
public enum FlockRemovedStatus {
NOT_REMOVED,
DISSOLVED,
UNLOADED
}
}
```
## FlockMembership Component
Each NPC member has a membership component:
```java
public class FlockMembership implements Component<EntityStore> {
// Reference to the flock entity
private Ref<EntityStore> flockRef;
}
```
## Creating Flocks
### Spawn with Flock
```java
// Spawn an NPC with its flock
Ref<EntityStore> flockRef = FlockPlugin.trySpawnFlock(
npcRef, // Initial NPC reference
npc, // NPC component
store, // Entity store
roleIndex, // NPC role index
position, // Spawn position
rotation, // Spawn rotation
flockDefinition, // FlockAsset config (determines size)
postSpawnCallback // Called for each spawned member
);
```
### Create Empty Flock
```java
// Create flock entity for manual member management
Ref<EntityStore> flockRef = FlockPlugin.createFlock(store, role);
// Or with explicit configuration
Ref<EntityStore> flockRef = FlockPlugin.createFlock(
store,
flockAsset, // FlockAsset configuration
allowedRoles // Roles that can join
);
```
### Join Existing Flock
```java
// Add NPC to existing flock
FlockMembershipSystems.join(npcRef, flockRef, store);
```
## Flock Assets
### FlockAsset
Base configuration for flocks:
```java
public class FlockAsset {
// Asset identifier
private String id;
// Pick group size for spawning
public abstract int pickFlockSize();
}
```
### RangeSizeFlockAsset
Flock with random size in range:
```json
{
"Type": "RangeSize",
"Id": "wolf_pack",
"MinSize": 3,
"MaxSize": 8
}
```
### WeightedSizeFlockAsset
Flock with weighted random size:
```json
{
"Type": "WeightedSize",
"Id": "deer_herd",
"Sizes": [
{ "Size": 2, "Weight": 1 },
{ "Size": 4, "Weight": 2 },
{ "Size": 6, "Weight": 1 }
]
}
```
## NPC Core Components
### BodyMotionFlock
Controls flocking movement behavior:
```json
{
"Type": "Flock",
"SeparationWeight": 1.5,
"AlignmentWeight": 1.0,
"CohesionWeight": 1.0
}
```
### ActionFlockJoin
Join a flock:
```json
{
"Type": "JoinFlock",
"FlockId": "wolf_pack"
}
```
### ActionFlockLeave
Leave current flock:
```json
{
"Type": "LeaveFlock"
}
```
### SensorFlockLeader
Detect flock leader:
```json
{
"Type": "FlockLeader",
"Output": "leader_ref"
}
```
### EntityFilterFlock
Filter entities by flock membership:
```json
{
"Type": "Flock",
"IncludeSelf": false,
"OnlyMembers": true
}
```
## Damage Sharing
Flocks share combat information:
```java
// Get damage data for the flock
Flock flock = store.getComponent(flockRef, Flock.getComponentType());
DamageData damageData = flock.getDamageData();
// Track kills for the flock
flock.onTargetKilled(componentAccessor, targetRef);
```
### Double Buffering
Damage data uses double buffering to avoid race conditions:
```java
// Called each tick
flock.swapDamageDataBuffers();
// currentDamageData contains last tick's data
// nextDamageData accumulates current tick's data
```
## Conditions
### FlockSizeCondition
Check flock size in decision making:
```json
{
"Type": "FlockSize",
"Min": 2,
"Max": 10
}
```
## Plugin Access
```java
FlockPlugin flockPlugin = FlockPlugin.get();
// Component types
ComponentType<EntityStore, Flock> flockType =
flockPlugin.getFlockComponentType();
ComponentType<EntityStore, FlockMembership> membershipType =
flockPlugin.getFlockMembershipComponentType();
ComponentType<EntityStore, PersistentFlockData> dataType =
flockPlugin.getPersistentFlockDataComponentType();
```
## Utility Methods
```java
// Check if entity is in a flock
boolean isMember = FlockPlugin.isFlockMember(npcRef, store);
// Get flock reference from entity
Ref<EntityStore> flockRef = FlockPlugin.getFlockReference(npcRef, store);
// Get flock component from entity
Flock flock = FlockPlugin.getFlock(store, npcRef);
```
## Flock Spawning Behavior
When spawning a flock:
1. Initial NPC is created
2. Flock entity is created with membership
3. Additional members spawn at same location
4. Members spread with slight random offset
5. Each member joins the flock
```java
// Members spawn with random offset
memberTransform.getPosition().assign(
x + RandomExtra.randomRange(-0.5, 0.5),
offsetY,
z + RandomExtra.randomRange(-0.5, 0.5)
);
```

View File

@@ -0,0 +1,302 @@
---
title: Comportement de Groupe
type: docs
weight: 10
---
Le systeme de groupe (flock) permet aux NPCs de former et se comporter comme des unites coordonnees.
**Package:** `com.hypixel.hytale.server.flock`
## Apercu
Les groupes sont des ensembles bases sur des entites qui permettent aux NPCs de:
- Se deplacer et agir comme une unite coordonnee
- Partager les informations de degats entre membres
- Apparaitre en tailles de groupe configurables
- Avoir des leaders designes
## Architecture
```
Systeme Groupe
├── Composants
│ ├── Flock - Composant entite groupe
│ ├── FlockMembership - Reference membre
│ └── PersistentFlockData - Donnees groupe sauvegardees
├── Assets
│ ├── FlockAsset - Configuration groupe
│ ├── RangeSizeFlockAsset - Taille par plage
│ └── WeightedSizeFlockAsset - Taille ponderee
├── Systemes
│ ├── FlockSystems - Logique groupe principale
│ ├── FlockMembershipSystems - Gestion membres
│ └── FlockDeathSystems - Gestion mort
└── Composants NPC
├── BodyMotionFlock - Comportement mouvement
├── ActionFlockJoin/Leave - Actions joindre/quitter
└── SensorFlockLeader - Detection leader
```
## Composant Flock
L'entite groupe contient l'etat partage:
```java
public class Flock implements Component<EntityStore> {
// Donnees degats partagees pour coordination combat
private DamageData currentDamageData;
private DamageData nextDamageData;
// Donnees degats specifiques leader
private DamageData currentLeaderDamageData;
private DamageData nextLeaderDamageData;
// Configuration groupe persistante
private PersistentFlockData flockData;
// Statut de suppression
public enum FlockRemovedStatus {
NOT_REMOVED,
DISSOLVED,
UNLOADED
}
}
```
## Composant FlockMembership
Chaque NPC membre a un composant d'appartenance:
```java
public class FlockMembership implements Component<EntityStore> {
// Reference vers l'entite groupe
private Ref<EntityStore> flockRef;
}
```
## Creation de Groupes
### Spawn avec Groupe
```java
// Faire apparaitre un NPC avec son groupe
Ref<EntityStore> flockRef = FlockPlugin.trySpawnFlock(
npcRef, // Reference NPC initial
npc, // Composant NPC
store, // Store entite
roleIndex, // Index role NPC
position, // Position spawn
rotation, // Rotation spawn
flockDefinition, // Config FlockAsset (determine taille)
postSpawnCallback // Appele pour chaque membre spawne
);
```
### Creer Groupe Vide
```java
// Creer entite groupe pour gestion manuelle des membres
Ref<EntityStore> flockRef = FlockPlugin.createFlock(store, role);
// Ou avec configuration explicite
Ref<EntityStore> flockRef = FlockPlugin.createFlock(
store,
flockAsset, // Configuration FlockAsset
allowedRoles // Roles pouvant rejoindre
);
```
### Rejoindre Groupe Existant
```java
// Ajouter NPC a groupe existant
FlockMembershipSystems.join(npcRef, flockRef, store);
```
## Assets de Groupe
### FlockAsset
Configuration de base pour groupes:
```java
public class FlockAsset {
// Identifiant asset
private String id;
// Choisir taille groupe pour spawn
public abstract int pickFlockSize();
}
```
### RangeSizeFlockAsset
Groupe avec taille aleatoire dans plage:
```json
{
"Type": "RangeSize",
"Id": "wolf_pack",
"MinSize": 3,
"MaxSize": 8
}
```
### WeightedSizeFlockAsset
Groupe avec taille aleatoire ponderee:
```json
{
"Type": "WeightedSize",
"Id": "deer_herd",
"Sizes": [
{ "Size": 2, "Weight": 1 },
{ "Size": 4, "Weight": 2 },
{ "Size": 6, "Weight": 1 }
]
}
```
## Composants Core NPC
### BodyMotionFlock
Controle comportement mouvement de groupe:
```json
{
"Type": "Flock",
"SeparationWeight": 1.5,
"AlignmentWeight": 1.0,
"CohesionWeight": 1.0
}
```
### ActionFlockJoin
Rejoindre un groupe:
```json
{
"Type": "JoinFlock",
"FlockId": "wolf_pack"
}
```
### ActionFlockLeave
Quitter groupe actuel:
```json
{
"Type": "LeaveFlock"
}
```
### SensorFlockLeader
Detecter leader du groupe:
```json
{
"Type": "FlockLeader",
"Output": "leader_ref"
}
```
### EntityFilterFlock
Filtrer entites par appartenance groupe:
```json
{
"Type": "Flock",
"IncludeSelf": false,
"OnlyMembers": true
}
```
## Partage de Degats
Les groupes partagent informations de combat:
```java
// Obtenir donnees degats du groupe
Flock flock = store.getComponent(flockRef, Flock.getComponentType());
DamageData damageData = flock.getDamageData();
// Suivre kills pour le groupe
flock.onTargetKilled(componentAccessor, targetRef);
```
### Double Buffering
Les donnees de degats utilisent double buffering pour eviter conditions de course:
```java
// Appele chaque tick
flock.swapDamageDataBuffers();
// currentDamageData contient donnees du tick precedent
// nextDamageData accumule donnees du tick actuel
```
## Conditions
### FlockSizeCondition
Verifier taille groupe dans prise de decision:
```json
{
"Type": "FlockSize",
"Min": 2,
"Max": 10
}
```
## Acces au Plugin
```java
FlockPlugin flockPlugin = FlockPlugin.get();
// Types de composants
ComponentType<EntityStore, Flock> flockType =
flockPlugin.getFlockComponentType();
ComponentType<EntityStore, FlockMembership> membershipType =
flockPlugin.getFlockMembershipComponentType();
ComponentType<EntityStore, PersistentFlockData> dataType =
flockPlugin.getPersistentFlockDataComponentType();
```
## Methodes Utilitaires
```java
// Verifier si entite dans un groupe
boolean isMember = FlockPlugin.isFlockMember(npcRef, store);
// Obtenir reference groupe depuis entite
Ref<EntityStore> flockRef = FlockPlugin.getFlockReference(npcRef, store);
// Obtenir composant groupe depuis entite
Flock flock = FlockPlugin.getFlock(store, npcRef);
```
## Comportement de Spawn des Groupes
Lors du spawn d'un groupe:
1. NPC initial est cree
2. Entite groupe est creee avec appartenance
3. Membres additionnels spawn au meme emplacement
4. Membres se dispersent avec leger offset aleatoire
5. Chaque membre rejoint le groupe
```java
// Membres spawn avec offset aleatoire
memberTransform.getPosition().assign(
x + RandomExtra.randomRange(-0.5, 0.5),
offsetY,
z + RandomExtra.randomRange(-0.5, 0.5)
);
```

View File

@@ -0,0 +1,57 @@
---
title: Inventory
type: docs
weight: 7
---
The inventory system in Hytale handles items, containers, and player inventories.
{{< cards >}}
{{< card link="itemstacks" title="ItemStacks" subtitle="Working with immutable item stacks" >}}
{{< card link="containers" title="Containers" subtitle="Inventory and container types" >}}
{{< card link="transactions" title="Transactions" subtitle="Safe item modifications" >}}
{{< /cards >}}
## Overview
The inventory system follows an immutable pattern:
- **ItemStack** - Immutable representation of items
- **Inventory** - Player's personal inventory
- **ItemContainer** - Generic container for items
- **Transactions** - Safe way to modify items
## Quick Start
```java
import com.hypixel.hytale.server.core.inventory.Inventory;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import java.util.logging.Level;
// Get player inventory
Player player = ...;
Inventory inv = player.getInventory();
// Get item in hand (via Inventory, not directly on Player)
ItemStack hand = inv.getItemInHand();
// Check if item exists
if (hand != null && !hand.isEmpty()) {
// Use getItemId() - Item.getName() doesn't exist
getLogger().at(Level.INFO).log("Holding: " + hand.getItemId());
}
```
## Key Concepts
{{< callout type="info" >}}
ItemStack is **immutable**. Methods like `withQuantity()` return a new ItemStack rather than modifying the original.
{{< /callout >}}
```java
// Immutable pattern
ItemStack original = new ItemStack("iron_sword", 1);
ItemStack modified = original.withQuantity(5); // New instance
// original still has quantity of 1
// modified has quantity of 5
```

View File

@@ -0,0 +1,57 @@
---
title: Inventaire
type: docs
weight: 7
---
Le système d'inventaire dans Hytale gère les objets, conteneurs et inventaires des joueurs.
{{< cards >}}
{{< card link="itemstacks" title="ItemStacks" subtitle="Travailler avec les piles d'objets immuables" >}}
{{< card link="containers" title="Conteneurs" subtitle="Types d'inventaires et conteneurs" >}}
{{< card link="transactions" title="Transactions" subtitle="Modifications sûres des objets" >}}
{{< /cards >}}
## Aperçu
Le système d'inventaire suit un pattern immuable :
- **ItemStack** - Représentation immuable des objets
- **Inventory** - Inventaire personnel du joueur
- **ItemContainer** - Conteneur générique pour les objets
- **Transactions** - Moyen sûr de modifier les objets
## Démarrage Rapide
```java
import com.hypixel.hytale.server.core.inventory.Inventory;
import com.hypixel.hytale.server.core.inventory.ItemStack;
import java.util.logging.Level;
// Obtenir l'inventaire du joueur
Player player = ...;
Inventory inv = player.getInventory();
// Obtenir l'objet en main (via Inventory, pas directement sur Player)
ItemStack hand = inv.getItemInHand();
// Vérifier si l'objet existe
if (hand != null && !hand.isEmpty()) {
// Utiliser getItemId() - Item.getName() n'existe pas
getLogger().at(Level.INFO).log("Tient : " + hand.getItemId());
}
```
## Concepts Clés
{{< callout type="info" >}}
ItemStack est **immuable**. Les méthodes comme `withQuantity()` retournent un nouveau ItemStack plutôt que de modifier l'original.
{{< /callout >}}
```java
// Pattern immuable
ItemStack original = new ItemStack("iron_sword", 1);
ItemStack modified = original.withQuantity(5); // Nouvelle instance
// original a toujours une quantité de 1
// modified a une quantité de 5
```

View File

@@ -0,0 +1,668 @@
---
title: Containers
type: docs
weight: 2
---
Containers hold and manage collections of ItemStacks in Hytale. The inventory system uses `ItemContainer` as the base abstraction with `Inventory` as the player's specialized multi-section container.
## Inventory Structure
Player inventories are composed of multiple `ItemContainer` sections:
```java
Inventory inv = player.getInventory();
// Get individual sections
ItemContainer storage = inv.getStorage(); // Main storage (36 slots default)
ItemContainer hotbar = inv.getHotbar(); // Hotbar (9 slots default)
ItemContainer armor = inv.getArmor(); // Armor slots
ItemContainer utility = inv.getUtility(); // Utility items (4 slots default)
ItemContainer backpack = inv.getBackpack(); // Backpack (expandable)
// Combined containers for operations across multiple sections
CombinedItemContainer hotbarFirst = inv.getCombinedHotbarFirst();
CombinedItemContainer storageFirst = inv.getCombinedStorageFirst();
```
### Section IDs
Each section has a constant ID for operations:
| Section | ID | Constant |
|---------|----|----|
| Hotbar | -1 | `Inventory.HOTBAR_SECTION_ID` |
| Storage | -2 | `Inventory.STORAGE_SECTION_ID` |
| Armor | -3 | `Inventory.ARMOR_SECTION_ID` |
| Utility | -5 | `Inventory.UTILITY_SECTION_ID` |
| Tools | -8 | `Inventory.TOOLS_SECTION_ID` (deprecated) |
| Backpack | -9 | `Inventory.BACKPACK_SECTION_ID` |
## ItemContainer Basics
### Reading Items
```java
ItemContainer container = inv.getStorage();
// Get capacity (NOT getSize!)
short capacity = container.getCapacity();
// Get item at slot (slot is SHORT type)
ItemStack item = container.getItemStack((short) 0);
// Check if empty
boolean empty = container.isEmpty();
// Safe item access
ItemStack stack = container.getItemStack((short) slot);
if (!ItemStack.isEmpty(stack)) {
String itemId = stack.getItemId();
int quantity = stack.getQuantity();
}
```
### Setting Items
```java
ItemContainer container = inv.getHotbar();
// Set item at slot - returns a Transaction
ItemStackSlotTransaction transaction = container.setItemStackForSlot(
(short) 0,
new ItemStack("iron_sword", 1)
);
// Check if operation succeeded
if (transaction.succeeded()) {
// Item was set successfully
}
// Clear a slot
container.setItemStackForSlot((short) 0, null);
```
### Adding Items
```java
ItemContainer container = inv.getStorage();
ItemStack toAdd = new ItemStack("wood_plank", 64);
// Add to first available slot (stacks with existing items first)
ItemStackTransaction transaction = container.addItemStack(toAdd);
// Check for remaining items that couldn't fit
ItemStack remainder = transaction.getRemainder();
if (!ItemStack.isEmpty(remainder)) {
// Some items couldn't fit
int leftover = remainder.getQuantity();
}
// Add to specific slot
ItemStackSlotTransaction slotTransaction = container.addItemStackToSlot(
(short) 5,
toAdd
);
// Check if container can fit items before adding
if (container.canAddItemStack(toAdd)) {
container.addItemStack(toAdd);
}
```
### Removing Items
```java
// Remove from specific slot
SlotTransaction transaction = container.removeItemStackFromSlot((short) 0);
// Remove specific quantity from slot
ItemStackSlotTransaction removeTransaction = container.removeItemStackFromSlot(
(short) 0,
10 // quantity to remove
);
// Remove specific item type from anywhere in container
ItemStack toRemove = new ItemStack("iron_ore", 5);
ItemStackTransaction itemTransaction = container.removeItemStack(toRemove);
// Check if items can be removed before removing
if (container.canRemoveItemStack(toRemove)) {
container.removeItemStack(toRemove);
}
```
## Active Slots
Players have active slots for hotbar and utility items:
```java
Inventory inv = player.getInventory();
// Get active hotbar slot (0-8)
byte activeHotbar = inv.getActiveHotbarSlot();
// Set active hotbar slot
inv.setActiveHotbarSlot((byte) 3);
// Get item currently in hand
ItemStack handItem = inv.getItemInHand();
// Get/set active utility slot
byte activeUtility = inv.getActiveUtilitySlot();
inv.setActiveUtilitySlot((byte) 1);
// Get utility item
ItemStack utilityItem = inv.getUtilityItem();
```
## Moving Items
### Between Slots
```java
Inventory inv = player.getInventory();
ItemContainer storage = inv.getStorage();
ItemContainer hotbar = inv.getHotbar();
// Move item from one slot to another in same container
MoveTransaction<SlotTransaction> transaction = storage.moveItemStackFromSlotToSlot(
(short) 0, // from slot
64, // quantity
storage, // to container
(short) 5 // to slot
);
// Move between different containers
storage.moveItemStackFromSlotToSlot(
(short) 0,
32,
hotbar,
(short) 0
);
// Use Inventory's moveItem for section-based moves
inv.moveItem(
Inventory.STORAGE_SECTION_ID, // from section
5, // from slot
10, // quantity
Inventory.HOTBAR_SECTION_ID, // to section
0 // to slot
);
```
### Smart Moving
```java
// Smart move considers item type for optimal placement
inv.smartMoveItem(
Inventory.STORAGE_SECTION_ID,
0, // slot
64, // quantity
SmartMoveType.EquipOrMergeStack // tries to equip armor or merge stacks
);
```
## Iterating Containers
```java
ItemContainer container = inv.getStorage();
// Iterate all non-empty slots
container.forEach((slot, itemStack) -> {
getLogger().at(Level.INFO).log("Slot " + slot + ": " +
itemStack.getItemId() + " x" + itemStack.getQuantity());
});
// Count items matching a condition
int swordCount = container.countItemStacks(
stack -> stack.getItemId().contains("sword")
);
// Check if container has stackable items
ItemStack testStack = new ItemStack("stone", 1);
boolean hasStackable = container.containsItemStacksStackableWith(testStack);
```
## Checking Contents
```java
ItemContainer container = inv.getStorage();
// Check if inventory contains specific item type and quantity
public boolean hasItems(ItemContainer container, String itemId, int amount) {
int count = container.countItemStacks(
stack -> stack.getItemId().equals(itemId)
);
return count >= amount;
}
// Find first slot with specific item
public short findSlotWithItem(ItemContainer container, String itemId) {
for (short i = 0; i < container.getCapacity(); i++) {
ItemStack stack = container.getItemStack(i);
if (!ItemStack.isEmpty(stack) && stack.getItemId().equals(itemId)) {
return i;
}
}
return -1; // Not found
}
// Count empty slots
public int countEmptySlots(ItemContainer container) {
int empty = 0;
for (short i = 0; i < container.getCapacity(); i++) {
if (ItemStack.isEmpty(container.getItemStack(i))) {
empty++;
}
}
return empty;
}
```
## Clearing Containers
```java
ItemContainer container = inv.getStorage();
// Clear entire container
ClearTransaction transaction = container.clear();
// Drop all items (returns list of dropped items)
List<ItemStack> droppedItems = container.dropAllItemStacks();
// Remove all items (returns list)
List<ItemStack> removedItems = container.removeAllItemStacks();
// Clear entire player inventory
inv.clear();
// Drop all from player inventory
List<ItemStack> allDropped = inv.dropAllItemStacks();
```
## Sorting
Available `SortType` values: `NAME`, `TYPE`, `RARITY`
```java
ItemContainer container = inv.getStorage();
// Sort items
container.sortItems(SortType.NAME);
// Sort via Inventory (also saves sort preference)
inv.sortStorage(SortType.NAME);
inv.setSortType(SortType.RARITY); // Or TYPE
```
## Container Events
```java
// Register for container change events
container.registerChangeEvent(event -> {
ItemContainer changedContainer = event.container();
Transaction transaction = event.transaction();
getLogger().at(Level.INFO).log("Container changed!");
});
// With priority
container.registerChangeEvent(EventPriority.EARLY, event -> {
// Handle early
});
```
## Transaction System
All modification operations return Transaction objects:
```java
// Transactions track success and changes
ItemStackTransaction transaction = container.addItemStack(itemStack);
if (transaction.succeeded()) {
ItemStack remainder = transaction.getRemainder();
// ...
}
// Slot transactions include slot info
ItemStackSlotTransaction slotTransaction = container.setItemStackForSlot(
(short) 0,
itemStack
);
if (slotTransaction.succeeded()) {
short slot = slotTransaction.getSlot();
ItemStack before = slotTransaction.getSlotBefore();
ItemStack after = slotTransaction.getSlotAfter();
}
```
## Operation Parameters
Most container operations support optional parameters:
```java
// Default values
DEFAULT_ADD_ALL_OR_NOTHING = false; // Partial adds allowed
DEFAULT_REMOVE_ALL_OR_NOTHING = true; // Only full removals
DEFAULT_FULL_STACKS = false; // Can split stacks
DEFAULT_EXACT_AMOUNT = true; // Exact quantities only
DEFAULT_FILTER = true; // Apply slot filters
// Add with parameters
container.addItemStack(itemStack, allOrNothing, fullStacks, filter);
// allOrNothing: if true, fails entirely if not all items fit
// fullStacks: if true, only fills empty slots (no stacking)
// filter: if true, respects slot filters
```
## Material and Resource Removal
Containers support removing items by material type or resource type (used for crafting):
{{< callout type="info" >}}
**MaterialQuantity constructor:** `MaterialQuantity(itemId, resourceTypeId, tag, quantity, metadata)`
At least one of `itemId`, `resourceTypeId`, or `tag` must be non-null.
{{< /callout >}}
```java
// Remove by material using itemId
MaterialQuantity materialByItem = new MaterialQuantity("iron_ingot", null, null, 5, null);
if (container.canRemoveMaterial(materialByItem)) {
MaterialTransaction transaction = container.removeMaterial(materialByItem);
}
// Remove by material using resourceTypeId
MaterialQuantity materialByResource = new MaterialQuantity(null, "iron", null, 5, null);
if (container.canRemoveMaterial(materialByResource)) {
MaterialTransaction transaction = container.removeMaterial(materialByResource);
}
// Remove by resource type (simpler constructor)
ResourceQuantity resource = new ResourceQuantity("wood", 10);
if (container.canRemoveResource(resource)) {
ResourceTransaction transaction = container.removeResource(resource);
}
// Remove by tag index
if (container.canRemoveTag(tagIndex, quantity)) {
TagTransaction transaction = container.removeTag(tagIndex, quantity);
}
// Bulk removal
List<MaterialQuantity> materials = List.of(
new MaterialQuantity("iron_ingot", null, null, 2, null),
new MaterialQuantity(null, null, "Wood", 5, null) // Using tag
);
if (container.canRemoveMaterials(materials)) {
container.removeMaterials(materials);
}
```
## Bulk Operations
Add or remove multiple items at once:
```java
// Add multiple items
List<ItemStack> items = List.of(
new ItemStack("iron_ore", 10),
new ItemStack("gold_ore", 5)
);
if (container.canAddItemStacks(items)) {
ListTransaction<ItemStackTransaction> transaction = container.addItemStacks(items);
}
// Remove multiple items
if (container.canRemoveItemStacks(items)) {
container.removeItemStacks(items);
}
// Add items in order (preserves slot positions)
container.addItemStacksOrdered(items);
container.addItemStacksOrdered((short) 5, items); // Starting at slot 5
```
## Advanced Move Operations
```java
// Move all items to another container
ListTransaction<MoveTransaction<ItemStackTransaction>> result =
storage.moveAllItemStacksTo(hotbar, backpack);
// Move all items matching a condition
storage.moveAllItemStacksTo(
item -> item.getItemId().contains("ore"),
hotbar
);
// Quick stack: only moves items that can stack with existing items
storage.quickStackTo(hotbar);
// Combine small stacks into one slot
container.combineItemStacksIntoSlot(targetContainer, (short) 0);
// Swap items between containers
storage.swapItems(
(short) 0, // source position
hotbar, // target container
(short) 0, // destination position
(short) 5 // number of slots to swap
);
```
## Replace Operations
```java
// Replace item in slot if it matches expected
ItemStackSlotTransaction transaction = container.replaceItemStackInSlot(
(short) 0,
expectedItem, // must match current item to proceed
newItem
);
// Replace all items using a function
container.replaceAll((slot, existing) -> {
if (existing.getItemId().equals("old_item")) {
return new ItemStack("new_item", existing.getQuantity());
}
return existing;
});
```
## Slot Filters
Control which items can go in which slots:
```java
// Set global filter for the container
container.setGlobalFilter(FilterType.WHITELIST);
// Set filter for specific slot
container.setSlotFilter(
FilterActionType.ADD, // Filter on add operations
(short) 0, // Slot
slotFilter // Filter implementation
);
```
## Best Practices
{{< callout type="info" >}}
**Container Tips:**
- Slot indices are `short`, not `int` - cast appropriately
- Always check `ItemStack.isEmpty(stack)` - handles both null and empty
- Use `getQuantity()` not `getCount()`
- Check transaction success with `succeeded()`
- Use combined containers for cross-section operations
- Player inventory changes trigger `LivingEntityInventoryChangeEvent`
{{< /callout >}}
```java
// Safe pattern for working with containers
public void safeAddItem(ItemContainer container, ItemStack item) {
if (ItemStack.isEmpty(item)) {
return;
}
if (!container.canAddItemStack(item)) {
// Handle full container
return;
}
ItemStackTransaction transaction = container.addItemStack(item);
if (!transaction.succeeded()) {
// Handle failure
}
}
```
## ItemContainer API Reference
### Core Methods
| Method | Returns | Description |
|--------|---------|-------------|
| `getCapacity()` | `short` | Total number of slots |
| `getItemStack(short)` | `ItemStack?` | Get item at slot |
| `isEmpty()` | `boolean` | True if no items |
| `clone()` | `ItemContainer` | Clone the container |
| `clear()` | `ClearTransaction` | Remove all items |
### Add Operations
| Method | Returns | Description |
|--------|---------|-------------|
| `canAddItemStack(ItemStack)` | `boolean` | Check if can add |
| `canAddItemStack(ItemStack, fullStacks, filter)` | `boolean` | Check with options |
| `addItemStack(ItemStack)` | `ItemStackTransaction` | Add to first available |
| `addItemStack(ItemStack, allOrNothing, fullStacks, filter)` | `ItemStackTransaction` | Add with options |
| `canAddItemStackToSlot(short, ItemStack, allOrNothing, filter)` | `boolean` | Check if can add to slot |
| `addItemStackToSlot(short, ItemStack)` | `ItemStackSlotTransaction` | Add to specific slot |
| `addItemStackToSlot(short, ItemStack, allOrNothing, filter)` | `ItemStackSlotTransaction` | Add to slot with options |
| `canAddItemStacks(List<ItemStack>)` | `boolean` | Check if can add multiple |
| `addItemStacks(List<ItemStack>)` | `ListTransaction` | Add multiple items |
| `addItemStacksOrdered(List<ItemStack>)` | `ListTransaction` | Add in order |
| `addItemStacksOrdered(short offset, List<ItemStack>)` | `ListTransaction` | Add in order from offset |
### Set Operations
| Method | Returns | Description |
|--------|---------|-------------|
| `setItemStackForSlot(short, ItemStack)` | `ItemStackSlotTransaction` | Set item at slot |
| `setItemStackForSlot(short, ItemStack, filter)` | `ItemStackSlotTransaction` | Set with filter option |
| `replaceItemStackInSlot(short, ItemStack expected, ItemStack new)` | `ItemStackSlotTransaction` | Replace if matches |
| `replaceAll(SlotReplacementFunction)` | `ListTransaction` | Replace all items |
### Remove Operations
| Method | Returns | Description |
|--------|---------|-------------|
| `removeItemStackFromSlot(short)` | `SlotTransaction` | Remove entire slot |
| `removeItemStackFromSlot(short, filter)` | `SlotTransaction` | Remove with filter |
| `removeItemStackFromSlot(short, quantity)` | `ItemStackSlotTransaction` | Remove quantity |
| `removeItemStackFromSlot(short, ItemStack, quantity)` | `ItemStackSlotTransaction` | Remove matching item |
| `canRemoveItemStack(ItemStack)` | `boolean` | Check if can remove |
| `removeItemStack(ItemStack)` | `ItemStackTransaction` | Remove item type |
| `canRemoveItemStacks(List<ItemStack>)` | `boolean` | Check if can remove multiple |
| `removeItemStacks(List<ItemStack>)` | `ListTransaction` | Remove multiple items |
| `removeAllItemStacks()` | `List<ItemStack>` | Remove and return all |
| `dropAllItemStacks()` | `List<ItemStack>` | Drop all (respects cantDrop) |
| `dropAllItemStacks(filter)` | `List<ItemStack>` | Drop with filter option |
### Material/Resource/Tag Removal
| Method | Returns | Description |
|--------|---------|-------------|
| `canRemoveMaterial(MaterialQuantity)` | `boolean` | Check material removal |
| `removeMaterial(MaterialQuantity)` | `MaterialTransaction` | Remove by material |
| `removeMaterialFromSlot(short, MaterialQuantity)` | `MaterialSlotTransaction` | Remove material from slot |
| `canRemoveMaterials(List<MaterialQuantity>)` | `boolean` | Check multiple materials |
| `removeMaterials(List<MaterialQuantity>)` | `ListTransaction` | Remove multiple materials |
| `canRemoveResource(ResourceQuantity)` | `boolean` | Check resource removal |
| `removeResource(ResourceQuantity)` | `ResourceTransaction` | Remove by resource |
| `removeResourceFromSlot(short, ResourceQuantity)` | `ResourceSlotTransaction` | Remove resource from slot |
| `canRemoveResources(List<ResourceQuantity>)` | `boolean` | Check multiple resources |
| `removeResources(List<ResourceQuantity>)` | `ListTransaction` | Remove multiple resources |
| `canRemoveTag(tagIndex, quantity)` | `boolean` | Check tag removal |
| `removeTag(tagIndex, quantity)` | `TagTransaction` | Remove by tag |
| `removeTagFromSlot(short, tagIndex, quantity)` | `TagSlotTransaction` | Remove tag from slot |
### Move Operations
| Method | Returns | Description |
|--------|---------|-------------|
| `moveItemStackFromSlot(short, ItemContainer)` | `MoveTransaction` | Move slot to container |
| `moveItemStackFromSlot(short, quantity, ItemContainer)` | `MoveTransaction` | Move quantity |
| `moveItemStackFromSlot(short, ItemContainer...)` | `ListTransaction` | Move to multiple containers |
| `moveItemStackFromSlotToSlot(short, quantity, ItemContainer, short)` | `MoveTransaction` | Move to specific slot |
| `moveAllItemStacksTo(ItemContainer...)` | `ListTransaction` | Move all items |
| `moveAllItemStacksTo(Predicate, ItemContainer...)` | `ListTransaction` | Move matching items |
| `quickStackTo(ItemContainer...)` | `ListTransaction` | Move stackable items only |
| `combineItemStacksIntoSlot(ItemContainer, short)` | `ListTransaction` | Combine stacks into slot |
| `swapItems(short srcPos, ItemContainer, short destPos, short length)` | `ListTransaction` | Swap item ranges |
### Utility Methods
| Method | Returns | Description |
|--------|---------|-------------|
| `forEach(ShortObjectConsumer)` | `void` | Iterate non-empty slots |
| `forEachWithMeta(consumer, meta)` | `void` | Iterate with metadata |
| `countItemStacks(Predicate)` | `int` | Count matching items (total quantity) |
| `containsItemStacksStackableWith(ItemStack)` | `boolean` | Check for stackable items |
| `sortItems(SortType)` | `ListTransaction` | Sort container |
| `registerChangeEvent(Consumer)` | `EventRegistration` | Listen for changes |
| `registerChangeEvent(EventPriority, Consumer)` | `EventRegistration` | Listen with priority |
| `setGlobalFilter(FilterType)` | `void` | Set container filter |
| `setSlotFilter(FilterActionType, short, SlotFilter)` | `void` | Set slot filter |
| `containsContainer(ItemContainer)` | `boolean` | Check if contains container |
### Static Methods
| Method | Returns | Description |
|--------|---------|-------------|
| `copy(from, to, remainder)` | `T` | Copy items between containers |
| `ensureContainerCapacity(container, capacity, supplier, remainder)` | `T` | Ensure container has capacity |
| `getNewContainer(capacity, supplier)` | `ItemContainer` | Create or get empty |
| `getMatchingResourceType(Item, resourceId)` | `ItemResourceType?` | Find resource type for item |
| `validateQuantity(int)` | `void` | Throws if < 0 |
| `validateSlotIndex(short, capacity)` | `void` | Throws if out of bounds |
### Static Constants
| Field | Type | Description |
|-------|------|-------------|
| `CODEC` | `CodecMapCodec<ItemContainer>` | Serialization codec |
| `DEFAULT_ADD_ALL_OR_NOTHING` | `boolean` | false |
| `DEFAULT_REMOVE_ALL_OR_NOTHING` | `boolean` | true |
| `DEFAULT_FULL_STACKS` | `boolean` | false |
| `DEFAULT_EXACT_AMOUNT` | `boolean` | true |
| `DEFAULT_FILTER` | `boolean` | true |
### Nested Classes
| Class | Description |
|-------|-------------|
| `ItemContainerChangeEvent` | Event record with `container()` and `transaction()` |
## Inventory API Reference
| Method | Returns | Description |
|--------|---------|-------------|
| `getStorage()` | `ItemContainer` | Main storage section |
| `getHotbar()` | `ItemContainer` | Hotbar section |
| `getArmor()` | `ItemContainer` | Armor section |
| `getUtility()` | `ItemContainer` | Utility section |
| `getBackpack()` | `ItemContainer` | Backpack section |
| `getSectionById(int)` | `ItemContainer?` | Get section by ID |
| `getItemInHand()` | `ItemStack?` | Currently held item |
| `getActiveHotbarSlot()` | `byte` | Active hotbar slot |
| `setActiveHotbarSlot(byte)` | `void` | Set active hotbar |
| `getCombinedHotbarFirst()` | `CombinedItemContainer` | Hotbar+Storage combined |
| `moveItem(...)` | `void` | Move between sections |
| `clear()` | `void` | Clear all sections |

View File

@@ -0,0 +1,668 @@
---
title: Conteneurs
type: docs
weight: 2
---
Les conteneurs gèrent des collections d'ItemStacks dans Hytale. Le système d'inventaire utilise `ItemContainer` comme abstraction de base avec `Inventory` comme conteneur multi-sections spécialisé du joueur.
## Structure de l'Inventaire
Les inventaires des joueurs sont composés de plusieurs sections `ItemContainer` :
```java
Inventory inv = player.getInventory();
// Obtenir les sections individuelles
ItemContainer storage = inv.getStorage(); // Stockage principal (36 slots par défaut)
ItemContainer hotbar = inv.getHotbar(); // Hotbar (9 slots par défaut)
ItemContainer armor = inv.getArmor(); // Emplacements d'armure
ItemContainer utility = inv.getUtility(); // Objets utilitaires (4 slots par défaut)
ItemContainer backpack = inv.getBackpack(); // Sac à dos (extensible)
// Conteneurs combinés pour opérations multi-sections
CombinedItemContainer hotbarFirst = inv.getCombinedHotbarFirst();
CombinedItemContainer storageFirst = inv.getCombinedStorageFirst();
```
### IDs de Section
Chaque section a un ID constant pour les opérations :
| Section | ID | Constante |
|---------|----|----|
| Hotbar | -1 | `Inventory.HOTBAR_SECTION_ID` |
| Stockage | -2 | `Inventory.STORAGE_SECTION_ID` |
| Armure | -3 | `Inventory.ARMOR_SECTION_ID` |
| Utilitaire | -5 | `Inventory.UTILITY_SECTION_ID` |
| Outils | -8 | `Inventory.TOOLS_SECTION_ID` (déprécié) |
| Sac à dos | -9 | `Inventory.BACKPACK_SECTION_ID` |
## Bases d'ItemContainer
### Lire les Objets
```java
ItemContainer container = inv.getStorage();
// Obtenir la capacité (PAS getSize!)
short capacity = container.getCapacity();
// Obtenir l'objet au slot (slot est de type SHORT)
ItemStack item = container.getItemStack((short) 0);
// Vérifier si vide
boolean empty = container.isEmpty();
// Accès sécurisé aux objets
ItemStack stack = container.getItemStack((short) slot);
if (!ItemStack.isEmpty(stack)) {
String itemId = stack.getItemId();
int quantity = stack.getQuantity();
}
```
### Définir des Objets
```java
ItemContainer container = inv.getHotbar();
// Définir l'objet au slot - retourne une Transaction
ItemStackSlotTransaction transaction = container.setItemStackForSlot(
(short) 0,
new ItemStack("iron_sword", 1)
);
// Vérifier si l'opération a réussi
if (transaction.succeeded()) {
// L'objet a été défini avec succès
}
// Vider un slot
container.setItemStackForSlot((short) 0, null);
```
### Ajouter des Objets
```java
ItemContainer container = inv.getStorage();
ItemStack toAdd = new ItemStack("wood_plank", 64);
// Ajouter au premier slot disponible (empile avec les objets existants d'abord)
ItemStackTransaction transaction = container.addItemStack(toAdd);
// Vérifier les objets restants qui n'ont pas pu rentrer
ItemStack remainder = transaction.getRemainder();
if (!ItemStack.isEmpty(remainder)) {
// Certains objets n'ont pas pu rentrer
int leftover = remainder.getQuantity();
}
// Ajouter à un slot spécifique
ItemStackSlotTransaction slotTransaction = container.addItemStackToSlot(
(short) 5,
toAdd
);
// Vérifier si le conteneur peut accueillir les objets avant d'ajouter
if (container.canAddItemStack(toAdd)) {
container.addItemStack(toAdd);
}
```
### Retirer des Objets
```java
// Retirer d'un slot spécifique
SlotTransaction transaction = container.removeItemStackFromSlot((short) 0);
// Retirer une quantité spécifique du slot
ItemStackSlotTransaction removeTransaction = container.removeItemStackFromSlot(
(short) 0,
10 // quantité à retirer
);
// Retirer un type d'objet spécifique de n'importe où dans le conteneur
ItemStack toRemove = new ItemStack("iron_ore", 5);
ItemStackTransaction itemTransaction = container.removeItemStack(toRemove);
// Vérifier si les objets peuvent être retirés avant de retirer
if (container.canRemoveItemStack(toRemove)) {
container.removeItemStack(toRemove);
}
```
## Slots Actifs
Les joueurs ont des slots actifs pour la hotbar et les objets utilitaires :
```java
Inventory inv = player.getInventory();
// Obtenir le slot actif de la hotbar (0-8)
byte activeHotbar = inv.getActiveHotbarSlot();
// Définir le slot actif de la hotbar
inv.setActiveHotbarSlot((byte) 3);
// Obtenir l'objet actuellement en main
ItemStack handItem = inv.getItemInHand();
// Obtenir/définir le slot utilitaire actif
byte activeUtility = inv.getActiveUtilitySlot();
inv.setActiveUtilitySlot((byte) 1);
// Obtenir l'objet utilitaire
ItemStack utilityItem = inv.getUtilityItem();
```
## Déplacer des Objets
### Entre les Slots
```java
Inventory inv = player.getInventory();
ItemContainer storage = inv.getStorage();
ItemContainer hotbar = inv.getHotbar();
// Déplacer un objet d'un slot à un autre dans le même conteneur
MoveTransaction<SlotTransaction> transaction = storage.moveItemStackFromSlotToSlot(
(short) 0, // depuis slot
64, // quantité
storage, // vers conteneur
(short) 5 // vers slot
);
// Déplacer entre différents conteneurs
storage.moveItemStackFromSlotToSlot(
(short) 0,
32,
hotbar,
(short) 0
);
// Utiliser moveItem de l'Inventory pour les déplacements par section
inv.moveItem(
Inventory.STORAGE_SECTION_ID, // depuis section
5, // depuis slot
10, // quantité
Inventory.HOTBAR_SECTION_ID, // vers section
0 // vers slot
);
```
### Déplacement Intelligent
```java
// Le déplacement intelligent considère le type d'objet pour un placement optimal
inv.smartMoveItem(
Inventory.STORAGE_SECTION_ID,
0, // slot
64, // quantité
SmartMoveType.EquipOrMergeStack // tente d'équiper l'armure ou fusionner les piles
);
```
## Itérer les Conteneurs
```java
ItemContainer container = inv.getStorage();
// Itérer tous les slots non vides
container.forEach((slot, itemStack) -> {
getLogger().at(Level.INFO).log("Slot " + slot + ": " +
itemStack.getItemId() + " x" + itemStack.getQuantity());
});
// Compter les objets correspondant à une condition
int swordCount = container.countItemStacks(
stack -> stack.getItemId().contains("sword")
);
// Vérifier si le conteneur a des objets empilables
ItemStack testStack = new ItemStack("stone", 1);
boolean hasStackable = container.containsItemStacksStackableWith(testStack);
```
## Vérifier le Contenu
```java
ItemContainer container = inv.getStorage();
// Vérifier si l'inventaire contient un type et quantité d'objet spécifiques
public boolean hasItems(ItemContainer container, String itemId, int amount) {
int count = container.countItemStacks(
stack -> stack.getItemId().equals(itemId)
);
return count >= amount;
}
// Trouver le premier slot avec un objet spécifique
public short findSlotWithItem(ItemContainer container, String itemId) {
for (short i = 0; i < container.getCapacity(); i++) {
ItemStack stack = container.getItemStack(i);
if (!ItemStack.isEmpty(stack) && stack.getItemId().equals(itemId)) {
return i;
}
}
return -1; // Non trouvé
}
// Compter les slots vides
public int countEmptySlots(ItemContainer container) {
int empty = 0;
for (short i = 0; i < container.getCapacity(); i++) {
if (ItemStack.isEmpty(container.getItemStack(i))) {
empty++;
}
}
return empty;
}
```
## Vider les Conteneurs
```java
ItemContainer container = inv.getStorage();
// Vider tout le conteneur
ClearTransaction transaction = container.clear();
// Lâcher tous les objets (retourne la liste des objets lâchés)
List<ItemStack> droppedItems = container.dropAllItemStacks();
// Retirer tous les objets (retourne la liste)
List<ItemStack> removedItems = container.removeAllItemStacks();
// Vider tout l'inventaire du joueur
inv.clear();
// Lâcher tout de l'inventaire du joueur
List<ItemStack> allDropped = inv.dropAllItemStacks();
```
## Tri
Valeurs `SortType` disponibles : `NAME`, `TYPE`, `RARITY`
```java
ItemContainer container = inv.getStorage();
// Trier les objets
container.sortItems(SortType.NAME);
// Trier via Inventory (sauvegarde aussi la préférence de tri)
inv.sortStorage(SortType.NAME);
inv.setSortType(SortType.RARITY); // Ou TYPE
```
## Événements de Conteneur
```java
// S'enregistrer pour les événements de changement de conteneur
container.registerChangeEvent(event -> {
ItemContainer changedContainer = event.container();
Transaction transaction = event.transaction();
getLogger().at(Level.INFO).log("Conteneur modifié !");
});
// Avec priorité
container.registerChangeEvent(EventPriority.EARLY, event -> {
// Gérer tôt
});
```
## Système de Transactions
Toutes les opérations de modification retournent des objets Transaction :
```java
// Les transactions suivent le succès et les changements
ItemStackTransaction transaction = container.addItemStack(itemStack);
if (transaction.succeeded()) {
ItemStack remainder = transaction.getRemainder();
// ...
}
// Les transactions de slot incluent les infos du slot
ItemStackSlotTransaction slotTransaction = container.setItemStackForSlot(
(short) 0,
itemStack
);
if (slotTransaction.succeeded()) {
short slot = slotTransaction.getSlot();
ItemStack before = slotTransaction.getSlotBefore();
ItemStack after = slotTransaction.getSlotAfter();
}
```
## Paramètres d'Opération
La plupart des opérations de conteneur supportent des paramètres optionnels :
```java
// Valeurs par défaut
DEFAULT_ADD_ALL_OR_NOTHING = false; // Ajouts partiels autorisés
DEFAULT_REMOVE_ALL_OR_NOTHING = true; // Uniquement retraits complets
DEFAULT_FULL_STACKS = false; // Peut diviser les piles
DEFAULT_EXACT_AMOUNT = true; // Quantités exactes uniquement
DEFAULT_FILTER = true; // Applique les filtres de slot
// Ajouter avec paramètres
container.addItemStack(itemStack, allOrNothing, fullStacks, filter);
// allOrNothing: si true, échoue entièrement si tous les objets ne rentrent pas
// fullStacks: si true, remplit uniquement les slots vides (pas d'empilement)
// filter: si true, respecte les filtres de slot
```
## Retrait par Matériau et Ressource
Les conteneurs supportent le retrait d'objets par type de matériau ou de ressource (utilisé pour le craft) :
{{< callout type="info" >}}
**Constructeur MaterialQuantity :** `MaterialQuantity(itemId, resourceTypeId, tag, quantity, metadata)`
Au moins un parmi `itemId`, `resourceTypeId`, ou `tag` doit être non-null.
{{< /callout >}}
```java
// Retirer par matériau en utilisant itemId
MaterialQuantity materialByItem = new MaterialQuantity("iron_ingot", null, null, 5, null);
if (container.canRemoveMaterial(materialByItem)) {
MaterialTransaction transaction = container.removeMaterial(materialByItem);
}
// Retirer par matériau en utilisant resourceTypeId
MaterialQuantity materialByResource = new MaterialQuantity(null, "iron", null, 5, null);
if (container.canRemoveMaterial(materialByResource)) {
MaterialTransaction transaction = container.removeMaterial(materialByResource);
}
// Retirer par type de ressource (constructeur plus simple)
ResourceQuantity resource = new ResourceQuantity("wood", 10);
if (container.canRemoveResource(resource)) {
ResourceTransaction transaction = container.removeResource(resource);
}
// Retirer par index de tag
if (container.canRemoveTag(tagIndex, quantity)) {
TagTransaction transaction = container.removeTag(tagIndex, quantity);
}
// Retrait en masse
List<MaterialQuantity> materials = List.of(
new MaterialQuantity("iron_ingot", null, null, 2, null),
new MaterialQuantity(null, null, "Wood", 5, null) // Avec tag
);
if (container.canRemoveMaterials(materials)) {
container.removeMaterials(materials);
}
```
## Opérations en Masse
Ajouter ou retirer plusieurs objets à la fois :
```java
// Ajouter plusieurs objets
List<ItemStack> items = List.of(
new ItemStack("iron_ore", 10),
new ItemStack("gold_ore", 5)
);
if (container.canAddItemStacks(items)) {
ListTransaction<ItemStackTransaction> transaction = container.addItemStacks(items);
}
// Retirer plusieurs objets
if (container.canRemoveItemStacks(items)) {
container.removeItemStacks(items);
}
// Ajouter les objets dans l'ordre (préserve les positions de slot)
container.addItemStacksOrdered(items);
container.addItemStacksOrdered((short) 5, items); // À partir du slot 5
```
## Opérations de Déplacement Avancées
```java
// Déplacer tous les objets vers un autre conteneur
ListTransaction<MoveTransaction<ItemStackTransaction>> result =
storage.moveAllItemStacksTo(hotbar, backpack);
// Déplacer tous les objets correspondant à une condition
storage.moveAllItemStacksTo(
item -> item.getItemId().contains("ore"),
hotbar
);
// Quick stack : déplace uniquement les objets qui peuvent s'empiler avec des objets existants
storage.quickStackTo(hotbar);
// Combiner les petites piles dans un slot
container.combineItemStacksIntoSlot(targetContainer, (short) 0);
// Échanger des objets entre conteneurs
storage.swapItems(
(short) 0, // position source
hotbar, // conteneur cible
(short) 0, // position destination
(short) 5 // nombre de slots à échanger
);
```
## Opérations de Remplacement
```java
// Remplacer l'objet dans le slot s'il correspond à l'attendu
ItemStackSlotTransaction transaction = container.replaceItemStackInSlot(
(short) 0,
expectedItem, // doit correspondre à l'objet actuel pour continuer
newItem
);
// Remplacer tous les objets avec une fonction
container.replaceAll((slot, existing) -> {
if (existing.getItemId().equals("old_item")) {
return new ItemStack("new_item", existing.getQuantity());
}
return existing;
});
```
## Filtres de Slot
Contrôler quels objets peuvent aller dans quels slots :
```java
// Définir un filtre global pour le conteneur
container.setGlobalFilter(FilterType.WHITELIST);
// Définir un filtre pour un slot spécifique
container.setSlotFilter(
FilterActionType.ADD, // Filtre sur les opérations d'ajout
(short) 0, // Slot
slotFilter // Implémentation du filtre
);
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Conseils pour les Conteneurs :**
- Les indices de slot sont `short`, pas `int` - castez correctement
- Vérifiez toujours `ItemStack.isEmpty(stack)` - gère null et vide
- Utilisez `getQuantity()` pas `getCount()`
- Vérifiez le succès de la transaction avec `succeeded()`
- Utilisez les conteneurs combinés pour les opérations multi-sections
- Les changements d'inventaire du joueur déclenchent `LivingEntityInventoryChangeEvent`
{{< /callout >}}
```java
// Pattern sécurisé pour travailler avec les conteneurs
public void safeAddItem(ItemContainer container, ItemStack item) {
if (ItemStack.isEmpty(item)) {
return;
}
if (!container.canAddItemStack(item)) {
// Gérer le conteneur plein
return;
}
ItemStackTransaction transaction = container.addItemStack(item);
if (!transaction.succeeded()) {
// Gérer l'échec
}
}
```
## Référence API ItemContainer
### Méthodes Principales
| Méthode | Retour | Description |
|--------|---------|-------------|
| `getCapacity()` | `short` | Nombre total de slots |
| `getItemStack(short)` | `ItemStack?` | Obtenir l'objet au slot |
| `isEmpty()` | `boolean` | True si aucun objet |
| `clone()` | `ItemContainer` | Cloner le conteneur |
| `clear()` | `ClearTransaction` | Retirer tous les objets |
### Opérations d'Ajout
| Méthode | Retour | Description |
|--------|---------|-------------|
| `canAddItemStack(ItemStack)` | `boolean` | Vérifier si peut ajouter |
| `canAddItemStack(ItemStack, fullStacks, filter)` | `boolean` | Vérifier avec options |
| `addItemStack(ItemStack)` | `ItemStackTransaction` | Ajouter au premier disponible |
| `addItemStack(ItemStack, allOrNothing, fullStacks, filter)` | `ItemStackTransaction` | Ajouter avec options |
| `canAddItemStackToSlot(short, ItemStack, allOrNothing, filter)` | `boolean` | Vérifier si peut ajouter au slot |
| `addItemStackToSlot(short, ItemStack)` | `ItemStackSlotTransaction` | Ajouter à un slot spécifique |
| `addItemStackToSlot(short, ItemStack, allOrNothing, filter)` | `ItemStackSlotTransaction` | Ajouter au slot avec options |
| `canAddItemStacks(List<ItemStack>)` | `boolean` | Vérifier si peut ajouter plusieurs |
| `addItemStacks(List<ItemStack>)` | `ListTransaction` | Ajouter plusieurs objets |
| `addItemStacksOrdered(List<ItemStack>)` | `ListTransaction` | Ajouter dans l'ordre |
| `addItemStacksOrdered(short offset, List<ItemStack>)` | `ListTransaction` | Ajouter dans l'ordre depuis offset |
### Opérations de Définition
| Méthode | Retour | Description |
|--------|---------|-------------|
| `setItemStackForSlot(short, ItemStack)` | `ItemStackSlotTransaction` | Définir l'objet au slot |
| `setItemStackForSlot(short, ItemStack, filter)` | `ItemStackSlotTransaction` | Définir avec option filtre |
| `replaceItemStackInSlot(short, ItemStack attendu, ItemStack nouveau)` | `ItemStackSlotTransaction` | Remplacer si correspondance |
| `replaceAll(SlotReplacementFunction)` | `ListTransaction` | Remplacer tous les objets |
### Opérations de Retrait
| Méthode | Retour | Description |
|--------|---------|-------------|
| `removeItemStackFromSlot(short)` | `SlotTransaction` | Retirer tout le slot |
| `removeItemStackFromSlot(short, filter)` | `SlotTransaction` | Retirer avec filtre |
| `removeItemStackFromSlot(short, quantity)` | `ItemStackSlotTransaction` | Retirer une quantité |
| `removeItemStackFromSlot(short, ItemStack, quantity)` | `ItemStackSlotTransaction` | Retirer objet correspondant |
| `canRemoveItemStack(ItemStack)` | `boolean` | Vérifier si peut retirer |
| `removeItemStack(ItemStack)` | `ItemStackTransaction` | Retirer type d'objet |
| `canRemoveItemStacks(List<ItemStack>)` | `boolean` | Vérifier si peut retirer plusieurs |
| `removeItemStacks(List<ItemStack>)` | `ListTransaction` | Retirer plusieurs objets |
| `removeAllItemStacks()` | `List<ItemStack>` | Retirer et retourner tous |
| `dropAllItemStacks()` | `List<ItemStack>` | Lâcher tous (respecte cantDrop) |
| `dropAllItemStacks(filter)` | `List<ItemStack>` | Lâcher avec option filtre |
### Retrait Matériau/Ressource/Tag
| Méthode | Retour | Description |
|--------|---------|-------------|
| `canRemoveMaterial(MaterialQuantity)` | `boolean` | Vérifier retrait matériau |
| `removeMaterial(MaterialQuantity)` | `MaterialTransaction` | Retirer par matériau |
| `removeMaterialFromSlot(short, MaterialQuantity)` | `MaterialSlotTransaction` | Retirer matériau du slot |
| `canRemoveMaterials(List<MaterialQuantity>)` | `boolean` | Vérifier plusieurs matériaux |
| `removeMaterials(List<MaterialQuantity>)` | `ListTransaction` | Retirer plusieurs matériaux |
| `canRemoveResource(ResourceQuantity)` | `boolean` | Vérifier retrait ressource |
| `removeResource(ResourceQuantity)` | `ResourceTransaction` | Retirer par ressource |
| `removeResourceFromSlot(short, ResourceQuantity)` | `ResourceSlotTransaction` | Retirer ressource du slot |
| `canRemoveResources(List<ResourceQuantity>)` | `boolean` | Vérifier plusieurs ressources |
| `removeResources(List<ResourceQuantity>)` | `ListTransaction` | Retirer plusieurs ressources |
| `canRemoveTag(tagIndex, quantity)` | `boolean` | Vérifier retrait tag |
| `removeTag(tagIndex, quantity)` | `TagTransaction` | Retirer par tag |
| `removeTagFromSlot(short, tagIndex, quantity)` | `TagSlotTransaction` | Retirer tag du slot |
### Opérations de Déplacement
| Méthode | Retour | Description |
|--------|---------|-------------|
| `moveItemStackFromSlot(short, ItemContainer)` | `MoveTransaction` | Déplacer slot vers conteneur |
| `moveItemStackFromSlot(short, quantity, ItemContainer)` | `MoveTransaction` | Déplacer quantité |
| `moveItemStackFromSlot(short, ItemContainer...)` | `ListTransaction` | Déplacer vers plusieurs conteneurs |
| `moveItemStackFromSlotToSlot(short, quantity, ItemContainer, short)` | `MoveTransaction` | Déplacer vers slot spécifique |
| `moveAllItemStacksTo(ItemContainer...)` | `ListTransaction` | Déplacer tous les objets |
| `moveAllItemStacksTo(Predicate, ItemContainer...)` | `ListTransaction` | Déplacer objets correspondants |
| `quickStackTo(ItemContainer...)` | `ListTransaction` | Déplacer uniquement empilables |
| `combineItemStacksIntoSlot(ItemContainer, short)` | `ListTransaction` | Combiner piles dans slot |
| `swapItems(short srcPos, ItemContainer, short destPos, short length)` | `ListTransaction` | Échanger plages d'objets |
### Méthodes Utilitaires
| Méthode | Retour | Description |
|--------|---------|-------------|
| `forEach(ShortObjectConsumer)` | `void` | Itérer slots non vides |
| `forEachWithMeta(consumer, meta)` | `void` | Itérer avec métadonnées |
| `countItemStacks(Predicate)` | `int` | Compter objets correspondants (quantité totale) |
| `containsItemStacksStackableWith(ItemStack)` | `boolean` | Vérifier objets empilables |
| `sortItems(SortType)` | `ListTransaction` | Trier conteneur |
| `registerChangeEvent(Consumer)` | `EventRegistration` | Écouter les changements |
| `registerChangeEvent(EventPriority, Consumer)` | `EventRegistration` | Écouter avec priorité |
| `setGlobalFilter(FilterType)` | `void` | Définir filtre conteneur |
| `setSlotFilter(FilterActionType, short, SlotFilter)` | `void` | Définir filtre slot |
| `containsContainer(ItemContainer)` | `boolean` | Vérifier si contient conteneur |
### Méthodes Statiques
| Méthode | Retour | Description |
|--------|---------|-------------|
| `copy(from, to, remainder)` | `T` | Copier objets entre conteneurs |
| `ensureContainerCapacity(container, capacity, supplier, remainder)` | `T` | S'assurer de la capacité |
| `getNewContainer(capacity, supplier)` | `ItemContainer` | Créer ou obtenir vide |
| `getMatchingResourceType(Item, resourceId)` | `ItemResourceType?` | Trouver type ressource pour objet |
| `validateQuantity(int)` | `void` | Lance exception si < 0 |
| `validateSlotIndex(short, capacity)` | `void` | Lance exception si hors limites |
### Constantes Statiques
| Champ | Type | Description |
|-------|------|-------------|
| `CODEC` | `CodecMapCodec<ItemContainer>` | Codec de sérialisation |
| `DEFAULT_ADD_ALL_OR_NOTHING` | `boolean` | false |
| `DEFAULT_REMOVE_ALL_OR_NOTHING` | `boolean` | true |
| `DEFAULT_FULL_STACKS` | `boolean` | false |
| `DEFAULT_EXACT_AMOUNT` | `boolean` | true |
| `DEFAULT_FILTER` | `boolean` | true |
### Classes Imbriquées
| Classe | Description |
|-------|-------------|
| `ItemContainerChangeEvent` | Record événement avec `container()` et `transaction()` |
## Référence API Inventory
| Méthode | Retour | Description |
|--------|---------|-------------|
| `getStorage()` | `ItemContainer` | Section de stockage principal |
| `getHotbar()` | `ItemContainer` | Section hotbar |
| `getArmor()` | `ItemContainer` | Section armure |
| `getUtility()` | `ItemContainer` | Section utilitaire |
| `getBackpack()` | `ItemContainer` | Section sac à dos |
| `getSectionById(int)` | `ItemContainer?` | Obtenir section par ID |
| `getItemInHand()` | `ItemStack?` | Objet actuellement tenu |
| `getActiveHotbarSlot()` | `byte` | Slot actif de la hotbar |
| `setActiveHotbarSlot(byte)` | `void` | Définir hotbar active |
| `getCombinedHotbarFirst()` | `CombinedItemContainer` | Hotbar+Stockage combinés |
| `moveItem(...)` | `void` | Déplacer entre sections |
| `clear()` | `void` | Vider toutes les sections |

View File

@@ -0,0 +1,354 @@
---
title: ItemStacks
type: docs
weight: 1
---
ItemStack represents a stack of items in Hytale with quantity, durability, and metadata.
## Creating ItemStacks
ItemStacks are created using constructors with an item ID:
```java
// Create by item ID
ItemStack sword = new ItemStack("iron_sword");
// Create with quantity
ItemStack materials = new ItemStack("wood_plank", 64);
// Create with quantity and metadata
BsonDocument metadata = new BsonDocument();
ItemStack customItem = new ItemStack("iron_sword", 1, metadata);
// Create with full parameters (durability)
ItemStack damagedSword = new ItemStack("iron_sword", 1, 50.0, 100.0, null);
// Parameters: itemId, quantity, durability, maxDurability, metadata
```
## ItemStack Properties
```java
ItemStack stack = new ItemStack("iron_sword", 1);
// Get the item ID
String itemId = stack.getItemId(); // "iron_sword"
// Get the Item asset
Item item = stack.getItem();
// Get quantity (NOT getCount!)
int quantity = stack.getQuantity();
// Check if empty
boolean empty = stack.isEmpty();
// Check validity
boolean valid = stack.isValid();
// Durability
double durability = stack.getDurability();
double maxDurability = stack.getMaxDurability();
boolean unbreakable = stack.isUnbreakable(); // true if maxDurability <= 0
boolean broken = stack.isBroken(); // true if durability == 0
```
## Modifying ItemStacks
ItemStack uses a `with*` pattern that returns NEW instances:
```java
ItemStack stack = new ItemStack("iron_sword", 1);
// Change quantity - returns NEW ItemStack or null if quantity is 0
ItemStack moreItems = stack.withQuantity(32);
// Change durability
ItemStack damaged = stack.withDurability(50.0);
// Increase durability
ItemStack repaired = stack.withIncreasedDurability(25.0);
// Restore full durability
ItemStack fullyRepaired = stack.withRestoredDurability(100.0);
// Change max durability
ItemStack stronger = stack.withMaxDurability(200.0);
// Change state (for items with states)
ItemStack newState = stack.withState("activated");
// Add/modify metadata
ItemStack withMeta = stack.withMetadata(metadataDocument);
// Add specific metadata value
ItemStack tagged = stack.withMetadata("CustomKey", Codec.STRING, "CustomValue");
```
{{< callout type="warning" >}}
**Important:** `withQuantity(0)` returns `null`, not an empty ItemStack. Always check for null when decreasing quantity!
{{< /callout >}}
## The EMPTY Constant
Use `ItemStack.EMPTY` for empty stacks:
```java
// Static empty instance (singleton)
ItemStack empty = ItemStack.EMPTY;
// Check for empty
if (stack.isEmpty()) {
// Stack is empty
}
// Static helper method
if (ItemStack.isEmpty(stack)) {
// Handles null and empty stacks
}
```
## Comparing ItemStacks
```java
ItemStack a = new ItemStack("iron_sword", 1);
ItemStack b = new ItemStack("iron_sword", 5);
ItemStack c = new ItemStack("diamond_sword", 1);
// Check if stackable (same itemId, durability, maxDurability, AND metadata)
// Note: Different quantities can stack, but durability values must match exactly
boolean canStack = a.isStackableWith(b);
// Check equivalent type (same itemId and metadata, ignores durability values)
boolean sameType = a.isEquivalentType(b);
// Check same item type only (just itemId comparison)
boolean sameItem = ItemStack.isSameItemType(a, c); // false
// Static helpers (handle nulls safely)
ItemStack.isStackableWith(a, b);
ItemStack.isEquivalentType(a, b);
```
## Working with Metadata
ItemStack supports BSON metadata for custom data:
```java
// Create metadata codec
KeyedCodec<String> OWNER_KEY = new KeyedCodec<>("Owner", Codec.STRING);
// Add metadata
ItemStack withOwner = stack.withMetadata(OWNER_KEY, "PlayerName");
// Read metadata
String owner = stack.getFromMetadataOrNull(OWNER_KEY);
// Read with key and codec
Integer level = stack.getFromMetadataOrNull("Level", Codec.INTEGER);
// Read with default from BuilderCodec
MyData data = stack.getFromMetadataOrDefault("Data", MyData.CODEC);
```
## Block Items
Check if an item can be placed as a block:
```java
ItemStack stack = new ItemStack("stone", 1);
// Get associated block key (null if not a block item)
String blockKey = stack.getBlockKey();
if (blockKey != null) {
// This item can be placed as a block
}
// Check via Item asset
Item item = stack.getItem();
if (item.hasBlockType()) {
String blockId = item.getBlockId();
}
```
## Common Patterns
### Consuming Items
```java
public ItemStack consumeOne(ItemStack stack) {
if (stack == null || stack.isEmpty()) {
return null;
}
int newQuantity = stack.getQuantity() - 1;
// withQuantity returns null if quantity is 0
return stack.withQuantity(newQuantity);
}
// Usage with container
public void useItem(ItemContainer container, short slot) {
ItemStack current = container.getItemStack(slot);
ItemStack remaining = consumeOne(current);
// remaining may be null if stack was depleted
container.setItemStackForSlot(slot, remaining);
}
```
### Checking Item Type
```java
public boolean isHoldingSword(ItemStack hand) {
if (hand == null || hand.isEmpty()) {
return false;
}
// Check item type by ID
return hand.getItemId().contains("sword");
// Or check via Item asset
// return hand.getItem().getCategory().equals("weapon");
}
```
### Splitting Stacks
```java
public ItemStack[] splitStack(ItemStack stack, int splitAmount) {
if (stack == null || stack.isEmpty()) {
return null;
}
int currentQuantity = stack.getQuantity();
if (splitAmount >= currentQuantity) {
return new ItemStack[] { stack, null };
}
// Create two stacks
ItemStack remaining = stack.withQuantity(currentQuantity - splitAmount);
ItemStack split = stack.withQuantity(splitAmount);
return new ItemStack[] { remaining, split };
}
```
### Merging Stacks
```java
public ItemStack[] mergeStacks(ItemStack target, ItemStack source) {
if (!target.isStackableWith(source)) {
return new ItemStack[] { target, source }; // Can't merge
}
int maxStack = target.getItem().getMaxStack();
int totalQuantity = target.getQuantity() + source.getQuantity();
if (totalQuantity <= maxStack) {
// Full merge
return new ItemStack[] {
target.withQuantity(totalQuantity),
null
};
}
// Partial merge
return new ItemStack[] {
target.withQuantity(maxStack),
source.withQuantity(totalQuantity - maxStack)
};
}
```
## Best Practices
{{< callout type="info" >}}
**Remember:**
- Use `getQuantity()` not `getCount()` - Hytale uses "quantity"
- `withQuantity(0)` returns `null` - check for this!
- Use `ItemStack.isEmpty(stack)` to handle both null and empty
- ItemStacks are mostly immutable - `with*` methods return new instances
- Use `isStackableWith()` before attempting to merge stacks
- `ResourceType.Id` must not be null when creating items via codecs
{{< /callout >}}
```java
// Good: Handle null from withQuantity
ItemStack result = stack.withQuantity(newQuantity);
if (result == null) {
// Stack depleted, handle appropriately
}
// Good: Safe empty check
if (ItemStack.isEmpty(stack)) {
// Handles both null and empty ItemStack.EMPTY
}
// Bad: Ignoring the returned value
stack.withQuantity(10); // Returns new stack, original unchanged!
```
## ItemStack API Reference
### Instance Methods
| Method | Returns | Description |
|--------|---------|-------------|
| `getItemId()` | `String` | The item type identifier |
| `getItem()` | `Item` | The Item asset (returns `Item.UNKNOWN` if not found) |
| `getQuantity()` | `int` | Stack size |
| `getDurability()` | `double` | Current durability |
| `getMaxDurability()` | `double` | Maximum durability |
| `isEmpty()` | `boolean` | True if itemId equals "Empty" |
| `isUnbreakable()` | `boolean` | True if maxDurability <= 0 |
| `isBroken()` | `boolean` | True if NOT unbreakable AND durability == 0 |
| `isValid()` | `boolean` | True if empty OR item asset exists |
| `isStackableWith(ItemStack)` | `boolean` | Same itemId, durability, maxDurability, metadata |
| `isEquivalentType(ItemStack)` | `boolean` | Same itemId and metadata (ignores durability) |
| `getBlockKey()` | `String?` | Block ID if item is placeable, null otherwise |
| `getOverrideDroppedItemAnimation()` | `boolean` | Animation override flag |
| `getMetadata()` | `BsonDocument?` | **Deprecated** - Returns cloned metadata |
| `getFromMetadataOrNull(KeyedCodec)` | `T?` | Get typed metadata value |
| `getFromMetadataOrNull(String, Codec)` | `T?` | Get typed metadata by key |
| `getFromMetadataOrDefault(String, BuilderCodec)` | `T` | Get metadata with default |
### Modifier Methods (return new ItemStack)
| Method | Returns | Description |
|--------|---------|-------------|
| `withQuantity(int)` | `ItemStack?` | **Returns null if quantity is 0** |
| `withDurability(double)` | `ItemStack` | Clamped to [0, maxDurability] |
| `withMaxDurability(double)` | `ItemStack` | Also clamps current durability |
| `withIncreasedDurability(double)` | `ItemStack` | Add to current durability |
| `withRestoredDurability(double)` | `ItemStack` | Set both durability and max |
| `withState(String)` | `ItemStack` | Change item state |
| `withMetadata(BsonDocument)` | `ItemStack` | Replace all metadata |
| `withMetadata(KeyedCodec, T)` | `ItemStack` | Set typed metadata |
| `withMetadata(String, Codec, T)` | `ItemStack` | Set metadata by key |
| `withMetadata(String, BsonValue)` | `ItemStack` | Set raw BSON value |
| `setOverrideDroppedItemAnimation(boolean)` | `void` | **Mutates in place** |
### Static Fields
| Field | Type | Description |
|-------|------|-------------|
| `EMPTY` | `ItemStack` | Singleton empty stack (itemId = "Empty") |
| `EMPTY_ARRAY` | `ItemStack[]` | Empty array constant |
| `CODEC` | `BuilderCodec<ItemStack>` | Serialization codec |
### Static Methods
| Method | Returns | Description |
|--------|---------|-------------|
| `isEmpty(ItemStack)` | `boolean` | Null-safe empty check |
| `isStackableWith(ItemStack, ItemStack)` | `boolean` | Null-safe stackable check |
| `isEquivalentType(ItemStack, ItemStack)` | `boolean` | Null-safe type check |
| `isSameItemType(ItemStack, ItemStack)` | `boolean` | Compare itemId only |
| `fromPacket(ItemQuantity)` | `ItemStack?` | Create from network packet |
### Nested Classes
| Class | Description |
|-------|-------------|
| `ItemStack.Metadata` | Contains `BLOCK_STATE` constant for block state metadata key |

View File

@@ -0,0 +1,354 @@
---
title: ItemStacks
type: docs
weight: 1
---
ItemStack représente une pile d'objets dans Hytale avec quantité, durabilité et métadonnées.
## Créer des ItemStacks
Les ItemStacks sont créés en utilisant des constructeurs avec un ID d'objet :
```java
// Créer par ID d'objet
ItemStack sword = new ItemStack("iron_sword");
// Créer avec quantité
ItemStack materials = new ItemStack("wood_plank", 64);
// Créer avec quantité et métadonnées
BsonDocument metadata = new BsonDocument();
ItemStack customItem = new ItemStack("iron_sword", 1, metadata);
// Créer avec tous les paramètres (durabilité)
ItemStack damagedSword = new ItemStack("iron_sword", 1, 50.0, 100.0, null);
// Paramètres: itemId, quantity, durability, maxDurability, metadata
```
## Propriétés d'ItemStack
```java
ItemStack stack = new ItemStack("iron_sword", 1);
// Obtenir l'ID de l'objet
String itemId = stack.getItemId(); // "iron_sword"
// Obtenir l'asset Item
Item item = stack.getItem();
// Obtenir la quantité (PAS getCount!)
int quantity = stack.getQuantity();
// Vérifier si vide
boolean empty = stack.isEmpty();
// Vérifier la validité
boolean valid = stack.isValid();
// Durabilité
double durability = stack.getDurability();
double maxDurability = stack.getMaxDurability();
boolean unbreakable = stack.isUnbreakable(); // true si maxDurability <= 0
boolean broken = stack.isBroken(); // true si durability == 0
```
## Modifier les ItemStacks
ItemStack utilise un pattern `with*` qui retourne de NOUVELLES instances :
```java
ItemStack stack = new ItemStack("iron_sword", 1);
// Changer la quantité - retourne un NOUVEL ItemStack ou null si quantité est 0
ItemStack moreItems = stack.withQuantity(32);
// Changer la durabilité
ItemStack damaged = stack.withDurability(50.0);
// Augmenter la durabilité
ItemStack repaired = stack.withIncreasedDurability(25.0);
// Restaurer la durabilité complète
ItemStack fullyRepaired = stack.withRestoredDurability(100.0);
// Changer la durabilité max
ItemStack stronger = stack.withMaxDurability(200.0);
// Changer l'état (pour les objets avec états)
ItemStack newState = stack.withState("activated");
// Ajouter/modifier les métadonnées
ItemStack withMeta = stack.withMetadata(metadataDocument);
// Ajouter une valeur de métadonnée spécifique
ItemStack tagged = stack.withMetadata("CustomKey", Codec.STRING, "CustomValue");
```
{{< callout type="warning" >}}
**Important :** `withQuantity(0)` retourne `null`, pas un ItemStack vide. Vérifiez toujours null quand vous diminuez la quantité !
{{< /callout >}}
## La Constante EMPTY
Utilisez `ItemStack.EMPTY` pour les piles vides :
```java
// Instance vide statique (singleton)
ItemStack empty = ItemStack.EMPTY;
// Vérifier si vide
if (stack.isEmpty()) {
// La pile est vide
}
// Méthode helper statique
if (ItemStack.isEmpty(stack)) {
// Gère les piles null et vides
}
```
## Comparer les ItemStacks
```java
ItemStack a = new ItemStack("iron_sword", 1);
ItemStack b = new ItemStack("iron_sword", 5);
ItemStack c = new ItemStack("diamond_sword", 1);
// Vérifier si empilables (même itemId, durability, maxDurability ET metadata)
// Note: Des quantités différentes peuvent s'empiler, mais les durabilités doivent correspondre exactement
boolean canStack = a.isStackableWith(b);
// Vérifier type équivalent (même itemId et metadata, ignore les durabilités)
boolean sameType = a.isEquivalentType(b);
// Vérifier même type d'objet seulement (juste itemId)
boolean sameItem = ItemStack.isSameItemType(a, c); // false
// Helpers statiques (gèrent les nulls en sécurité)
ItemStack.isStackableWith(a, b);
ItemStack.isEquivalentType(a, b);
```
## Travailler avec les Métadonnées
ItemStack supporte les métadonnées BSON pour les données personnalisées :
```java
// Créer un codec de métadonnée
KeyedCodec<String> OWNER_KEY = new KeyedCodec<>("Owner", Codec.STRING);
// Ajouter des métadonnées
ItemStack withOwner = stack.withMetadata(OWNER_KEY, "PlayerName");
// Lire les métadonnées
String owner = stack.getFromMetadataOrNull(OWNER_KEY);
// Lire avec clé et codec
Integer level = stack.getFromMetadataOrNull("Level", Codec.INTEGER);
// Lire avec défaut depuis BuilderCodec
MyData data = stack.getFromMetadataOrDefault("Data", MyData.CODEC);
```
## Objets de Bloc
Vérifier si un objet peut être placé comme bloc :
```java
ItemStack stack = new ItemStack("stone", 1);
// Obtenir la clé de bloc associée (null si pas un objet de bloc)
String blockKey = stack.getBlockKey();
if (blockKey != null) {
// Cet objet peut être placé comme bloc
}
// Vérifier via l'asset Item
Item item = stack.getItem();
if (item.hasBlockType()) {
String blockId = item.getBlockId();
}
```
## Patterns Courants
### Consommer des Objets
```java
public ItemStack consumeOne(ItemStack stack) {
if (stack == null || stack.isEmpty()) {
return null;
}
int newQuantity = stack.getQuantity() - 1;
// withQuantity retourne null si quantité est 0
return stack.withQuantity(newQuantity);
}
// Utilisation avec conteneur
public void useItem(ItemContainer container, short slot) {
ItemStack current = container.getItemStack(slot);
ItemStack remaining = consumeOne(current);
// remaining peut être null si la pile est épuisée
container.setItemStackForSlot(slot, remaining);
}
```
### Vérifier le Type d'Objet
```java
public boolean isHoldingSword(ItemStack hand) {
if (hand == null || hand.isEmpty()) {
return false;
}
// Vérifier le type d'objet par ID
return hand.getItemId().contains("sword");
// Ou vérifier via l'asset Item
// return hand.getItem().getCategory().equals("weapon");
}
```
### Diviser des Piles
```java
public ItemStack[] splitStack(ItemStack stack, int splitAmount) {
if (stack == null || stack.isEmpty()) {
return null;
}
int currentQuantity = stack.getQuantity();
if (splitAmount >= currentQuantity) {
return new ItemStack[] { stack, null };
}
// Créer deux piles
ItemStack remaining = stack.withQuantity(currentQuantity - splitAmount);
ItemStack split = stack.withQuantity(splitAmount);
return new ItemStack[] { remaining, split };
}
```
### Fusionner des Piles
```java
public ItemStack[] mergeStacks(ItemStack target, ItemStack source) {
if (!target.isStackableWith(source)) {
return new ItemStack[] { target, source }; // Ne peut pas fusionner
}
int maxStack = target.getItem().getMaxStack();
int totalQuantity = target.getQuantity() + source.getQuantity();
if (totalQuantity <= maxStack) {
// Fusion complète
return new ItemStack[] {
target.withQuantity(totalQuantity),
null
};
}
// Fusion partielle
return new ItemStack[] {
target.withQuantity(maxStack),
source.withQuantity(totalQuantity - maxStack)
};
}
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Rappelez-vous :**
- Utilisez `getQuantity()` pas `getCount()` - Hytale utilise "quantity"
- `withQuantity(0)` retourne `null` - vérifiez cela !
- Utilisez `ItemStack.isEmpty(stack)` pour gérer null et vide
- Les ItemStacks sont principalement immuables - les méthodes `with*` retournent de nouvelles instances
- Utilisez `isStackableWith()` avant de tenter de fusionner des piles
- `ResourceType.Id` ne doit pas être null lors de la création d'objets via codecs
{{< /callout >}}
```java
// Bon : Gérer null de withQuantity
ItemStack result = stack.withQuantity(newQuantity);
if (result == null) {
// Pile épuisée, gérer appropriément
}
// Bon : Vérification vide sécurisée
if (ItemStack.isEmpty(stack)) {
// Gère à la fois null et ItemStack.EMPTY vide
}
// Mauvais : Ignorer la valeur retournée
stack.withQuantity(10); // Retourne nouvelle pile, original inchangé !
```
## Référence API ItemStack
### Méthodes d'Instance
| Méthode | Retour | Description |
|--------|---------|-------------|
| `getItemId()` | `String` | L'identifiant du type d'objet |
| `getItem()` | `Item` | L'asset Item (retourne `Item.UNKNOWN` si non trouvé) |
| `getQuantity()` | `int` | Taille de la pile |
| `getDurability()` | `double` | Durabilité actuelle |
| `getMaxDurability()` | `double` | Durabilité maximum |
| `isEmpty()` | `boolean` | True si itemId égale "Empty" |
| `isUnbreakable()` | `boolean` | True si maxDurability <= 0 |
| `isBroken()` | `boolean` | True si PAS incassable ET durability == 0 |
| `isValid()` | `boolean` | True si vide OU l'asset existe |
| `isStackableWith(ItemStack)` | `boolean` | Même itemId, durability, maxDurability, metadata |
| `isEquivalentType(ItemStack)` | `boolean` | Même itemId et metadata (ignore durabilité) |
| `getBlockKey()` | `String?` | ID bloc si plaçable, null sinon |
| `getOverrideDroppedItemAnimation()` | `boolean` | Flag d'override animation |
| `getMetadata()` | `BsonDocument?` | **Déprécié** - Retourne metadata clonée |
| `getFromMetadataOrNull(KeyedCodec)` | `T?` | Obtenir valeur metadata typée |
| `getFromMetadataOrNull(String, Codec)` | `T?` | Obtenir metadata par clé |
| `getFromMetadataOrDefault(String, BuilderCodec)` | `T` | Obtenir metadata avec défaut |
### Méthodes de Modification (retournent nouveau ItemStack)
| Méthode | Retour | Description |
|--------|---------|-------------|
| `withQuantity(int)` | `ItemStack?` | **Retourne null si quantité est 0** |
| `withDurability(double)` | `ItemStack` | Borné à [0, maxDurability] |
| `withMaxDurability(double)` | `ItemStack` | Borne aussi la durabilité actuelle |
| `withIncreasedDurability(double)` | `ItemStack` | Ajoute à la durabilité |
| `withRestoredDurability(double)` | `ItemStack` | Définit durabilité et max |
| `withState(String)` | `ItemStack` | Change l'état de l'objet |
| `withMetadata(BsonDocument)` | `ItemStack` | Remplace toutes les metadata |
| `withMetadata(KeyedCodec, T)` | `ItemStack` | Définit metadata typée |
| `withMetadata(String, Codec, T)` | `ItemStack` | Définit metadata par clé |
| `withMetadata(String, BsonValue)` | `ItemStack` | Définit valeur BSON brute |
| `setOverrideDroppedItemAnimation(boolean)` | `void` | **Mute en place** |
### Champs Statiques
| Champ | Type | Description |
|-------|------|-------------|
| `EMPTY` | `ItemStack` | Pile vide singleton (itemId = "Empty") |
| `EMPTY_ARRAY` | `ItemStack[]` | Constante tableau vide |
| `CODEC` | `BuilderCodec<ItemStack>` | Codec de sérialisation |
### Méthodes Statiques
| Méthode | Retour | Description |
|--------|---------|-------------|
| `isEmpty(ItemStack)` | `boolean` | Vérification vide null-safe |
| `isStackableWith(ItemStack, ItemStack)` | `boolean` | Vérification empilable null-safe |
| `isEquivalentType(ItemStack, ItemStack)` | `boolean` | Vérification type null-safe |
| `isSameItemType(ItemStack, ItemStack)` | `boolean` | Compare itemId seulement |
| `fromPacket(ItemQuantity)` | `ItemStack?` | Créer depuis paquet réseau |
### Classes Imbriquées
| Classe | Description |
|-------|-------------|
| `ItemStack.Metadata` | Contient constante `BLOCK_STATE` pour clé metadata état bloc |

View File

@@ -0,0 +1,430 @@
---
title: Transactions
type: docs
weight: 3
---
Transactions track the results of inventory operations in Hytale. Every modification to an `ItemContainer` returns a Transaction object that describes what happened.
## Transaction Interface
All transactions implement the base `Transaction` interface:
```java
public interface Transaction {
// Did the operation succeed?
boolean succeeded();
// Was a specific slot modified?
boolean wasSlotModified(short slot);
}
```
## Transaction Types
### SlotTransaction
Tracks changes to a single slot:
```java
SlotTransaction transaction = container.removeItemStackFromSlot((short) 0);
if (transaction.succeeded()) {
// What was in the slot before
ItemStack before = transaction.getSlotBefore();
// What is in the slot now
ItemStack after = transaction.getSlotAfter();
// The item that was removed/output
ItemStack output = transaction.getOutput();
// Which slot was affected
short slot = transaction.getSlot();
// What type of action (ADD, REMOVE, REPLACE)
ActionType action = transaction.getAction();
}
```
### ItemStackSlotTransaction
Extended slot transaction with additional details:
```java
ItemStackSlotTransaction transaction = container.setItemStackForSlot(
(short) 0,
new ItemStack("iron_sword", 1)
);
if (transaction.succeeded()) {
short slot = transaction.getSlot();
ItemStack before = transaction.getSlotBefore();
ItemStack after = transaction.getSlotAfter();
// Check options used
boolean wasFiltered = transaction.isFilter();
boolean wasAllOrNothing = transaction.isAllOrNothing();
}
```
### ItemStackTransaction
Tracks operations that may affect multiple slots:
```java
ItemStack toAdd = new ItemStack("stone", 128);
ItemStackTransaction transaction = container.addItemStack(toAdd);
if (transaction.succeeded()) {
// Items that couldn't fit (null if all fit)
ItemStack remainder = transaction.getRemainder();
// Original item we tried to add
ItemStack query = transaction.getQuery();
// List of all slot transactions that occurred
List<ItemStackSlotTransaction> slotTransactions = transaction.getSlotTransactions();
for (ItemStackSlotTransaction slotTx : slotTransactions) {
getLogger().at(Level.INFO).log("Modified slot " + slotTx.getSlot());
}
}
```
### MoveTransaction
Tracks moving items between containers:
```java
MoveTransaction<SlotTransaction> transaction = storage.moveItemStackFromSlotToSlot(
(short) 0,
32,
hotbar,
(short) 0
);
if (transaction.succeeded()) {
// Transaction for removing from source
SlotTransaction removeTransaction = transaction.getRemoveTransaction();
// Transaction for adding to destination
SlotTransaction addTransaction = transaction.getAddTransaction();
// The destination container
ItemContainer destination = transaction.getOtherContainer();
// Direction of the move
MoveType moveType = transaction.getMoveType();
}
```
### ListTransaction
Wraps multiple transactions:
```java
List<ItemStack> items = List.of(
new ItemStack("stone", 64),
new ItemStack("wood", 64)
);
ListTransaction<ItemStackTransaction> transaction = container.addItemStacks(items);
if (transaction.succeeded()) {
List<ItemStackTransaction> results = transaction.getList();
for (ItemStackTransaction result : results) {
if (result.succeeded()) {
ItemStack remainder = result.getRemainder();
// ...
}
}
}
```
## ActionType
Operations are categorized by action type:
```java
public enum ActionType {
SET, // Items set (add=true, remove=false, destroy=true)
ADD, // Items added to slot (add=true, remove=false, destroy=false)
REMOVE, // Items removed from slot (add=false, remove=true, destroy=false)
REPLACE // Slot contents replaced (add=true, remove=true, destroy=false)
}
// Check action characteristics
if (action.isAdd()) { /* operation adds items */ }
if (action.isRemove()) { /* operation removes items */ }
if (action.isDestroy()) { /* operation destroys slot contents */ }
```
## MoveType
Direction of move operations:
```java
public enum MoveType {
MOVE_TO_SELF, // Items being moved TO this container
MOVE_FROM_SELF // Items being moved FROM this container
}
```
## Common Patterns
### Check Before Modify
```java
// Safe pattern: verify first, then execute
public boolean safeTransfer(ItemContainer from, ItemContainer to, String itemId, int amount) {
ItemStack toRemove = new ItemStack(itemId, amount);
// Check both operations can succeed
if (!from.canRemoveItemStack(toRemove)) {
return false; // Not enough items
}
if (!to.canAddItemStack(toRemove)) {
return false; // No space
}
// Execute removal
ItemStackTransaction removeResult = from.removeItemStack(toRemove);
if (!removeResult.succeeded()) {
return false;
}
// Execute addition
ItemStack removed = removeResult.getQuery();
ItemStackTransaction addResult = to.addItemStack(removed);
// Handle any remainder
ItemStack remainder = addResult.getRemainder();
if (!ItemStack.isEmpty(remainder)) {
// Put remainder back
from.addItemStack(remainder);
}
return true;
}
```
### Handling Remainders
```java
public void giveItemSafe(Player player, String itemId, int quantity) {
ItemContainer storage = player.getInventory().getStorage();
ItemStack item = new ItemStack(itemId, quantity);
ItemStackTransaction result = storage.addItemStack(item);
if (!result.succeeded()) {
player.sendMessage(Message.raw("Inventory full!"));
return;
}
ItemStack remainder = result.getRemainder();
if (!ItemStack.isEmpty(remainder)) {
player.sendMessage(Message.raw(
"Only received " + (quantity - remainder.getQuantity()) + " items, " +
"inventory full!"
));
}
}
```
### Atomic Operations
```java
// allOrNothing=true ensures partial operations don't happen
public boolean buyItem(Player player, String itemId, int price, int quantity) {
Inventory inv = player.getInventory();
ItemContainer storage = inv.getStorage();
ItemStack currency = new ItemStack("gold_coin", price);
ItemStack item = new ItemStack(itemId, quantity);
// Check both operations can succeed fully
if (!storage.canRemoveItemStack(currency)) {
player.sendMessage(Message.raw("Not enough gold!"));
return false;
}
if (!storage.canAddItemStack(item)) {
player.sendMessage(Message.raw("Inventory full!"));
return false;
}
// Remove currency with allOrNothing=true
ItemStackTransaction removeResult = storage.removeItemStack(currency, true, true);
if (!removeResult.succeeded()) {
return false;
}
// Add item
ItemStackTransaction addResult = storage.addItemStack(item, true, false, true);
if (!addResult.succeeded()) {
// Rollback: return the currency
storage.addItemStack(currency);
return false;
}
return true;
}
```
### Tracking Changes
```java
// Use wasSlotModified to check specific slots
public void onContainerChange(ItemContainer.ItemContainerChangeEvent event) {
Transaction transaction = event.transaction();
// Check if hotbar slot 0 was affected
if (transaction.wasSlotModified((short) 0)) {
getLogger().at(Level.INFO).log("First slot was modified!");
}
// Check all hotbar slots
for (short i = 0; i < 9; i++) {
if (transaction.wasSlotModified(i)) {
getLogger().at(Level.INFO).log("Hotbar slot " + i + " modified");
}
}
}
```
## Transaction Options
Many operations accept optional parameters:
| Parameter | Description | Default |
|-----------|-------------|---------|
| `allOrNothing` | If true, operation fails if not all items can be processed | `false` |
| `fullStacks` | If true, only add to empty slots (not partial stacks) | `false` |
| `exactAmount` | If true, must remove exact quantity requested | `true` |
| `filter` | If true, respect slot filters | `true` |
```java
// Default behavior
container.addItemStack(item);
// With options
container.addItemStack(item, true, false, true); // allOrNothing, fullStacks, filter
container.removeItemStack(item, true, true); // allOrNothing, filter
```
## Best Practices
{{< callout type="info" >}}
**Transaction Tips:**
- Always check `succeeded()` before accessing results
- Handle remainders when adding items
- Use `canAddItemStack()`/`canRemoveItemStack()` for pre-validation
- Use `allOrNothing=true` for critical operations
- Check `wasSlotModified()` to track specific slot changes
{{< /callout >}}
```java
// Good: Check success and handle remainder
ItemStackTransaction tx = container.addItemStack(item);
if (tx.succeeded()) {
ItemStack remainder = tx.getRemainder();
if (!ItemStack.isEmpty(remainder)) {
// Handle leftover items
}
}
// Bad: Assume success
container.addItemStack(item); // Might fail silently!
```
## Transaction API Reference
### Transaction Interface
| Method | Returns | Description |
|--------|---------|-------------|
| `succeeded()` | `boolean` | True if operation succeeded |
| `wasSlotModified(short)` | `boolean` | True if specific slot was modified |
### SlotTransaction
| Method | Returns | Description |
|--------|---------|-------------|
| `getSlot()` | `short` | The affected slot index |
| `getAction()` | `ActionType` | Type of action performed |
| `getSlotBefore()` | `ItemStack?` | Contents before operation |
| `getSlotAfter()` | `ItemStack?` | Contents after operation |
| `getOutput()` | `ItemStack?` | Items removed/output |
| `isAllOrNothing()` | `boolean` | allOrNothing parameter used |
| `isExactAmount()` | `boolean` | exactAmount parameter used |
| `isFilter()` | `boolean` | filter parameter used |
| Static | Type | Description |
|--------|------|-------------|
| `FAILED_ADD` | `SlotTransaction` | Pre-built failed add transaction |
### ItemStackSlotTransaction (extends SlotTransaction)
| Method | Returns | Description |
|--------|---------|-------------|
| `isAddToExistingSlot()` | `boolean` | True if added to existing stack |
| `getQuery()` | `ItemStack?` | Original item requested |
| `getRemainder()` | `ItemStack?` | Items that couldn't fit |
### ItemStackTransaction
| Method | Returns | Description |
|--------|---------|-------------|
| `getAction()` | `ActionType?` | Type of action performed |
| `getQuery()` | `ItemStack?` | Original item requested |
| `getRemainder()` | `ItemStack?` | Items that couldn't fit |
| `isAllOrNothing()` | `boolean` | allOrNothing parameter used |
| `isFilter()` | `boolean` | filter parameter used |
| `getSlotTransactions()` | `List<ItemStackSlotTransaction>` | All slot transactions |
| Static | Type | Description |
|--------|------|-------------|
| `FAILED_ADD` | `ItemStackTransaction` | Pre-built failed add transaction |
### MoveTransaction<T extends Transaction>
| Method | Returns | Description |
|--------|---------|-------------|
| `getRemoveTransaction()` | `SlotTransaction` | Transaction for removal |
| `getMoveType()` | `MoveType` | Direction of move |
| `getOtherContainer()` | `ItemContainer` | The other container involved |
| `getAddTransaction()` | `T` | Transaction for addition |
| `toInverted(ItemContainer)` | `MoveTransaction<T>` | Create inverted view for other container |
### ListTransaction<T extends Transaction>
| Method | Returns | Description |
|--------|---------|-------------|
| `getList()` | `List<T>` | All transactions in list |
| `size()` | `int` | Number of transactions |
| Static | Type | Description |
|--------|------|-------------|
| `EMPTY_SUCCESSFUL_TRANSACTION` | `ListTransaction<?>` | Pre-built empty success |
| `EMPTY_FAILED_TRANSACTION` | `ListTransaction<?>` | Pre-built empty failure |
| `getEmptyTransaction(boolean)` | `ListTransaction<T>` | Get empty success/failure |
### ActionType Enum
| Value | isAdd | isRemove | isDestroy | Description |
|-------|-------|----------|-----------|-------------|
| `SET` | true | false | true | Set slot contents |
| `ADD` | true | false | false | Add items to slot |
| `REMOVE` | false | true | false | Remove items from slot |
| `REPLACE` | true | true | false | Replace slot contents |
### MoveType Enum
| Value | Description |
|-------|-------------|
| `MOVE_TO_SELF` | Items moving to this container |
| `MOVE_FROM_SELF` | Items moving from this container |

View File

@@ -0,0 +1,430 @@
---
title: Transactions
type: docs
weight: 3
---
Les transactions suivent les résultats des opérations d'inventaire dans Hytale. Chaque modification d'un `ItemContainer` retourne un objet Transaction qui décrit ce qui s'est passé.
## Interface Transaction
Toutes les transactions implémentent l'interface de base `Transaction` :
```java
public interface Transaction {
// L'opération a-t-elle réussi ?
boolean succeeded();
// Un slot spécifique a-t-il été modifié ?
boolean wasSlotModified(short slot);
}
```
## Types de Transactions
### SlotTransaction
Suit les changements d'un seul slot :
```java
SlotTransaction transaction = container.removeItemStackFromSlot((short) 0);
if (transaction.succeeded()) {
// Ce qui était dans le slot avant
ItemStack before = transaction.getSlotBefore();
// Ce qui est dans le slot maintenant
ItemStack after = transaction.getSlotAfter();
// L'objet qui a été retiré/sorti
ItemStack output = transaction.getOutput();
// Quel slot a été affecté
short slot = transaction.getSlot();
// Quel type d'action (ADD, REMOVE, REPLACE)
ActionType action = transaction.getAction();
}
```
### ItemStackSlotTransaction
Transaction de slot étendue avec des détails supplémentaires :
```java
ItemStackSlotTransaction transaction = container.setItemStackForSlot(
(short) 0,
new ItemStack("iron_sword", 1)
);
if (transaction.succeeded()) {
short slot = transaction.getSlot();
ItemStack before = transaction.getSlotBefore();
ItemStack after = transaction.getSlotAfter();
// Vérifier les options utilisées
boolean wasFiltered = transaction.isFilter();
boolean wasAllOrNothing = transaction.isAllOrNothing();
}
```
### ItemStackTransaction
Suit les opérations qui peuvent affecter plusieurs slots :
```java
ItemStack toAdd = new ItemStack("stone", 128);
ItemStackTransaction transaction = container.addItemStack(toAdd);
if (transaction.succeeded()) {
// Objets qui n'ont pas pu rentrer (null si tous ont rentré)
ItemStack remainder = transaction.getRemainder();
// Objet original qu'on a essayé d'ajouter
ItemStack query = transaction.getQuery();
// Liste de toutes les transactions de slot qui ont eu lieu
List<ItemStackSlotTransaction> slotTransactions = transaction.getSlotTransactions();
for (ItemStackSlotTransaction slotTx : slotTransactions) {
getLogger().at(Level.INFO).log("Slot modifié " + slotTx.getSlot());
}
}
```
### MoveTransaction
Suit le déplacement d'objets entre conteneurs :
```java
MoveTransaction<SlotTransaction> transaction = storage.moveItemStackFromSlotToSlot(
(short) 0,
32,
hotbar,
(short) 0
);
if (transaction.succeeded()) {
// Transaction pour le retrait de la source
SlotTransaction removeTransaction = transaction.getRemoveTransaction();
// Transaction pour l'ajout à la destination
SlotTransaction addTransaction = transaction.getAddTransaction();
// Le conteneur de destination
ItemContainer destination = transaction.getOtherContainer();
// Direction du déplacement
MoveType moveType = transaction.getMoveType();
}
```
### ListTransaction
Encapsule plusieurs transactions :
```java
List<ItemStack> items = List.of(
new ItemStack("stone", 64),
new ItemStack("wood", 64)
);
ListTransaction<ItemStackTransaction> transaction = container.addItemStacks(items);
if (transaction.succeeded()) {
List<ItemStackTransaction> results = transaction.getList();
for (ItemStackTransaction result : results) {
if (result.succeeded()) {
ItemStack remainder = result.getRemainder();
// ...
}
}
}
```
## ActionType
Les opérations sont catégorisées par type d'action :
```java
public enum ActionType {
SET, // Objets définis (add=true, remove=false, destroy=true)
ADD, // Objets ajoutés au slot (add=true, remove=false, destroy=false)
REMOVE, // Objets retirés du slot (add=false, remove=true, destroy=false)
REPLACE // Contenu du slot remplacé (add=true, remove=true, destroy=false)
}
// Vérifier les caractéristiques de l'action
if (action.isAdd()) { /* opération ajoute des objets */ }
if (action.isRemove()) { /* opération retire des objets */ }
if (action.isDestroy()) { /* opération détruit le contenu du slot */ }
```
## MoveType
Direction des opérations de déplacement :
```java
public enum MoveType {
MOVE_TO_SELF, // Objets déplacés VERS ce conteneur
MOVE_FROM_SELF // Objets déplacés DEPUIS ce conteneur
}
```
## Patterns Courants
### Vérifier Avant de Modifier
```java
// Pattern sûr : vérifier d'abord, puis exécuter
public boolean safeTransfer(ItemContainer from, ItemContainer to, String itemId, int amount) {
ItemStack toRemove = new ItemStack(itemId, amount);
// Vérifier que les deux opérations peuvent réussir
if (!from.canRemoveItemStack(toRemove)) {
return false; // Pas assez d'objets
}
if (!to.canAddItemStack(toRemove)) {
return false; // Pas de place
}
// Exécuter le retrait
ItemStackTransaction removeResult = from.removeItemStack(toRemove);
if (!removeResult.succeeded()) {
return false;
}
// Exécuter l'ajout
ItemStack removed = removeResult.getQuery();
ItemStackTransaction addResult = to.addItemStack(removed);
// Gérer les restes éventuels
ItemStack remainder = addResult.getRemainder();
if (!ItemStack.isEmpty(remainder)) {
// Remettre les restes
from.addItemStack(remainder);
}
return true;
}
```
### Gérer les Restes
```java
public void giveItemSafe(Player player, String itemId, int quantity) {
ItemContainer storage = player.getInventory().getStorage();
ItemStack item = new ItemStack(itemId, quantity);
ItemStackTransaction result = storage.addItemStack(item);
if (!result.succeeded()) {
player.sendMessage(Message.raw("Inventaire plein !"));
return;
}
ItemStack remainder = result.getRemainder();
if (!ItemStack.isEmpty(remainder)) {
player.sendMessage(Message.raw(
"Seulement reçu " + (quantity - remainder.getQuantity()) + " objets, " +
"inventaire plein !"
));
}
}
```
### Opérations Atomiques
```java
// allOrNothing=true garantit que les opérations partielles ne se produisent pas
public boolean buyItem(Player player, String itemId, int price, int quantity) {
Inventory inv = player.getInventory();
ItemContainer storage = inv.getStorage();
ItemStack currency = new ItemStack("gold_coin", price);
ItemStack item = new ItemStack(itemId, quantity);
// Vérifier que les deux opérations peuvent réussir complètement
if (!storage.canRemoveItemStack(currency)) {
player.sendMessage(Message.raw("Pas assez d'or !"));
return false;
}
if (!storage.canAddItemStack(item)) {
player.sendMessage(Message.raw("Inventaire plein !"));
return false;
}
// Retirer la monnaie avec allOrNothing=true
ItemStackTransaction removeResult = storage.removeItemStack(currency, true, true);
if (!removeResult.succeeded()) {
return false;
}
// Ajouter l'objet
ItemStackTransaction addResult = storage.addItemStack(item, true, false, true);
if (!addResult.succeeded()) {
// Annuler : rendre la monnaie
storage.addItemStack(currency);
return false;
}
return true;
}
```
### Suivre les Changements
```java
// Utiliser wasSlotModified pour vérifier des slots spécifiques
public void onContainerChange(ItemContainer.ItemContainerChangeEvent event) {
Transaction transaction = event.transaction();
// Vérifier si le slot 0 de la hotbar a été affecté
if (transaction.wasSlotModified((short) 0)) {
getLogger().at(Level.INFO).log("Premier slot modifié !");
}
// Vérifier tous les slots de la hotbar
for (short i = 0; i < 9; i++) {
if (transaction.wasSlotModified(i)) {
getLogger().at(Level.INFO).log("Slot hotbar " + i + " modifié");
}
}
}
```
## Options de Transaction
De nombreuses opérations acceptent des paramètres optionnels :
| Paramètre | Description | Par défaut |
|-----------|-------------|---------|
| `allOrNothing` | Si vrai, l'opération échoue si tous les objets ne peuvent pas être traités | `false` |
| `fullStacks` | Si vrai, ajoute seulement aux slots vides (pas aux piles partielles) | `false` |
| `exactAmount` | Si vrai, doit retirer exactement la quantité demandée | `true` |
| `filter` | Si vrai, respecte les filtres de slot | `true` |
```java
// Comportement par défaut
container.addItemStack(item);
// Avec options
container.addItemStack(item, true, false, true); // allOrNothing, fullStacks, filter
container.removeItemStack(item, true, true); // allOrNothing, filter
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Conseils pour les Transactions :**
- Toujours vérifier `succeeded()` avant d'accéder aux résultats
- Gérer les restes lors de l'ajout d'objets
- Utiliser `canAddItemStack()`/`canRemoveItemStack()` pour la pré-validation
- Utiliser `allOrNothing=true` pour les opérations critiques
- Vérifier `wasSlotModified()` pour suivre les changements de slots spécifiques
{{< /callout >}}
```java
// Bon : Vérifier le succès et gérer les restes
ItemStackTransaction tx = container.addItemStack(item);
if (tx.succeeded()) {
ItemStack remainder = tx.getRemainder();
if (!ItemStack.isEmpty(remainder)) {
// Gérer les objets restants
}
}
// Mauvais : Supposer le succès
container.addItemStack(item); // Pourrait échouer silencieusement !
```
## Référence API Transaction
### Interface Transaction
| Méthode | Retour | Description |
|--------|---------|-------------|
| `succeeded()` | `boolean` | True si l'opération a réussi |
| `wasSlotModified(short)` | `boolean` | True si le slot spécifique a été modifié |
### SlotTransaction
| Méthode | Retour | Description |
|--------|---------|-------------|
| `getSlot()` | `short` | Index du slot affecté |
| `getAction()` | `ActionType` | Type d'action effectuée |
| `getSlotBefore()` | `ItemStack?` | Contenu avant l'opération |
| `getSlotAfter()` | `ItemStack?` | Contenu après l'opération |
| `getOutput()` | `ItemStack?` | Objets retirés/sortis |
| `isAllOrNothing()` | `boolean` | Paramètre allOrNothing utilisé |
| `isExactAmount()` | `boolean` | Paramètre exactAmount utilisé |
| `isFilter()` | `boolean` | Paramètre filter utilisé |
| Statique | Type | Description |
|----------|------|-------------|
| `FAILED_ADD` | `SlotTransaction` | Transaction d'ajout échouée pré-construite |
### ItemStackSlotTransaction (étend SlotTransaction)
| Méthode | Retour | Description |
|--------|---------|-------------|
| `isAddToExistingSlot()` | `boolean` | True si ajouté à une pile existante |
| `getQuery()` | `ItemStack?` | Objet original demandé |
| `getRemainder()` | `ItemStack?` | Objets qui n'ont pas pu rentrer |
### ItemStackTransaction
| Méthode | Retour | Description |
|--------|---------|-------------|
| `getAction()` | `ActionType?` | Type d'action effectuée |
| `getQuery()` | `ItemStack?` | Objet original demandé |
| `getRemainder()` | `ItemStack?` | Objets qui n'ont pas pu rentrer |
| `isAllOrNothing()` | `boolean` | Paramètre allOrNothing utilisé |
| `isFilter()` | `boolean` | Paramètre filter utilisé |
| `getSlotTransactions()` | `List<ItemStackSlotTransaction>` | Toutes les transactions de slot |
| Statique | Type | Description |
|----------|------|-------------|
| `FAILED_ADD` | `ItemStackTransaction` | Transaction d'ajout échouée pré-construite |
### MoveTransaction<T extends Transaction>
| Méthode | Retour | Description |
|--------|---------|-------------|
| `getRemoveTransaction()` | `SlotTransaction` | Transaction de retrait |
| `getMoveType()` | `MoveType` | Direction du déplacement |
| `getOtherContainer()` | `ItemContainer` | L'autre conteneur impliqué |
| `getAddTransaction()` | `T` | Transaction d'ajout |
| `toInverted(ItemContainer)` | `MoveTransaction<T>` | Créer vue inversée pour l'autre conteneur |
### ListTransaction<T extends Transaction>
| Méthode | Retour | Description |
|--------|---------|-------------|
| `getList()` | `List<T>` | Toutes les transactions de la liste |
| `size()` | `int` | Nombre de transactions |
| Statique | Type | Description |
|----------|------|-------------|
| `EMPTY_SUCCESSFUL_TRANSACTION` | `ListTransaction<?>` | Succès vide pré-construit |
| `EMPTY_FAILED_TRANSACTION` | `ListTransaction<?>` | Échec vide pré-construit |
| `getEmptyTransaction(boolean)` | `ListTransaction<T>` | Obtenir succès/échec vide |
### Enum ActionType
| Valeur | isAdd | isRemove | isDestroy | Description |
|--------|-------|----------|-----------|-------------|
| `SET` | true | false | true | Définir le contenu du slot |
| `ADD` | true | false | false | Ajouter des objets au slot |
| `REMOVE` | false | true | false | Retirer des objets du slot |
| `REPLACE` | true | true | false | Remplacer le contenu du slot |
### Enum MoveType
| Valeur | Description |
|--------|-------------|
| `MOVE_TO_SELF` | Objets déplacés vers ce conteneur |
| `MOVE_FROM_SELF` | Objets déplacés depuis ce conteneur |

View File

@@ -0,0 +1,291 @@
---
title: Mounts
type: docs
weight: 11
---
The mount system allows entities to ride other entities or sit on blocks.
**Package:** `com.hypixel.hytale.builtin.mounts`
## Overview
The mount system supports:
- Riding NPC entities
- Sitting on block-based seats
- Minecart vehicle mechanics
- Mount-specific movement controls
## Architecture
```
Mount System
├── Components
│ ├── NPCMountComponent - Mount NPC configuration
│ ├── MountedComponent - "I am riding something"
│ ├── MountedByComponent - "Something rides me"
│ ├── BlockMountComponent - Block seat
│ └── MinecartComponent - Minecart vehicle
├── Systems
│ ├── MountSystems - Core mount logic
│ └── NPCMountSystems - NPC-specific logic
├── Interactions
│ ├── MountInteraction - Mount NPC
│ ├── SeatingInteraction - Sit on block
│ └── SpawnMinecartInteraction - Create minecart
└── Commands
├── MountCommand - Force mount
├── MountCheckCommand - Check status
└── DismountCommand - Force dismount
```
## Components
### NPCMountComponent
Attached to NPCs that can be mounted:
```java
public class NPCMountComponent implements Component<EntityStore> {
// Player currently riding (if any)
private PlayerRef ownerPlayerRef;
// Original NPC role before mounting
private int originalRoleIndex;
// Mount configuration
// Movement speeds, abilities, etc.
}
```
### MountedComponent
Attached to entities that are riding something:
```java
public class MountedComponent implements Component<EntityStore> {
// Reference to what we're riding
private Ref<EntityStore> mountRef;
}
```
### MountedByComponent
Attached to entities being ridden:
```java
public class MountedByComponent implements Component<EntityStore> {
// Reference to the rider
private Ref<EntityStore> riderRef;
}
```
### BlockMountComponent
Chunk component for block-based seating:
```java
public class BlockMountComponent implements Component<ChunkStore> {
// Block position and configuration
// Player currently seated
}
```
### MinecartComponent
Vehicle component for minecarts:
```java
public class MinecartComponent implements Component<EntityStore> {
// Rail movement configuration
// Speed, acceleration, etc.
}
```
## Mounting NPCs
### Mount Interaction
Players mount NPCs via interaction:
```java
// Registered as "Mount" interaction type
Interaction.CODEC.register("Mount",
MountInteraction.class,
MountInteraction.CODEC);
```
### Mounting Process
1. Player interacts with mountable NPC
2. `NPCMountComponent` is added/configured
3. `MountedComponent` added to player
4. `MountedByComponent` added to NPC
5. NPC role changes to "mounted" behavior
6. Player movement settings updated
### Dismounting
Dismounting can happen:
- Player manually dismounts
- Player disconnects
- Mount or player dies
- Command forces dismount
```java
// Dismount NPC
MountPlugin.dismountNpc(store, mountEntityId);
// Reset player movement settings
MountPlugin.resetOriginalPlayerMovementSettings(playerRef, store);
```
## Block Seating
### Seating Interaction
Players sit on blocks via interaction:
```java
// Registered as "Seating" interaction type
Interaction.CODEC.register("Seating",
SeatingInteraction.class,
SeatingInteraction.CODEC);
```
### Block Mount API
```java
BlockMountAPI api = BlockMountAPI.get();
// Check if block is a seat
boolean isSeat = api.isSeat(world, blockPos);
// Get seated player
PlayerRef seated = api.getSeatedPlayer(world, blockPos);
```
## Minecarts
### Spawn Minecart Interaction
Create minecarts via interaction:
```java
// Registered as "SpawnMinecart" interaction type
Interaction.CODEC.register("SpawnMinecart",
SpawnMinecartInteraction.class,
SpawnMinecartInteraction.CODEC);
```
### Minecart Behavior
- Follows rail tracks
- Configurable speed and acceleration
- Can carry players and items
- Collision with other minecarts
## NPC Core Component
NPCs can use mount actions:
```java
// Registered core component type
NPCPlugin.get().registerCoreComponentType("Mount", BuilderActionMount::new);
```
```json
{
"Type": "Mount",
"MountTarget": "player",
"Duration": 10.0
}
```
## Commands
### /mount
Force mount a player on an NPC:
```
/mount <player> <npc_selector>
```
### /mountcheck
Check mount status:
```
/mountcheck <player>
```
### /dismount
Force dismount a player:
```
/dismount <player>
```
## Plugin Access
```java
MountPlugin mounts = MountPlugin.getInstance();
// Component types
ComponentType<EntityStore, NPCMountComponent> npcMountType =
mounts.getMountComponentType();
ComponentType<EntityStore, MountedComponent> mountedType =
mounts.getMountedComponentType();
ComponentType<EntityStore, MountedByComponent> mountedByType =
mounts.getMountedByComponentType();
ComponentType<EntityStore, MinecartComponent> minecartType =
mounts.getMinecartComponentType();
ComponentType<ChunkStore, BlockMountComponent> blockMountType =
mounts.getBlockMountComponentType();
```
## Systems
### Mount Tracking
```java
// Update mount position tracking
MountSystems.TrackerUpdate
// Remove mount tracking on entity removal
MountSystems.TrackerRemove
```
### Death Handling
```java
// Dismount player when they die
NPCMountSystems.DismountOnPlayerDeath
// Dismount when mount dies
NPCMountSystems.DismountOnMountDeath
```
### Movement
```java
// Handle player mount input
MountSystems.HandleMountInput
// Teleport mounted entity with mount
MountSystems.TeleportMountedEntity
```
## Events
### Player Disconnect
When player disconnects while mounted:
```java
// Automatically dismount on disconnect
getEventRegistry().register(PlayerDisconnectEvent.class,
MountPlugin::onPlayerDisconnect);
```

View File

@@ -0,0 +1,291 @@
---
title: Montures
type: docs
weight: 11
---
Le systeme de montures permet aux entites de chevaucher d'autres entites ou s'asseoir sur des blocs.
**Package:** `com.hypixel.hytale.builtin.mounts`
## Apercu
Le systeme de montures supporte:
- Chevaucher des entites NPC
- S'asseoir sur des sieges bases sur blocs
- Mecaniques de vehicules minecart
- Controles de mouvement specifiques aux montures
## Architecture
```
Systeme Montures
├── Composants
│ ├── NPCMountComponent - Config monture NPC
│ ├── MountedComponent - "Je chevauche quelque chose"
│ ├── MountedByComponent - "Quelque chose me chevauche"
│ ├── BlockMountComponent - Siege bloc
│ └── MinecartComponent - Vehicule minecart
├── Systemes
│ ├── MountSystems - Logique monture principale
│ └── NPCMountSystems - Logique specifique NPC
├── Interactions
│ ├── MountInteraction - Monter NPC
│ ├── SeatingInteraction - S'asseoir sur bloc
│ └── SpawnMinecartInteraction - Creer minecart
└── Commandes
├── MountCommand - Forcer montage
├── MountCheckCommand - Verifier statut
└── DismountCommand - Forcer demontage
```
## Composants
### NPCMountComponent
Attache aux NPCs qui peuvent etre montes:
```java
public class NPCMountComponent implements Component<EntityStore> {
// Joueur chevauchant actuellement (si present)
private PlayerRef ownerPlayerRef;
// Role NPC original avant montage
private int originalRoleIndex;
// Configuration monture
// Vitesses mouvement, capacites, etc.
}
```
### MountedComponent
Attache aux entites qui chevauchent quelque chose:
```java
public class MountedComponent implements Component<EntityStore> {
// Reference vers ce qu'on chevauche
private Ref<EntityStore> mountRef;
}
```
### MountedByComponent
Attache aux entites etant chevauchees:
```java
public class MountedByComponent implements Component<EntityStore> {
// Reference vers le cavalier
private Ref<EntityStore> riderRef;
}
```
### BlockMountComponent
Composant chunk pour sieges bases sur blocs:
```java
public class BlockMountComponent implements Component<ChunkStore> {
// Position bloc et configuration
// Joueur actuellement assis
}
```
### MinecartComponent
Composant vehicule pour minecarts:
```java
public class MinecartComponent implements Component<EntityStore> {
// Configuration mouvement rails
// Vitesse, acceleration, etc.
}
```
## Monter des NPCs
### Interaction Monture
Les joueurs montent les NPCs via interaction:
```java
// Enregistre comme type interaction "Mount"
Interaction.CODEC.register("Mount",
MountInteraction.class,
MountInteraction.CODEC);
```
### Processus de Montage
1. Joueur interagit avec NPC montable
2. `NPCMountComponent` est ajoute/configure
3. `MountedComponent` ajoute au joueur
4. `MountedByComponent` ajoute au NPC
5. Role NPC change vers comportement "monte"
6. Parametres mouvement joueur mis a jour
### Demontage
Le demontage peut survenir:
- Joueur demonte manuellement
- Joueur se deconnecte
- Monture ou joueur meurt
- Commande force demontage
```java
// Demonter NPC
MountPlugin.dismountNpc(store, mountEntityId);
// Reinitialiser parametres mouvement joueur
MountPlugin.resetOriginalPlayerMovementSettings(playerRef, store);
```
## Sieges de Blocs
### Interaction Siege
Les joueurs s'assoient sur blocs via interaction:
```java
// Enregistre comme type interaction "Seating"
Interaction.CODEC.register("Seating",
SeatingInteraction.class,
SeatingInteraction.CODEC);
```
### API Monture Bloc
```java
BlockMountAPI api = BlockMountAPI.get();
// Verifier si bloc est un siege
boolean isSeat = api.isSeat(world, blockPos);
// Obtenir joueur assis
PlayerRef seated = api.getSeatedPlayer(world, blockPos);
```
## Minecarts
### Interaction Spawn Minecart
Creer minecarts via interaction:
```java
// Enregistre comme type interaction "SpawnMinecart"
Interaction.CODEC.register("SpawnMinecart",
SpawnMinecartInteraction.class,
SpawnMinecartInteraction.CODEC);
```
### Comportement Minecart
- Suit les rails
- Vitesse et acceleration configurables
- Peut transporter joueurs et items
- Collision avec autres minecarts
## Composant Core NPC
Les NPCs peuvent utiliser actions de monture:
```java
// Type composant core enregistre
NPCPlugin.get().registerCoreComponentType("Mount", BuilderActionMount::new);
```
```json
{
"Type": "Mount",
"MountTarget": "player",
"Duration": 10.0
}
```
## Commandes
### /mount
Forcer un joueur a monter un NPC:
```
/mount <joueur> <selecteur_npc>
```
### /mountcheck
Verifier statut monture:
```
/mountcheck <joueur>
```
### /dismount
Forcer demontage d'un joueur:
```
/dismount <joueur>
```
## Acces au Plugin
```java
MountPlugin mounts = MountPlugin.getInstance();
// Types de composants
ComponentType<EntityStore, NPCMountComponent> npcMountType =
mounts.getMountComponentType();
ComponentType<EntityStore, MountedComponent> mountedType =
mounts.getMountedComponentType();
ComponentType<EntityStore, MountedByComponent> mountedByType =
mounts.getMountedByComponentType();
ComponentType<EntityStore, MinecartComponent> minecartType =
mounts.getMinecartComponentType();
ComponentType<ChunkStore, BlockMountComponent> blockMountType =
mounts.getBlockMountComponentType();
```
## Systemes
### Suivi Monture
```java
// Mettre a jour suivi position monture
MountSystems.TrackerUpdate
// Supprimer suivi monture a suppression entite
MountSystems.TrackerRemove
```
### Gestion Mort
```java
// Demonter joueur quand il meurt
NPCMountSystems.DismountOnPlayerDeath
// Demonter quand monture meurt
NPCMountSystems.DismountOnMountDeath
```
### Mouvement
```java
// Gerer input joueur sur monture
MountSystems.HandleMountInput
// Teleporter entite montee avec monture
MountSystems.TeleportMountedEntity
```
## Evenements
### Deconnexion Joueur
Quand joueur se deconnecte en montant:
```java
// Demonter automatiquement a deconnexion
getEventRegistry().register(PlayerDisconnectEvent.class,
MountPlugin::onPlayerDisconnect);
```

View File

@@ -0,0 +1,86 @@
---
title: NPC System
type: docs
weight: 10
---
The NPC (Non-Player Character) system provides a complete framework for creating intelligent game characters with autonomous behavior, decision-making, and navigation capabilities.
**Package:** `com.hypixel.hytale.server.npc`
{{< cards >}}
{{< card link="npc-basics" title="NPC Basics" subtitle="Creating and configuring NPCs" >}}
{{< card link="npc-components" title="NPC Components" subtitle="Core components and data" >}}
{{< card link="npc-ai" title="NPC AI" subtitle="Blackboard, decisions, and sensors" >}}
{{< card link="npc-movement" title="NPC Movement" subtitle="Navigation and pathfinding" >}}
{{< card link="npc-commands" title="NPC Commands" subtitle="Admin and debug commands" >}}
{{< /cards >}}
## Architecture Overview
The NPC system is built on several interconnected subsystems:
```
NPCEntity
├── Blackboard (shared state/memory)
├── Role (behavioral template)
│ ├── Instructions (high-level goals)
│ ├── Sensors (perception)
│ └── Actions (behaviors)
├── DecisionMaker (AI logic)
│ ├── Evaluators (condition checking)
│ └── Options (action selection)
└── Movement
├── MotionController (movement execution)
├── PathFollower (path tracking)
└── NavigationGraph (A* pathfinding)
```
## Quick Example
```java
// Register NPC-related event
getEventRegistry().register(NPCSpawnEvent.class, event -> {
NPCEntity npc = event.getNPC();
// Access blackboard for state
Blackboard blackboard = npc.getBlackboard();
// Get current role
Role role = npc.getRole();
// Check if NPC has target
if (blackboard.hasTarget()) {
Entity target = blackboard.getTarget();
}
});
```
## Key Classes
| Class | Description |
|-------|-------------|
| `NPCEntity` | Base class for all NPCs |
| `NPCPlugin` | Plugin entry point for NPC system |
| `Blackboard` | Shared state container for NPC data |
| `Role` | Defines NPC behavior template |
| `DecisionMaker` | AI decision logic |
| `MotionController` | Movement execution |
| `PathFollower` | Path tracking and following |
## Subpackages
| Package | Files | Description |
|---------|-------|-------------|
| `corecomponents/` | 327 | Core ECS components for NPCs |
| `asset/` | 152 | NPC asset configuration |
| `util/` | 50 | Utility classes |
| `blackboard/` | 30 | State management |
| `movement/` | 27 | Movement behaviors |
| `systems/` | 25 | ECS systems |
| `commands/` | 23 | Admin commands |
| `decisionmaker/` | 22 | AI decision logic |
| `sensorinfo/` | 20 | Perception system |
| `role/` | 17 | Role definitions |
| `instructions/` | 14 | High-level behaviors |
| `navigation/` | 12 | Pathfinding |

View File

@@ -0,0 +1,86 @@
---
title: Système NPC
type: docs
weight: 10
---
Le système NPC (Non-Player Character) fournit un framework complet pour créer des personnages de jeu intelligents avec un comportement autonome, une prise de décision et des capacités de navigation.
**Package:** `com.hypixel.hytale.server.npc`
{{< cards >}}
{{< card link="npc-basics" title="Bases NPC" subtitle="Création et configuration des NPCs" >}}
{{< card link="npc-components" title="Composants NPC" subtitle="Composants de base et données" >}}
{{< card link="npc-ai" title="IA NPC" subtitle="Blackboard, décisions et capteurs" >}}
{{< card link="npc-movement" title="Mouvement NPC" subtitle="Navigation et pathfinding" >}}
{{< card link="npc-commands" title="Commandes NPC" subtitle="Commandes admin et debug" >}}
{{< /cards >}}
## Vue d'Ensemble de l'Architecture
Le système NPC est construit sur plusieurs sous-systèmes interconnectés :
```
NPCEntity
├── Blackboard (état/mémoire partagé)
├── Role (template comportemental)
│ ├── Instructions (objectifs haut niveau)
│ ├── Sensors (perception)
│ └── Actions (comportements)
├── DecisionMaker (logique IA)
│ ├── Evaluators (vérification conditions)
│ └── Options (sélection actions)
└── Movement
├── MotionController (exécution mouvement)
├── PathFollower (suivi de chemin)
└── NavigationGraph (pathfinding A*)
```
## Exemple Rapide
```java
// Enregistrer un événement lié aux NPCs
getEventRegistry().register(NPCSpawnEvent.class, event -> {
NPCEntity npc = event.getNPC();
// Accéder au blackboard pour l'état
Blackboard blackboard = npc.getBlackboard();
// Obtenir le rôle actuel
Role role = npc.getRole();
// Vérifier si le NPC a une cible
if (blackboard.hasTarget()) {
Entity target = blackboard.getTarget();
}
});
```
## Classes Principales
| Classe | Description |
|--------|-------------|
| `NPCEntity` | Classe de base pour tous les NPCs |
| `NPCPlugin` | Point d'entrée plugin pour le système NPC |
| `Blackboard` | Conteneur d'état partagé pour les données NPC |
| `Role` | Définit le template de comportement NPC |
| `DecisionMaker` | Logique de décision IA |
| `MotionController` | Exécution des mouvements |
| `PathFollower` | Suivi et parcours de chemin |
## Sous-packages
| Package | Fichiers | Description |
|---------|----------|-------------|
| `corecomponents/` | 327 | Composants ECS de base pour NPCs |
| `asset/` | 152 | Configuration des assets NPC |
| `util/` | 50 | Classes utilitaires |
| `blackboard/` | 30 | Gestion d'état |
| `movement/` | 27 | Comportements de mouvement |
| `systems/` | 25 | Systèmes ECS |
| `commands/` | 23 | Commandes admin |
| `decisionmaker/` | 22 | Logique de décision IA |
| `sensorinfo/` | 20 | Système de perception |
| `role/` | 17 | Définitions de rôles |
| `instructions/` | 14 | Comportements haut niveau |
| `navigation/` | 12 | Pathfinding |

View File

@@ -0,0 +1,373 @@
---
title: NPC AI
type: docs
weight: 3
---
The NPC AI system provides intelligent decision-making through blackboards, decision makers, sensors, and instructions.
**Packages:**
- `com.hypixel.hytale.server.npc.blackboard`
- `com.hypixel.hytale.server.npc.decisionmaker`
- `com.hypixel.hytale.server.npc.sensorinfo`
- `com.hypixel.hytale.server.npc.instructions`
## Blackboard System
The Blackboard is a shared memory space where NPC components communicate through key-value pairs.
### Blackboard Class
```java
public class Blackboard {
private Map<BlackboardKey<?>, Object> data;
// Store value
public <T> void set(BlackboardKey<T> key, T value);
// Retrieve value
public <T> T get(BlackboardKey<T> key);
public <T> T getOrDefault(BlackboardKey<T> key, T defaultValue);
// Check existence
public boolean has(BlackboardKey<?> key);
// Remove value
public void remove(BlackboardKey<?> key);
// Clear all
public void clear();
}
```
### BlackboardKey
Type-safe keys for blackboard access:
```java
// Predefined keys
public class BlackboardKeys {
public static final BlackboardKey<Entity> TARGET =
new BlackboardKey<>("target", Entity.class);
public static final BlackboardKey<Vector3d> HOME_POSITION =
new BlackboardKey<>("home_position", Vector3d.class);
public static final BlackboardKey<Float> ALERT_LEVEL =
new BlackboardKey<>("alert_level", Float.class);
public static final BlackboardKey<Boolean> IN_COMBAT =
new BlackboardKey<>("in_combat", Boolean.class);
}
// Custom keys
BlackboardKey<String> CUSTOM_KEY = new BlackboardKey<>("custom_data", String.class);
```
### Using the Blackboard
```java
NPCEntity npc = // get NPC
Blackboard bb = npc.getBlackboard();
// Set target
bb.set(BlackboardKeys.TARGET, targetEntity);
// Get home position
Vector3d home = bb.getOrDefault(BlackboardKeys.HOME_POSITION, npc.getPosition());
// Check combat status
if (bb.getOrDefault(BlackboardKeys.IN_COMBAT, false)) {
// Handle combat
}
```
## Decision Maker System
The Decision Maker evaluates options and selects the best action for the NPC to take.
### DecisionMaker Interface
```java
public interface DecisionMaker {
// Evaluate and select best option
Option evaluate(NPCEntity npc, Blackboard blackboard);
// Get all available options
List<Option> getOptions();
// Add option
void addOption(Option option);
}
```
### Option Class
Options represent possible actions:
```java
public class Option {
private String id;
private Evaluator evaluator;
private Action action;
private float basePriority;
// Calculate score based on context
public float evaluate(NPCEntity npc, Blackboard blackboard);
// Execute the action
public void execute(NPCEntity npc, Blackboard blackboard);
}
```
### Evaluator Interface
Evaluators calculate option scores:
```java
public interface Evaluator {
// Return score from 0.0 to 1.0
float evaluate(NPCEntity npc, Blackboard blackboard);
}
// Built-in evaluators
public class Evaluators {
// Returns 1.0 if target exists
public static final Evaluator HAS_TARGET = (npc, bb) ->
bb.has(BlackboardKeys.TARGET) ? 1.0f : 0.0f;
// Returns health percentage
public static final Evaluator HEALTH_PERCENT = (npc, bb) ->
npc.getHealth() / npc.getMaxHealth();
// Returns 1.0 if at home
public static final Evaluator AT_HOME = (npc, bb) -> {
Vector3d home = bb.get(BlackboardKeys.HOME_POSITION);
return npc.getPosition().distance(home) < 5.0 ? 1.0f : 0.0f;
};
}
```
### Creating a Decision Maker
```java
DecisionMaker dm = new StandardDecisionMaker();
// Add attack option
dm.addOption(new Option(
"attack",
Evaluators.HAS_TARGET,
new AttackAction(),
10.0f // High priority
));
// Add flee option
dm.addOption(new Option(
"flee",
(npc, bb) -> npc.getHealth() < 20 ? 1.0f : 0.0f,
new FleeAction(),
15.0f // Higher priority when triggered
));
// Add wander option
dm.addOption(new Option(
"wander",
(npc, bb) -> 0.3f, // Low constant score
new WanderAction(),
1.0f // Low priority fallback
));
npc.setDecisionMaker(dm);
```
## Sensor System
Sensors gather information about the world and update the blackboard.
### Sensor Interface
```java
public interface Sensor {
// Process sensor input
void sense(NPCEntity npc, Blackboard blackboard, float deltaTime);
// Get sensor type
String getSensorType();
}
```
### SensorInfo Classes
Sensor information containers:
```java
// Visual detection info
public class VisualSensorInfo {
private List<Entity> visibleEntities;
private float detectionRange;
private float fieldOfView;
public List<Entity> getVisibleEntities();
public boolean canSee(Entity entity);
}
// Audio detection info
public class AudioSensorInfo {
private List<SoundEvent> heardSounds;
private float hearingRange;
public List<SoundEvent> getHeardSounds();
public Vector3d getLoudestSoundPosition();
}
// Threat detection info
public class ThreatSensorInfo {
private List<Entity> threats;
private Entity primaryThreat;
private float threatLevel;
}
```
### Built-in Sensors
```java
// Visual sensor - detects visible entities
public class VisualSensor implements Sensor {
private float range = 20.0f;
private float fov = 120.0f; // degrees
@Override
public void sense(NPCEntity npc, Blackboard bb, float dt) {
List<Entity> visible = findVisibleEntities(npc);
bb.set(BlackboardKeys.VISIBLE_ENTITIES, visible);
}
}
// Proximity sensor - detects nearby entities
public class ProximitySensor implements Sensor {
private float range = 5.0f;
@Override
public void sense(NPCEntity npc, Blackboard bb, float dt) {
List<Entity> nearby = findNearbyEntities(npc, range);
bb.set(BlackboardKeys.NEARBY_ENTITIES, nearby);
}
}
// Damage sensor - reacts to damage taken
public class DamageSensor implements Sensor {
@Override
public void sense(NPCEntity npc, Blackboard bb, float dt) {
if (npc.wasRecentlyDamaged()) {
bb.set(BlackboardKeys.LAST_ATTACKER, npc.getLastAttacker());
bb.set(BlackboardKeys.ALERT_LEVEL, 1.0f);
}
}
}
```
## Instruction System
Instructions define high-level behavioral goals.
### Instruction Interface
```java
public interface Instruction {
// Check if instruction should activate
boolean shouldActivate(NPCEntity npc, Blackboard blackboard);
// Execute instruction logic
void execute(NPCEntity npc, Blackboard blackboard, float deltaTime);
// Check if instruction is complete
boolean isComplete(NPCEntity npc, Blackboard blackboard);
// Get priority
float getPriority();
}
```
### Built-in Instructions
```java
// Wander instruction
public class WanderInstruction implements Instruction {
private float wanderRadius;
private float minWaitTime;
private float maxWaitTime;
@Override
public boolean shouldActivate(NPCEntity npc, Blackboard bb) {
return !bb.has(BlackboardKeys.TARGET);
}
}
// Guard instruction
public class GuardInstruction implements Instruction {
private Vector3d guardPosition;
private float guardRadius;
@Override
public void execute(NPCEntity npc, Blackboard bb, float dt) {
if (npc.getPosition().distance(guardPosition) > guardRadius) {
bb.set(BlackboardKeys.MOVE_TARGET, guardPosition);
}
}
}
// Follow instruction
public class FollowInstruction implements Instruction {
private Entity followTarget;
private float followDistance;
}
// Patrol instruction
public class PatrolInstruction implements Instruction {
private List<Vector3d> patrolPoints;
private int currentPoint;
}
```
## Combining AI Systems
```java
public void setupNPCAI(NPCEntity npc) {
// Configure blackboard
Blackboard bb = npc.getBlackboard();
bb.set(BlackboardKeys.HOME_POSITION, npc.getPosition());
bb.set(BlackboardKeys.AGGRO_RANGE, 15.0f);
// Add sensors
npc.addSensor(new VisualSensor(20.0f, 120.0f));
npc.addSensor(new ProximitySensor(5.0f));
npc.addSensor(new DamageSensor());
// Configure decision maker
DecisionMaker dm = new StandardDecisionMaker();
dm.addOption(new Option("attack", hasHostileTarget, attackAction, 10.0f));
dm.addOption(new Option("flee", lowHealth, fleeAction, 15.0f));
dm.addOption(new Option("patrol", isGuard, patrolAction, 5.0f));
dm.addOption(new Option("idle", always, idleAction, 1.0f));
npc.setDecisionMaker(dm);
// Add instructions
npc.addInstruction(new GuardInstruction(guardPost, 10.0f));
npc.addInstruction(new ReactToThreatInstruction());
}
```
## Best Practices
{{< callout type="info" >}}
**AI Guidelines:**
- Use the Blackboard for all inter-component communication
- Keep Evaluators simple and fast - they run frequently
- Use appropriate sensor ranges to balance awareness vs performance
- Design Instructions to be interruptible
- Test AI behavior with various scenarios
{{< /callout >}}
{{< callout type="warning" >}}
**Performance:** Large numbers of NPCs with complex AI can impact performance. Consider:
- Reducing sensor update frequency for distant NPCs
- Using LOD (Level of Detail) for AI complexity
- Limiting pathfinding requests
{{< /callout >}}

View File

@@ -0,0 +1,373 @@
---
title: IA NPC
type: docs
weight: 3
---
Le système d'IA NPC fournit une prise de décision intelligente via les blackboards, decision makers, capteurs et instructions.
**Packages:**
- `com.hypixel.hytale.server.npc.blackboard`
- `com.hypixel.hytale.server.npc.decisionmaker`
- `com.hypixel.hytale.server.npc.sensorinfo`
- `com.hypixel.hytale.server.npc.instructions`
## Système Blackboard
Le Blackboard est un espace mémoire partagé où les composants NPC communiquent via des paires clé-valeur.
### Classe Blackboard
```java
public class Blackboard {
private Map<BlackboardKey<?>, Object> data;
// Stocker une valeur
public <T> void set(BlackboardKey<T> key, T value);
// Récupérer une valeur
public <T> T get(BlackboardKey<T> key);
public <T> T getOrDefault(BlackboardKey<T> key, T defaultValue);
// Vérifier l'existence
public boolean has(BlackboardKey<?> key);
// Supprimer une valeur
public void remove(BlackboardKey<?> key);
// Tout effacer
public void clear();
}
```
### BlackboardKey
Clés typées pour l'accès au blackboard :
```java
// Clés prédéfinies
public class BlackboardKeys {
public static final BlackboardKey<Entity> TARGET =
new BlackboardKey<>("target", Entity.class);
public static final BlackboardKey<Vector3d> HOME_POSITION =
new BlackboardKey<>("home_position", Vector3d.class);
public static final BlackboardKey<Float> ALERT_LEVEL =
new BlackboardKey<>("alert_level", Float.class);
public static final BlackboardKey<Boolean> IN_COMBAT =
new BlackboardKey<>("in_combat", Boolean.class);
}
// Clés personnalisées
BlackboardKey<String> CUSTOM_KEY = new BlackboardKey<>("custom_data", String.class);
```
### Utiliser le Blackboard
```java
NPCEntity npc = // obtenir le NPC
Blackboard bb = npc.getBlackboard();
// Définir la cible
bb.set(BlackboardKeys.TARGET, targetEntity);
// Obtenir la position d'origine
Vector3d home = bb.getOrDefault(BlackboardKeys.HOME_POSITION, npc.getPosition());
// Vérifier le statut de combat
if (bb.getOrDefault(BlackboardKeys.IN_COMBAT, false)) {
// Gérer le combat
}
```
## Système Decision Maker
Le Decision Maker évalue les options et sélectionne la meilleure action pour le NPC.
### Interface DecisionMaker
```java
public interface DecisionMaker {
// Évaluer et sélectionner la meilleure option
Option evaluate(NPCEntity npc, Blackboard blackboard);
// Obtenir toutes les options disponibles
List<Option> getOptions();
// Ajouter une option
void addOption(Option option);
}
```
### Classe Option
Les options représentent les actions possibles :
```java
public class Option {
private String id;
private Evaluator evaluator;
private Action action;
private float basePriority;
// Calculer le score selon le contexte
public float evaluate(NPCEntity npc, Blackboard blackboard);
// Exécuter l'action
public void execute(NPCEntity npc, Blackboard blackboard);
}
```
### Interface Evaluator
Les évaluateurs calculent les scores des options :
```java
public interface Evaluator {
// Retourne un score de 0.0 à 1.0
float evaluate(NPCEntity npc, Blackboard blackboard);
}
// Évaluateurs intégrés
public class Evaluators {
// Retourne 1.0 si une cible existe
public static final Evaluator HAS_TARGET = (npc, bb) ->
bb.has(BlackboardKeys.TARGET) ? 1.0f : 0.0f;
// Retourne le pourcentage de vie
public static final Evaluator HEALTH_PERCENT = (npc, bb) ->
npc.getHealth() / npc.getMaxHealth();
// Retourne 1.0 si à la maison
public static final Evaluator AT_HOME = (npc, bb) -> {
Vector3d home = bb.get(BlackboardKeys.HOME_POSITION);
return npc.getPosition().distance(home) < 5.0 ? 1.0f : 0.0f;
};
}
```
### Créer un Decision Maker
```java
DecisionMaker dm = new StandardDecisionMaker();
// Ajouter option d'attaque
dm.addOption(new Option(
"attack",
Evaluators.HAS_TARGET,
new AttackAction(),
10.0f // Haute priorité
));
// Ajouter option de fuite
dm.addOption(new Option(
"flee",
(npc, bb) -> npc.getHealth() < 20 ? 1.0f : 0.0f,
new FleeAction(),
15.0f // Priorité plus haute quand déclenché
));
// Ajouter option d'errance
dm.addOption(new Option(
"wander",
(npc, bb) -> 0.3f, // Score constant bas
new WanderAction(),
1.0f // Basse priorité par défaut
));
npc.setDecisionMaker(dm);
```
## Système de Capteurs
Les capteurs collectent des informations sur le monde et mettent à jour le blackboard.
### Interface Sensor
```java
public interface Sensor {
// Traiter l'entrée du capteur
void sense(NPCEntity npc, Blackboard blackboard, float deltaTime);
// Obtenir le type de capteur
String getSensorType();
}
```
### Classes SensorInfo
Conteneurs d'informations de capteurs :
```java
// Info de détection visuelle
public class VisualSensorInfo {
private List<Entity> visibleEntities;
private float detectionRange;
private float fieldOfView;
public List<Entity> getVisibleEntities();
public boolean canSee(Entity entity);
}
// Info de détection audio
public class AudioSensorInfo {
private List<SoundEvent> heardSounds;
private float hearingRange;
public List<SoundEvent> getHeardSounds();
public Vector3d getLoudestSoundPosition();
}
// Info de détection de menace
public class ThreatSensorInfo {
private List<Entity> threats;
private Entity primaryThreat;
private float threatLevel;
}
```
### Capteurs Intégrés
```java
// Capteur visuel - détecte les entités visibles
public class VisualSensor implements Sensor {
private float range = 20.0f;
private float fov = 120.0f; // degrés
@Override
public void sense(NPCEntity npc, Blackboard bb, float dt) {
List<Entity> visible = findVisibleEntities(npc);
bb.set(BlackboardKeys.VISIBLE_ENTITIES, visible);
}
}
// Capteur de proximité - détecte les entités proches
public class ProximitySensor implements Sensor {
private float range = 5.0f;
@Override
public void sense(NPCEntity npc, Blackboard bb, float dt) {
List<Entity> nearby = findNearbyEntities(npc, range);
bb.set(BlackboardKeys.NEARBY_ENTITIES, nearby);
}
}
// Capteur de dégâts - réagit aux dégâts subis
public class DamageSensor implements Sensor {
@Override
public void sense(NPCEntity npc, Blackboard bb, float dt) {
if (npc.wasRecentlyDamaged()) {
bb.set(BlackboardKeys.LAST_ATTACKER, npc.getLastAttacker());
bb.set(BlackboardKeys.ALERT_LEVEL, 1.0f);
}
}
}
```
## Système d'Instructions
Les instructions définissent des objectifs comportementaux de haut niveau.
### Interface Instruction
```java
public interface Instruction {
// Vérifier si l'instruction doit s'activer
boolean shouldActivate(NPCEntity npc, Blackboard blackboard);
// Exécuter la logique de l'instruction
void execute(NPCEntity npc, Blackboard blackboard, float deltaTime);
// Vérifier si l'instruction est complète
boolean isComplete(NPCEntity npc, Blackboard blackboard);
// Obtenir la priorité
float getPriority();
}
```
### Instructions Intégrées
```java
// Instruction d'errance
public class WanderInstruction implements Instruction {
private float wanderRadius;
private float minWaitTime;
private float maxWaitTime;
@Override
public boolean shouldActivate(NPCEntity npc, Blackboard bb) {
return !bb.has(BlackboardKeys.TARGET);
}
}
// Instruction de garde
public class GuardInstruction implements Instruction {
private Vector3d guardPosition;
private float guardRadius;
@Override
public void execute(NPCEntity npc, Blackboard bb, float dt) {
if (npc.getPosition().distance(guardPosition) > guardRadius) {
bb.set(BlackboardKeys.MOVE_TARGET, guardPosition);
}
}
}
// Instruction de suivi
public class FollowInstruction implements Instruction {
private Entity followTarget;
private float followDistance;
}
// Instruction de patrouille
public class PatrolInstruction implements Instruction {
private List<Vector3d> patrolPoints;
private int currentPoint;
}
```
## Combiner les Systèmes d'IA
```java
public void setupNPCAI(NPCEntity npc) {
// Configurer le blackboard
Blackboard bb = npc.getBlackboard();
bb.set(BlackboardKeys.HOME_POSITION, npc.getPosition());
bb.set(BlackboardKeys.AGGRO_RANGE, 15.0f);
// Ajouter les capteurs
npc.addSensor(new VisualSensor(20.0f, 120.0f));
npc.addSensor(new ProximitySensor(5.0f));
npc.addSensor(new DamageSensor());
// Configurer le decision maker
DecisionMaker dm = new StandardDecisionMaker();
dm.addOption(new Option("attack", hasHostileTarget, attackAction, 10.0f));
dm.addOption(new Option("flee", lowHealth, fleeAction, 15.0f));
dm.addOption(new Option("patrol", isGuard, patrolAction, 5.0f));
dm.addOption(new Option("idle", always, idleAction, 1.0f));
npc.setDecisionMaker(dm);
// Ajouter les instructions
npc.addInstruction(new GuardInstruction(guardPost, 10.0f));
npc.addInstruction(new ReactToThreatInstruction());
}
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives IA :**
- Utilisez le Blackboard pour toute communication inter-composants
- Gardez les Évaluateurs simples et rapides - ils s'exécutent fréquemment
- Utilisez des portées de capteurs appropriées pour équilibrer conscience vs performance
- Concevez les Instructions pour être interruptibles
- Testez le comportement IA avec différents scénarios
{{< /callout >}}
{{< callout type="warning" >}}
**Performance :** Un grand nombre de NPCs avec une IA complexe peut impacter les performances. Considérez :
- Réduire la fréquence de mise à jour des capteurs pour les NPCs distants
- Utiliser le LOD (Level of Detail) pour la complexité IA
- Limiter les requêtes de pathfinding
{{< /callout >}}

View File

@@ -0,0 +1,217 @@
---
title: NPC Basics
type: docs
weight: 1
---
This guide covers the fundamentals of creating and configuring NPCs in Hytale.
**Package:** `com.hypixel.hytale.server.npc`
## NPCEntity Class
`NPCEntity` is the base class for all non-player characters. It extends the entity hierarchy and provides NPC-specific functionality.
```java
public class NPCEntity extends LivingEntity {
// Core NPC functionality
private Blackboard blackboard;
private Role role;
private DecisionMaker decisionMaker;
private MotionController motionController;
}
```
### Creating an NPC
NPCs are typically created through the asset system or spawned programmatically:
```java
// Spawn NPC from asset
NPCEntity npc = world.spawnNPC("villager", position);
// Configure NPC after spawn
npc.setRole(customRole);
npc.getBlackboard().setHomePosition(position);
```
## NPC Assets
NPC definitions are configured through YAML asset files:
```yaml
# npc/villager.yaml
Type: NPC
Id: villager
DisplayName: "Villager"
Model: models/characters/villager
Role: roles/villager_role
Stats:
Health: 100
Speed: 3.0
Components:
- Type: NPCBrain
- Type: Interactable
- Type: DialogueCapable
```
### Asset Structure
| Property | Type | Description |
|----------|------|-------------|
| `Type` | String | Must be "NPC" |
| `Id` | String | Unique identifier |
| `DisplayName` | String | Display name |
| `Model` | String | Model asset reference |
| `Role` | String | Default role reference |
| `Stats` | Object | Base statistics |
| `Components` | Array | Component configurations |
## NPC Roles
Roles define the behavioral template for an NPC. They specify what instructions, sensors, and actions the NPC can use.
```yaml
# roles/villager_role.yaml
Type: Role
Id: villager_role
Instructions:
- Wander
- ReactToThreats
- Interact
Sensors:
- Type: VisualSensor
Range: 15.0
- Type: AudioSensor
Range: 10.0
Actions:
- Walk
- Run
- Talk
- Trade
```
### Role Components
```java
public class Role {
private List<Instruction> instructions;
private List<Sensor> sensors;
private List<Action> availableActions;
// Get active instruction
public Instruction getCurrentInstruction();
// Check if action is available
public boolean hasAction(String actionId);
}
```
## NPC Systems
The NPC module registers several ECS systems for processing NPC behavior:
| System | Description |
|--------|-------------|
| `NPCBrainSystem` | Processes AI decisions |
| `NPCMovementSystem` | Handles movement updates |
| `NPCSensorSystem` | Processes sensor inputs |
| `NPCAnimationSystem` | Updates animations |
| `NPCInteractionSystem` | Handles interactions |
### System Registration
```java
public class NPCPlugin extends JavaPlugin {
@Override
public void start() {
// Systems are auto-registered by NPCPlugin
// Custom systems can be added:
getEntityStoreRegistry().registerSystem(
new CustomNPCSystem()
);
}
}
```
## NPC Utilities
The `util/` package provides helper classes:
### NPCUtils
```java
// Find nearest NPC
NPCEntity nearest = NPCUtils.findNearest(position, world, 50.0);
// Get all NPCs in area
List<NPCEntity> npcsInArea = NPCUtils.getNPCsInRadius(
position, world, 25.0
);
// Check line of sight
boolean canSee = NPCUtils.hasLineOfSight(npc, target);
```
### NPCSpawner
```java
// Spawn with configuration
NPCEntity npc = NPCSpawner.spawn(
world,
"villager",
position,
config -> {
config.setRole("merchant");
config.setFaction("town");
}
);
```
## Common Patterns
### Setting Up an NPC Shop
```java
NPCEntity merchant = world.spawnNPC("merchant", shopPosition);
merchant.getBlackboard().set("shop_inventory", inventory);
merchant.getBlackboard().set("is_merchant", true);
merchant.setRole(merchantRole);
```
### Creating a Guard NPC
```java
NPCEntity guard = world.spawnNPC("guard", guardPosition);
Blackboard bb = guard.getBlackboard();
bb.setPatrolPath(patrolWaypoints);
bb.setHostileToFactions(List.of("bandits", "monsters"));
guard.setRole(guardRole);
```
### NPC with Custom Behavior
```java
NPCEntity custom = world.spawnNPC("custom_npc", position);
// Add custom component
custom.getEntityStore().addComponent(new CustomBehaviorComponent());
// Set custom decision maker
custom.setDecisionMaker(new CustomDecisionMaker());
```
## Best Practices
{{< callout type="info" >}}
**NPC Guidelines:**
- Always set a Role for NPCs to define their behavior
- Use the Blackboard for all NPC state storage
- Configure appropriate sensors for the NPC's awareness needs
- Use the asset system for NPC definitions when possible
- Consider performance with large numbers of NPCs
{{< /callout >}}
{{< callout type="warning" >}}
**Thread Safety:** NPC operations should be performed on the world's ticking thread. Use `world.isInThread()` to verify before making changes.
{{< /callout >}}

View File

@@ -0,0 +1,217 @@
---
title: Bases NPC
type: docs
weight: 1
---
Ce guide couvre les fondamentaux de la création et de la configuration des NPCs dans Hytale.
**Package:** `com.hypixel.hytale.server.npc`
## Classe NPCEntity
`NPCEntity` est la classe de base pour tous les personnages non-joueurs. Elle étend la hiérarchie d'entités et fournit des fonctionnalités spécifiques aux NPCs.
```java
public class NPCEntity extends LivingEntity {
// Fonctionnalités NPC de base
private Blackboard blackboard;
private Role role;
private DecisionMaker decisionMaker;
private MotionController motionController;
}
```
### Créer un NPC
Les NPCs sont généralement créés via le système d'assets ou générés programmatiquement :
```java
// Générer un NPC depuis un asset
NPCEntity npc = world.spawnNPC("villager", position);
// Configurer le NPC après génération
npc.setRole(customRole);
npc.getBlackboard().setHomePosition(position);
```
## Assets NPC
Les définitions NPC sont configurées via des fichiers d'assets YAML :
```yaml
# npc/villager.yaml
Type: NPC
Id: villager
DisplayName: "Villageois"
Model: models/characters/villager
Role: roles/villager_role
Stats:
Health: 100
Speed: 3.0
Components:
- Type: NPCBrain
- Type: Interactable
- Type: DialogueCapable
```
### Structure d'Asset
| Propriété | Type | Description |
|-----------|------|-------------|
| `Type` | String | Doit être "NPC" |
| `Id` | String | Identifiant unique |
| `DisplayName` | String | Nom d'affichage |
| `Model` | String | Référence asset modèle |
| `Role` | String | Référence rôle par défaut |
| `Stats` | Object | Statistiques de base |
| `Components` | Array | Configurations de composants |
## Rôles NPC
Les rôles définissent le template comportemental d'un NPC. Ils spécifient quelles instructions, capteurs et actions le NPC peut utiliser.
```yaml
# roles/villager_role.yaml
Type: Role
Id: villager_role
Instructions:
- Wander
- ReactToThreats
- Interact
Sensors:
- Type: VisualSensor
Range: 15.0
- Type: AudioSensor
Range: 10.0
Actions:
- Walk
- Run
- Talk
- Trade
```
### Composants de Rôle
```java
public class Role {
private List<Instruction> instructions;
private List<Sensor> sensors;
private List<Action> availableActions;
// Obtenir l'instruction active
public Instruction getCurrentInstruction();
// Vérifier si une action est disponible
public boolean hasAction(String actionId);
}
```
## Systèmes NPC
Le module NPC enregistre plusieurs systèmes ECS pour traiter le comportement des NPCs :
| Système | Description |
|---------|-------------|
| `NPCBrainSystem` | Traite les décisions IA |
| `NPCMovementSystem` | Gère les mises à jour de mouvement |
| `NPCSensorSystem` | Traite les entrées des capteurs |
| `NPCAnimationSystem` | Met à jour les animations |
| `NPCInteractionSystem` | Gère les interactions |
### Enregistrement des Systèmes
```java
public class NPCPlugin extends JavaPlugin {
@Override
public void start() {
// Les systèmes sont auto-enregistrés par NPCPlugin
// Des systèmes personnalisés peuvent être ajoutés :
getEntityStoreRegistry().registerSystem(
new CustomNPCSystem()
);
}
}
```
## Utilitaires NPC
Le package `util/` fournit des classes d'aide :
### NPCUtils
```java
// Trouver le NPC le plus proche
NPCEntity nearest = NPCUtils.findNearest(position, world, 50.0);
// Obtenir tous les NPCs dans une zone
List<NPCEntity> npcsInArea = NPCUtils.getNPCsInRadius(
position, world, 25.0
);
// Vérifier la ligne de vue
boolean canSee = NPCUtils.hasLineOfSight(npc, target);
```
### NPCSpawner
```java
// Générer avec configuration
NPCEntity npc = NPCSpawner.spawn(
world,
"villager",
position,
config -> {
config.setRole("merchant");
config.setFaction("town");
}
);
```
## Patterns Courants
### Configurer une Boutique NPC
```java
NPCEntity merchant = world.spawnNPC("merchant", shopPosition);
merchant.getBlackboard().set("shop_inventory", inventory);
merchant.getBlackboard().set("is_merchant", true);
merchant.setRole(merchantRole);
```
### Créer un NPC Garde
```java
NPCEntity guard = world.spawnNPC("guard", guardPosition);
Blackboard bb = guard.getBlackboard();
bb.setPatrolPath(patrolWaypoints);
bb.setHostileToFactions(List.of("bandits", "monsters"));
guard.setRole(guardRole);
```
### NPC avec Comportement Personnalisé
```java
NPCEntity custom = world.spawnNPC("custom_npc", position);
// Ajouter un composant personnalisé
custom.getEntityStore().addComponent(new CustomBehaviorComponent());
// Définir un decision maker personnalisé
custom.setDecisionMaker(new CustomDecisionMaker());
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives NPC :**
- Définissez toujours un Rôle pour les NPCs pour définir leur comportement
- Utilisez le Blackboard pour tout stockage d'état NPC
- Configurez les capteurs appropriés pour les besoins de perception du NPC
- Utilisez le système d'assets pour les définitions NPC quand possible
- Considérez les performances avec un grand nombre de NPCs
{{< /callout >}}
{{< callout type="warning" >}}
**Thread Safety :** Les opérations NPC doivent être effectuées sur le thread de tick du monde. Utilisez `world.isInThread()` pour vérifier avant de faire des changements.
{{< /callout >}}

View File

@@ -0,0 +1,374 @@
---
title: NPC Commands
type: docs
weight: 5
---
The NPC system includes 23 admin and debug commands for managing and testing NPCs.
**Package:** `com.hypixel.hytale.server.npc.commands`
## Spawning Commands
### /npc spawn
Spawns an NPC at a location:
```
/npc spawn <npc_type> [position] [--role <role>] [--name <name>]
```
**Arguments:**
| Argument | Type | Description |
|----------|------|-------------|
| `npc_type` | String | NPC asset ID |
| `position` | Position | Spawn location (default: player position) |
| `--role` | String | Override default role |
| `--name` | String | Custom display name |
**Examples:**
```
/npc spawn villager
/npc spawn guard ~ ~1 ~ --role patrol_guard
/npc spawn merchant --name "Bob the Trader"
```
### /npc despawn
Removes an NPC:
```
/npc despawn <target>
```
**Arguments:**
| Argument | Type | Description |
|----------|------|-------------|
| `target` | NPC | Target NPC (raycast or selector) |
### /npc despawnall
Removes all NPCs in radius:
```
/npc despawnall [radius]
```
**Arguments:**
| Argument | Type | Description |
|----------|------|-------------|
| `radius` | Float | Radius in blocks (default: 50) |
## Information Commands
### /npc info
Displays NPC information:
```
/npc info [target]
```
**Output:**
```
NPC Info: Villager (villager_001)
Position: 100.5, 64.0, -200.3
Role: villager_role
State: IDLE
Health: 100/100
Target: None
Current Instruction: Wander
```
### /npc list
Lists all NPCs:
```
/npc list [--radius <radius>] [--type <type>]
```
**Arguments:**
| Argument | Type | Description |
|----------|------|-------------|
| `--radius` | Float | Search radius |
| `--type` | String | Filter by NPC type |
### /npc debug
Toggles debug visualization:
```
/npc debug <mode>
```
**Modes:**
| Mode | Description |
|------|-------------|
| `path` | Show pathfinding |
| `sensors` | Show sensor ranges |
| `state` | Show AI state |
| `target` | Show targeting |
| `all` | Show everything |
| `off` | Disable debug |
## Behavior Commands
### /npc role
Changes NPC role:
```
/npc role <target> <role>
```
**Examples:**
```
/npc role @nearest guard_role
/npc role @e[type=villager] merchant_role
```
### /npc state
Forces NPC state:
```
/npc state <target> <state>
```
**States:**
```
/npc state @nearest IDLE
/npc state @nearest ATTACKING
/npc state @nearest FLEEING
```
### /npc target
Sets NPC target:
```
/npc target <npc> <target_entity>
```
**Examples:**
```
/npc target @nearest @p
/npc target guard_01 @e[type=zombie,limit=1]
```
### /npc cleartarget
Clears NPC target:
```
/npc cleartarget <target>
```
## Movement Commands
### /npc moveto
Commands NPC to move to position:
```
/npc moveto <target> <position>
```
**Examples:**
```
/npc moveto @nearest ~ ~ ~10
/npc moveto guard_01 100 64 -200
```
### /npc follow
Commands NPC to follow entity:
```
/npc follow <npc> <target> [distance]
```
**Examples:**
```
/npc follow @nearest @p 3.0
```
### /npc stop
Stops NPC movement:
```
/npc stop <target>
```
### /npc patrol
Sets patrol path:
```
/npc patrol <target> <point1> <point2> [point3...]
```
**Examples:**
```
/npc patrol guard_01 0 64 0 10 64 0 10 64 10 0 64 10
```
### /npc home
Sets NPC home position:
```
/npc home <target> [position]
```
## Blackboard Commands
### /npc blackboard get
Gets blackboard value:
```
/npc blackboard get <target> <key>
```
**Examples:**
```
/npc blackboard get @nearest alert_level
/npc blackboard get guard_01 home_position
```
### /npc blackboard set
Sets blackboard value:
```
/npc blackboard set <target> <key> <value>
```
**Examples:**
```
/npc blackboard set @nearest alert_level 1.0
/npc blackboard set @nearest is_hostile true
```
### /npc blackboard clear
Clears blackboard:
```
/npc blackboard clear <target> [key]
```
## AI Commands
### /npc think
Forces AI decision cycle:
```
/npc think <target>
```
### /npc instruction
Forces instruction:
```
/npc instruction <target> <instruction>
```
**Examples:**
```
/npc instruction @nearest flee
/npc instruction guard_01 attack
```
### /npc sensor
Toggles sensor:
```
/npc sensor <target> <sensor> <enabled>
```
**Examples:**
```
/npc sensor @nearest visual false
/npc sensor @nearest audio true
```
## Faction Commands
### /npc faction
Sets NPC faction:
```
/npc faction <target> <faction>
```
**Examples:**
```
/npc faction @nearest town_guard
/npc faction villager_01 merchants
```
### /npc relation
Sets faction relation:
```
/npc relation <faction1> <faction2> <relation>
```
**Relations:** `ALLIED`, `FRIENDLY`, `NEUTRAL`, `UNFRIENDLY`, `HOSTILE`
**Examples:**
```
/npc relation guards bandits HOSTILE
/npc relation merchants town_guard ALLIED
```
## Command Permissions
| Command | Permission |
|---------|------------|
| `/npc spawn` | `hytale.command.npc.spawn` |
| `/npc despawn` | `hytale.command.npc.despawn` |
| `/npc info` | `hytale.command.npc.info` |
| `/npc debug` | `hytale.command.npc.debug` |
| `/npc role` | `hytale.command.npc.role` |
| `/npc blackboard` | `hytale.command.npc.blackboard` |
## Registering Custom NPC Commands
```java
public class MyNPCCommand extends AbstractCommand {
public MyNPCCommand() {
super("npc custom", "Custom NPC command");
withRequiredArg("target", "Target NPC", ArgTypes.NPC_REF);
}
@Override
protected CompletableFuture<Void> execute(CommandContext ctx) {
NPCEntity npc = ctx.get("target");
// Custom logic
return null;
}
}
// Register in plugin setup
getCommandRegistry().register(new MyNPCCommand());
```
## Best Practices
{{< callout type="info" >}}
**Command Guidelines:**
- Use selectors like `@nearest` for targeting NPCs
- Debug commands are invaluable for testing AI
- Blackboard commands allow runtime behavior modification
- Use permission checks for admin commands
{{< /callout >}}

View File

@@ -0,0 +1,374 @@
---
title: Commandes NPC
type: docs
weight: 5
---
Le système NPC inclut 23 commandes admin et debug pour gérer et tester les NPCs.
**Package:** `com.hypixel.hytale.server.npc.commands`
## Commandes de Spawn
### /npc spawn
Fait apparaître un NPC à une position :
```
/npc spawn <npc_type> [position] [--role <role>] [--name <name>]
```
**Arguments :**
| Argument | Type | Description |
|----------|------|-------------|
| `npc_type` | String | ID d'asset NPC |
| `position` | Position | Position de spawn (défaut: position du joueur) |
| `--role` | String | Remplacer le rôle par défaut |
| `--name` | String | Nom d'affichage personnalisé |
**Exemples :**
```
/npc spawn villager
/npc spawn guard ~ ~1 ~ --role patrol_guard
/npc spawn merchant --name "Bob le Marchand"
```
### /npc despawn
Supprime un NPC :
```
/npc despawn <target>
```
**Arguments :**
| Argument | Type | Description |
|----------|------|-------------|
| `target` | NPC | NPC cible (raycast ou sélecteur) |
### /npc despawnall
Supprime tous les NPCs dans un rayon :
```
/npc despawnall [radius]
```
**Arguments :**
| Argument | Type | Description |
|----------|------|-------------|
| `radius` | Float | Rayon en blocs (défaut: 50) |
## Commandes d'Information
### /npc info
Affiche les informations d'un NPC :
```
/npc info [target]
```
**Sortie :**
```
NPC Info: Villageois (villager_001)
Position: 100.5, 64.0, -200.3
Role: villager_role
State: IDLE
Health: 100/100
Target: None
Current Instruction: Wander
```
### /npc list
Liste tous les NPCs :
```
/npc list [--radius <radius>] [--type <type>]
```
**Arguments :**
| Argument | Type | Description |
|----------|------|-------------|
| `--radius` | Float | Rayon de recherche |
| `--type` | String | Filtrer par type de NPC |
### /npc debug
Active/désactive la visualisation debug :
```
/npc debug <mode>
```
**Modes :**
| Mode | Description |
|------|-------------|
| `path` | Afficher le pathfinding |
| `sensors` | Afficher les portées des capteurs |
| `state` | Afficher l'état IA |
| `target` | Afficher le ciblage |
| `all` | Tout afficher |
| `off` | Désactiver le debug |
## Commandes de Comportement
### /npc role
Change le rôle du NPC :
```
/npc role <target> <role>
```
**Exemples :**
```
/npc role @nearest guard_role
/npc role @e[type=villager] merchant_role
```
### /npc state
Force l'état du NPC :
```
/npc state <target> <state>
```
**États :**
```
/npc state @nearest IDLE
/npc state @nearest ATTACKING
/npc state @nearest FLEEING
```
### /npc target
Définit la cible du NPC :
```
/npc target <npc> <target_entity>
```
**Exemples :**
```
/npc target @nearest @p
/npc target guard_01 @e[type=zombie,limit=1]
```
### /npc cleartarget
Efface la cible du NPC :
```
/npc cleartarget <target>
```
## Commandes de Mouvement
### /npc moveto
Ordonne au NPC de se déplacer vers une position :
```
/npc moveto <target> <position>
```
**Exemples :**
```
/npc moveto @nearest ~ ~ ~10
/npc moveto guard_01 100 64 -200
```
### /npc follow
Ordonne au NPC de suivre une entité :
```
/npc follow <npc> <target> [distance]
```
**Exemples :**
```
/npc follow @nearest @p 3.0
```
### /npc stop
Arrête le mouvement du NPC :
```
/npc stop <target>
```
### /npc patrol
Définit un chemin de patrouille :
```
/npc patrol <target> <point1> <point2> [point3...]
```
**Exemples :**
```
/npc patrol guard_01 0 64 0 10 64 0 10 64 10 0 64 10
```
### /npc home
Définit la position d'origine du NPC :
```
/npc home <target> [position]
```
## Commandes Blackboard
### /npc blackboard get
Obtient une valeur du blackboard :
```
/npc blackboard get <target> <key>
```
**Exemples :**
```
/npc blackboard get @nearest alert_level
/npc blackboard get guard_01 home_position
```
### /npc blackboard set
Définit une valeur du blackboard :
```
/npc blackboard set <target> <key> <value>
```
**Exemples :**
```
/npc blackboard set @nearest alert_level 1.0
/npc blackboard set @nearest is_hostile true
```
### /npc blackboard clear
Efface le blackboard :
```
/npc blackboard clear <target> [key]
```
## Commandes IA
### /npc think
Force un cycle de décision IA :
```
/npc think <target>
```
### /npc instruction
Force une instruction :
```
/npc instruction <target> <instruction>
```
**Exemples :**
```
/npc instruction @nearest flee
/npc instruction guard_01 attack
```
### /npc sensor
Active/désactive un capteur :
```
/npc sensor <target> <sensor> <enabled>
```
**Exemples :**
```
/npc sensor @nearest visual false
/npc sensor @nearest audio true
```
## Commandes de Faction
### /npc faction
Définit la faction du NPC :
```
/npc faction <target> <faction>
```
**Exemples :**
```
/npc faction @nearest town_guard
/npc faction villager_01 merchants
```
### /npc relation
Définit la relation entre factions :
```
/npc relation <faction1> <faction2> <relation>
```
**Relations :** `ALLIED`, `FRIENDLY`, `NEUTRAL`, `UNFRIENDLY`, `HOSTILE`
**Exemples :**
```
/npc relation guards bandits HOSTILE
/npc relation merchants town_guard ALLIED
```
## Permissions des Commandes
| Commande | Permission |
|----------|------------|
| `/npc spawn` | `hytale.command.npc.spawn` |
| `/npc despawn` | `hytale.command.npc.despawn` |
| `/npc info` | `hytale.command.npc.info` |
| `/npc debug` | `hytale.command.npc.debug` |
| `/npc role` | `hytale.command.npc.role` |
| `/npc blackboard` | `hytale.command.npc.blackboard` |
## Enregistrer des Commandes NPC Personnalisées
```java
public class MyNPCCommand extends AbstractCommand {
public MyNPCCommand() {
super("npc custom", "Commande NPC personnalisée");
withRequiredArg("target", "NPC cible", ArgTypes.NPC_REF);
}
@Override
protected CompletableFuture<Void> execute(CommandContext ctx) {
NPCEntity npc = ctx.get("target");
// Logique personnalisée
return null;
}
}
// Enregistrer dans le setup du plugin
getCommandRegistry().register(new MyNPCCommand());
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives des Commandes :**
- Utilisez des sélecteurs comme `@nearest` pour cibler les NPCs
- Les commandes debug sont précieuses pour tester l'IA
- Les commandes blackboard permettent la modification du comportement à l'exécution
- Utilisez les vérifications de permissions pour les commandes admin
{{< /callout >}}

View File

@@ -0,0 +1,348 @@
---
title: NPC Components
type: docs
weight: 2
---
NPC components are ECS components that store data and state for NPCs. The system includes over 300 core components for various NPC functionalities.
**Package:** `com.hypixel.hytale.server.npc.corecomponents`
## Core Components Overview
NPC components follow the ECS pattern where components are pure data containers attached to entity stores.
```java
// Access NPC components
EntityStore store = npc.getEntityStore();
NPCBrainComponent brain = store.getComponent(NPCBrainComponent.class);
```
## Brain Components
### NPCBrainComponent
The main AI processing component:
```java
public class NPCBrainComponent {
private DecisionMaker decisionMaker;
private float thinkInterval;
private float lastThinkTime;
public void setDecisionMaker(DecisionMaker maker);
public DecisionMaker getDecisionMaker();
public boolean shouldThink(float currentTime);
}
```
### NPCMemoryComponent
Stores NPC memories and knowledge:
```java
public class NPCMemoryComponent {
private Map<String, Memory> memories;
private float memoryDuration;
public void remember(String key, Object value, float duration);
public <T> T recall(String key, Class<T> type);
public boolean hasMemory(String key);
public void forget(String key);
}
```
## State Components
### NPCStateComponent
Current behavioral state:
```java
public class NPCStateComponent {
private NPCState currentState;
private NPCState previousState;
private float stateEnterTime;
public enum NPCState {
IDLE,
WALKING,
RUNNING,
ATTACKING,
FLEEING,
INTERACTING,
SLEEPING,
DEAD
}
}
```
### NPCAlertComponent
Alertness and awareness level:
```java
public class NPCAlertComponent {
private AlertLevel alertLevel;
private float alertDecayRate;
private Entity alertSource;
public enum AlertLevel {
RELAXED, // Normal state
CURIOUS, // Something caught attention
ALERT, // Actively investigating
ALARMED, // Threat detected
COMBAT // In combat
}
}
```
## Target Components
### NPCTargetComponent
Current target tracking:
```java
public class NPCTargetComponent {
private Ref<EntityStore> currentTarget;
private TargetType targetType;
private float targetAcquiredTime;
private Vector3d lastKnownPosition;
public enum TargetType {
HOSTILE,
FRIENDLY,
NEUTRAL,
OBJECT
}
}
```
### NPCFocusComponent
Visual focus point:
```java
public class NPCFocusComponent {
private Vector3d focusPoint;
private Ref<EntityStore> focusEntity;
private float focusStrength;
private boolean shouldLookAt;
}
```
## Movement Components
### NPCMovementComponent
Movement configuration:
```java
public class NPCMovementComponent {
private float walkSpeed;
private float runSpeed;
private float turnSpeed;
private boolean canJump;
private boolean canSwim;
private boolean canClimb;
}
```
### NPCPathComponent
Path following data:
```java
public class NPCPathComponent {
private List<Vector3d> currentPath;
private int currentWaypointIndex;
private float pathRecalculateInterval;
private float lastPathTime;
public Vector3d getCurrentWaypoint();
public Vector3d getNextWaypoint();
public boolean hasReachedWaypoint(Vector3d position, float threshold);
}
```
### NPCNavigationComponent
Navigation settings:
```java
public class NPCNavigationComponent {
private float avoidanceRadius;
private float pathfindingRange;
private int maxPathLength;
private NavigationFlags flags;
public static class NavigationFlags {
public boolean avoidWater;
public boolean avoidFire;
public boolean canUseDoors;
public boolean canBreakBlocks;
}
}
```
## Combat Components
### NPCCombatComponent
Combat capabilities:
```java
public class NPCCombatComponent {
private float attackRange;
private float attackCooldown;
private float lastAttackTime;
private DamageType preferredDamageType;
private List<String> availableAttacks;
}
```
### NPCAggroComponent
Aggression management:
```java
public class NPCAggroComponent {
private Map<Ref<EntityStore>, Float> aggroTable;
private float aggroDecayRate;
private float aggroRange;
public void addAggro(Ref<EntityStore> entity, float amount);
public void removeAggro(Ref<EntityStore> entity);
public Ref<EntityStore> getHighestAggroTarget();
}
```
## Social Components
### NPCFactionComponent
Faction affiliation:
```java
public class NPCFactionComponent {
private String factionId;
private Map<String, FactionRelation> relations;
public enum FactionRelation {
ALLIED,
FRIENDLY,
NEUTRAL,
UNFRIENDLY,
HOSTILE
}
public FactionRelation getRelation(String otherFaction);
}
```
### NPCDialogueComponent
Dialogue capabilities:
```java
public class NPCDialogueComponent {
private String dialogueTreeId;
private Map<String, Boolean> dialogueFlags;
private Ref<EntityStore> currentSpeaker;
public boolean hasDialogue();
public void startDialogue(Player player);
}
```
## Utility Components
### NPCScheduleComponent
Daily schedule:
```java
public class NPCScheduleComponent {
private Map<Integer, ScheduleEntry> schedule;
public static class ScheduleEntry {
public int startHour;
public int endHour;
public String activity;
public Vector3d location;
}
public ScheduleEntry getCurrentActivity(int worldHour);
}
```
### NPCInventoryComponent
NPC inventory:
```java
public class NPCInventoryComponent {
private ItemContainer inventory;
private ItemStack equippedWeapon;
private ItemStack equippedArmor;
public ItemStack getEquippedWeapon();
public void equipItem(ItemStack item);
}
```
## Registering Custom Components
```java
public class MyPlugin extends JavaPlugin {
@Override
public void start() {
// Register custom NPC component
getEntityStoreRegistry().registerComponent(
"custom_npc_data",
CustomNPCComponent.class,
CustomNPCComponent::new
);
}
}
public class CustomNPCComponent {
private String customData;
private int customValue;
// Component data fields
}
```
## Component Access Patterns
```java
// Safe component access
public void processNPC(NPCEntity npc) {
EntityStore store = npc.getEntityStore();
// Check if component exists
if (store.hasComponent(NPCCombatComponent.class)) {
NPCCombatComponent combat = store.getComponent(NPCCombatComponent.class);
// Process combat logic
}
// Get or create component
NPCStateComponent state = store.getOrCreateComponent(
NPCStateComponent.class,
NPCStateComponent::new
);
}
```
## Best Practices
{{< callout type="info" >}}
**Component Guidelines:**
- Components should be pure data - no complex logic
- Use components for state that needs to persist
- Access components through EntityStore, not directly
- Check component existence before access
- Use appropriate component for each type of data
{{< /callout >}}

View File

@@ -0,0 +1,348 @@
---
title: Composants NPC
type: docs
weight: 2
---
Les composants NPC sont des composants ECS qui stockent les données et l'état des NPCs. Le système inclut plus de 300 composants de base pour diverses fonctionnalités NPC.
**Package:** `com.hypixel.hytale.server.npc.corecomponents`
## Vue d'Ensemble des Composants
Les composants NPC suivent le pattern ECS où les composants sont des conteneurs de données pures attachés aux entity stores.
```java
// Accéder aux composants NPC
EntityStore store = npc.getEntityStore();
NPCBrainComponent brain = store.getComponent(NPCBrainComponent.class);
```
## Composants Cerveau
### NPCBrainComponent
Le composant principal de traitement IA :
```java
public class NPCBrainComponent {
private DecisionMaker decisionMaker;
private float thinkInterval;
private float lastThinkTime;
public void setDecisionMaker(DecisionMaker maker);
public DecisionMaker getDecisionMaker();
public boolean shouldThink(float currentTime);
}
```
### NPCMemoryComponent
Stocke les mémoires et connaissances du NPC :
```java
public class NPCMemoryComponent {
private Map<String, Memory> memories;
private float memoryDuration;
public void remember(String key, Object value, float duration);
public <T> T recall(String key, Class<T> type);
public boolean hasMemory(String key);
public void forget(String key);
}
```
## Composants d'État
### NPCStateComponent
État comportemental actuel :
```java
public class NPCStateComponent {
private NPCState currentState;
private NPCState previousState;
private float stateEnterTime;
public enum NPCState {
IDLE,
WALKING,
RUNNING,
ATTACKING,
FLEEING,
INTERACTING,
SLEEPING,
DEAD
}
}
```
### NPCAlertComponent
Niveau de vigilance et de conscience :
```java
public class NPCAlertComponent {
private AlertLevel alertLevel;
private float alertDecayRate;
private Entity alertSource;
public enum AlertLevel {
RELAXED, // État normal
CURIOUS, // Quelque chose a attiré l'attention
ALERT, // Investigation active
ALARMED, // Menace détectée
COMBAT // En combat
}
}
```
## Composants de Cible
### NPCTargetComponent
Suivi de cible actuel :
```java
public class NPCTargetComponent {
private Ref<EntityStore> currentTarget;
private TargetType targetType;
private float targetAcquiredTime;
private Vector3d lastKnownPosition;
public enum TargetType {
HOSTILE,
FRIENDLY,
NEUTRAL,
OBJECT
}
}
```
### NPCFocusComponent
Point de focus visuel :
```java
public class NPCFocusComponent {
private Vector3d focusPoint;
private Ref<EntityStore> focusEntity;
private float focusStrength;
private boolean shouldLookAt;
}
```
## Composants de Mouvement
### NPCMovementComponent
Configuration de mouvement :
```java
public class NPCMovementComponent {
private float walkSpeed;
private float runSpeed;
private float turnSpeed;
private boolean canJump;
private boolean canSwim;
private boolean canClimb;
}
```
### NPCPathComponent
Données de suivi de chemin :
```java
public class NPCPathComponent {
private List<Vector3d> currentPath;
private int currentWaypointIndex;
private float pathRecalculateInterval;
private float lastPathTime;
public Vector3d getCurrentWaypoint();
public Vector3d getNextWaypoint();
public boolean hasReachedWaypoint(Vector3d position, float threshold);
}
```
### NPCNavigationComponent
Paramètres de navigation :
```java
public class NPCNavigationComponent {
private float avoidanceRadius;
private float pathfindingRange;
private int maxPathLength;
private NavigationFlags flags;
public static class NavigationFlags {
public boolean avoidWater;
public boolean avoidFire;
public boolean canUseDoors;
public boolean canBreakBlocks;
}
}
```
## Composants de Combat
### NPCCombatComponent
Capacités de combat :
```java
public class NPCCombatComponent {
private float attackRange;
private float attackCooldown;
private float lastAttackTime;
private DamageType preferredDamageType;
private List<String> availableAttacks;
}
```
### NPCAggroComponent
Gestion de l'aggro :
```java
public class NPCAggroComponent {
private Map<Ref<EntityStore>, Float> aggroTable;
private float aggroDecayRate;
private float aggroRange;
public void addAggro(Ref<EntityStore> entity, float amount);
public void removeAggro(Ref<EntityStore> entity);
public Ref<EntityStore> getHighestAggroTarget();
}
```
## Composants Sociaux
### NPCFactionComponent
Affiliation de faction :
```java
public class NPCFactionComponent {
private String factionId;
private Map<String, FactionRelation> relations;
public enum FactionRelation {
ALLIED,
FRIENDLY,
NEUTRAL,
UNFRIENDLY,
HOSTILE
}
public FactionRelation getRelation(String otherFaction);
}
```
### NPCDialogueComponent
Capacités de dialogue :
```java
public class NPCDialogueComponent {
private String dialogueTreeId;
private Map<String, Boolean> dialogueFlags;
private Ref<EntityStore> currentSpeaker;
public boolean hasDialogue();
public void startDialogue(Player player);
}
```
## Composants Utilitaires
### NPCScheduleComponent
Programme journalier :
```java
public class NPCScheduleComponent {
private Map<Integer, ScheduleEntry> schedule;
public static class ScheduleEntry {
public int startHour;
public int endHour;
public String activity;
public Vector3d location;
}
public ScheduleEntry getCurrentActivity(int worldHour);
}
```
### NPCInventoryComponent
Inventaire NPC :
```java
public class NPCInventoryComponent {
private ItemContainer inventory;
private ItemStack equippedWeapon;
private ItemStack equippedArmor;
public ItemStack getEquippedWeapon();
public void equipItem(ItemStack item);
}
```
## Enregistrer des Composants Personnalisés
```java
public class MyPlugin extends JavaPlugin {
@Override
public void start() {
// Enregistrer un composant NPC personnalisé
getEntityStoreRegistry().registerComponent(
"custom_npc_data",
CustomNPCComponent.class,
CustomNPCComponent::new
);
}
}
public class CustomNPCComponent {
private String customData;
private int customValue;
// Champs de données du composant
}
```
## Patterns d'Accès aux Composants
```java
// Accès sécurisé aux composants
public void processNPC(NPCEntity npc) {
EntityStore store = npc.getEntityStore();
// Vérifier si le composant existe
if (store.hasComponent(NPCCombatComponent.class)) {
NPCCombatComponent combat = store.getComponent(NPCCombatComponent.class);
// Traiter la logique de combat
}
// Obtenir ou créer un composant
NPCStateComponent state = store.getOrCreateComponent(
NPCStateComponent.class,
NPCStateComponent::new
);
}
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives des Composants :**
- Les composants doivent être des données pures - pas de logique complexe
- Utilisez les composants pour l'état qui doit persister
- Accédez aux composants via EntityStore, pas directement
- Vérifiez l'existence du composant avant l'accès
- Utilisez le composant approprié pour chaque type de données
{{< /callout >}}

View File

@@ -0,0 +1,365 @@
---
title: NPC Movement
type: docs
weight: 4
---
The NPC movement system handles navigation, pathfinding, and motion control for NPCs.
**Packages:**
- `com.hypixel.hytale.server.npc.movement`
- `com.hypixel.hytale.server.npc.navigation`
## Motion Controller
The `MotionController` executes movement commands and manages NPC locomotion.
### MotionController Class
```java
public class MotionController {
private NPCEntity npc;
private MovementState state;
private float currentSpeed;
private Vector3d targetVelocity;
// Movement commands
public void moveTo(Vector3d target);
public void moveInDirection(Vector3d direction);
public void stop();
// Speed control
public void setSpeed(float speed);
public void walk();
public void run();
public void sprint();
// State queries
public boolean isMoving();
public boolean hasReachedTarget();
public MovementState getState();
}
```
### Movement States
```java
public enum MovementState {
IDLE, // Not moving
WALKING, // Normal movement
RUNNING, // Fast movement
SPRINTING, // Maximum speed
JUMPING, // In air (jump)
FALLING, // In air (fall)
SWIMMING, // In water
CLIMBING, // On ladder/vine
SLIDING // On slope
}
```
### Using MotionController
```java
NPCEntity npc = // get NPC
MotionController motion = npc.getMotionController();
// Move to position
motion.moveTo(targetPosition);
// Set movement speed
motion.run(); // or motion.setSpeed(5.0f);
// Check if arrived
if (motion.hasReachedTarget()) {
// Destination reached
}
// Stop movement
motion.stop();
```
## Path Follower
The `PathFollower` tracks and follows calculated paths.
### PathFollower Class
```java
public class PathFollower {
private List<Vector3d> path;
private int currentIndex;
private float waypointRadius;
private boolean smoothPath;
// Path management
public void setPath(List<Vector3d> path);
public void clearPath();
public boolean hasPath();
// Following
public Vector3d getNextWaypoint();
public void advanceToNextWaypoint();
public boolean hasReachedWaypoint(Vector3d position);
// Progress
public float getPathProgress(); // 0.0 to 1.0
public int getRemainingWaypoints();
}
```
### Path Following Example
```java
PathFollower pathFollower = npc.getPathFollower();
MotionController motion = npc.getMotionController();
// Set a path
pathFollower.setPath(calculatedPath);
// In update loop
if (pathFollower.hasPath()) {
Vector3d nextWaypoint = pathFollower.getNextWaypoint();
// Move towards waypoint
motion.moveTo(nextWaypoint);
// Check if reached
if (pathFollower.hasReachedWaypoint(npc.getPosition())) {
pathFollower.advanceToNextWaypoint();
}
}
```
## Navigation Graph
The navigation system uses A* pathfinding on a navigation graph.
### NavigationGraph Class
```java
public class NavigationGraph {
// Find path between points
public List<Vector3d> findPath(
Vector3d start,
Vector3d end,
NavigationConfig config
);
// Check if point is navigable
public boolean isNavigable(Vector3d position);
// Get nearest navigable point
public Vector3d getNearestNavigablePoint(Vector3d position);
}
```
### NavigationConfig
```java
public class NavigationConfig {
private float maxDistance; // Maximum path length
private float stepHeight; // Max step up height
private float entityWidth; // Entity collision width
private float entityHeight; // Entity collision height
private boolean canSwim; // Allow water paths
private boolean canClimb; // Allow ladder/vine paths
private boolean canOpenDoors; // Allow door traversal
private Set<String> avoidBlocks; // Blocks to avoid
// Builder pattern
public static NavigationConfig builder()
.maxDistance(100.0f)
.stepHeight(1.0f)
.entityWidth(0.6f)
.entityHeight(1.8f)
.canSwim(false)
.build();
}
```
### Pathfinding Example
```java
NavigationGraph navGraph = world.getNavigationGraph();
NavigationConfig config = NavigationConfig.builder()
.maxDistance(50.0f)
.canSwim(true)
.build();
List<Vector3d> path = navGraph.findPath(
npc.getPosition(),
targetPosition,
config
);
if (path != null && !path.isEmpty()) {
npc.getPathFollower().setPath(path);
}
```
## Movement Behaviors
Pre-built movement behaviors for common patterns.
### WanderBehavior
```java
public class WanderBehavior {
private float wanderRadius;
private float minPauseDuration;
private float maxPauseDuration;
public WanderBehavior(float radius) {
this.wanderRadius = radius;
}
public void update(NPCEntity npc, float deltaTime) {
if (!npc.getMotionController().isMoving()) {
Vector3d wanderTarget = calculateWanderTarget(npc);
npc.getMotionController().moveTo(wanderTarget);
}
}
}
```
### FollowBehavior
```java
public class FollowBehavior {
private Entity target;
private float followDistance;
private float catchUpDistance;
public void update(NPCEntity npc, float deltaTime) {
float distance = npc.getPosition().distance(target.getPosition());
if (distance > catchUpDistance) {
npc.getMotionController().run();
} else if (distance > followDistance) {
npc.getMotionController().walk();
npc.getMotionController().moveTo(target.getPosition());
} else {
npc.getMotionController().stop();
}
}
}
```
### PatrolBehavior
```java
public class PatrolBehavior {
private List<Vector3d> patrolPoints;
private int currentPointIndex;
private boolean loop;
private float waitTimeAtPoint;
public void update(NPCEntity npc, float deltaTime) {
Vector3d currentTarget = patrolPoints.get(currentPointIndex);
MotionController motion = npc.getMotionController();
if (motion.hasReachedTarget()) {
// Wait at point
currentPointIndex = (currentPointIndex + 1) % patrolPoints.size();
} else {
motion.moveTo(currentTarget);
}
}
}
```
### FleeBehavior
```java
public class FleeBehavior {
private float fleeDistance;
private Entity threat;
public void update(NPCEntity npc, float deltaTime) {
Vector3d awayFromThreat = npc.getPosition()
.subtract(threat.getPosition())
.normalize()
.multiply(fleeDistance);
Vector3d fleeTarget = npc.getPosition().add(awayFromThreat);
npc.getMotionController().sprint();
npc.getMotionController().moveTo(fleeTarget);
}
}
```
## Obstacle Avoidance
```java
public class ObstacleAvoidance {
private float avoidanceRadius;
private float lookAheadDistance;
public Vector3d calculateAvoidanceVector(
NPCEntity npc,
Vector3d desiredDirection
) {
// Cast rays to detect obstacles
List<RaycastHit> obstacles = castAvoidanceRays(npc, desiredDirection);
if (obstacles.isEmpty()) {
return desiredDirection;
}
// Calculate avoidance steering
Vector3d avoidance = Vector3d.ZERO;
for (RaycastHit hit : obstacles) {
Vector3d away = npc.getPosition().subtract(hit.position).normalize();
avoidance = avoidance.add(away);
}
return desiredDirection.add(avoidance.normalize()).normalize();
}
}
```
## Movement Systems
ECS systems that process NPC movement:
```java
// Movement update system
public class NPCMovementSystem implements System {
@Override
public void update(float deltaTime) {
for (NPCEntity npc : npcsWithMovement) {
MotionController motion = npc.getMotionController();
PathFollower path = npc.getPathFollower();
// Update path following
if (path.hasPath()) {
updatePathFollowing(npc, motion, path, deltaTime);
}
// Apply movement
motion.update(deltaTime);
}
}
}
```
## Best Practices
{{< callout type="info" >}}
**Movement Guidelines:**
- Use NavigationConfig appropriate for the NPC type
- Cache paths when possible to avoid frequent recalculation
- Use path smoothing for more natural movement
- Consider entity size when pathfinding
- Implement obstacle avoidance for dynamic environments
{{< /callout >}}
{{< callout type="warning" >}}
**Pathfinding Performance:**
- Limit pathfinding requests per frame
- Use shorter max distances when possible
- Cache frequently used paths
- Consider hierarchical pathfinding for large worlds
{{< /callout >}}

View File

@@ -0,0 +1,365 @@
---
title: Mouvement NPC
type: docs
weight: 4
---
Le système de mouvement NPC gère la navigation, le pathfinding et le contrôle de mouvement pour les NPCs.
**Packages:**
- `com.hypixel.hytale.server.npc.movement`
- `com.hypixel.hytale.server.npc.navigation`
## Motion Controller
Le `MotionController` exécute les commandes de mouvement et gère la locomotion des NPCs.
### Classe MotionController
```java
public class MotionController {
private NPCEntity npc;
private MovementState state;
private float currentSpeed;
private Vector3d targetVelocity;
// Commandes de mouvement
public void moveTo(Vector3d target);
public void moveInDirection(Vector3d direction);
public void stop();
// Contrôle de vitesse
public void setSpeed(float speed);
public void walk();
public void run();
public void sprint();
// Requêtes d'état
public boolean isMoving();
public boolean hasReachedTarget();
public MovementState getState();
}
```
### États de Mouvement
```java
public enum MovementState {
IDLE, // Immobile
WALKING, // Mouvement normal
RUNNING, // Mouvement rapide
SPRINTING, // Vitesse maximum
JUMPING, // En l'air (saut)
FALLING, // En l'air (chute)
SWIMMING, // Dans l'eau
CLIMBING, // Sur échelle/liane
SLIDING // Sur pente
}
```
### Utiliser MotionController
```java
NPCEntity npc = // obtenir le NPC
MotionController motion = npc.getMotionController();
// Se déplacer vers une position
motion.moveTo(targetPosition);
// Définir la vitesse de mouvement
motion.run(); // ou motion.setSpeed(5.0f);
// Vérifier si arrivé
if (motion.hasReachedTarget()) {
// Destination atteinte
}
// Arrêter le mouvement
motion.stop();
```
## Path Follower
Le `PathFollower` suit et parcourt les chemins calculés.
### Classe PathFollower
```java
public class PathFollower {
private List<Vector3d> path;
private int currentIndex;
private float waypointRadius;
private boolean smoothPath;
// Gestion de chemin
public void setPath(List<Vector3d> path);
public void clearPath();
public boolean hasPath();
// Suivi
public Vector3d getNextWaypoint();
public void advanceToNextWaypoint();
public boolean hasReachedWaypoint(Vector3d position);
// Progression
public float getPathProgress(); // 0.0 à 1.0
public int getRemainingWaypoints();
}
```
### Exemple de Suivi de Chemin
```java
PathFollower pathFollower = npc.getPathFollower();
MotionController motion = npc.getMotionController();
// Définir un chemin
pathFollower.setPath(calculatedPath);
// Dans la boucle de mise à jour
if (pathFollower.hasPath()) {
Vector3d nextWaypoint = pathFollower.getNextWaypoint();
// Se déplacer vers le waypoint
motion.moveTo(nextWaypoint);
// Vérifier si atteint
if (pathFollower.hasReachedWaypoint(npc.getPosition())) {
pathFollower.advanceToNextWaypoint();
}
}
```
## Navigation Graph
Le système de navigation utilise le pathfinding A* sur un graphe de navigation.
### Classe NavigationGraph
```java
public class NavigationGraph {
// Trouver un chemin entre deux points
public List<Vector3d> findPath(
Vector3d start,
Vector3d end,
NavigationConfig config
);
// Vérifier si un point est navigable
public boolean isNavigable(Vector3d position);
// Obtenir le point navigable le plus proche
public Vector3d getNearestNavigablePoint(Vector3d position);
}
```
### NavigationConfig
```java
public class NavigationConfig {
private float maxDistance; // Longueur max du chemin
private float stepHeight; // Hauteur max de marche
private float entityWidth; // Largeur de collision entité
private float entityHeight; // Hauteur de collision entité
private boolean canSwim; // Autoriser chemins aquatiques
private boolean canClimb; // Autoriser échelles/lianes
private boolean canOpenDoors; // Autoriser traversée de portes
private Set<String> avoidBlocks; // Blocs à éviter
// Pattern builder
public static NavigationConfig builder()
.maxDistance(100.0f)
.stepHeight(1.0f)
.entityWidth(0.6f)
.entityHeight(1.8f)
.canSwim(false)
.build();
}
```
### Exemple de Pathfinding
```java
NavigationGraph navGraph = world.getNavigationGraph();
NavigationConfig config = NavigationConfig.builder()
.maxDistance(50.0f)
.canSwim(true)
.build();
List<Vector3d> path = navGraph.findPath(
npc.getPosition(),
targetPosition,
config
);
if (path != null && !path.isEmpty()) {
npc.getPathFollower().setPath(path);
}
```
## Comportements de Mouvement
Comportements de mouvement pré-construits pour les patterns courants.
### WanderBehavior
```java
public class WanderBehavior {
private float wanderRadius;
private float minPauseDuration;
private float maxPauseDuration;
public WanderBehavior(float radius) {
this.wanderRadius = radius;
}
public void update(NPCEntity npc, float deltaTime) {
if (!npc.getMotionController().isMoving()) {
Vector3d wanderTarget = calculateWanderTarget(npc);
npc.getMotionController().moveTo(wanderTarget);
}
}
}
```
### FollowBehavior
```java
public class FollowBehavior {
private Entity target;
private float followDistance;
private float catchUpDistance;
public void update(NPCEntity npc, float deltaTime) {
float distance = npc.getPosition().distance(target.getPosition());
if (distance > catchUpDistance) {
npc.getMotionController().run();
} else if (distance > followDistance) {
npc.getMotionController().walk();
npc.getMotionController().moveTo(target.getPosition());
} else {
npc.getMotionController().stop();
}
}
}
```
### PatrolBehavior
```java
public class PatrolBehavior {
private List<Vector3d> patrolPoints;
private int currentPointIndex;
private boolean loop;
private float waitTimeAtPoint;
public void update(NPCEntity npc, float deltaTime) {
Vector3d currentTarget = patrolPoints.get(currentPointIndex);
MotionController motion = npc.getMotionController();
if (motion.hasReachedTarget()) {
// Attendre au point
currentPointIndex = (currentPointIndex + 1) % patrolPoints.size();
} else {
motion.moveTo(currentTarget);
}
}
}
```
### FleeBehavior
```java
public class FleeBehavior {
private float fleeDistance;
private Entity threat;
public void update(NPCEntity npc, float deltaTime) {
Vector3d awayFromThreat = npc.getPosition()
.subtract(threat.getPosition())
.normalize()
.multiply(fleeDistance);
Vector3d fleeTarget = npc.getPosition().add(awayFromThreat);
npc.getMotionController().sprint();
npc.getMotionController().moveTo(fleeTarget);
}
}
```
## Évitement d'Obstacles
```java
public class ObstacleAvoidance {
private float avoidanceRadius;
private float lookAheadDistance;
public Vector3d calculateAvoidanceVector(
NPCEntity npc,
Vector3d desiredDirection
) {
// Lancer des rayons pour détecter les obstacles
List<RaycastHit> obstacles = castAvoidanceRays(npc, desiredDirection);
if (obstacles.isEmpty()) {
return desiredDirection;
}
// Calculer la direction d'évitement
Vector3d avoidance = Vector3d.ZERO;
for (RaycastHit hit : obstacles) {
Vector3d away = npc.getPosition().subtract(hit.position).normalize();
avoidance = avoidance.add(away);
}
return desiredDirection.add(avoidance.normalize()).normalize();
}
}
```
## Systèmes de Mouvement
Systèmes ECS qui traitent le mouvement des NPCs :
```java
// Système de mise à jour de mouvement
public class NPCMovementSystem implements System {
@Override
public void update(float deltaTime) {
for (NPCEntity npc : npcsWithMovement) {
MotionController motion = npc.getMotionController();
PathFollower path = npc.getPathFollower();
// Mettre à jour le suivi de chemin
if (path.hasPath()) {
updatePathFollowing(npc, motion, path, deltaTime);
}
// Appliquer le mouvement
motion.update(deltaTime);
}
}
}
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives de Mouvement :**
- Utilisez un NavigationConfig approprié au type de NPC
- Mettez en cache les chemins quand possible pour éviter les recalculs fréquents
- Utilisez le lissage de chemin pour un mouvement plus naturel
- Considérez la taille de l'entité lors du pathfinding
- Implémentez l'évitement d'obstacles pour les environnements dynamiques
{{< /callout >}}
{{< callout type="warning" >}}
**Performance du Pathfinding :**
- Limitez les requêtes de pathfinding par frame
- Utilisez des distances max plus courtes quand possible
- Mettez en cache les chemins fréquemment utilisés
- Considérez le pathfinding hiérarchique pour les grands mondes
{{< /callout >}}

View File

@@ -0,0 +1,904 @@
---
title: Player API
type: docs
weight: 4
---
The Player class provides extensive APIs for player management, including communication, health, inventory, permissions, and more.
## API Overview
{{< cards cols="3" >}}
{{< card link="#identity--connection" title="Identity & Connection" subtitle="Name, UUID, network" >}}
{{< card link="#communication" title="Communication" subtitle="Messages, titles, sounds" >}}
{{< card link="#health--combat" title="Health & Combat" subtitle="HP, damage, effects" >}}
{{< card link="#inventory-api" title="Inventory" subtitle="Items, slots, armor" >}}
{{< card link="#permissions" title="Permissions" subtitle="Access control" >}}
{{< card link="#movement--teleportation" title="Movement" subtitle="Teleport, position" >}}
{{< /cards >}}
---
## Identity & Connection
### Basic Information
{{< tabs items="Methods,Example" >}}
{{< tab >}}
| Method | Return Type | Description |
|--------|-------------|-------------|
| `getUuid()` | `UUID` | Unique player identifier |
| `getDisplayName()` | `String` | Display name (from PlayerRef username) |
| `getClientViewRadius()` | `int` | Player's view radius in chunks |
| `getViewRadius()` | `int` | Effective view radius (min of client and server) |
| `hasPermission(String)` | `boolean` | Check permission |
| `sendMessage(Message)` | `void` | Send message to player |
| `getInventory()` | `Inventory` | Player's inventory |
| `getGameMode()` | `GameMode` | Current game mode |
{{< /tab >}}
{{< tab >}}
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Access Player component via ref.getStore()
Player player = ref.getStore().getComponent(ref, Player.getComponentType());
UUID uuid = player.getUuid();
String displayName = player.getDisplayName();
int viewRadius = player.getViewRadius();
GameMode mode = player.getGameMode();
// Check permission
if (player.hasPermission("myplugin.admin")) {
player.sendMessage(Message.raw("You are an admin!"));
}
// Get PlayerRef for more info
PlayerRef playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType());
String username = playerRef.getUsername();
String language = playerRef.getLanguage();
```
{{< /tab >}}
{{< /tabs >}}
### PlayerRef (Thread-Safe Reference)
{{< callout type="info" >}}
**Important:** `PlayerRef` is a component that provides thread-safe access to player data. It's stored in the entity component system and manages the player's connection, position, and various managers.
{{< /callout >}}
{{< tabs items="Methods,Entity Reference,Connection & Messaging,Server Transfer" >}}
{{< tab >}}
| Method | Return Type | Description |
|--------|-------------|-------------|
| `getUuid()` | `UUID` | Player's unique identifier |
| `getUsername()` | `String` | Player's username |
| `getLanguage()` | `String` | Player's language setting |
| `setLanguage(String)` | `void` | Update language |
| `getReference()` | `Ref<EntityStore>` | Entity reference (null if not in world) |
| `getHolder()` | `Holder<EntityStore>` | Entity holder (when not in store) |
| `isValid()` | `boolean` | Whether reference or holder exists |
| `getTransform()` | `Transform` | Current position and rotation |
| `getHeadRotation()` | `Vector3f` | Head rotation |
| `getPacketHandler()` | `PacketHandler` | Network handler |
| `getChunkTracker()` | `ChunkTracker` | Chunk loading tracker |
| `getHiddenPlayersManager()` | `HiddenPlayersManager` | Player visibility manager |
{{< /tab >}}
{{< tab >}}
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Get PlayerRef via ref.getStore()
PlayerRef playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType());
// Check if player is valid
if (playerRef != null && playerRef.isValid()) {
UUID uuid = playerRef.getUuid();
String username = playerRef.getUsername();
// Get entity reference
Ref<EntityStore> entityRef = playerRef.getReference();
if (entityRef != null) {
// Player is in a world - access via entityRef.getStore()
Player player = entityRef.getStore().getComponent(entityRef, Player.getComponentType());
}
}
// Get component from PlayerRef (convenience method)
Player playerComponent = playerRef.getComponent(Player.getComponentType());
```
{{< /tab >}}
{{< tab >}}
```java
// Send message to player
playerRef.sendMessage(Message.translation("welcome.message"));
// Get packet handler for more advanced operations
PacketHandler handler = playerRef.getPacketHandler();
// Get player position info
Transform transform = playerRef.getTransform();
Vector3d position = transform.getPosition();
Vector3f rotation = transform.getRotation();
Vector3f headRotation = playerRef.getHeadRotation();
// Update position (called internally by systems)
playerRef.updatePosition(world, transform, headRotation);
```
{{< /tab >}}
{{< tab >}}
```java
// Transfer player to another server (for server networks)
playerRef.referToServer("play.example.com", 25565);
// With custom data payload
byte[] referralData = serializePlayerData(playerRef);
playerRef.referToServer("play.example.com", 25565, referralData);
// Note: Max referral data size is 4096 bytes
```
{{< /tab >}}
{{< /tabs >}}
---
## Communication
### Sending Messages
{{< tabs items="Chat Messages,Translation Messages,Notifications" >}}
{{< tab >}}
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Send chat message via PlayerRef
PlayerRef playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType());
// Simple message
playerRef.sendMessage(Message.raw("Hello, World!"));
// Raw message
playerRef.sendMessage(Message.raw("Plain text message"));
// Using Message interface (IMessageReceiver)
Player player = ref.getStore().getComponent(ref, Player.getComponentType());
player.sendMessage(Message.raw("Message via Player interface"));
```
{{< /tab >}}
{{< tab >}}
```java
// Using translation keys
playerRef.sendMessage(Message.translation("welcome.message"));
// With parameters
playerRef.sendMessage(
Message.translation("greeting.message")
.param("name", playerRef.getUsername())
);
// With color
playerRef.sendMessage(
Message.translation("server.warning")
.color("#ff5555")
);
```
{{< /tab >}}
{{< tab >}}
```java
// Hytale uses Notifications instead of titles/action bars
PacketHandler handler = playerRef.getPacketHandler();
// Simple notification
NotificationUtil.sendNotification(handler, "Welcome to the server!");
// Notification with style
NotificationUtil.sendNotification(handler,
Message.raw("Achievement Unlocked!"),
NotificationStyle.Default
);
// With secondary message
NotificationUtil.sendNotification(handler,
Message.raw("Quest Complete"),
Message.raw("You earned 100 XP"),
NotificationStyle.Default
);
// With icon
NotificationUtil.sendNotification(handler,
Message.raw("New Item"),
"icon_name",
NotificationStyle.Default
);
// With item display
NotificationUtil.sendNotification(handler,
Message.raw("You received:"),
Message.raw("Diamond x10"),
itemWithMetadata,
NotificationStyle.Default
);
// Send to all players in universe
NotificationUtil.sendNotificationToUniverse(
Message.raw("Server restarting in 5 minutes!")
);
```
{{< /tab >}}
{{< /tabs >}}
### Playing Sounds
```java
// Play 3D sound to player
SoundUtil.playSoundEvent3dToPlayer(
playerRef,
TempAssetIdUtil.getSoundEventIndex("SFX_UI_Notification"),
SoundCategory.UI,
position,
componentAccessor
);
// Play 2D sound (no position)
SoundUtil.playSoundEvent2d(
ref,
TempAssetIdUtil.getSoundEventIndex("SFX_Player_Pickup_Item"),
SoundCategory.UI,
componentAccessor
);
// Play sound via world
world.playSound(soundEvent, position, volume, pitch);
```
---
## Health & Stats
{{< callout type="info" >}}
Hytale uses an **EntityStats system** for managing health and other stats. Stats are managed via `EntityStatComponent` and include Health, Oxygen, Stamina, Mana, SignatureEnergy, and Ammo.
{{< /callout >}}
### Entity Stats System
{{< tabs items="Available Stats,Accessing Stats,Modifying Stats" >}}
{{< tab >}}
| Stat | Description |
|------|-------------|
| `Health` | Entity health points |
| `Oxygen` | Breath/oxygen level |
| `Stamina` | Physical stamina |
| `Mana` | Magical energy |
| `SignatureEnergy` | Special ability energy |
| `Ammo` | Ammunition count |
These stats are defined via asset configuration and indexed at runtime.
```java
// Get stat indices (from DefaultEntityStatTypes)
int healthIndex = DefaultEntityStatTypes.getHealth();
int staminaIndex = DefaultEntityStatTypes.getStamina();
int manaIndex = DefaultEntityStatTypes.getMana();
```
{{< /tab >}}
{{< tab >}}
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Get EntityStatComponent from entity via ref.getStore()
EntityStatComponent statsComponent = ref.getStore().getComponent(
ref,
EntityStatComponent.getComponentType()
);
// Get specific stat value
int healthIndex = DefaultEntityStatTypes.getHealth();
float currentHealth = statsComponent.getValue(healthIndex);
float maxHealth = statsComponent.getMaxValue(healthIndex);
// Check percentage
float healthPercent = currentHealth / maxHealth;
```
{{< /tab >}}
{{< tab >}}
```java
// Modify stat values through the stats system
// Stats are typically modified through the EntityStatsModule
// and related systems (DamageSystems, HealSystems, etc.)
// Example: Apply damage via component
// The DamageSystems handle damage application
ref.getStore().putComponent(ref, Damage.getComponentType(),
new Damage(damageAmount, damageSource));
```
{{< /tab >}}
{{< /tabs >}}
### Invulnerability
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Make player invulnerable (adds Invulnerable component)
ref.getStore().putComponent(ref, Invulnerable.getComponentType(), Invulnerable.INSTANCE);
// Remove invulnerability
ref.getStore().tryRemoveComponent(ref, Invulnerable.getComponentType());
// Check invulnerability
boolean isInvulnerable = ref.getStore().getArchetype(ref)
.contains(Invulnerable.getComponentType());
// Note: Creative mode automatically sets Invulnerable
```
### Spawn Protection
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Players have temporary invulnerability after spawning
Player player = ref.getStore().getComponent(ref, Player.getComponentType());
// Check if player has spawn protection (3 seconds after spawn)
if (player.hasSpawnProtection()) {
// Player is temporarily invulnerable
}
// Check if waiting for client ready
if (player.isWaitingForClientReady()) {
// Player hasn't finished loading yet
}
```
---
## Inventory API
### Basic Inventory Operations
{{< tabs items="Get Items,Set Items,Utility Methods" >}}
{{< tab >}}
```java
// Get inventory
PlayerInventory inventory = player.getInventory();
// Get item in hand
ItemStack heldItem = player.getHeldItem();
ItemStack mainHand = player.getMainHandItem();
ItemStack offHand = player.getOffHandItem();
// Get specific slot
ItemStack slot5 = inventory.getItem(5);
// Get armor
ArmorInventory armor = player.getArmor();
ItemStack helmet = armor.getHelmet();
ItemStack chestplate = armor.getChestplate();
ItemStack leggings = armor.getLeggings();
ItemStack boots = armor.getBoots();
```
{{< /tab >}}
{{< tab >}}
```java
// Set held item
ItemStack sword = new ItemStack(ItemTypes.DIAMOND_SWORD);
player.setHeldItem(sword);
// Set specific slot
inventory.setItem(0, new ItemStack(ItemTypes.APPLE, 64));
// Set armor
armor.setHelmet(new ItemStack(ItemTypes.DIAMOND_HELMET));
armor.setChestplate(new ItemStack(ItemTypes.DIAMOND_CHESTPLATE));
armor.setLeggings(new ItemStack(ItemTypes.DIAMOND_LEGGINGS));
armor.setBoots(new ItemStack(ItemTypes.DIAMOND_BOOTS));
// Clear slot
inventory.setItem(5, null);
```
{{< /tab >}}
{{< tab >}}
```java
// Give item (finds empty slot)
ItemStack diamond = new ItemStack(ItemTypes.DIAMOND, 64);
boolean success = player.giveItem(diamond);
if (!success) {
// Inventory full, drop at feet
player.dropItem(diamond);
}
// Check for item
if (inventory.contains(ItemTypes.DIAMOND)) {
getLogger().at(Level.INFO).log("Player has diamonds");
}
// Remove item
inventory.remove(ItemTypes.DIAMOND, 10);
// Clear inventory
inventory.clear();
// Count items
int diamondCount = inventory.countItem(ItemTypes.DIAMOND);
```
{{< /tab >}}
{{< /tabs >}}
### Advanced Inventory
```java
// Open another inventory (chest, custom UI)
Inventory chest = world.getBlockEntity(chestPos).getInventory();
player.openInventory(chest);
// Create custom inventory
Inventory customInv = new Inventory(27, "Shop"); // 27 slots
customInv.setItem(13, new ItemStack(ItemTypes.DIAMOND, 1));
player.openInventory(customInv);
// Close open inventory
player.closeInventory();
// Get cursor item (what player is holding with mouse)
ItemStack cursor = player.getCursorItem();
player.setCursorItem(null);
```
---
## Permissions
### Permission Checking
{{< tabs items="Basic Checks,Permission Levels,Custom Permissions" >}}
{{< tab >}}
```java
// Check single permission
if (player.hasPermission("myplugin.admin")) {
player.sendMessage("You are an admin!");
}
// Check operator status
if (player.isOp()) {
player.sendMessage("You are an operator!");
}
// Multiple permission check
if (player.hasPermission("myplugin.fly") ||
player.hasPermission("myplugin.admin")) {
enableFlight(player);
}
```
{{< /tab >}}
{{< tab >}}
```java
// Permission hierarchy example
public boolean hasMinimumRank(Player player, String rank) {
switch (rank) {
case "owner":
return player.hasPermission("rank.owner");
case "admin":
return player.hasPermission("rank.admin") ||
player.hasPermission("rank.owner");
case "mod":
return player.hasPermission("rank.mod") ||
player.hasPermission("rank.admin") ||
player.hasPermission("rank.owner");
default:
return true;
}
}
```
{{< /tab >}}
{{< tab >}}
```java
// Grant permission
player.addPermission("myplugin.vip");
// Revoke permission
player.removePermission("myplugin.vip");
// Get all permissions
Set<String> permissions = player.getPermissions();
for (String perm : permissions) {
getLogger().at(Level.INFO).log("Has permission: " + perm);
}
// Set operator status
player.setOp(true);
```
{{< /tab >}}
{{< /tabs >}}
---
## Movement & Teleportation
### Position & Movement
{{< tabs items="Position Methods,Movement State,Velocity" >}}
{{< tab >}}
| Method | Return Type | Description |
|--------|-------------|-------------|
| `getPosition()` | `Vector3d` | Current position |
| `setPosition(Vector3d)` | `void` | Set position |
| `getEyePosition()` | `Vector3d` | Eye level position |
| `getRotation()` | `Vector3f` | Look direction |
| `setRotation(Vector3f)` | `void` | Set look direction |
| `getEyeDirection()` | `Vector3d` | Normalized look vector |
{{< /tab >}}
{{< tab >}}
| Method | Return Type | Description |
|--------|-------------|-------------|
| `isOnGround()` | `boolean` | Whether on solid ground |
| `isSprinting()` | `boolean` | Whether sprinting |
| `setSprinting(boolean)` | `void` | Set sprint state |
| `isSneaking()` | `boolean` | Whether sneaking |
| `setSneaking(boolean)` | `void` | Set sneak state |
| `isFlying()` | `boolean` | Whether flying |
| `setFlying(boolean)` | `void` | Set flying state |
| `isSwimming()` | `boolean` | Whether swimming |
{{< /tab >}}
{{< tab >}}
```java
// Get velocity
Vector3d velocity = player.getVelocity();
// Set velocity (launch player)
player.setVelocity(new Vector3d(0, 1.5, 0));
// Add to velocity
player.addVelocity(new Vector3d(1, 0, 0));
// Apply knockback
player.knockback(new Vector3d(-0.5, 0.5, -0.5));
```
{{< /tab >}}
{{< /tabs >}}
### Teleportation
{{< callout type="warning" >}}
Hytale uses a **component-based teleportation system**. You don't call `player.teleport()` directly. Instead, you add a `Teleport` component to the entity, and the system handles the teleportation.
{{< /callout >}}
{{< tabs items="Basic Usage,Cross-World,Options,System Details" >}}
{{< tab >}}
```java
// Get player reference from command context
Ref<EntityStore> playerRef = context.senderAsPlayerRef();
// Teleport to position in same world
Teleport teleport = new Teleport(
new Vector3d(100, 64, 100), // position
new Vector3f(0, 90, 0) // rotation (pitch, yaw, roll)
);
// Add the teleport component to trigger teleportation
playerRef.getStore().putComponent(playerRef, Teleport.getComponentType(), teleport);
// Using Transform
Transform destination = new Transform(position, rotation);
Teleport teleportFromTransform = new Teleport(destination);
```
{{< /tab >}}
{{< tab >}}
```java
// Get player reference from command context
Ref<EntityStore> playerRef = context.senderAsPlayerRef();
// Teleport to another world
World targetWorld = Universe.get().getWorld("other_world");
Teleport teleport = new Teleport(
targetWorld, // target world
new Vector3d(0, 64, 0), // position
new Vector3f(0, 0, 0) // rotation
);
playerRef.getStore().putComponent(playerRef, Teleport.getComponentType(), teleport);
// The TeleportSystems.PlayerMoveSystem handles:
// - Same world: Direct position update
// - Different world: Player removal and re-addition to target world
```
{{< /tab >}}
{{< tab >}}
```java
// Teleport with options using fluent builder
Teleport teleport = new Teleport(targetWorld, position, rotation)
.withHeadRotation(new Vector3f(0, 45, 0)) // Set head rotation
.withResetRoll() // Reset roll to 0
.withoutVelocityReset(); // Keep current velocity
// By default:
// - resetVelocity = true (player stops moving)
// - headRotation = same as body rotation
```
{{< /tab >}}
{{< tab >}}
The teleportation flow:
1. **Add Teleport component** to entity
2. **TeleportSystems.PlayerMoveSystem** detects the component
3. System sends `ClientTeleport` packet to player
4. **PendingTeleport** component tracks the teleport
5. When client confirms, position is updated
6. **Teleport component is removed** automatically
```java
// Internal packet sent:
ClientTeleport packet = new ClientTeleport(
teleportId,
new ModelTransform(position, direction, headRotation),
resetVelocity
);
```
{{< /tab >}}
{{< /tabs >}}
---
## Game Mode
{{< callout type="info" >}}
Hytale currently has **two game modes**: `Adventure` and `Creative`. These differ from Minecraft's game modes.
{{< /callout >}}
{{< tabs items="Available Modes,Getting/Setting,Creative Features" >}}
{{< tab >}}
| Mode | Value | Description |
|------|-------|-------------|
| `GameMode.Adventure` | 0 | Standard gameplay with survival mechanics |
| `GameMode.Creative` | 1 | Creative mode with building freedom |
{{< /tab >}}
{{< tab >}}
```java
// Get current game mode
GameMode mode = player.getGameMode();
// Check game mode
if (mode == GameMode.Creative) {
// Creative mode logic
} else if (mode == GameMode.Adventure) {
// Adventure mode logic
}
// Set game mode (requires component accessor)
// Note: This is typically done through systems/commands
Player.setGameMode(playerRef, GameMode.Creative, componentAccessor);
```
{{< /tab >}}
{{< tab >}}
When in Creative mode:
- Player is marked as `Invulnerable`
- `canFly` is enabled in MovementManager
- `canDecreaseItemStackDurability` returns false
- `canApplyItemStackPenalties` returns false
- Permission groups from GameModeType are applied
```java
// GameMode affects these behaviors automatically:
// - Flight enabled/disabled
// - Invulnerability
// - Item durability loss
// - Block placement restrictions
```
{{< /tab >}}
{{< /tabs >}}
---
## Server Actions
### Disconnecting Players
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Disconnect player via PacketHandler
PlayerRef playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType());
// Disconnect with reason
playerRef.getPacketHandler().disconnect("You have been kicked!");
// Disconnect with translation message
playerRef.getPacketHandler().disconnect(
Message.translation("server.kick.reason").toString()
);
```
### Player Lookup
```java
// Get all online players through Universe
Universe universe = Universe.get();
// Players are accessed through world contexts
World world = universe.getWorld("main");
// Iterate players in world via entity store
// Players are entities with PlayerRef component
world.getEntityStore().forEach((ref, store) -> {
PlayerRef playerRef = store.getComponent(ref, PlayerRef.getComponentType());
if (playerRef != null) {
String username = playerRef.getUsername();
UUID uuid = playerRef.getUuid();
// Process player...
}
});
```
### Server Transfer
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Transfer player to another server (for server networks)
PlayerRef playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType());
// Redirect player to another server
playerRef.referToServer("play.example.com", 25565);
// With custom data (max 4096 bytes)
byte[] playerData = serializePlayerState(playerRef);
playerRef.referToServer("play.example.com", 25565, playerData);
```
---
## Practical Examples
### Welcome Plugin
```java
public class WelcomePlugin extends ServerPlugin {
@Override
public void setup(PluginSetup pluginSetup) {
// Register event listeners
}
@Override
public void start() {
// Subscribe to player connect event
HytaleServer.get().getEventBus()
.subscribe(PlayerConnectEvent.class, this::onPlayerConnect);
}
private void onPlayerConnect(PlayerConnectEvent event) {
String username = event.getUsername();
UUID uuid = event.getUuid();
getLogger().at(Level.INFO).log("Player connected: " + username + " (" + uuid + ")");
// Send welcome message after player is ready
// Note: PlayerReadyEvent has String key, so use registerGlobal
getEventRegistry().registerGlobal(PlayerReadyEvent.class, readyEvent -> {
Player player = readyEvent.getPlayer();
player.sendMessage(Message.raw("Welcome to the server, " + username + "!"));
});
}
@Override
public void shutdown() {
// Cleanup
}
}
```
### Permission Check Example
```java
public class PermissionPlugin extends ServerPlugin {
@Override
public void start() {
CommandRegistry registry = getCommandRegistry();
registry.register("admin", context -> {
CommandSender sender = context.getSource();
// Check permission
if (sender instanceof Player player) {
if (!player.hasPermission("myplugin.admin")) {
player.sendMessage(Message.raw("No permission!"));
return;
}
player.sendMessage(Message.raw("You are an admin!"));
}
});
}
}
```
### Inventory Management
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Working with player inventory
Player player = ref.getStore().getComponent(ref, Player.getComponentType());
Inventory inventory = player.getInventory();
// Add item to inventory
ItemStack itemStack = new ItemStack(itemType, amount);
inventory.addItem(itemStack);
// Send inventory update to client
player.sendInventory();
// Access hotbar
HotbarManager hotbar = player.getHotbarManager();
```
---
## Best Practices
{{< callout type="info" >}}
**Player API Guidelines:**
- Use `PlayerRef` to get thread-safe player information
- Check `isValid()` before operations on stored references
- Use permissions via `player.hasPermission()` for access control
- Understand the component system: players are entities with components
{{< /callout >}}
{{< callout type="warning" >}}
**Component System:** Players in Hytale are entities managed by an ECS. Access player data via `ref.getStore()` and appropriate component types (`Player.getComponentType()`, `PlayerRef.getComponentType()`).
{{< /callout >}}
{{< callout type="error" >}}
**Critical:** Never store `Ref<EntityStore>` or `Player` references directly in static fields. Use `UUID` for storage and retrieve references via the component system when needed.
{{< /callout >}}

View File

@@ -0,0 +1,904 @@
---
title: Player API
type: docs
weight: 4
---
The Player class provides extensive APIs for player management, including communication, health, inventory, permissions, and more.
## API Overview
{{< cards cols="3" >}}
{{< card link="#identity--connection" title="Identity & Connection" subtitle="Name, UUID, network" >}}
{{< card link="#communication" title="Communication" subtitle="Messages, titles, sounds" >}}
{{< card link="#health--combat" title="Health & Combat" subtitle="HP, damage, effects" >}}
{{< card link="#inventory-api" title="Inventory" subtitle="Items, slots, armor" >}}
{{< card link="#permissions" title="Permissions" subtitle="Access control" >}}
{{< card link="#movement--teleportation" title="Movement" subtitle="Teleport, position" >}}
{{< /cards >}}
---
## Identity & Connection
### Basic Information
{{< tabs items="Methods,Example" >}}
{{< tab >}}
| Method | Return Type | Description |
|--------|-------------|-------------|
| `getUuid()` | `UUID` | Unique player identifier |
| `getDisplayName()` | `String` | Display name (from PlayerRef username) |
| `getClientViewRadius()` | `int` | Player's view radius in chunks |
| `getViewRadius()` | `int` | Effective view radius (min of client and server) |
| `hasPermission(String)` | `boolean` | Check permission |
| `sendMessage(Message)` | `void` | Send message to player |
| `getInventory()` | `Inventory` | Player's inventory |
| `getGameMode()` | `GameMode` | Current game mode |
{{< /tab >}}
{{< tab >}}
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Access Player component via ref.getStore()
Player player = ref.getStore().getComponent(ref, Player.getComponentType());
UUID uuid = player.getUuid();
String displayName = player.getDisplayName();
int viewRadius = player.getViewRadius();
GameMode mode = player.getGameMode();
// Check permission
if (player.hasPermission("myplugin.admin")) {
player.sendMessage(Message.raw("You are an admin!"));
}
// Get PlayerRef for more info
PlayerRef playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType());
String username = playerRef.getUsername();
String language = playerRef.getLanguage();
```
{{< /tab >}}
{{< /tabs >}}
### PlayerRef (Thread-Safe Reference)
{{< callout type="info" >}}
**Important:** `PlayerRef` is a component that provides thread-safe access to player data. It's stored in the entity component system and manages the player's connection, position, and various managers.
{{< /callout >}}
{{< tabs items="Methods,Entity Reference,Connection & Messaging,Server Transfer" >}}
{{< tab >}}
| Method | Return Type | Description |
|--------|-------------|-------------|
| `getUuid()` | `UUID` | Player's unique identifier |
| `getUsername()` | `String` | Player's username |
| `getLanguage()` | `String` | Player's language setting |
| `setLanguage(String)` | `void` | Update language |
| `getReference()` | `Ref<EntityStore>` | Entity reference (null if not in world) |
| `getHolder()` | `Holder<EntityStore>` | Entity holder (when not in store) |
| `isValid()` | `boolean` | Whether reference or holder exists |
| `getTransform()` | `Transform` | Current position and rotation |
| `getHeadRotation()` | `Vector3f` | Head rotation |
| `getPacketHandler()` | `PacketHandler` | Network handler |
| `getChunkTracker()` | `ChunkTracker` | Chunk loading tracker |
| `getHiddenPlayersManager()` | `HiddenPlayersManager` | Player visibility manager |
{{< /tab >}}
{{< tab >}}
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Get PlayerRef via ref.getStore()
PlayerRef playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType());
// Check if player is valid
if (playerRef != null && playerRef.isValid()) {
UUID uuid = playerRef.getUuid();
String username = playerRef.getUsername();
// Get entity reference
Ref<EntityStore> entityRef = playerRef.getReference();
if (entityRef != null) {
// Player is in a world - access via entityRef.getStore()
Player player = entityRef.getStore().getComponent(entityRef, Player.getComponentType());
}
}
// Get component from PlayerRef (convenience method)
Player playerComponent = playerRef.getComponent(Player.getComponentType());
```
{{< /tab >}}
{{< tab >}}
```java
// Send message to player
playerRef.sendMessage(Message.translation("welcome.message"));
// Get packet handler for more advanced operations
PacketHandler handler = playerRef.getPacketHandler();
// Get player position info
Transform transform = playerRef.getTransform();
Vector3d position = transform.getPosition();
Vector3f rotation = transform.getRotation();
Vector3f headRotation = playerRef.getHeadRotation();
// Update position (called internally by systems)
playerRef.updatePosition(world, transform, headRotation);
```
{{< /tab >}}
{{< tab >}}
```java
// Transfer player to another server (for server networks)
playerRef.referToServer("play.example.com", 25565);
// With custom data payload
byte[] referralData = serializePlayerData(playerRef);
playerRef.referToServer("play.example.com", 25565, referralData);
// Note: Max referral data size is 4096 bytes
```
{{< /tab >}}
{{< /tabs >}}
---
## Communication
### Sending Messages
{{< tabs items="Chat Messages,Translation Messages,Notifications" >}}
{{< tab >}}
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Send chat message via PlayerRef
PlayerRef playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType());
// Simple message
playerRef.sendMessage(Message.raw("Hello, World!"));
// Raw message
playerRef.sendMessage(Message.raw("Plain text message"));
// Using Message interface (IMessageReceiver)
Player player = ref.getStore().getComponent(ref, Player.getComponentType());
player.sendMessage(Message.raw("Message via Player interface"));
```
{{< /tab >}}
{{< tab >}}
```java
// Using translation keys
playerRef.sendMessage(Message.translation("welcome.message"));
// With parameters
playerRef.sendMessage(
Message.translation("greeting.message")
.param("name", playerRef.getUsername())
);
// With color
playerRef.sendMessage(
Message.translation("server.warning")
.color("#ff5555")
);
```
{{< /tab >}}
{{< tab >}}
```java
// Hytale uses Notifications instead of titles/action bars
PacketHandler handler = playerRef.getPacketHandler();
// Simple notification
NotificationUtil.sendNotification(handler, "Welcome to the server!");
// Notification with style
NotificationUtil.sendNotification(handler,
Message.raw("Achievement Unlocked!"),
NotificationStyle.Default
);
// With secondary message
NotificationUtil.sendNotification(handler,
Message.raw("Quest Complete"),
Message.raw("You earned 100 XP"),
NotificationStyle.Default
);
// With icon
NotificationUtil.sendNotification(handler,
Message.raw("New Item"),
"icon_name",
NotificationStyle.Default
);
// With item display
NotificationUtil.sendNotification(handler,
Message.raw("You received:"),
Message.raw("Diamond x10"),
itemWithMetadata,
NotificationStyle.Default
);
// Send to all players in universe
NotificationUtil.sendNotificationToUniverse(
Message.raw("Server restarting in 5 minutes!")
);
```
{{< /tab >}}
{{< /tabs >}}
### Playing Sounds
```java
// Play 3D sound to player
SoundUtil.playSoundEvent3dToPlayer(
playerRef,
TempAssetIdUtil.getSoundEventIndex("SFX_UI_Notification"),
SoundCategory.UI,
position,
componentAccessor
);
// Play 2D sound (no position)
SoundUtil.playSoundEvent2d(
ref,
TempAssetIdUtil.getSoundEventIndex("SFX_Player_Pickup_Item"),
SoundCategory.UI,
componentAccessor
);
// Play sound via world
world.playSound(soundEvent, position, volume, pitch);
```
---
## Health & Stats
{{< callout type="info" >}}
Hytale uses an **EntityStats system** for managing health and other stats. Stats are managed via `EntityStatComponent` and include Health, Oxygen, Stamina, Mana, SignatureEnergy, and Ammo.
{{< /callout >}}
### Entity Stats System
{{< tabs items="Available Stats,Accessing Stats,Modifying Stats" >}}
{{< tab >}}
| Stat | Description |
|------|-------------|
| `Health` | Entity health points |
| `Oxygen` | Breath/oxygen level |
| `Stamina` | Physical stamina |
| `Mana` | Magical energy |
| `SignatureEnergy` | Special ability energy |
| `Ammo` | Ammunition count |
These stats are defined via asset configuration and indexed at runtime.
```java
// Get stat indices (from DefaultEntityStatTypes)
int healthIndex = DefaultEntityStatTypes.getHealth();
int staminaIndex = DefaultEntityStatTypes.getStamina();
int manaIndex = DefaultEntityStatTypes.getMana();
```
{{< /tab >}}
{{< tab >}}
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Get EntityStatComponent from entity via ref.getStore()
EntityStatComponent statsComponent = ref.getStore().getComponent(
ref,
EntityStatComponent.getComponentType()
);
// Get specific stat value
int healthIndex = DefaultEntityStatTypes.getHealth();
float currentHealth = statsComponent.getValue(healthIndex);
float maxHealth = statsComponent.getMaxValue(healthIndex);
// Check percentage
float healthPercent = currentHealth / maxHealth;
```
{{< /tab >}}
{{< tab >}}
```java
// Modify stat values through the stats system
// Stats are typically modified through the EntityStatsModule
// and related systems (DamageSystems, HealSystems, etc.)
// Example: Apply damage via component
// The DamageSystems handle damage application
ref.getStore().putComponent(ref, Damage.getComponentType(),
new Damage(damageAmount, damageSource));
```
{{< /tab >}}
{{< /tabs >}}
### Invulnerability
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Make player invulnerable (adds Invulnerable component)
ref.getStore().putComponent(ref, Invulnerable.getComponentType(), Invulnerable.INSTANCE);
// Remove invulnerability
ref.getStore().tryRemoveComponent(ref, Invulnerable.getComponentType());
// Check invulnerability
boolean isInvulnerable = ref.getStore().getArchetype(ref)
.contains(Invulnerable.getComponentType());
// Note: Creative mode automatically sets Invulnerable
```
### Spawn Protection
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Players have temporary invulnerability after spawning
Player player = ref.getStore().getComponent(ref, Player.getComponentType());
// Check if player has spawn protection (3 seconds after spawn)
if (player.hasSpawnProtection()) {
// Player is temporarily invulnerable
}
// Check if waiting for client ready
if (player.isWaitingForClientReady()) {
// Player hasn't finished loading yet
}
```
---
## Inventory API
### Basic Inventory Operations
{{< tabs items="Get Items,Set Items,Utility Methods" >}}
{{< tab >}}
```java
// Get inventory
PlayerInventory inventory = player.getInventory();
// Get item in hand
ItemStack heldItem = player.getHeldItem();
ItemStack mainHand = player.getMainHandItem();
ItemStack offHand = player.getOffHandItem();
// Get specific slot
ItemStack slot5 = inventory.getItem(5);
// Get armor
ArmorInventory armor = player.getArmor();
ItemStack helmet = armor.getHelmet();
ItemStack chestplate = armor.getChestplate();
ItemStack leggings = armor.getLeggings();
ItemStack boots = armor.getBoots();
```
{{< /tab >}}
{{< tab >}}
```java
// Set held item
ItemStack sword = new ItemStack(ItemTypes.DIAMOND_SWORD);
player.setHeldItem(sword);
// Set specific slot
inventory.setItem(0, new ItemStack(ItemTypes.APPLE, 64));
// Set armor
armor.setHelmet(new ItemStack(ItemTypes.DIAMOND_HELMET));
armor.setChestplate(new ItemStack(ItemTypes.DIAMOND_CHESTPLATE));
armor.setLeggings(new ItemStack(ItemTypes.DIAMOND_LEGGINGS));
armor.setBoots(new ItemStack(ItemTypes.DIAMOND_BOOTS));
// Clear slot
inventory.setItem(5, null);
```
{{< /tab >}}
{{< tab >}}
```java
// Give item (finds empty slot)
ItemStack diamond = new ItemStack(ItemTypes.DIAMOND, 64);
boolean success = player.giveItem(diamond);
if (!success) {
// Inventory full, drop at feet
player.dropItem(diamond);
}
// Check for item
if (inventory.contains(ItemTypes.DIAMOND)) {
getLogger().at(Level.INFO).log("Player has diamonds");
}
// Remove item
inventory.remove(ItemTypes.DIAMOND, 10);
// Clear inventory
inventory.clear();
// Count items
int diamondCount = inventory.countItem(ItemTypes.DIAMOND);
```
{{< /tab >}}
{{< /tabs >}}
### Advanced Inventory
```java
// Open another inventory (chest, custom UI)
Inventory chest = world.getBlockEntity(chestPos).getInventory();
player.openInventory(chest);
// Create custom inventory
Inventory customInv = new Inventory(27, "Shop"); // 27 slots
customInv.setItem(13, new ItemStack(ItemTypes.DIAMOND, 1));
player.openInventory(customInv);
// Close open inventory
player.closeInventory();
// Get cursor item (what player is holding with mouse)
ItemStack cursor = player.getCursorItem();
player.setCursorItem(null);
```
---
## Permissions
### Permission Checking
{{< tabs items="Basic Checks,Permission Levels,Custom Permissions" >}}
{{< tab >}}
```java
// Check single permission
if (player.hasPermission("myplugin.admin")) {
player.sendMessage("You are an admin!");
}
// Check operator status
if (player.isOp()) {
player.sendMessage("You are an operator!");
}
// Multiple permission check
if (player.hasPermission("myplugin.fly") ||
player.hasPermission("myplugin.admin")) {
enableFlight(player);
}
```
{{< /tab >}}
{{< tab >}}
```java
// Permission hierarchy example
public boolean hasMinimumRank(Player player, String rank) {
switch (rank) {
case "owner":
return player.hasPermission("rank.owner");
case "admin":
return player.hasPermission("rank.admin") ||
player.hasPermission("rank.owner");
case "mod":
return player.hasPermission("rank.mod") ||
player.hasPermission("rank.admin") ||
player.hasPermission("rank.owner");
default:
return true;
}
}
```
{{< /tab >}}
{{< tab >}}
```java
// Grant permission
player.addPermission("myplugin.vip");
// Revoke permission
player.removePermission("myplugin.vip");
// Get all permissions
Set<String> permissions = player.getPermissions();
for (String perm : permissions) {
getLogger().at(Level.INFO).log("Has permission: " + perm);
}
// Set operator status
player.setOp(true);
```
{{< /tab >}}
{{< /tabs >}}
---
## Movement & Teleportation
### Position & Movement
{{< tabs items="Position Methods,Movement State,Velocity" >}}
{{< tab >}}
| Method | Return Type | Description |
|--------|-------------|-------------|
| `getPosition()` | `Vector3d` | Current position |
| `setPosition(Vector3d)` | `void` | Set position |
| `getEyePosition()` | `Vector3d` | Eye level position |
| `getRotation()` | `Vector3f` | Look direction |
| `setRotation(Vector3f)` | `void` | Set look direction |
| `getEyeDirection()` | `Vector3d` | Normalized look vector |
{{< /tab >}}
{{< tab >}}
| Method | Return Type | Description |
|--------|-------------|-------------|
| `isOnGround()` | `boolean` | Whether on solid ground |
| `isSprinting()` | `boolean` | Whether sprinting |
| `setSprinting(boolean)` | `void` | Set sprint state |
| `isSneaking()` | `boolean` | Whether sneaking |
| `setSneaking(boolean)` | `void` | Set sneak state |
| `isFlying()` | `boolean` | Whether flying |
| `setFlying(boolean)` | `void` | Set flying state |
| `isSwimming()` | `boolean` | Whether swimming |
{{< /tab >}}
{{< tab >}}
```java
// Get velocity
Vector3d velocity = player.getVelocity();
// Set velocity (launch player)
player.setVelocity(new Vector3d(0, 1.5, 0));
// Add to velocity
player.addVelocity(new Vector3d(1, 0, 0));
// Apply knockback
player.knockback(new Vector3d(-0.5, 0.5, -0.5));
```
{{< /tab >}}
{{< /tabs >}}
### Teleportation
{{< callout type="warning" >}}
Hytale uses a **component-based teleportation system**. You don't call `player.teleport()` directly. Instead, you add a `Teleport` component to the entity, and the system handles the teleportation.
{{< /callout >}}
{{< tabs items="Basic Usage,Cross-World,Options,System Details" >}}
{{< tab >}}
```java
// Get player reference from command context
Ref<EntityStore> playerRef = context.senderAsPlayerRef();
// Teleport to position in same world
Teleport teleport = new Teleport(
new Vector3d(100, 64, 100), // position
new Vector3f(0, 90, 0) // rotation (pitch, yaw, roll)
);
// Add the teleport component to trigger teleportation
playerRef.getStore().putComponent(playerRef, Teleport.getComponentType(), teleport);
// Using Transform
Transform destination = new Transform(position, rotation);
Teleport teleportFromTransform = new Teleport(destination);
```
{{< /tab >}}
{{< tab >}}
```java
// Get player reference from command context
Ref<EntityStore> playerRef = context.senderAsPlayerRef();
// Teleport to another world
World targetWorld = Universe.get().getWorld("other_world");
Teleport teleport = new Teleport(
targetWorld, // target world
new Vector3d(0, 64, 0), // position
new Vector3f(0, 0, 0) // rotation
);
playerRef.getStore().putComponent(playerRef, Teleport.getComponentType(), teleport);
// The TeleportSystems.PlayerMoveSystem handles:
// - Same world: Direct position update
// - Different world: Player removal and re-addition to target world
```
{{< /tab >}}
{{< tab >}}
```java
// Teleport with options using fluent builder
Teleport teleport = new Teleport(targetWorld, position, rotation)
.withHeadRotation(new Vector3f(0, 45, 0)) // Set head rotation
.withResetRoll() // Reset roll to 0
.withoutVelocityReset(); // Keep current velocity
// By default:
// - resetVelocity = true (player stops moving)
// - headRotation = same as body rotation
```
{{< /tab >}}
{{< tab >}}
The teleportation flow:
1. **Add Teleport component** to entity
2. **TeleportSystems.PlayerMoveSystem** detects the component
3. System sends `ClientTeleport` packet to player
4. **PendingTeleport** component tracks the teleport
5. When client confirms, position is updated
6. **Teleport component is removed** automatically
```java
// Internal packet sent:
ClientTeleport packet = new ClientTeleport(
teleportId,
new ModelTransform(position, direction, headRotation),
resetVelocity
);
```
{{< /tab >}}
{{< /tabs >}}
---
## Game Mode
{{< callout type="info" >}}
Hytale currently has **two game modes**: `Adventure` and `Creative`. These differ from Minecraft's game modes.
{{< /callout >}}
{{< tabs items="Available Modes,Getting/Setting,Creative Features" >}}
{{< tab >}}
| Mode | Value | Description |
|------|-------|-------------|
| `GameMode.Adventure` | 0 | Standard gameplay with survival mechanics |
| `GameMode.Creative` | 1 | Creative mode with building freedom |
{{< /tab >}}
{{< tab >}}
```java
// Get current game mode
GameMode mode = player.getGameMode();
// Check game mode
if (mode == GameMode.Creative) {
// Creative mode logic
} else if (mode == GameMode.Adventure) {
// Adventure mode logic
}
// Set game mode (requires component accessor)
// Note: This is typically done through systems/commands
Player.setGameMode(playerRef, GameMode.Creative, componentAccessor);
```
{{< /tab >}}
{{< tab >}}
When in Creative mode:
- Player is marked as `Invulnerable`
- `canFly` is enabled in MovementManager
- `canDecreaseItemStackDurability` returns false
- `canApplyItemStackPenalties` returns false
- Permission groups from GameModeType are applied
```java
// GameMode affects these behaviors automatically:
// - Flight enabled/disabled
// - Invulnerability
// - Item durability loss
// - Block placement restrictions
```
{{< /tab >}}
{{< /tabs >}}
---
## Server Actions
### Disconnecting Players
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Disconnect player via PacketHandler
PlayerRef playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType());
// Disconnect with reason
playerRef.getPacketHandler().disconnect("You have been kicked!");
// Disconnect with translation message
playerRef.getPacketHandler().disconnect(
Message.translation("server.kick.reason").toString()
);
```
### Player Lookup
```java
// Get all online players through Universe
Universe universe = Universe.get();
// Players are accessed through world contexts
World world = universe.getWorld("main");
// Iterate players in world via entity store
// Players are entities with PlayerRef component
world.getEntityStore().forEach((ref, store) -> {
PlayerRef playerRef = store.getComponent(ref, PlayerRef.getComponentType());
if (playerRef != null) {
String username = playerRef.getUsername();
UUID uuid = playerRef.getUuid();
// Process player...
}
});
```
### Server Transfer
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Transfer player to another server (for server networks)
PlayerRef playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType());
// Redirect player to another server
playerRef.referToServer("play.example.com", 25565);
// With custom data (max 4096 bytes)
byte[] playerData = serializePlayerState(playerRef);
playerRef.referToServer("play.example.com", 25565, playerData);
```
---
## Practical Examples
### Welcome Plugin
```java
public class WelcomePlugin extends ServerPlugin {
@Override
public void setup(PluginSetup pluginSetup) {
// Register event listeners
}
@Override
public void start() {
// Subscribe to player connect event
HytaleServer.get().getEventBus()
.subscribe(PlayerConnectEvent.class, this::onPlayerConnect);
}
private void onPlayerConnect(PlayerConnectEvent event) {
String username = event.getUsername();
UUID uuid = event.getUuid();
getLogger().at(Level.INFO).log("Player connected: " + username + " (" + uuid + ")");
// Send welcome message after player is ready
// Note: PlayerReadyEvent has String key, so use registerGlobal
getEventRegistry().registerGlobal(PlayerReadyEvent.class, readyEvent -> {
Player player = readyEvent.getPlayer();
player.sendMessage(Message.raw("Welcome to the server, " + username + "!"));
});
}
@Override
public void shutdown() {
// Cleanup
}
}
```
### Permission Check Example
```java
public class PermissionPlugin extends ServerPlugin {
@Override
public void start() {
CommandRegistry registry = getCommandRegistry();
registry.register("admin", context -> {
CommandSender sender = context.getSource();
// Check permission
if (sender instanceof Player player) {
if (!player.hasPermission("myplugin.admin")) {
player.sendMessage(Message.raw("No permission!"));
return;
}
player.sendMessage(Message.raw("You are an admin!"));
}
});
}
}
```
### Inventory Management
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Working with player inventory
Player player = ref.getStore().getComponent(ref, Player.getComponentType());
Inventory inventory = player.getInventory();
// Add item to inventory
ItemStack itemStack = new ItemStack(itemType, amount);
inventory.addItem(itemStack);
// Send inventory update to client
player.sendInventory();
// Access hotbar
HotbarManager hotbar = player.getHotbarManager();
```
---
## Best Practices
{{< callout type="info" >}}
**Player API Guidelines:**
- Use `PlayerRef` to get thread-safe player information
- Check `isValid()` before operations on stored references
- Use permissions via `player.hasPermission()` for access control
- Understand the component system: players are entities with components
{{< /callout >}}
{{< callout type="warning" >}}
**Component System:** Players in Hytale are entities managed by an ECS. Access player data via `ref.getStore()` and appropriate component types (`Player.getComponentType()`, `PlayerRef.getComponentType()`).
{{< /callout >}}
{{< callout type="error" >}}
**Critical:** Never store `Ref<EntityStore>` or `Player` references directly in static fields. Use `UUID` for storage and retrieve references via the component system when needed.
{{< /callout >}}

View File

@@ -0,0 +1,266 @@
---
title: Spawning Entities
type: docs
weight: 3
---
Create and spawn entities into the world by registering entity classes and using the World API.
## Registering Entities
Before spawning entities, register them with the EntityRegistry in your plugin's `start()` method:
```java
@Override
public void start() {
// Register entity with key, class, constructor, and codec
getEntityRegistry().registerEntity(
"my_custom_entity",
CustomEntity.class,
world -> new CustomEntity(world),
CustomEntity.CODEC
);
}
```
### Registration Parameters
| Parameter | Type | Description |
|-----------|------|-------------|
| key | `String` | Unique identifier for the entity type |
| clazz | `Class<T>` | The entity class |
| constructor | `Function<World, T>` | Factory function that creates entity instances |
| codec | `DirectDecodeCodec<T>` | Serialization codec for persistence |
## Entity Base Classes
Hytale provides two main entity base classes:
### Entity
Base class for all entities. Implements `Component<EntityStore>`.
```java
public class SimpleEntity extends Entity {
public static final DirectDecodeCodec<SimpleEntity> CODEC =
Entity.CODEC.extend(SimpleEntity.class, SimpleEntity::new);
public SimpleEntity() {
super();
}
public SimpleEntity(World world) {
super(world);
}
}
```
### LivingEntity
Extended base class for entities with health, inventory, and stats.
```java
public class CustomMob extends LivingEntity {
public static final DirectDecodeCodec<CustomMob> CODEC =
LivingEntity.CODEC.extend(CustomMob.class, CustomMob::new);
public CustomMob() {
super();
}
public CustomMob(World world) {
super(world);
}
@Override
protected Inventory createDefaultInventory() {
return new Inventory(36); // 36 slots
}
}
```
## Spawning Entities
Use the World API to spawn registered entities:
```java
// Get the world
World world = player.getWorld();
// Create entity instance
CustomMob mob = new CustomMob(world);
// Define spawn position and rotation
Vector3d position = new Vector3d(100, 64, 200);
Vector3f rotation = new Vector3f(0, 90, 0); // Facing east
// Spawn the entity (deprecated but functional)
world.spawnEntity(mob, position, rotation);
```
### addEntity Method
For more control over spawn reasons, use `addEntity`:
```java
// AddReason options: SPAWN, LOAD, TRANSFER
world.addEntity(mob, position, rotation, AddReason.SPAWN);
```
{{< callout type="warning" >}}
**Deprecation Notice:** Both `spawnEntity` and `addEntity` methods on World are deprecated. The modern approach uses the component-based EntityStore system directly. However, these methods still work and are the simplest way to spawn entities.
{{< /callout >}}
## Entity Lifecycle
### Loading into World
When an entity is added to a world:
```java
// Called automatically when entity is added
entity.loadIntoWorld(world);
// Entity receives network ID
int networkId = entity.getNetworkId();
// Reference is created for component access
Ref<EntityStore> ref = entity.getReference();
```
### Removing Entities
```java
// Remove an entity from the world
boolean removed = entity.remove();
// Check if entity was already removed
if (entity.wasRemoved()) {
// Entity no longer valid
}
```
## Entity Events
### EntityRemoveEvent
Listen for entity removal:
```java
getEventRegistry().register(EntityRemoveEvent.class, event -> {
Entity entity = event.getEntity();
getLogger().at(Level.INFO).log("Entity removed: " + entity.getClass().getSimpleName());
});
```
### LivingEntityInventoryChangeEvent
Track inventory changes on living entities:
```java
getEventRegistry().register(LivingEntityInventoryChangeEvent.class, event -> {
LivingEntity entity = event.getEntity();
// Handle inventory change
});
```
{{< callout type="info" >}}
**No Spawn Event:** Hytale does not have an EntitySpawnEvent. To track spawns, consider implementing custom logic in your entity constructor or using the component system's add callbacks.
{{< /callout >}}
## Complete Example
```java
public class MyPlugin extends JavaPlugin {
@Override
public void start() {
// Register custom entity
getEntityRegistry().registerEntity(
"boss_monster",
BossMonster.class,
world -> new BossMonster(world),
BossMonster.CODEC
);
// Listen for removal
getEventRegistry().register(EntityRemoveEvent.class, this::onEntityRemove);
}
private void onEntityRemove(EntityRemoveEvent event) {
if (event.getEntity() instanceof BossMonster) {
getLogger().at(Level.INFO).log("Boss monster was defeated!");
}
}
public void spawnBoss(Player player) {
World world = player.getWorld();
// Create boss at player's location
BossMonster boss = new BossMonster(world);
// Get player position and add offset
Vector3d spawnPos = player.getTransformComponent().getPosition();
spawnPos = new Vector3d(spawnPos.x + 5, spawnPos.y, spawnPos.z);
// Face the player
Vector3f rotation = new Vector3f(0, 180, 0);
// Spawn
world.spawnEntity(boss, spawnPos, rotation);
}
}
public class BossMonster extends LivingEntity {
public static final DirectDecodeCodec<BossMonster> CODEC =
LivingEntity.CODEC.extend(BossMonster.class, BossMonster::new);
public BossMonster() {
super();
}
public BossMonster(World world) {
super(world);
}
@Override
protected Inventory createDefaultInventory() {
return new Inventory(9); // Small inventory
}
@Override
public boolean isCollidable() {
return true;
}
}
```
## Thread Safety
{{< callout type="error" >}}
**Important:** Entity spawning should be done on the world's ticking thread. If calling from an async context, use the world's executor:
{{< /callout >}}
```java
// Safe spawning from async context
world.execute(() -> {
world.spawnEntity(entity, position, rotation);
});
```
## Spawn Validation
The world validates spawn positions:
```java
// Position requirements:
// - X and Z must be within +/- 33554432
// - Y must be >= -32
// Invalid positions will throw IllegalArgumentException
try {
world.spawnEntity(entity, invalidPosition, rotation);
} catch (IllegalArgumentException e) {
getLogger().warning("Invalid spawn position: " + e.getMessage());
}
```

View File

@@ -0,0 +1,266 @@
---
title: Faire Apparaître des Entités
type: docs
weight: 3
---
Créez et faites apparaître des entités dans le monde en enregistrant des classes d'entités et en utilisant l'API World.
## Enregistrer des Entités
Avant de faire apparaître des entités, enregistrez-les avec l'EntityRegistry dans la méthode `start()` de votre plugin :
```java
@Override
public void start() {
// Enregistrer l'entité avec clé, classe, constructeur et codec
getEntityRegistry().registerEntity(
"my_custom_entity",
CustomEntity.class,
world -> new CustomEntity(world),
CustomEntity.CODEC
);
}
```
### Paramètres d'Enregistrement
| Paramètre | Type | Description |
|-----------|------|-------------|
| key | `String` | Identifiant unique pour le type d'entité |
| clazz | `Class<T>` | La classe de l'entité |
| constructor | `Function<World, T>` | Fonction factory qui crée les instances |
| codec | `DirectDecodeCodec<T>` | Codec de sérialisation pour la persistance |
## Classes de Base d'Entité
Hytale fournit deux classes de base principales :
### Entity
Classe de base pour toutes les entités. Implémente `Component<EntityStore>`.
```java
public class SimpleEntity extends Entity {
public static final DirectDecodeCodec<SimpleEntity> CODEC =
Entity.CODEC.extend(SimpleEntity.class, SimpleEntity::new);
public SimpleEntity() {
super();
}
public SimpleEntity(World world) {
super(world);
}
}
```
### LivingEntity
Classe de base étendue pour les entités avec santé, inventaire et statistiques.
```java
public class CustomMob extends LivingEntity {
public static final DirectDecodeCodec<CustomMob> CODEC =
LivingEntity.CODEC.extend(CustomMob.class, CustomMob::new);
public CustomMob() {
super();
}
public CustomMob(World world) {
super(world);
}
@Override
protected Inventory createDefaultInventory() {
return new Inventory(36); // 36 emplacements
}
}
```
## Faire Apparaître des Entités
Utilisez l'API World pour faire apparaître des entités enregistrées :
```java
// Obtenir le monde
World world = player.getWorld();
// Créer l'instance de l'entité
CustomMob mob = new CustomMob(world);
// Définir la position et rotation de spawn
Vector3d position = new Vector3d(100, 64, 200);
Vector3f rotation = new Vector3f(0, 90, 0); // Face à l'est
// Faire apparaître l'entité (déprécié mais fonctionnel)
world.spawnEntity(mob, position, rotation);
```
### Méthode addEntity
Pour plus de contrôle sur les raisons de spawn, utilisez `addEntity` :
```java
// Options AddReason : SPAWN, LOAD, TRANSFER
world.addEntity(mob, position, rotation, AddReason.SPAWN);
```
{{< callout type="warning" >}}
**Avis de Dépréciation :** Les méthodes `spawnEntity` et `addEntity` sur World sont dépréciées. L'approche moderne utilise directement le système EntityStore basé sur les composants. Cependant, ces méthodes fonctionnent toujours et sont le moyen le plus simple de faire apparaître des entités.
{{< /callout >}}
## Cycle de Vie des Entités
### Chargement dans le Monde
Quand une entité est ajoutée à un monde :
```java
// Appelé automatiquement quand l'entité est ajoutée
entity.loadIntoWorld(world);
// L'entité reçoit un ID réseau
int networkId = entity.getNetworkId();
// Une référence est créée pour l'accès aux composants
Ref<EntityStore> ref = entity.getReference();
```
### Supprimer des Entités
```java
// Supprimer une entité du monde
boolean removed = entity.remove();
// Vérifier si l'entité a déjà été supprimée
if (entity.wasRemoved()) {
// L'entité n'est plus valide
}
```
## Événements d'Entité
### EntityRemoveEvent
Écouter la suppression d'entités :
```java
getEventRegistry().register(EntityRemoveEvent.class, event -> {
Entity entity = event.getEntity();
getLogger().at(Level.INFO).log("Entité supprimée: " + entity.getClass().getSimpleName());
});
```
### LivingEntityInventoryChangeEvent
Suivre les changements d'inventaire sur les entités vivantes :
```java
getEventRegistry().register(LivingEntityInventoryChangeEvent.class, event -> {
LivingEntity entity = event.getEntity();
// Gérer le changement d'inventaire
});
```
{{< callout type="info" >}}
**Pas d'Événement de Spawn :** Hytale n'a pas d'EntitySpawnEvent. Pour suivre les spawns, envisagez d'implémenter une logique personnalisée dans le constructeur de votre entité ou d'utiliser les callbacks d'ajout du système de composants.
{{< /callout >}}
## Exemple Complet
```java
public class MyPlugin extends JavaPlugin {
@Override
public void start() {
// Enregistrer l'entité personnalisée
getEntityRegistry().registerEntity(
"boss_monster",
BossMonster.class,
world -> new BossMonster(world),
BossMonster.CODEC
);
// Écouter les suppressions
getEventRegistry().register(EntityRemoveEvent.class, this::onEntityRemove);
}
private void onEntityRemove(EntityRemoveEvent event) {
if (event.getEntity() instanceof BossMonster) {
getLogger().at(Level.INFO).log("Le boss a été vaincu !");
}
}
public void spawnBoss(Player player) {
World world = player.getWorld();
// Créer le boss à la position du joueur
BossMonster boss = new BossMonster(world);
// Obtenir la position du joueur et ajouter un décalage
Vector3d spawnPos = player.getTransformComponent().getPosition();
spawnPos = new Vector3d(spawnPos.x + 5, spawnPos.y, spawnPos.z);
// Faire face au joueur
Vector3f rotation = new Vector3f(0, 180, 0);
// Spawner
world.spawnEntity(boss, spawnPos, rotation);
}
}
public class BossMonster extends LivingEntity {
public static final DirectDecodeCodec<BossMonster> CODEC =
LivingEntity.CODEC.extend(BossMonster.class, BossMonster::new);
public BossMonster() {
super();
}
public BossMonster(World world) {
super(world);
}
@Override
protected Inventory createDefaultInventory() {
return new Inventory(9); // Petit inventaire
}
@Override
public boolean isCollidable() {
return true;
}
}
```
## Thread Safety
{{< callout type="error" >}}
**Important :** Le spawn d'entités doit être fait sur le thread de tick du monde. Si vous appelez depuis un contexte asynchrone, utilisez l'exécuteur du monde :
{{< /callout >}}
```java
// Spawn sécurisé depuis un contexte asynchrone
world.execute(() -> {
world.spawnEntity(entity, position, rotation);
});
```
## Validation du Spawn
Le monde valide les positions de spawn :
```java
// Exigences de position :
// - X et Z doivent être dans +/- 33554432
// - Y doit être >= -32
// Les positions invalides lèvent IllegalArgumentException
try {
world.spawnEntity(entity, invalidPosition, rotation);
} catch (IllegalArgumentException e) {
getLogger().warning("Position de spawn invalide: " + e.getMessage());
}
```

View File

@@ -0,0 +1,112 @@
---
title: Spawning
type: docs
weight: 6
---
The spawning system provides comprehensive NPC spawn management through markers, beacons, world spawning, and suppression mechanics.
**Package:** `com.hypixel.hytale.server.spawning`
## Architecture
```
SpawningPlugin
├── Managers
│ ├── WorldSpawnManager - World-level NPC spawning
│ ├── BeaconSpawnManager - Beacon-based spawning
│ └── SpawnManager<W, S> - Base spawn manager
├── Assets
│ ├── SpawnMarker - Marker spawn definitions
│ ├── SpawnSuppression - Suppression zones
│ ├── WorldNPCSpawn - World spawn configs
│ └── BeaconNPCSpawn - Beacon spawn configs
├── Components
│ ├── SpawnMarkerEntity - Marker entity data
│ ├── SpawnSuppressionComponent - Entity suppression
│ ├── LocalSpawnController - Player-local spawning
│ ├── WorldSpawnData - World spawn state
│ └── ChunkSpawnData - Chunk spawn state
├── Controllers
│ ├── SpawnController - Base controller
│ ├── BeaconSpawnController - Beacon controller
│ └── LocalSpawnController - Local controller
├── Systems
│ ├── WorldSpawningSystem - World spawn logic
│ ├── SpawnMarkerSystems - Marker processing
│ ├── SpawnBeaconSystems - Beacon processing
│ └── SpawnSuppressionSystems - Suppression logic
├── Interactions
│ └── TriggerSpawnMarkersInteraction
└── Commands
└── SpawnCommand (enable, disable, beacons, markers, populate, stats, suppression)
```
## Spawn Types
### Spawn Markers
Static spawn points that spawn NPCs with configurable respawn timing:
```java
SpawnMarker marker = SpawnMarker.getAssetMap().getAsset("village_guard");
IWeightedMap<SpawnConfiguration> npcs = marker.getWeightedConfigurations();
```
**Asset Location:** `NPC/Spawn/Markers/`
### Spawn Beacons
Dynamic spawn points associated with entities that trigger spawning in a radius:
```java
BeaconSpawnManager manager = SpawningPlugin.get().getBeaconSpawnManager();
List<BeaconSpawnWrapper> beacons = manager.getBeaconSpawns(environmentId);
```
**Asset Location:** `NPC/Spawn/Beacons/`
### World Spawning
Ambient NPC spawning based on environment and biome:
```java
WorldSpawnManager manager = SpawningPlugin.get().getWorldSpawnManager();
```
**Asset Location:** `NPC/Spawn/World/`
### Spawn Suppression
Zones that prevent NPC spawning within a radius:
```java
SpawnSuppression suppression = SpawnSuppression.getAssetMap().getAsset("safe_zone");
double radius = suppression.getRadius();
int[] suppressedGroups = suppression.getSuppressedGroupIds();
```
**Asset Location:** `NPC/Spawn/Suppression/`
## Commands
| Command | Description |
|---------|-------------|
| `/spawning enable [world]` | Enable NPC spawning in world |
| `/spawning disable [world]` | Disable NPC spawning in world |
| `/spawning beacons` | Beacon spawn management |
| `/spawning markers` | Spawn marker management |
| `/spawning populate` | Force spawn population |
| `/spawning stats` | View spawning statistics |
| `/spawning suppression` | Suppression zone management |
**Alias:** `/sp`
## Section Contents
{{< cards >}}
{{< card link="world-spawning" title="World Spawning" subtitle="Environment-based NPC spawning" icon="globe" >}}
{{< card link="spawn-suppression" title="Spawn Suppression" subtitle="Prevent spawns in areas" icon="shield-exclamation" >}}
{{< card link="local-spawning" title="Local Spawning" subtitle="Player-proximity spawning" icon="user" >}}
{{< card link="spawner-assets" title="Spawner Assets" subtitle="Markers and beacons configuration" icon="document-text" >}}
{{< /cards >}}

View File

@@ -0,0 +1,112 @@
---
title: Spawning
type: docs
weight: 6
---
Le systeme de spawning fournit une gestion complete du spawn de NPCs via des marqueurs, beacons, spawning mondial et mecaniques de suppression.
**Package:** `com.hypixel.hytale.server.spawning`
## Architecture
```
SpawningPlugin
├── Managers
│ ├── WorldSpawnManager - Spawning NPC niveau monde
│ ├── BeaconSpawnManager - Spawning base sur beacons
│ └── SpawnManager<W, S> - Manager de spawn de base
├── Assets
│ ├── SpawnMarker - Definitions de spawn marqueur
│ ├── SpawnSuppression - Zones de suppression
│ ├── WorldNPCSpawn - Configs spawn monde
│ └── BeaconNPCSpawn - Configs spawn beacon
├── Composants
│ ├── SpawnMarkerEntity - Donnees entite marqueur
│ ├── SpawnSuppressionComponent - Suppression entite
│ ├── LocalSpawnController - Spawning local joueur
│ ├── WorldSpawnData - Etat spawn monde
│ └── ChunkSpawnData - Etat spawn chunk
├── Controleurs
│ ├── SpawnController - Controleur de base
│ ├── BeaconSpawnController - Controleur beacon
│ └── LocalSpawnController - Controleur local
├── Systemes
│ ├── WorldSpawningSystem - Logique spawn monde
│ ├── SpawnMarkerSystems - Traitement marqueurs
│ ├── SpawnBeaconSystems - Traitement beacons
│ └── SpawnSuppressionSystems - Logique suppression
├── Interactions
│ └── TriggerSpawnMarkersInteraction
└── Commandes
└── SpawnCommand (enable, disable, beacons, markers, populate, stats, suppression)
```
## Types de Spawn
### Marqueurs de Spawn
Points de spawn statiques qui font apparaitre des NPCs avec timing de reapparition configurable:
```java
SpawnMarker marker = SpawnMarker.getAssetMap().getAsset("village_guard");
IWeightedMap<SpawnConfiguration> npcs = marker.getWeightedConfigurations();
```
**Emplacement Asset:** `NPC/Spawn/Markers/`
### Beacons de Spawn
Points de spawn dynamiques associes a des entites qui declenchent le spawning dans un rayon:
```java
BeaconSpawnManager manager = SpawningPlugin.get().getBeaconSpawnManager();
List<BeaconSpawnWrapper> beacons = manager.getBeaconSpawns(environmentId);
```
**Emplacement Asset:** `NPC/Spawn/Beacons/`
### Spawning Mondial
Spawning ambiant de NPCs base sur l'environnement et le biome:
```java
WorldSpawnManager manager = SpawningPlugin.get().getWorldSpawnManager();
```
**Emplacement Asset:** `NPC/Spawn/World/`
### Suppression de Spawn
Zones qui empechent le spawn de NPCs dans un rayon:
```java
SpawnSuppression suppression = SpawnSuppression.getAssetMap().getAsset("safe_zone");
double radius = suppression.getRadius();
int[] suppressedGroups = suppression.getSuppressedGroupIds();
```
**Emplacement Asset:** `NPC/Spawn/Suppression/`
## Commandes
| Commande | Description |
|----------|-------------|
| `/spawning enable [monde]` | Activer le spawning NPC dans le monde |
| `/spawning disable [monde]` | Desactiver le spawning NPC dans le monde |
| `/spawning beacons` | Gestion des spawn beacons |
| `/spawning markers` | Gestion des marqueurs de spawn |
| `/spawning populate` | Forcer la population de spawn |
| `/spawning stats` | Voir les statistiques de spawning |
| `/spawning suppression` | Gestion des zones de suppression |
**Alias:** `/sp`
## Contenu de la Section
{{< cards >}}
{{< card link="world-spawning" title="Spawning Mondial" subtitle="Spawning NPC base sur l'environnement" icon="globe" >}}
{{< card link="spawn-suppression" title="Suppression de Spawn" subtitle="Empecher les spawns dans des zones" icon="shield-exclamation" >}}
{{< card link="local-spawning" title="Spawning Local" subtitle="Spawning par proximite joueur" icon="user" >}}
{{< card link="spawner-assets" title="Assets Spawner" subtitle="Configuration marqueurs et beacons" icon="document-text" >}}
{{< /cards >}}

View File

@@ -0,0 +1,212 @@
---
title: Local Spawning
type: docs
weight: 3
---
Local spawning manages NPC spawning around players, ensuring active gameplay areas have appropriate creature density.
**Package:** `com.hypixel.hytale.server.spawning.local`
## Architecture
```
Local Spawning
├── Components
│ ├── LocalSpawnController - Player spawn controller
│ ├── LocalSpawnBeacon - Local spawn point
│ └── LocalSpawnState - Spawn state tracking
└── Systems
├── LocalSpawnSetupSystem - Initialize local spawning
├── LocalSpawnControllerSystem - Process controllers
├── LocalSpawnBeaconSystem - Process beacons
└── LocalSpawnForceTriggerSystem - Manual triggers
```
## LocalSpawnController
Controls local spawning around a player:
```java
public class LocalSpawnController implements Component<EntityStore> {
private double timeToNextRunSeconds;
public void setTimeToNextRunSeconds(double seconds);
public boolean tickTimeToNextRunSeconds(float dt);
}
```
### Component Access
```java
// Get component type
ComponentType<EntityStore, LocalSpawnController> type =
SpawningPlugin.get().getLocalSpawnControllerComponentType();
// Access on player entity
LocalSpawnController controller = store.getComponent(playerRef, type);
```
### Spawn Timing
The controller uses a configurable delay before spawning:
```java
// Initial delay configured in SpawningPlugin
double joinDelay = SpawningPlugin.get().getLocalSpawnControllerJoinDelay();
```
This prevents immediate spawning when a player joins, allowing time for world loading.
## LocalSpawnBeacon
Defines local spawn points around players:
```java
public class LocalSpawnBeacon {
// Position and configuration for local spawns
// Dynamically created based on player position
}
```
### Beacon Usage
Local spawn beacons are automatically managed:
1. Created around active players
2. Updated as players move
3. Removed when players leave area
## LocalSpawnState
Tracks local spawn state:
```java
public class LocalSpawnState {
// Current spawn state for a local spawn controller
// Tracks active spawns and cooldowns
}
```
## Systems
### LocalSpawnSetupSystem
Initializes local spawning for players:
```java
getEntityStoreRegistry().registerSystem(new LocalSpawnSetupSystem());
```
This system:
- Attaches `LocalSpawnController` to new players
- Configures initial spawn parameters
- Sets up spawn beacons
### LocalSpawnControllerSystem
Processes spawn controllers each tick:
```java
getEntityStoreRegistry().registerSystem(new LocalSpawnControllerSystem());
```
This system:
- Decrements spawn timers
- Checks spawn conditions
- Triggers spawn jobs when ready
### LocalSpawnBeaconSystem
Manages local spawn beacons:
```java
getEntityStoreRegistry().registerSystem(new LocalSpawnBeaconSystem());
```
This system:
- Updates beacon positions
- Processes beacon spawn logic
- Handles beacon lifecycle
### LocalSpawnForceTriggerSystem
Handles forced spawn triggers:
```java
getEntityStoreRegistry().registerSystem(new LocalSpawnForceTriggerSystem());
```
Used for:
- Debug commands
- Event-triggered spawning
- Manual population
## API Usage
### Get Local Spawn Controller
```java
ComponentType<EntityStore, LocalSpawnController> type =
LocalSpawnController.getComponentType();
LocalSpawnController controller = store.getComponent(playerRef, type);
if (controller != null) {
// Player has local spawning enabled
}
```
### Force Spawn Update
```java
// Reset spawn timer to trigger immediate check
LocalSpawnController controller = store.getComponent(playerRef, type);
controller.setTimeToNextRunSeconds(0);
```
### Check Spawn Ready
```java
// Check if spawn timer has elapsed
boolean ready = controller.tickTimeToNextRunSeconds(deltaTime);
if (ready) {
// Time to attempt spawning
}
```
## Configuration
### Join Delay
Configure the delay before local spawning starts for new players:
```java
double delay = SpawningPlugin.get().getLocalSpawnControllerJoinDelay();
```
This prevents:
- Immediate spawn-in ambushes
- Overwhelming new players
- Spawning before world is loaded
### Spawn Radius
Local spawning uses a configured radius around players:
```java
// Spawn radius determines how far from player NPCs can spawn
// Configured per spawn definition
```
## Integration with World Spawning
Local spawning works alongside world spawning:
| System | Scope | Trigger |
|--------|-------|---------|
| World Spawning | Chunk-based | Chunk loading |
| Local Spawning | Player-centric | Player proximity |
Both systems:
- Respect spawn suppression
- Use same NPC pool
- Share spawn limits

View File

@@ -0,0 +1,212 @@
---
title: Spawning Local
type: docs
weight: 3
---
Le spawning local gere le spawn de NPCs autour des joueurs, assurant que les zones de jeu actives ont une densite de creatures appropriee.
**Package:** `com.hypixel.hytale.server.spawning.local`
## Architecture
```
Spawning Local
├── Composants
│ ├── LocalSpawnController - Controleur spawn joueur
│ ├── LocalSpawnBeacon - Point de spawn local
│ └── LocalSpawnState - Suivi etat spawn
└── Systemes
├── LocalSpawnSetupSystem - Initialiser spawning local
├── LocalSpawnControllerSystem - Traiter controleurs
├── LocalSpawnBeaconSystem - Traiter beacons
└── LocalSpawnForceTriggerSystem - Declencheurs manuels
```
## LocalSpawnController
Controle le spawning local autour d'un joueur:
```java
public class LocalSpawnController implements Component<EntityStore> {
private double timeToNextRunSeconds;
public void setTimeToNextRunSeconds(double seconds);
public boolean tickTimeToNextRunSeconds(float dt);
}
```
### Acces au Composant
```java
// Obtenir type de composant
ComponentType<EntityStore, LocalSpawnController> type =
SpawningPlugin.get().getLocalSpawnControllerComponentType();
// Acceder sur entite joueur
LocalSpawnController controller = store.getComponent(playerRef, type);
```
### Timing de Spawn
Le controleur utilise un delai configurable avant spawning:
```java
// Delai initial configure dans SpawningPlugin
double joinDelay = SpawningPlugin.get().getLocalSpawnControllerJoinDelay();
```
Cela empeche le spawning immediat quand un joueur rejoint, laissant le temps au monde de charger.
## LocalSpawnBeacon
Definit les points de spawn locaux autour des joueurs:
```java
public class LocalSpawnBeacon {
// Position et configuration pour spawns locaux
// Cree dynamiquement base sur position joueur
}
```
### Utilisation des Beacons
Les beacons de spawn locaux sont geres automatiquement:
1. Crees autour des joueurs actifs
2. Mis a jour quand joueurs bougent
3. Supprimes quand joueurs quittent la zone
## LocalSpawnState
Suit l'etat de spawn local:
```java
public class LocalSpawnState {
// Etat de spawn actuel pour un controleur local
// Suit spawns actifs et cooldowns
}
```
## Systemes
### LocalSpawnSetupSystem
Initialise le spawning local pour les joueurs:
```java
getEntityStoreRegistry().registerSystem(new LocalSpawnSetupSystem());
```
Ce systeme:
- Attache `LocalSpawnController` aux nouveaux joueurs
- Configure parametres de spawn initiaux
- Configure les beacons de spawn
### LocalSpawnControllerSystem
Traite les controleurs de spawn a chaque tick:
```java
getEntityStoreRegistry().registerSystem(new LocalSpawnControllerSystem());
```
Ce systeme:
- Decremente les timers de spawn
- Verifie les conditions de spawn
- Declenche jobs de spawn quand pret
### LocalSpawnBeaconSystem
Gere les beacons de spawn locaux:
```java
getEntityStoreRegistry().registerSystem(new LocalSpawnBeaconSystem());
```
Ce systeme:
- Met a jour positions des beacons
- Traite logique spawn beacon
- Gere cycle de vie beacon
### LocalSpawnForceTriggerSystem
Gere les declencheurs de spawn forces:
```java
getEntityStoreRegistry().registerSystem(new LocalSpawnForceTriggerSystem());
```
Utilise pour:
- Commandes debug
- Spawning declenche par evenement
- Population manuelle
## Utilisation de l'API
### Obtenir Controleur Spawn Local
```java
ComponentType<EntityStore, LocalSpawnController> type =
LocalSpawnController.getComponentType();
LocalSpawnController controller = store.getComponent(playerRef, type);
if (controller != null) {
// Le joueur a le spawning local active
}
```
### Forcer Mise a Jour Spawn
```java
// Reset timer spawn pour declencher verification immediate
LocalSpawnController controller = store.getComponent(playerRef, type);
controller.setTimeToNextRunSeconds(0);
```
### Verifier Pret pour Spawn
```java
// Verifier si timer spawn ecoule
boolean ready = controller.tickTimeToNextRunSeconds(deltaTime);
if (ready) {
// Temps de tenter spawning
}
```
## Configuration
### Delai de Connexion
Configurer le delai avant que spawning local demarre pour nouveaux joueurs:
```java
double delay = SpawningPlugin.get().getLocalSpawnControllerJoinDelay();
```
Cela empeche:
- Embuscades immediates au spawn
- Submerger les nouveaux joueurs
- Spawning avant chargement monde
### Rayon de Spawn
Le spawning local utilise un rayon configure autour des joueurs:
```java
// Le rayon de spawn determine a quelle distance du joueur les NPCs peuvent spawn
// Configure par definition de spawn
```
## Integration avec Spawning Mondial
Le spawning local fonctionne avec le spawning mondial:
| Systeme | Portee | Declencheur |
|---------|--------|-------------|
| Spawning Mondial | Base chunk | Chargement chunk |
| Spawning Local | Centre joueur | Proximite joueur |
Les deux systemes:
- Respectent la suppression de spawn
- Utilisent le meme pool NPC
- Partagent les limites de spawn

View File

@@ -0,0 +1,249 @@
---
title: Spawn Suppression
type: docs
weight: 2
---
Spawn suppression allows preventing NPC spawning within defined areas, useful for safe zones, player bases, and controlled environments.
**Package:** `com.hypixel.hytale.server.spawning.suppression`
## Architecture
```
Spawn Suppression
├── Assets
│ └── SpawnSuppression - Suppression zone definitions
├── Components
│ ├── SpawnSuppressionComponent - Entity-attached suppression
│ ├── SpawnSuppressionController - Suppression logic
│ ├── ChunkSuppressionEntry - Chunk suppression data
│ └── ChunkSuppressionQueue - Pending suppressions
├── Systems
│ ├── SpawnSuppressionSystems - Main suppression logic
│ ├── ChunkSuppressionSystems - Chunk-level processing
│ └── SpawnMarkerSuppressionSystem - Marker suppression
└── Utilities
├── SuppressionSpanHelper - Span calculations
└── SpawnSuppressorEntry - Suppressor tracking
```
## SpawnSuppression Asset
### Asset Configuration
```yaml
# NPC/Spawn/Suppression/safe_zone.json
{
"Id": "safe_zone",
"SuppressionRadius": 50.0,
"SuppressedGroups": ["hostile", "neutral_aggressive"],
"SuppressSpawnMarkers": true
}
```
### Asset Fields
| Field | Type | Description |
|-------|------|-------------|
| `Id` | String | Unique suppression identifier |
| `SuppressionRadius` | Double | Radius of suppression effect |
| `SuppressedGroups` | String[] | NPCGroup IDs to suppress |
| `SuppressSpawnMarkers` | Boolean | Also suppress spawn markers |
### Asset Access
```java
// Get suppression asset
SpawnSuppression suppression = SpawnSuppression.getAssetMap().getAsset("safe_zone");
// Get properties
double radius = suppression.getRadius();
int[] suppressedGroups = suppression.getSuppressedGroupIds();
boolean suppressMarkers = suppression.isSuppressSpawnMarkers();
```
## SpawnSuppressionComponent
Attaches suppression behavior to entities:
```java
public class SpawnSuppressionComponent implements Component<EntityStore> {
private String spawnSuppression; // Asset ID reference
public String getSpawnSuppression();
public void setSpawnSuppression(String spawnSuppression);
}
```
### Component Usage
```java
// Get component type
ComponentType<EntityStore, SpawnSuppressionComponent> type =
SpawningPlugin.get().getSpawnSuppressorComponentType();
// Attach to entity
SpawnSuppressionComponent comp = new SpawnSuppressionComponent("safe_zone");
store.setComponent(entityRef, type, comp);
// Read from entity
SpawnSuppressionComponent existing = store.getComponent(entityRef, type);
String suppressionId = existing.getSpawnSuppression();
```
### Component Codec
```java
public static final BuilderCodec<SpawnSuppressionComponent> CODEC = BuilderCodec.builder(...)
.append(new KeyedCodec<>("SpawnSuppression", Codec.STRING), ...)
.build();
```
## Suppression Controller
### SpawnSuppressionController
Manages active suppression zones:
```java
// Controller tracks active suppressions
// Calculates affected chunks
// Updates suppression state
```
## Chunk Suppression
### ChunkSuppressionEntry
Tracks suppression state per chunk:
```java
// Records which suppressions affect each chunk
// Cached for efficient spawn checks
```
### ChunkSuppressionQueue
Queue of pending suppression updates:
```java
// Handles async suppression calculations
// Processes additions and removals
```
### ChunkSuppressionSystems
Processes chunk-level suppression:
```java
getEntityStoreRegistry().registerSystem(new ChunkSuppressionSystems());
```
## Suppression Systems
### SpawnSuppressionSystems
Main suppression logic:
```java
// Processes SpawnSuppressionComponent entities
// Updates affected chunk data
// Triggers suppression state changes
```
### SpawnMarkerSuppressionSystem
Handles spawn marker suppression:
```java
// Disables markers within suppression radius
// Re-enables when suppression removed
```
## Suppression Utilities
### SuppressionSpanHelper
Calculates suppression spans:
```java
SuppressionSpanHelper helper = new SuppressionSpanHelper();
// Computes which chunks fall within suppression radius
// Handles edge cases at chunk boundaries
```
### SpawnSuppressorEntry
Tracks individual suppressor entities:
```java
// Links entity reference to suppression config
// Enables efficient lookup and cleanup
```
## API Usage
### Create Suppression Zone
```java
// 1. Define asset in JSON
// NPC/Spawn/Suppression/player_base.json
// 2. Attach component to entity
SpawnSuppressionComponent comp = new SpawnSuppressionComponent("player_base");
store.setComponent(entityRef, SpawnSuppressionComponent.getComponentType(), comp);
```
### Check If Position Suppressed
```java
// Suppression is checked automatically during spawn attempts
// The spawning system queries suppression state per chunk
```
### Remove Suppression
```java
// Remove component to disable suppression
store.removeComponent(entityRef, SpawnSuppressionComponent.getComponentType());
```
## Suppression Behavior
### Radius Calculation
Suppression uses 3D distance with optimization:
- X/Z: Affects entire chunks within radius
- Y: Uses exact distance calculation
This allows NPCs to spawn in caves below or sky above a suppression zone.
### Group Filtering
Only specified NPC groups are suppressed:
```yaml
{
"SuppressedGroups": ["hostile"]
// Hostile NPCs blocked
// Friendly/neutral NPCs can still spawn
}
```
### Marker Suppression
When `SuppressSpawnMarkers` is true:
- Spawn markers within radius stop functioning
- They resume when suppression is removed
- Does not permanently delete markers
## Commands
Access via `/spawning suppression`:
| Subcommand | Description |
|------------|-------------|
| `list` | List active suppressions |
| `info <id>` | Show suppression details |
| `clear <id>` | Remove suppression |

View File

@@ -0,0 +1,249 @@
---
title: Suppression de Spawn
type: docs
weight: 2
---
La suppression de spawn permet d'empecher le spawn de NPCs dans des zones definies, utile pour les zones sures, bases de joueurs et environnements controles.
**Package:** `com.hypixel.hytale.server.spawning.suppression`
## Architecture
```
Suppression de Spawn
├── Assets
│ └── SpawnSuppression - Definitions zones de suppression
├── Composants
│ ├── SpawnSuppressionComponent - Suppression attachee entite
│ ├── SpawnSuppressionController - Logique de suppression
│ ├── ChunkSuppressionEntry - Donnees suppression chunk
│ └── ChunkSuppressionQueue - Suppressions en attente
├── Systemes
│ ├── SpawnSuppressionSystems - Logique suppression principale
│ ├── ChunkSuppressionSystems - Traitement niveau chunk
│ └── SpawnMarkerSuppressionSystem - Suppression marqueurs
└── Utilitaires
├── SuppressionSpanHelper - Calculs d'etendue
└── SpawnSuppressorEntry - Suivi suppresseurs
```
## Asset SpawnSuppression
### Configuration Asset
```yaml
# NPC/Spawn/Suppression/safe_zone.json
{
"Id": "safe_zone",
"SuppressionRadius": 50.0,
"SuppressedGroups": ["hostile", "neutral_aggressive"],
"SuppressSpawnMarkers": true
}
```
### Champs Asset
| Champ | Type | Description |
|-------|------|-------------|
| `Id` | String | Identifiant unique de suppression |
| `SuppressionRadius` | Double | Rayon d'effet de suppression |
| `SuppressedGroups` | String[] | IDs NPCGroup a supprimer |
| `SuppressSpawnMarkers` | Boolean | Supprimer aussi les marqueurs |
### Acces Asset
```java
// Obtenir asset de suppression
SpawnSuppression suppression = SpawnSuppression.getAssetMap().getAsset("safe_zone");
// Obtenir proprietes
double radius = suppression.getRadius();
int[] suppressedGroups = suppression.getSuppressedGroupIds();
boolean suppressMarkers = suppression.isSuppressSpawnMarkers();
```
## SpawnSuppressionComponent
Attache le comportement de suppression aux entites:
```java
public class SpawnSuppressionComponent implements Component<EntityStore> {
private String spawnSuppression; // Reference ID asset
public String getSpawnSuppression();
public void setSpawnSuppression(String spawnSuppression);
}
```
### Utilisation du Composant
```java
// Obtenir type de composant
ComponentType<EntityStore, SpawnSuppressionComponent> type =
SpawningPlugin.get().getSpawnSuppressorComponentType();
// Attacher a une entite
SpawnSuppressionComponent comp = new SpawnSuppressionComponent("safe_zone");
store.setComponent(entityRef, type, comp);
// Lire depuis entite
SpawnSuppressionComponent existing = store.getComponent(entityRef, type);
String suppressionId = existing.getSpawnSuppression();
```
### Codec du Composant
```java
public static final BuilderCodec<SpawnSuppressionComponent> CODEC = BuilderCodec.builder(...)
.append(new KeyedCodec<>("SpawnSuppression", Codec.STRING), ...)
.build();
```
## Controleur de Suppression
### SpawnSuppressionController
Gere les zones de suppression actives:
```java
// Le controleur suit les suppressions actives
// Calcule les chunks affectes
// Met a jour l'etat de suppression
```
## Suppression de Chunk
### ChunkSuppressionEntry
Suit l'etat de suppression par chunk:
```java
// Enregistre quelles suppressions affectent chaque chunk
// Mis en cache pour verifications spawn efficaces
```
### ChunkSuppressionQueue
File d'attente de mises a jour de suppression:
```java
// Gere les calculs de suppression async
// Traite ajouts et suppressions
```
### ChunkSuppressionSystems
Traite la suppression niveau chunk:
```java
getEntityStoreRegistry().registerSystem(new ChunkSuppressionSystems());
```
## Systemes de Suppression
### SpawnSuppressionSystems
Logique de suppression principale:
```java
// Traite les entites SpawnSuppressionComponent
// Met a jour les donnees de chunk affectes
// Declenche les changements d'etat de suppression
```
### SpawnMarkerSuppressionSystem
Gere la suppression des marqueurs de spawn:
```java
// Desactive les marqueurs dans le rayon de suppression
// Reactive quand suppression supprimee
```
## Utilitaires de Suppression
### SuppressionSpanHelper
Calcule les etendues de suppression:
```java
SuppressionSpanHelper helper = new SuppressionSpanHelper();
// Calcule quels chunks tombent dans le rayon
// Gere les cas limites aux frontieres de chunk
```
### SpawnSuppressorEntry
Suit les entites suppresseurs individuelles:
```java
// Lie reference entite a config suppression
// Permet lookup et nettoyage efficaces
```
## Utilisation de l'API
### Creer une Zone de Suppression
```java
// 1. Definir asset en JSON
// NPC/Spawn/Suppression/player_base.json
// 2. Attacher composant a entite
SpawnSuppressionComponent comp = new SpawnSuppressionComponent("player_base");
store.setComponent(entityRef, SpawnSuppressionComponent.getComponentType(), comp);
```
### Verifier si Position Supprimee
```java
// La suppression est verifiee automatiquement lors des tentatives de spawn
// Le systeme de spawn interroge l'etat de suppression par chunk
```
### Supprimer une Suppression
```java
// Retirer composant pour desactiver suppression
store.removeComponent(entityRef, SpawnSuppressionComponent.getComponentType());
```
## Comportement de Suppression
### Calcul de Rayon
La suppression utilise une distance 3D avec optimisation:
- X/Z: Affecte des chunks entiers dans le rayon
- Y: Utilise calcul de distance exact
Cela permet aux NPCs de spawn dans les grottes en dessous ou le ciel au-dessus d'une zone de suppression.
### Filtrage par Groupe
Seuls les groupes NPC specifies sont supprimes:
```yaml
{
"SuppressedGroups": ["hostile"]
// NPCs hostiles bloques
// NPCs amicaux/neutres peuvent toujours spawn
}
```
### Suppression de Marqueurs
Quand `SuppressSpawnMarkers` est vrai:
- Les marqueurs dans le rayon cessent de fonctionner
- Ils reprennent quand suppression retiree
- Ne supprime pas definitivement les marqueurs
## Commandes
Acces via `/spawning suppression`:
| Sous-commande | Description |
|---------------|-------------|
| `list` | Lister les suppressions actives |
| `info <id>` | Afficher details suppression |
| `clear <id>` | Retirer suppression |

View File

@@ -0,0 +1,304 @@
---
title: Spawner Assets
type: docs
weight: 4
---
Spawner assets define how and where NPCs spawn, including markers for static spawn points and beacons for dynamic spawning.
**Package:** `com.hypixel.hytale.server.spawning.assets`
## Spawn Markers
### SpawnMarker Asset
Markers are static spawn points that spawn NPCs with configurable respawn timing.
**Asset Location:** `NPC/Spawn/Markers/`
```yaml
# NPC/Spawn/Markers/village_guard.json
{
"Id": "village_guard",
"Model": "spawn_marker_hostile",
"ExclusionRadius": 15.0,
"MaxDropHeight": 2.0,
"RealtimeRespawn": true,
"ManualTrigger": false,
"DeactivationDistance": 40.0,
"DeactivationTime": 5.0,
"NPCs": [
{
"Name": "guard_soldier",
"Weight": 70.0,
"RealtimeRespawnTime": 120.0
},
{
"Name": "guard_captain",
"Weight": 30.0,
"RealtimeRespawnTime": 300.0
}
]
}
```
### SpawnMarker Fields
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `Id` | String | Required | Unique marker identifier |
| `Model` | String | Config default | Visual model in creative mode |
| `NPCs` | SpawnConfiguration[] | Required | Weighted NPC list |
| `ExclusionRadius` | Double | 0 | Player exclusion radius |
| `MaxDropHeight` | Double | 2.0 | Max spawn height offset |
| `RealtimeRespawn` | Boolean | false | Use real vs game time |
| `ManualTrigger` | Boolean | false | Require manual activation |
| `DeactivationDistance` | Double | 40.0 | Distance to deactivate |
| `DeactivationTime` | Double | 5.0 | Seconds before deactivation |
### SpawnConfiguration
Individual NPC spawn entry in weighted pool:
```java
public class SpawnConfiguration implements IWeightedElement {
protected String npc; // NPC role name
protected double weight; // Spawn weight
protected double realtimeRespawnTime; // Seconds (realtime)
protected Duration spawnAfterGameTime; // Duration (game time)
protected String flockDefinitionId; // Optional flock
}
```
```yaml
{
"Name": "forest_deer",
"Weight": 50.0,
"RealtimeRespawnTime": 60.0,
"SpawnAfterGameTime": "PT1H",
"Flock": "deer_herd"
}
```
### Respawn Timing
Choose between realtime or game time respawning:
**Realtime:** Uses `RealtimeRespawnTime` (seconds)
```yaml
{
"RealtimeRespawn": true,
"NPCs": [{ "RealtimeRespawnTime": 120.0 }]
}
```
**Game Time:** Uses `SpawnAfterGameTime` (ISO 8601 duration)
```yaml
{
"RealtimeRespawn": false,
"NPCs": [{ "SpawnAfterGameTime": "P1DT6H" }]
}
```
Duration format: `P[days]DT[hours]H[minutes]M[seconds]S`
### Flock Spawning
Spawn a group of NPCs around the main spawn:
```yaml
{
"Name": "wolf_alpha",
"Flock": "wolf_pack"
}
```
The flock definition specifies additional NPCs to spawn around the main one.
## Spawn Beacons
### BeaconNPCSpawn
Dynamic spawn points that spawn NPCs within a radius.
**Asset Location:** `NPC/Spawn/Beacons/`
```yaml
# NPC/Spawn/Beacons/dungeon_spawner.json
{
"Id": "dungeon_spawner",
"NPCRole": "skeleton_warrior",
"SpawnWeight": 10,
"MinGroupSize": 2,
"MaxGroupSize": 5,
"Environments": ["dungeon_dark"]
}
```
### SpawnBeacon Component
Entities with spawn beacon behavior:
```java
public class SpawnBeacon {
// Configuration for beacon spawning
// Triggers spawning within radius
}
```
### Beacon Systems
```java
// SpawnBeaconSystems processes active beacons
getEntityStoreRegistry().registerSystem(new SpawnBeaconSystems());
// BeaconSpatialSystem handles spatial queries
getEntityStoreRegistry().registerSystem(new BeaconSpatialSystem());
```
## World Spawn Configuration
### WorldNPCSpawn
Environment-based ambient spawning.
**Asset Location:** `NPC/Spawn/World/`
```yaml
# NPC/Spawn/World/forest_fauna.json
{
"Id": "forest_fauna",
"NPCRole": "rabbit",
"SpawnWeight": 20,
"MinGroupSize": 1,
"MaxGroupSize": 3,
"Environments": ["forest", "grassland"],
"LightType": "Day",
"MinLightLevel": 8
}
```
### NPCSpawn Base Fields
| Field | Type | Description |
|-------|------|-------------|
| `NPCRole` | String | NPC role to spawn |
| `SpawnWeight` | Integer | Spawn probability weight |
| `MinGroupSize` | Integer | Minimum NPCs to spawn |
| `MaxGroupSize` | Integer | Maximum NPCs to spawn |
### Environment Filtering
Restrict spawning to specific environments:
```yaml
{
"Environments": ["cave_dark", "dungeon"]
}
```
### Light Level Filtering
```yaml
{
"LightType": "Night",
"MinLightLevel": 0,
"MaxLightLevel": 7
}
```
Light types: `Any`, `Day`, `Night`
## Spawn Marker Entity
### SpawnMarkerEntity Component
Entity representing a spawn marker in the world:
```java
ComponentType<EntityStore, SpawnMarkerEntity> type =
SpawningPlugin.get().getSpawnMarkerEntityComponentType();
```
### Marker Block State
Spawn markers can be placed as blocks:
```java
public class SpawnMarkerBlockState {
// Block-based spawn marker
}
public class SpawnMarkerBlockReference {
// Reference to marker block
}
```
## Interactions
### TriggerSpawnMarkersInteraction
Manually trigger spawn markers via interaction:
```yaml
# Item or block interaction
{
"Type": "TriggerSpawnMarkers",
"MarkerIds": ["ambush_1", "ambush_2"],
"Radius": 50.0
}
```
Registered as:
```java
getCodecRegistry(Interaction.CODEC).register(
"TriggerSpawnMarkers",
TriggerSpawnMarkersInteraction.class,
TriggerSpawnMarkersInteraction.CODEC
);
```
## API Usage
### Access Spawn Marker Assets
```java
// Get marker asset
SpawnMarker marker = SpawnMarker.getAssetMap().getAsset("village_guard");
// Get weighted configurations
IWeightedMap<SpawnConfiguration> npcs = marker.getWeightedConfigurations();
// Get random NPC from pool
SpawnConfiguration selected = npcs.getRandom(random);
String npcRole = selected.getNpc();
```
### Access Spawn Suppression
```java
SpawnSuppression suppression = SpawnSuppression.getAssetMap().getAsset("safe_zone");
double radius = suppression.getRadius();
```
### Validate Marker Asset
```java
// Validation happens on plugin start
// Warns about:
// - Missing respawn times
// - Conflicting realtime/game time settings
// - Invalid NPC roles
```
## Asset Loading Order
Spawn assets have dependencies:
```java
// Markers load after models and NPC roles
HytaleAssetStore.builder(...)
.loadsAfter(ModelAsset.class)
.loadsAfter(NPCRole.class)
.build();
```

View File

@@ -0,0 +1,304 @@
---
title: Assets Spawner
type: docs
weight: 4
---
Les assets spawner definissent comment et ou les NPCs apparaissent, incluant les marqueurs pour points de spawn statiques et les beacons pour spawning dynamique.
**Package:** `com.hypixel.hytale.server.spawning.assets`
## Marqueurs de Spawn
### Asset SpawnMarker
Les marqueurs sont des points de spawn statiques qui font apparaitre des NPCs avec timing de reapparition configurable.
**Emplacement Asset:** `NPC/Spawn/Markers/`
```yaml
# NPC/Spawn/Markers/village_guard.json
{
"Id": "village_guard",
"Model": "spawn_marker_hostile",
"ExclusionRadius": 15.0,
"MaxDropHeight": 2.0,
"RealtimeRespawn": true,
"ManualTrigger": false,
"DeactivationDistance": 40.0,
"DeactivationTime": 5.0,
"NPCs": [
{
"Name": "guard_soldier",
"Weight": 70.0,
"RealtimeRespawnTime": 120.0
},
{
"Name": "guard_captain",
"Weight": 30.0,
"RealtimeRespawnTime": 300.0
}
]
}
```
### Champs SpawnMarker
| Champ | Type | Defaut | Description |
|-------|------|--------|-------------|
| `Id` | String | Requis | Identifiant unique marqueur |
| `Model` | String | Defaut config | Modele visuel en mode creatif |
| `NPCs` | SpawnConfiguration[] | Requis | Liste NPC ponderee |
| `ExclusionRadius` | Double | 0 | Rayon exclusion joueur |
| `MaxDropHeight` | Double | 2.0 | Offset hauteur spawn max |
| `RealtimeRespawn` | Boolean | false | Temps reel vs temps jeu |
| `ManualTrigger` | Boolean | false | Necessiter activation manuelle |
| `DeactivationDistance` | Double | 40.0 | Distance de desactivation |
| `DeactivationTime` | Double | 5.0 | Secondes avant desactivation |
### SpawnConfiguration
Entree spawn NPC individuelle dans pool pondere:
```java
public class SpawnConfiguration implements IWeightedElement {
protected String npc; // Nom role NPC
protected double weight; // Poids de spawn
protected double realtimeRespawnTime; // Secondes (temps reel)
protected Duration spawnAfterGameTime; // Duree (temps jeu)
protected String flockDefinitionId; // Flock optionnel
}
```
```yaml
{
"Name": "forest_deer",
"Weight": 50.0,
"RealtimeRespawnTime": 60.0,
"SpawnAfterGameTime": "PT1H",
"Flock": "deer_herd"
}
```
### Timing de Reapparition
Choisir entre reapparition temps reel ou temps de jeu:
**Temps Reel:** Utilise `RealtimeRespawnTime` (secondes)
```yaml
{
"RealtimeRespawn": true,
"NPCs": [{ "RealtimeRespawnTime": 120.0 }]
}
```
**Temps de Jeu:** Utilise `SpawnAfterGameTime` (duree ISO 8601)
```yaml
{
"RealtimeRespawn": false,
"NPCs": [{ "SpawnAfterGameTime": "P1DT6H" }]
}
```
Format duree: `P[jours]DT[heures]H[minutes]M[secondes]S`
### Spawning de Flock
Faire apparaitre un groupe de NPCs autour du spawn principal:
```yaml
{
"Name": "wolf_alpha",
"Flock": "wolf_pack"
}
```
La definition flock specifie les NPCs additionnels a spawn autour du principal.
## Beacons de Spawn
### BeaconNPCSpawn
Points de spawn dynamiques qui font apparaitre des NPCs dans un rayon.
**Emplacement Asset:** `NPC/Spawn/Beacons/`
```yaml
# NPC/Spawn/Beacons/dungeon_spawner.json
{
"Id": "dungeon_spawner",
"NPCRole": "skeleton_warrior",
"SpawnWeight": 10,
"MinGroupSize": 2,
"MaxGroupSize": 5,
"Environments": ["dungeon_dark"]
}
```
### Composant SpawnBeacon
Entites avec comportement beacon de spawn:
```java
public class SpawnBeacon {
// Configuration pour spawning beacon
// Declenche spawning dans un rayon
}
```
### Systemes Beacon
```java
// SpawnBeaconSystems traite les beacons actifs
getEntityStoreRegistry().registerSystem(new SpawnBeaconSystems());
// BeaconSpatialSystem gere les requetes spatiales
getEntityStoreRegistry().registerSystem(new BeaconSpatialSystem());
```
## Configuration Spawn Mondial
### WorldNPCSpawn
Spawning ambiant base sur l'environnement.
**Emplacement Asset:** `NPC/Spawn/World/`
```yaml
# NPC/Spawn/World/forest_fauna.json
{
"Id": "forest_fauna",
"NPCRole": "rabbit",
"SpawnWeight": 20,
"MinGroupSize": 1,
"MaxGroupSize": 3,
"Environments": ["forest", "grassland"],
"LightType": "Day",
"MinLightLevel": 8
}
```
### Champs de Base NPCSpawn
| Champ | Type | Description |
|-------|------|-------------|
| `NPCRole` | String | Role NPC a spawner |
| `SpawnWeight` | Integer | Poids probabilite spawn |
| `MinGroupSize` | Integer | Minimum NPCs a spawner |
| `MaxGroupSize` | Integer | Maximum NPCs a spawner |
### Filtrage par Environnement
Restreindre spawning a des environnements specifiques:
```yaml
{
"Environments": ["cave_dark", "dungeon"]
}
```
### Filtrage par Niveau de Lumiere
```yaml
{
"LightType": "Night",
"MinLightLevel": 0,
"MaxLightLevel": 7
}
```
Types de lumiere: `Any`, `Day`, `Night`
## Entite Marqueur de Spawn
### Composant SpawnMarkerEntity
Entite representant un marqueur de spawn dans le monde:
```java
ComponentType<EntityStore, SpawnMarkerEntity> type =
SpawningPlugin.get().getSpawnMarkerEntityComponentType();
```
### Etat Bloc Marqueur
Les marqueurs de spawn peuvent etre places comme blocs:
```java
public class SpawnMarkerBlockState {
// Marqueur de spawn base bloc
}
public class SpawnMarkerBlockReference {
// Reference au bloc marqueur
}
```
## Interactions
### TriggerSpawnMarkersInteraction
Declencher manuellement des marqueurs de spawn via interaction:
```yaml
# Interaction item ou bloc
{
"Type": "TriggerSpawnMarkers",
"MarkerIds": ["ambush_1", "ambush_2"],
"Radius": 50.0
}
```
Enregistre comme:
```java
getCodecRegistry(Interaction.CODEC).register(
"TriggerSpawnMarkers",
TriggerSpawnMarkersInteraction.class,
TriggerSpawnMarkersInteraction.CODEC
);
```
## Utilisation de l'API
### Acceder aux Assets Marqueur de Spawn
```java
// Obtenir asset marqueur
SpawnMarker marker = SpawnMarker.getAssetMap().getAsset("village_guard");
// Obtenir configurations ponderees
IWeightedMap<SpawnConfiguration> npcs = marker.getWeightedConfigurations();
// Obtenir NPC aleatoire depuis pool
SpawnConfiguration selected = npcs.getRandom(random);
String npcRole = selected.getNpc();
```
### Acceder a la Suppression de Spawn
```java
SpawnSuppression suppression = SpawnSuppression.getAssetMap().getAsset("safe_zone");
double radius = suppression.getRadius();
```
### Valider Asset Marqueur
```java
// La validation se fait au demarrage du plugin
// Avertit sur:
// - Temps de reapparition manquants
// - Parametres temps reel/jeu conflictuels
// - Roles NPC invalides
```
## Ordre de Chargement Assets
Les assets spawn ont des dependances:
```java
// Les marqueurs chargent apres modeles et roles NPC
HytaleAssetStore.builder(...)
.loadsAfter(ModelAsset.class)
.loadsAfter(NPCRole.class)
.build();
```

View File

@@ -0,0 +1,260 @@
---
title: World Spawning
type: docs
weight: 1
---
World spawning manages ambient NPC spawning based on environment, biome, and chunk conditions.
**Package:** `com.hypixel.hytale.server.spawning.world`
## Architecture
```
World Spawning
├── Manager
│ └── WorldSpawnManager - Central spawn coordination
├── Components
│ ├── WorldSpawnData - World-level spawn state
│ ├── ChunkSpawnData - Per-chunk spawn tracking
│ ├── ChunkSpawnedNPCData - Spawned NPC records
│ └── SpawnJobData - Active spawn jobs
├── Systems
│ ├── WorldSpawningSystem - Main spawning logic
│ ├── WorldSpawnTrackingSystem - Track spawned entities
│ ├── WorldSpawnJobSystems - Job processing
│ ├── ChunkSpawningSystems - Chunk-level spawning
│ └── MoonPhaseChangeEventSystem - Lunar spawn modifiers
└── Config
├── WorldNPCSpawn - Spawn definitions
├── NPCSpawn - Base spawn config
└── RoleSpawnParameters - Role-specific params
```
## WorldSpawnManager
Central manager for world-level NPC spawning:
```java
WorldSpawnManager manager = SpawningPlugin.get().getWorldSpawnManager();
```
The manager coordinates spawning across chunks based on:
- Environment type
- Biome conditions
- Light levels
- Moon phase
- Player proximity
## World Spawn Configuration
### WorldNPCSpawn Asset
```yaml
# NPC/Spawn/World/forest_creatures.json
{
"Id": "forest_creatures",
"NPCRole": "deer",
"SpawnWeight": 10,
"MinGroupSize": 1,
"MaxGroupSize": 3,
"Environments": ["forest", "plains"],
"LightType": "Day",
"MinLightLevel": 8,
"MaxLightLevel": 15
}
```
### NPCSpawn Base Configuration
```java
public class NPCSpawn {
protected String npcRole; // NPC role to spawn
protected int spawnWeight; // Spawn probability weight
protected int minGroupSize; // Minimum group size
protected int maxGroupSize; // Maximum group size
}
```
### RoleSpawnParameters
Per-role spawn parameters:
```java
public class RoleSpawnParameters {
// Role-specific spawn configuration
// Defines constraints and behaviors per NPC type
}
```
## Environment-Based Spawning
### EnvironmentSpawnParameters
```java
EnvironmentSpawnParameters params = new EnvironmentSpawnParameters();
// Parameters specific to environment type
```
### Light Type Filtering
```java
public enum LightType {
Any, // Spawn at any light level
Day, // Only spawn during day
Night // Only spawn at night
}
```
Used with `LightRangePredicate` to filter spawn positions:
```java
LightRangePredicate predicate = new LightRangePredicate(minLight, maxLight, lightType);
boolean canSpawn = predicate.test(world, position);
```
## Chunk-Level Management
### ChunkSpawnData
Tracks spawn state per chunk:
```java
ComponentType<ChunkStore, ChunkSpawnData> type =
SpawningPlugin.get().getChunkSpawnDataComponentType();
ChunkSpawnData data = chunkStore.getComponent(chunkRef, type);
```
### ChunkSpawnedNPCData
Records which NPCs were spawned in a chunk:
```java
// Tracks spawned entities for cleanup and respawning
```
### ChunkEnvironmentSpawnData
Environment-specific spawn data per chunk:
```java
// Caches environment conditions for spawn decisions
```
## Spawn Job System
### SpawnJob
Base class for spawn operations:
```java
public class SpawnJob {
// Asynchronous spawn operation
}
```
### SpawnJobData Component
Tracks active spawn jobs:
```java
SpawnJobData jobData = store.getComponent(worldRef, SpawnJobData.getComponentType());
```
### WorldSpawnJobSystems
Processes spawn jobs:
```java
// System that executes pending spawn jobs
// Handles spawn position validation
// Creates NPCs when conditions are met
```
## World Spawn Systems
### WorldSpawningSystem
Main system for world spawning logic:
```java
getEntityStoreRegistry().registerSystem(new WorldSpawningSystem());
```
This system:
1. Checks spawn conditions per chunk
2. Selects appropriate NPC types
3. Validates spawn positions
4. Creates spawn jobs
### WorldSpawnTrackingSystem
Tracks spawned entities for management:
```java
// Monitors spawned NPCs
// Handles despawn when conditions change
// Updates spawn counts
```
### MoonPhaseChangeEventSystem
Adjusts spawning based on moon phase:
```java
// Modifies spawn rates during different moon phases
// Enables special night spawns during full moon
```
## Position Selection
### FloodFillPositionSelector
Finds valid spawn positions using flood fill:
```java
FloodFillPositionSelector selector = new FloodFillPositionSelector();
// Searches for suitable spawn locations
```
### RandomChunkColumnIterator
Iterates through random positions in a chunk column:
```java
RandomChunkColumnIterator iterator = new RandomChunkColumnIterator();
// Provides random positions for spawn attempts
```
### ChunkColumnMask
Masks specific areas within a chunk:
```java
ChunkColumnMask mask = new ChunkColumnMask();
// Excludes certain positions from spawning
```
## API Usage
### Check World Spawn State
```java
WorldSpawnData worldData = store.getComponent(worldRef, WorldSpawnData.getComponentType());
```
### Get Spawn Wrapper
```java
WorldSpawnWrapper wrapper = manager.getSpawnWrapper(spawnIndex);
WorldNPCSpawn spawn = wrapper.getSpawn();
```
## Spawn Statistics
Track spawning metrics with `WorldNPCSpawnStat`:
```java
// Records spawn attempts, successes, and failures
// Used by /spawning stats command
```

View File

@@ -0,0 +1,260 @@
---
title: Spawning Mondial
type: docs
weight: 1
---
Le spawning mondial gere le spawn ambiant de NPCs base sur l'environnement, le biome et les conditions de chunk.
**Package:** `com.hypixel.hytale.server.spawning.world`
## Architecture
```
Spawning Mondial
├── Manager
│ └── WorldSpawnManager - Coordination centrale du spawn
├── Composants
│ ├── WorldSpawnData - Etat spawn niveau monde
│ ├── ChunkSpawnData - Suivi spawn par chunk
│ ├── ChunkSpawnedNPCData - Enregistrements NPCs spawnes
│ └── SpawnJobData - Jobs de spawn actifs
├── Systemes
│ ├── WorldSpawningSystem - Logique principale de spawn
│ ├── WorldSpawnTrackingSystem - Suivi entites spawnees
│ ├── WorldSpawnJobSystems - Traitement des jobs
│ ├── ChunkSpawningSystems - Spawning niveau chunk
│ └── MoonPhaseChangeEventSystem - Modificateurs lunaires
└── Config
├── WorldNPCSpawn - Definitions de spawn
├── NPCSpawn - Config spawn de base
└── RoleSpawnParameters - Params specifiques role
```
## WorldSpawnManager
Manager central pour le spawning NPC niveau monde:
```java
WorldSpawnManager manager = SpawningPlugin.get().getWorldSpawnManager();
```
Le manager coordonne le spawning a travers les chunks base sur:
- Type d'environnement
- Conditions de biome
- Niveaux de lumiere
- Phase lunaire
- Proximite des joueurs
## Configuration Spawn Mondial
### Asset WorldNPCSpawn
```yaml
# NPC/Spawn/World/forest_creatures.json
{
"Id": "forest_creatures",
"NPCRole": "deer",
"SpawnWeight": 10,
"MinGroupSize": 1,
"MaxGroupSize": 3,
"Environments": ["forest", "plains"],
"LightType": "Day",
"MinLightLevel": 8,
"MaxLightLevel": 15
}
```
### Configuration de Base NPCSpawn
```java
public class NPCSpawn {
protected String npcRole; // Role NPC a spawner
protected int spawnWeight; // Poids de probabilite
protected int minGroupSize; // Taille groupe minimum
protected int maxGroupSize; // Taille groupe maximum
}
```
### RoleSpawnParameters
Parametres de spawn par role:
```java
public class RoleSpawnParameters {
// Configuration spawn specifique au role
// Definit contraintes et comportements par type NPC
}
```
## Spawning Base sur l'Environnement
### EnvironmentSpawnParameters
```java
EnvironmentSpawnParameters params = new EnvironmentSpawnParameters();
// Parametres specifiques au type d'environnement
```
### Filtrage par Type de Lumiere
```java
public enum LightType {
Any, // Spawn a tout niveau de lumiere
Day, // Spawn uniquement le jour
Night // Spawn uniquement la nuit
}
```
Utilise avec `LightRangePredicate` pour filtrer les positions de spawn:
```java
LightRangePredicate predicate = new LightRangePredicate(minLight, maxLight, lightType);
boolean canSpawn = predicate.test(world, position);
```
## Gestion Niveau Chunk
### ChunkSpawnData
Suit l'etat de spawn par chunk:
```java
ComponentType<ChunkStore, ChunkSpawnData> type =
SpawningPlugin.get().getChunkSpawnDataComponentType();
ChunkSpawnData data = chunkStore.getComponent(chunkRef, type);
```
### ChunkSpawnedNPCData
Enregistre quels NPCs ont ete spawnes dans un chunk:
```java
// Suit les entites spawnees pour nettoyage et respawn
```
### ChunkEnvironmentSpawnData
Donnees de spawn specifiques a l'environnement par chunk:
```java
// Met en cache les conditions d'environnement pour decisions de spawn
```
## Systeme de Jobs de Spawn
### SpawnJob
Classe de base pour operations de spawn:
```java
public class SpawnJob {
// Operation de spawn asynchrone
}
```
### Composant SpawnJobData
Suit les jobs de spawn actifs:
```java
SpawnJobData jobData = store.getComponent(worldRef, SpawnJobData.getComponentType());
```
### WorldSpawnJobSystems
Traite les jobs de spawn:
```java
// Systeme qui execute les jobs de spawn en attente
// Gere la validation des positions de spawn
// Cree les NPCs quand conditions remplies
```
## Systemes de Spawn Mondial
### WorldSpawningSystem
Systeme principal pour la logique de spawn mondial:
```java
getEntityStoreRegistry().registerSystem(new WorldSpawningSystem());
```
Ce systeme:
1. Verifie les conditions de spawn par chunk
2. Selectionne les types NPC appropries
3. Valide les positions de spawn
4. Cree les jobs de spawn
### WorldSpawnTrackingSystem
Suit les entites spawnees pour gestion:
```java
// Surveille les NPCs spawnes
// Gere le despawn quand conditions changent
// Met a jour les compteurs de spawn
```
### MoonPhaseChangeEventSystem
Ajuste le spawning selon la phase lunaire:
```java
// Modifie les taux de spawn pendant differentes phases
// Active spawns nocturnes speciaux pendant pleine lune
```
## Selection de Position
### FloodFillPositionSelector
Trouve les positions de spawn valides via flood fill:
```java
FloodFillPositionSelector selector = new FloodFillPositionSelector();
// Recherche des emplacements de spawn adequats
```
### RandomChunkColumnIterator
Itere a travers des positions aleatoires dans une colonne de chunk:
```java
RandomChunkColumnIterator iterator = new RandomChunkColumnIterator();
// Fournit positions aleatoires pour tentatives de spawn
```
### ChunkColumnMask
Masque des zones specifiques dans un chunk:
```java
ChunkColumnMask mask = new ChunkColumnMask();
// Exclut certaines positions du spawning
```
## Utilisation de l'API
### Verifier l'Etat de Spawn Mondial
```java
WorldSpawnData worldData = store.getComponent(worldRef, WorldSpawnData.getComponentType());
```
### Obtenir un Wrapper de Spawn
```java
WorldSpawnWrapper wrapper = manager.getSpawnWrapper(spawnIndex);
WorldNPCSpawn spawn = wrapper.getSpawn();
```
## Statistiques de Spawn
Suivre les metriques de spawn avec `WorldNPCSpawnStat`:
```java
// Enregistre tentatives, succes et echecs de spawn
// Utilise par la commande /spawning stats
```

View File

@@ -0,0 +1,255 @@
---
title: Interaction System
type: docs
weight: 8
---
The Interaction system is Hytale's powerful data-driven behavior framework. It defines how items, blocks, and entities behave when used or activated.
## Overview
Interactions are primarily configured through asset files rather than Java code. They form chains of operations that execute when a player performs actions like clicking, attacking, or using items.
{{< cards cols="2" >}}
{{< card link="interaction-types" title="Interaction Types" subtitle="100+ built-in interaction types" icon="lightning-bolt" >}}
{{< card link="selectors" title="Selectors" subtitle="Target entities and blocks" icon="cursor-click" >}}
{{< card link="combat" title="Combat" subtitle="Damage calculation and knockback" icon="fire" >}}
{{< /cards >}}
## Key Concepts
### Interaction Assets
Interactions are assets loaded from data packs. Assets are stored in an indexed map - you first get the index, then retrieve the asset:
```java
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction;
import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap;
// Get the asset map
IndexedLookupTableAssetMap<String, Interaction> assetMap = Interaction.getAssetMap();
// Get index from string ID
int index = assetMap.getIndex("my_interaction");
// Get interaction by index
Interaction interaction = assetMap.getAsset(index);
// Or combined (but check for null!)
if (index != Integer.MIN_VALUE) {
Interaction myInteraction = assetMap.getAsset(index);
}
```
{{< callout type="warning" >}}
**Important:** `getAsset()` takes an `int` index, not a String. Use `getIndex()` first to convert the string ID to an index. Returns `Integer.MIN_VALUE` if not found.
{{< /callout >}}
### Interaction Context
When interactions run, they receive an `InteractionContext` with:
- **Entity reference** - The entity performing the interaction
- **Held item** - The item being used
- **Target block/entity** - What's being targeted
- **State tracking** - Progress and timing information
### Interaction States
**Package:** `com.hypixel.hytale.protocol`
```java
public enum InteractionState {
Finished(0), // Completed successfully
Skip(1), // Skipped by user
ItemChanged(2), // Cancelled due to item change
Failed(3), // Failed to execute
NotFinished(4); // Still running
}
```
| State | Value | Description |
|-------|-------|-------------|
| `Finished` | 0 | Interaction completed successfully |
| `Skip` | 1 | User requested to skip |
| `ItemChanged` | 2 | Cancelled because held item changed |
| `Failed` | 3 | Interaction failed to execute |
| `NotFinished` | 4 | Still running, not yet complete |
## Interaction Categories
### Block Interactions
Operations on blocks:
| Interaction | Description |
|-------------|-------------|
| `PlaceBlockInteraction` | Place a block |
| `BreakBlockInteraction` | Break a block |
| `DestroyBlockInteraction` | Destroy without drops |
| `ChangeBlockInteraction` | Change block type |
| `UseBlockInteraction` | Interact with block |
| `PlaceFluidInteraction` | Place fluid |
### Item Interactions
Operations with items:
| Interaction | Description |
|-------------|-------------|
| `AddItemInteraction` | Add item to inventory |
| `EquipItemInteraction` | Equip an item |
| `ModifyInventoryInteraction` | Modify inventory contents |
### Entity Interactions
Operations on entities:
| Interaction | Description |
|-------------|-------------|
| `UseEntityInteraction` | Interact with entity |
| `RemoveEntityInteraction` | Remove an entity |
| `ApplyEffectInteraction` | Apply status effect |
| `ClearEntityEffectInteraction` | Remove effects |
### Combat Interactions
Damage and combat:
| Interaction | Description |
|-------------|-------------|
| `DamageCalculator` | Calculate damage |
| `DamageEffects` | Apply damage effects |
| `Knockback` | Apply knockback |
| `DirectionalKnockback` | Directional knockback |
| `ForceKnockback` | Force-based knockback |
### UI Interactions
User interface:
| Interaction | Description |
|-------------|-------------|
| `OpenContainerInteraction` | Open container UI |
| `OpenPageInteraction` | Open a page |
| `OpenCustomUIInteraction` | Open custom UI |
| `SendMessageInteraction` | Send message to player |
### Structure Interactions
World modification:
| Interaction | Description |
|-------------|-------------|
| `SpawnPrefabInteraction` | Spawn a prefab/structure |
| `LaunchProjectileInteraction` | Launch projectile |
### Control Flow
Chain and condition interactions:
| Interaction | Description |
|-------------|-------------|
| `SerialInteraction` | Run interactions in sequence |
| `ParallelInteraction` | Run interactions simultaneously |
| `SelectInteraction` | Choose between interactions |
| `ConditionInteraction` | Conditional execution |
| `RepeatInteraction` | Repeat an interaction |
| `CancelChainInteraction` | Cancel the chain |
### Stat Interactions
Entity statistics:
| Interaction | Description |
|-------------|-------------|
| `ChangeStatInteraction` | Modify a stat |
| `StatsConditionInteraction` | Check stat conditions |
## Interaction Properties
Common properties on all interactions:
```yaml
# Example interaction configuration
MyInteraction:
Type: PlaceBlock
RunTime: 0.5 # Duration in seconds
ViewDistance: 96.0 # Visibility distance
HorizontalSpeedMultiplier: 1.0
CancelOnItemChange: true
Effects:
WaitForAnimationToFinish: true
ItemAnimationId: "swing"
Rules:
# Activation rules
```
### InteractionEffects
Visual and audio effects:
- `ItemAnimationId` - Animation to play
- `WaitForAnimationToFinish` - Wait for animation
- Sound effects
- Particle effects
### InteractionRules
Control when interactions can run:
- Game mode restrictions
- State requirements
- Cooldown checks
## Selectors
Selectors determine what entities or blocks are targeted:
| Selector | Description |
|----------|-------------|
| `RaycastSelector` | Cast ray from camera |
| `AOECircleSelector` | Area of effect circle |
| `AOECylinderSelector` | Area of effect cylinder |
| `StabSelector` | Close-range stab |
| `HorizontalSelector` | Horizontal sweep |
## Accessing Interactions
### From Items
Items define their interactions in asset files. At runtime:
```java
ItemStack item = player.getInventory().getActiveItem();
Item itemAsset = item.getItem();
// Item defines its primary/secondary interactions
```
### From Blocks
Blocks define use interactions:
```java
BlockType blockType = world.getBlockType(position);
// Block defines its use interaction
```
## Best Practices
{{< callout type="info" >}}
**Interaction Guidelines:**
- Interactions are primarily data-driven through assets
- Use existing interaction types when possible
- Chain interactions for complex behaviors
- Use selectors to target appropriately
- Consider game mode in rules
{{< /callout >}}
{{< callout type="warning" >}}
**Performance Note:** Complex interaction chains with many parallel operations can impact performance. Keep chains efficient.
{{< /callout >}}
## Further Reading
For detailed information on specific interaction types, see the subpages.

View File

@@ -0,0 +1,255 @@
---
title: Système d'Interactions
type: docs
weight: 8
---
Le système d'Interactions est le puissant framework de comportements piloté par les données de Hytale. Il définit comment les objets, blocs et entités se comportent lorsqu'ils sont utilisés ou activés.
## Vue d'Ensemble
Les interactions sont principalement configurées via des fichiers d'assets plutôt que du code Java. Elles forment des chaînes d'opérations qui s'exécutent lorsqu'un joueur effectue des actions comme cliquer, attaquer ou utiliser des objets.
{{< cards cols="2" >}}
{{< card link="interaction-types" title="Types d'Interactions" subtitle="100+ types d'interactions intégrés" icon="lightning-bolt" >}}
{{< card link="selectors" title="Sélecteurs" subtitle="Cibler entités et blocs" icon="cursor-click" >}}
{{< card link="combat" title="Combat" subtitle="Calcul de dégâts et knockback" icon="fire" >}}
{{< /cards >}}
## Concepts Clés
### Assets d'Interaction
Les interactions sont des assets chargés depuis les data packs. Les assets sont stockés dans une map indexée - vous devez d'abord obtenir l'index, puis récupérer l'asset :
```java
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction;
import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap;
// Obtenir la map d'assets
IndexedLookupTableAssetMap<String, Interaction> assetMap = Interaction.getAssetMap();
// Obtenir l'index depuis l'ID string
int index = assetMap.getIndex("my_interaction");
// Obtenir l'interaction par index
Interaction interaction = assetMap.getAsset(index);
// Ou combiné (mais vérifiez null !)
if (index != Integer.MIN_VALUE) {
Interaction myInteraction = assetMap.getAsset(index);
}
```
{{< callout type="warning" >}}
**Important :** `getAsset()` prend un `int` index, pas un String. Utilisez `getIndex()` d'abord pour convertir l'ID string en index. Retourne `Integer.MIN_VALUE` si non trouvé.
{{< /callout >}}
### Contexte d'Interaction
Quand les interactions s'exécutent, elles reçoivent un `InteractionContext` avec :
- **Référence d'entité** - L'entité effectuant l'interaction
- **Objet tenu** - L'objet utilisé
- **Bloc/entité cible** - Ce qui est ciblé
- **Suivi d'état** - Informations de progression et timing
### États d'Interaction
**Package :** `com.hypixel.hytale.protocol`
```java
public enum InteractionState {
Finished(0), // Terminé avec succès
Skip(1), // Ignoré par l'utilisateur
ItemChanged(2), // Annulé suite à changement d'objet
Failed(3), // Échec d'exécution
NotFinished(4); // Toujours en cours
}
```
| État | Valeur | Description |
|------|--------|-------------|
| `Finished` | 0 | Interaction terminée avec succès |
| `Skip` | 1 | L'utilisateur a demandé d'ignorer |
| `ItemChanged` | 2 | Annulé car l'objet tenu a changé |
| `Failed` | 3 | L'interaction a échoué |
| `NotFinished` | 4 | Toujours en cours, pas encore terminé |
## Catégories d'Interactions
### Interactions de Blocs
Opérations sur les blocs :
| Interaction | Description |
|-------------|-------------|
| `PlaceBlockInteraction` | Placer un bloc |
| `BreakBlockInteraction` | Casser un bloc |
| `DestroyBlockInteraction` | Détruire sans drops |
| `ChangeBlockInteraction` | Changer le type de bloc |
| `UseBlockInteraction` | Interagir avec un bloc |
| `PlaceFluidInteraction` | Placer un fluide |
### Interactions d'Objets
Opérations avec les objets :
| Interaction | Description |
|-------------|-------------|
| `AddItemInteraction` | Ajouter un objet à l'inventaire |
| `EquipItemInteraction` | Équiper un objet |
| `ModifyInventoryInteraction` | Modifier le contenu de l'inventaire |
### Interactions d'Entités
Opérations sur les entités :
| Interaction | Description |
|-------------|-------------|
| `UseEntityInteraction` | Interagir avec une entité |
| `RemoveEntityInteraction` | Supprimer une entité |
| `ApplyEffectInteraction` | Appliquer un effet de statut |
| `ClearEntityEffectInteraction` | Supprimer les effets |
### Interactions de Combat
Dégâts et combat :
| Interaction | Description |
|-------------|-------------|
| `DamageCalculator` | Calculer les dégâts |
| `DamageEffects` | Appliquer les effets de dégâts |
| `Knockback` | Appliquer le knockback |
| `DirectionalKnockback` | Knockback directionnel |
| `ForceKnockback` | Knockback basé sur la force |
### Interactions UI
Interface utilisateur :
| Interaction | Description |
|-------------|-------------|
| `OpenContainerInteraction` | Ouvrir l'UI de conteneur |
| `OpenPageInteraction` | Ouvrir une page |
| `OpenCustomUIInteraction` | Ouvrir une UI personnalisée |
| `SendMessageInteraction` | Envoyer un message au joueur |
### Interactions de Structures
Modification du monde :
| Interaction | Description |
|-------------|-------------|
| `SpawnPrefabInteraction` | Faire apparaître un prefab/structure |
| `LaunchProjectileInteraction` | Lancer un projectile |
### Flux de Contrôle
Interactions de chaînage et conditions :
| Interaction | Description |
|-------------|-------------|
| `SerialInteraction` | Exécuter les interactions en séquence |
| `ParallelInteraction` | Exécuter les interactions simultanément |
| `SelectInteraction` | Choisir entre les interactions |
| `ConditionInteraction` | Exécution conditionnelle |
| `RepeatInteraction` | Répéter une interaction |
| `CancelChainInteraction` | Annuler la chaîne |
### Interactions de Stats
Statistiques d'entités :
| Interaction | Description |
|-------------|-------------|
| `ChangeStatInteraction` | Modifier une stat |
| `StatsConditionInteraction` | Vérifier les conditions de stats |
## Propriétés d'Interaction
Propriétés communes à toutes les interactions :
```yaml
# Exemple de configuration d'interaction
MyInteraction:
Type: PlaceBlock
RunTime: 0.5 # Durée en secondes
ViewDistance: 96.0 # Distance de visibilité
HorizontalSpeedMultiplier: 1.0
CancelOnItemChange: true
Effects:
WaitForAnimationToFinish: true
ItemAnimationId: "swing"
Rules:
# Règles d'activation
```
### InteractionEffects
Effets visuels et audio :
- `ItemAnimationId` - Animation à jouer
- `WaitForAnimationToFinish` - Attendre l'animation
- Effets sonores
- Effets de particules
### InteractionRules
Contrôle quand les interactions peuvent s'exécuter :
- Restrictions de mode de jeu
- Exigences d'état
- Vérifications de cooldown
## Sélecteurs
Les sélecteurs déterminent quelles entités ou blocs sont ciblés :
| Sélecteur | Description |
|-----------|-------------|
| `RaycastSelector` | Lancer un rayon depuis la caméra |
| `AOECircleSelector` | Zone d'effet circulaire |
| `AOECylinderSelector` | Zone d'effet cylindrique |
| `StabSelector` | Estoc à courte portée |
| `HorizontalSelector` | Balayage horizontal |
## Accéder aux Interactions
### Depuis les Objets
Les objets définissent leurs interactions dans les fichiers d'assets. À l'exécution :
```java
ItemStack item = player.getInventory().getActiveItem();
Item itemAsset = item.getItem();
// L'objet définit ses interactions primaires/secondaires
```
### Depuis les Blocs
Les blocs définissent les interactions d'utilisation :
```java
BlockType blockType = world.getBlockType(position);
// Le bloc définit son interaction d'utilisation
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives d'Interaction :**
- Les interactions sont principalement pilotées par les données via les assets
- Utilisez les types d'interaction existants quand possible
- Chaînez les interactions pour des comportements complexes
- Utilisez les sélecteurs pour cibler de manière appropriée
- Considérez le mode de jeu dans les règles
{{< /callout >}}
{{< callout type="warning" >}}
**Note de Performance :** Les chaînes d'interaction complexes avec de nombreuses opérations parallèles peuvent impacter les performances. Gardez les chaînes efficaces.
{{< /callout >}}
## Lectures Complémentaires
Pour des informations détaillées sur les types d'interaction spécifiques, consultez les sous-pages.

View File

@@ -0,0 +1,270 @@
---
title: Selectors
type: docs
weight: 2
---
Selectors determine which entities or blocks are targeted by an interaction.
**Package:** `com.hypixel.hytale.server.core.modules.interaction.interaction.config.selector`
## Selector Types
### RaycastSelector
Casts a ray from the player's view to find targets:
```yaml
Selector:
Type: Raycast
Range: 5.0
BlockCollision: true
EntityCollision: true
```
Properties:
- `Range` - Maximum distance
- `BlockCollision` - Stop at blocks
- `EntityCollision` - Detect entities
### AOECircleSelector
Selects all targets in a circular area:
```yaml
Selector:
Type: AOECircle
Radius: 3.0
Height: 2.0
```
Properties:
- `Radius` - Circle radius
- `Height` - Vertical extent
### AOECylinderSelector
Selects targets in a cylindrical area:
```yaml
Selector:
Type: AOECylinder
Radius: 4.0
Height: 3.0
HeightOffset: 0.0
```
### StabSelector
Close-range forward attack:
```yaml
Selector:
Type: Stab
Range: 2.0
Width: 1.5
```
### HorizontalSelector
Horizontal sweep attack:
```yaml
Selector:
Type: Horizontal
Range: 3.0
Angle: 90.0
```
## Selector Interface
The `Selector` interface provides methods for targeting:
```java
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.function.consumer.TriIntConsumer;
import com.hypixel.hytale.math.vector.Vector4d;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
public interface Selector {
// Select target entities - consumer receives entity ref AND hit location
void selectTargetEntities(
CommandBuffer<EntityStore> buffer,
Ref<EntityStore> attacker,
BiConsumer<Ref<EntityStore>, Vector4d> consumer,
Predicate<Ref<EntityStore>> filter
);
// Select target blocks
void selectTargetBlocks(
CommandBuffer<EntityStore> buffer,
Ref<EntityStore> attacker,
TriIntConsumer consumer
);
// Update selector state each tick
void tick(
CommandBuffer<EntityStore> buffer,
Ref<EntityStore> entity,
float startTime,
float currentTime
);
}
```
{{< callout type="info" >}}
**Note:** The instance method `selectTargetEntities()` provides hit location via `Vector4d`, while the static `selectNearbyEntities()` only provides entity references.
{{< /callout >}}
## Entity Matchers
Filter which entities can be selected:
### VulnerableMatcher
Only select entities that can take damage:
```yaml
Matcher:
Type: Vulnerable
```
### PlayerMatcher
Only select players:
```yaml
Matcher:
Type: Player
```
## Static Selection Utilities
The `Selector` interface provides static methods for common operations:
### Select Nearby Blocks
```java
Selector.selectNearbyBlocks(
commandBuffer,
attackerRef,
range,
(x, y, z) -> {
// Process each block position
}
);
// Or from a position
Selector.selectNearbyBlocks(
position,
range,
(x, y, z) -> {
// Process each block position
}
);
```
### Select Nearby Entities
```java
import java.util.function.Consumer;
import java.util.function.Predicate;
Selector.selectNearbyEntities(
commandBuffer,
attackerRef,
range,
(Consumer<Ref<EntityStore>>) entityRef -> {
// Process each entity
},
(Predicate<Ref<EntityStore>>) entityRef -> {
// Filter predicate - return true to include
return true;
}
);
// Or from a position with ComponentAccessor
Selector.selectNearbyEntities(
componentAccessor,
position,
range,
entityRef -> {
// Process each entity
},
entityRef -> true // Filter or null
);
```
{{< callout type="info" >}}
**Note:** The consumer receives `Ref<EntityStore>` only. For entity hit positions (like `Vector4d`), use `selectTargetEntities()` from a Selector instance instead.
{{< /callout >}}
## Practical Examples
### Sword Slash
```yaml
SwordSlash:
Type: DamageCalculator
Selector:
Type: Horizontal
Range: 2.5
Angle: 120.0
Matcher:
Type: Vulnerable
Damage:
Base: 10.0
Type: Physical
```
### Healing Aura
```yaml
HealingAura:
Type: ChangeStatInteraction
Selector:
Type: AOECircle
Radius: 5.0
Height: 3.0
Matcher:
Type: Player
Stat: Health
Amount: 5.0
```
### Mining
```yaml
Mining:
Type: BreakBlock
Selector:
Type: Raycast
Range: 4.0
BlockCollision: true
EntityCollision: false
```
## Best Practices
{{< callout type="info" >}}
**Selector Guidelines:**
- Use appropriate range for the interaction type
- Apply filters to avoid unintended targets
- Consider performance with large AOE selectors
- Use raycast for precision attacks
- Always check `entity.isValid()` before accessing entity components
{{< /callout >}}
{{< callout type="warning" >}}
**Entity Validity:** Before interacting with an entity, verify it is still valid to prevent null pointer exceptions:
```java
if (entityRef.isValid()) {
// Safe to access entity components
// ... perform operations
}
```
The server validates entity references internally, but custom code should always check validity with `Ref.isValid()`.
{{< /callout >}}

View File

@@ -0,0 +1,270 @@
---
title: Sélecteurs
type: docs
weight: 2
---
Les sélecteurs déterminent quelles entités ou blocs sont ciblés par une interaction.
**Package:** `com.hypixel.hytale.server.core.modules.interaction.interaction.config.selector`
## Types de Sélecteurs
### RaycastSelector
Lance un rayon depuis la vue du joueur pour trouver des cibles :
```yaml
Selector:
Type: Raycast
Range: 5.0
BlockCollision: true
EntityCollision: true
```
Propriétés :
- `Range` - Distance maximale
- `BlockCollision` - S'arrêter aux blocs
- `EntityCollision` - Détecter les entités
### AOECircleSelector
Sélectionne toutes les cibles dans une zone circulaire :
```yaml
Selector:
Type: AOECircle
Radius: 3.0
Height: 2.0
```
Propriétés :
- `Radius` - Rayon du cercle
- `Height` - Étendue verticale
### AOECylinderSelector
Sélectionne les cibles dans une zone cylindrique :
```yaml
Selector:
Type: AOECylinder
Radius: 4.0
Height: 3.0
HeightOffset: 0.0
```
### StabSelector
Attaque vers l'avant à courte portée :
```yaml
Selector:
Type: Stab
Range: 2.0
Width: 1.5
```
### HorizontalSelector
Attaque en balayage horizontal :
```yaml
Selector:
Type: Horizontal
Range: 3.0
Angle: 90.0
```
## Interface Selector
L'interface `Selector` fournit des méthodes pour le ciblage :
```java
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.function.consumer.TriIntConsumer;
import com.hypixel.hytale.math.vector.Vector4d;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
public interface Selector {
// Sélectionner les entités cibles - le consumer reçoit la ref ET la position de frappe
void selectTargetEntities(
CommandBuffer<EntityStore> buffer,
Ref<EntityStore> attacker,
BiConsumer<Ref<EntityStore>, Vector4d> consumer,
Predicate<Ref<EntityStore>> filter
);
// Sélectionner les blocs cibles
void selectTargetBlocks(
CommandBuffer<EntityStore> buffer,
Ref<EntityStore> attacker,
TriIntConsumer consumer
);
// Mettre à jour l'état du sélecteur à chaque tick
void tick(
CommandBuffer<EntityStore> buffer,
Ref<EntityStore> entity,
float startTime,
float currentTime
);
}
```
{{< callout type="info" >}}
**Note :** La méthode d'instance `selectTargetEntities()` fournit la position de frappe via `Vector4d`, tandis que la méthode statique `selectNearbyEntities()` fournit uniquement les références d'entités.
{{< /callout >}}
## Filtres d'Entités
Filtrer quelles entités peuvent être sélectionnées :
### VulnerableMatcher
Sélectionner uniquement les entités pouvant subir des dégâts :
```yaml
Matcher:
Type: Vulnerable
```
### PlayerMatcher
Sélectionner uniquement les joueurs :
```yaml
Matcher:
Type: Player
```
## Utilitaires de Sélection Statique
L'interface `Selector` fournit des méthodes statiques pour les opérations courantes :
### Sélectionner les Blocs Proches
```java
Selector.selectNearbyBlocks(
commandBuffer,
attackerRef,
range,
(x, y, z) -> {
// Traiter chaque position de bloc
}
);
// Ou depuis une position
Selector.selectNearbyBlocks(
position,
range,
(x, y, z) -> {
// Traiter chaque position de bloc
}
);
```
### Sélectionner les Entités Proches
```java
import java.util.function.Consumer;
import java.util.function.Predicate;
Selector.selectNearbyEntities(
commandBuffer,
attackerRef,
range,
(Consumer<Ref<EntityStore>>) entityRef -> {
// Traiter chaque entité
},
(Predicate<Ref<EntityStore>>) entityRef -> {
// Prédicat de filtre - retourner true pour inclure
return true;
}
);
// Ou depuis une position avec ComponentAccessor
Selector.selectNearbyEntities(
componentAccessor,
position,
range,
entityRef -> {
// Traiter chaque entité
},
entityRef -> true // Filtre ou null
);
```
{{< callout type="info" >}}
**Note :** Le consumer reçoit uniquement `Ref<EntityStore>`. Pour les positions de frappe d'entité (comme `Vector4d`), utilisez plutôt `selectTargetEntities()` d'une instance de Selector.
{{< /callout >}}
## Exemples Pratiques
### Tranchant d'Épée
```yaml
SwordSlash:
Type: DamageCalculator
Selector:
Type: Horizontal
Range: 2.5
Angle: 120.0
Matcher:
Type: Vulnerable
Damage:
Base: 10.0
Type: Physical
```
### Aura de Soin
```yaml
HealingAura:
Type: ChangeStatInteraction
Selector:
Type: AOECircle
Radius: 5.0
Height: 3.0
Matcher:
Type: Player
Stat: Health
Amount: 5.0
```
### Minage
```yaml
Mining:
Type: BreakBlock
Selector:
Type: Raycast
Range: 4.0
BlockCollision: true
EntityCollision: false
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives des Sélecteurs :**
- Utilisez une portée appropriée pour le type d'interaction
- Appliquez des filtres pour éviter les cibles non voulues
- Considérez les performances avec les grands sélecteurs AOE
- Utilisez le raycast pour les attaques de précision
- Vérifiez toujours `entity.isValid()` avant d'accéder aux composants d'entité
{{< /callout >}}
{{< callout type="warning" >}}
**Validité des Entités :** Avant d'interagir avec une entité, vérifiez qu'elle est toujours valide pour éviter les exceptions de pointeur null :
```java
if (entityRef.isValid()) {
// Accès sécurisé aux composants de l'entité
// ... effectuer les opérations
}
```
Le serveur valide les références d'entité en interne, mais le code personnalisé doit toujours vérifier la validité avec `Ref.isValid()`.
{{< /callout >}}

View File

@@ -0,0 +1,215 @@
---
title: PlayerRef
type: docs
weight: 4
---
PlayerRef provides a thread-safe way to reference players across worlds and threads.
## Why PlayerRef?
{{< callout type="warning" >}}
Direct Player references should never be stored or used across threads. Always use PlayerRef for:
- Async operations
- Storing player references
- Cross-world player tracking
{{< /callout >}}
## Getting a PlayerRef
```java
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import java.util.UUID;
Player player = ...;
PlayerRef ref = player.getPlayerRef();
// PlayerRef contains essential player data
UUID uuid = ref.getUuid();
String name = ref.getUsername(); // Note: getUsername(), NOT getName()
```
## Using PlayerRef in Async Code
```java
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.component.Ref;
import java.util.concurrent.CompletableFuture;
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef ref = event.getPlayerRef();
World world = event.getWorld();
// Use ref in async operations
CompletableFuture.runAsync(() -> {
// Safe to use ref here
PlayerData data = loadPlayerData(ref.getUuid());
// Return to world thread to interact with player
if (world != null) {
world.execute(() -> {
Ref<EntityStore> entityRef = ref.getReference();
if (entityRef != null && entityRef.isValid()) {
applyData(entityRef, data);
}
// sendMessage is directly on PlayerRef
ref.sendMessage(Message.raw("Your data has been loaded!"));
});
}
});
});
```
## PlayerRef Methods
```java
PlayerRef ref = player.getPlayerRef();
// Get player UUID (always available)
UUID uuid = ref.getUuid();
// Get player username (always available)
// Note: It's getUsername(), NOT getName()
String name = ref.getUsername();
// Get entity reference
Ref<EntityStore> entityRef = ref.getReference();
// Check if reference is valid
boolean isValid = ref.isValid();
// Send message directly to player
ref.sendMessage(Message.raw("Hello!"));
// Get transform (position/rotation)
Transform transform = ref.getTransform();
```
{{< callout type="warning" >}}
**Note:** `PlayerRef.getPlayer()` does NOT exist. PlayerRef stores essential data that survives world changes, but doesn't hold a direct Player reference.
{{< /callout >}}
## World PlayerRefs
Worlds provide access to PlayerRefs:
```java
import com.hypixel.hytale.server.core.universe.Universe;
import java.util.Collection;
import java.util.List;
World world = Universe.get().getWorld("default");
// Get all player refs in this world
Collection<PlayerRef> playerRefs = world.getPlayerRefs();
// Get all active players (returns List<Player>)
List<Player> players = world.getPlayers();
```
## Storing Player References
```java
import com.hypixel.hytale.server.core.event.events.player.PlayerDisconnectEvent;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class MyPlugin extends JavaPlugin {
// Store PlayerRefs, NOT Players
private final Map<UUID, PlayerData> playerData = new ConcurrentHashMap<>();
public MyPlugin(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef ref = event.getPlayerRef();
playerData.put(ref.getUuid(), new PlayerData(ref));
});
getEventRegistry().register(PlayerDisconnectEvent.class, event -> {
playerData.remove(event.getPlayerRef().getUuid());
});
}
}
class PlayerData {
private final PlayerRef playerRef;
private int score;
public PlayerData(PlayerRef ref) {
this.playerRef = ref;
}
public void addScore(int amount) {
this.score += amount;
playerRef.sendMessage(Message.raw("Score: " + score));
}
}
```
## Cross-World Messaging
```java
// Send message to player regardless of their current world
public void sendGlobalMessage(PlayerRef ref, String message) {
// PlayerRef.sendMessage handles cross-world messaging directly
ref.sendMessage(Message.raw(message));
}
// Broadcast to all players in all worlds
public void broadcastAll(String message) {
Message msg = Message.raw(message);
for (World world : Universe.get().getWorlds().values()) {
for (PlayerRef ref : world.getPlayerRefs()) {
ref.sendMessage(msg);
}
}
}
```
## PlayerRef vs Player
| Feature | Player | PlayerRef |
|---------|--------|-----------|
| Thread-safe | No | Yes |
| Storable | No | Yes |
| Cross-world | No | Yes |
| Get UUID | Yes | Yes |
| Get Username | Yes (`getDisplayName()`) | Yes (`getUsername()`) |
| Send Message | Yes | Yes (directly) |
| Get Position | Via TransformComponent | Via `getTransform()` |
## Best Practices
{{< callout type="info" >}}
**Always use PlayerRef when:**
- Storing player references beyond the current method
- Passing to async tasks
- Tracking players across world changes
- Building player-related data structures
{{< /callout >}}
```java
// Good: Store PlayerRef
private final Map<UUID, PlayerRef> trackedPlayers = new ConcurrentHashMap<>();
// Bad: Store Player directly
// private final Map<UUID, Player> players = new HashMap<>(); // DON'T DO THIS
// Good: Use ref in async
CompletableFuture.runAsync(() -> {
PlayerRef ref = ...;
// ref is safe here
});
// Bad: Use Player in async
// CompletableFuture.runAsync(() -> {
// Player player = ...; // NOT SAFE
// });
```

View File

@@ -0,0 +1,215 @@
---
title: PlayerRef
type: docs
weight: 4
---
PlayerRef fournit un moyen thread-safe de référencer les joueurs à travers les mondes et les threads.
## Pourquoi PlayerRef ?
{{< callout type="warning" >}}
Les références directes à Player ne doivent jamais être stockées ou utilisées à travers les threads. Utilisez toujours PlayerRef pour :
- Les opérations asynchrones
- Le stockage de références de joueurs
- Le suivi des joueurs entre mondes
{{< /callout >}}
## Obtenir un PlayerRef
```java
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import java.util.UUID;
Player player = ...;
PlayerRef ref = player.getPlayerRef();
// PlayerRef contient les données essentielles du joueur
UUID uuid = ref.getUuid();
String name = ref.getUsername(); // Note : getUsername(), PAS getName()
```
## Utiliser PlayerRef dans le Code Async
```java
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.component.Ref;
import java.util.concurrent.CompletableFuture;
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef ref = event.getPlayerRef();
World world = event.getWorld();
// Utiliser ref dans les opérations async
CompletableFuture.runAsync(() -> {
// Sûr d'utiliser ref ici
PlayerData data = loadPlayerData(ref.getUuid());
// Retourner au thread du monde pour interagir avec le joueur
if (world != null) {
world.execute(() -> {
Ref<EntityStore> entityRef = ref.getReference();
if (entityRef != null && entityRef.isValid()) {
applyData(entityRef, data);
}
// sendMessage est directement sur PlayerRef
ref.sendMessage(Message.raw("Vos données ont été chargées !"));
});
}
});
});
```
## Méthodes de PlayerRef
```java
PlayerRef ref = player.getPlayerRef();
// Obtenir l'UUID du joueur (toujours disponible)
UUID uuid = ref.getUuid();
// Obtenir le nom d'utilisateur du joueur (toujours disponible)
// Note : C'est getUsername(), PAS getName()
String name = ref.getUsername();
// Obtenir la référence d'entité
Ref<EntityStore> entityRef = ref.getReference();
// Vérifier si la référence est valide
boolean isValid = ref.isValid();
// Envoyer un message directement au joueur
ref.sendMessage(Message.raw("Bonjour !"));
// Obtenir le transform (position/rotation)
Transform transform = ref.getTransform();
```
{{< callout type="warning" >}}
**Note :** `PlayerRef.getPlayer()` n'existe PAS. PlayerRef stocke les données essentielles qui survivent aux changements de monde, mais ne détient pas de référence Player directe.
{{< /callout >}}
## PlayerRefs du Monde
Les mondes fournissent l'accès aux PlayerRefs :
```java
import com.hypixel.hytale.server.core.universe.Universe;
import java.util.Collection;
import java.util.List;
World world = Universe.get().getWorld("default");
// Obtenir tous les refs de joueurs dans ce monde
Collection<PlayerRef> playerRefs = world.getPlayerRefs();
// Obtenir tous les joueurs actifs (retourne List<Player>)
List<Player> players = world.getPlayers();
```
## Stocker les Références de Joueurs
```java
import com.hypixel.hytale.server.core.event.events.player.PlayerDisconnectEvent;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class MyPlugin extends JavaPlugin {
// Stocker les PlayerRefs, PAS les Players
private final Map<UUID, PlayerData> playerData = new ConcurrentHashMap<>();
public MyPlugin(JavaPluginInit init) {
super(init);
}
@Override
public void setup() {
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef ref = event.getPlayerRef();
playerData.put(ref.getUuid(), new PlayerData(ref));
});
getEventRegistry().register(PlayerDisconnectEvent.class, event -> {
playerData.remove(event.getPlayerRef().getUuid());
});
}
}
class PlayerData {
private final PlayerRef playerRef;
private int score;
public PlayerData(PlayerRef ref) {
this.playerRef = ref;
}
public void addScore(int amount) {
this.score += amount;
playerRef.sendMessage(Message.raw("Score : " + score));
}
}
```
## Messagerie Inter-Mondes
```java
// Envoyer un message au joueur peu importe son monde actuel
public void sendGlobalMessage(PlayerRef ref, String message) {
// PlayerRef.sendMessage gère la messagerie inter-mondes directement
ref.sendMessage(Message.raw(message));
}
// Diffuser à tous les joueurs dans tous les mondes
public void broadcastAll(String message) {
Message msg = Message.raw(message);
for (World world : Universe.get().getWorlds().values()) {
for (PlayerRef ref : world.getPlayerRefs()) {
ref.sendMessage(msg);
}
}
}
```
## PlayerRef vs Player
| Fonctionnalité | Player | PlayerRef |
|----------------|--------|-----------|
| Thread-safe | Non | Oui |
| Stockable | Non | Oui |
| Inter-mondes | Non | Oui |
| Obtenir UUID | Oui | Oui |
| Obtenir Nom | Oui (`getDisplayName()`) | Oui (`getUsername()`) |
| Envoyer Message | Oui | Oui (directement) |
| Obtenir Position | Via TransformComponent | Via `getTransform()` |
## Bonnes Pratiques
{{< callout type="info" >}}
**Utilisez toujours PlayerRef quand :**
- Vous stockez des références de joueurs au-delà de la méthode actuelle
- Vous passez à des tâches asynchrones
- Vous suivez les joueurs à travers les changements de monde
- Vous construisez des structures de données liées aux joueurs
{{< /callout >}}
```java
// Bien : Stocker PlayerRef
private final Map<UUID, PlayerRef> trackedPlayers = new ConcurrentHashMap<>();
// Mauvais : Stocker Player directement
// private final Map<UUID, Player> players = new HashMap<>(); // NE FAITES PAS ÇA
// Bien : Utiliser ref en async
CompletableFuture.runAsync(() -> {
PlayerRef ref = ...;
// ref est sûr ici
});
// Mauvais : Utiliser Player en async
// CompletableFuture.runAsync(() -> {
// Player player = ...; // PAS SÛR
// });
```

View File

@@ -0,0 +1,147 @@
---
title: Portals
type: docs
weight: 6
---
The portals system provides mechanics for inter-dimensional travel between worlds, including portal devices, void events, and fragment instances.
**Package:** `com.hypixel.hytale.builtin.portals`
## Architecture
```
Portals System
├── Components
│ ├── PortalDevice - Portal block component
│ ├── VoidEvent - Void invasion event
│ └── VoidSpawner - Void spawner entity
├── Resources
│ └── PortalWorld - Portal world data
├── Interactions
│ ├── EnterPortalInteraction - Enter portal
│ └── ReturnPortalInteraction - Return from portal
├── Systems
│ ├── PortalTrackerSystems - UI tracking
│ ├── PortalInvalidDestinationSystem - Destination validation
│ ├── VoidEventStagesSystem - Void event progression
│ └── VoidSpawnerSystems - Spawner management
└── Commands
├── FragmentCommands - Fragment management
├── LeaveCommand - Leave portal world
└── VoidEventCommands - Void event control
```
## Core Concepts
### Portal Device
A portal device is a block-based entity that can transport players to fragment worlds:
```java
// PortalDevice is a ChunkStore component
PortalDevice portal = BlockModule.get().getComponent(
PortalDevice.getComponentType(),
world, x, y, z
);
// Get destination world
World targetWorld = portal.getDestinationWorld();
```
### Portal World
A portal world is a temporary instance created when players activate a portal:
```java
// PortalWorld is an EntityStore resource
PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType());
// Check if this is a portal world
if (portalWorld.exists()) {
Transform spawn = portalWorld.getSpawnPoint();
double remaining = portalWorld.getRemainingSeconds(world);
}
```
### Fragment Instances
Fragments are temporary worlds with:
- Time limit before auto-closure
- Spawn point for returning players
- Death tracking (players who died cannot re-enter)
- Optional void invasion events
## Key Features
### Portal States
Portals have three visual states:
- **Off** - Portal is inactive
- **Spawning** - Instance is being created
- **On/Active** - Portal is ready for travel
### Time Limits
Portal worlds have configurable time limits:
```java
int totalTime = portalWorld.getTimeLimitSeconds();
double elapsed = portalWorld.getElapsedSeconds(world);
double remaining = portalWorld.getRemainingSeconds(world);
```
### Death Tracking
Players who die in a portal world cannot re-enter:
```java
Set<UUID> deaths = portalWorld.getDiedInWorld();
if (deaths.contains(playerUuid)) {
// Player died here - show "DIED_IN_WORLD" state
}
```
### Cursed Items
Items can become "cursed" in portal worlds and are removed on death:
```java
// Uncurse items when returning
CursedItems.uncurseAll(inventory.getCombinedEverything());
```
## Plugin Access
```java
PortalsPlugin portals = PortalsPlugin.getInstance();
// Component types
ComponentType<ChunkStore, PortalDevice> portalDeviceType =
portals.getPortalDeviceComponentType();
ComponentType<EntityStore, VoidEvent> voidEventType =
portals.getVoidEventComponentType();
ComponentType<EntityStore, VoidSpawner> voidSpawnerType =
portals.getVoidPortalComponentType();
// Resource type
ResourceType<EntityStore, PortalWorld> portalWorldType =
portals.getPortalResourceType();
```
## Concurrent Fragments Limit
The system limits concurrent portal fragments:
```java
public static final int MAX_CONCURRENT_FRAGMENTS = 4;
int active = portals.countActiveFragments();
if (active >= MAX_CONCURRENT_FRAGMENTS) {
// Cannot create new fragments
}
```
## Related Topics
{{< cards >}}
{{< card link="portal-components" title="Portal Components" subtitle="PortalDevice, VoidEvent components" >}}
{{< card link="portal-systems" title="Portal Systems" subtitle="ECS systems for portal logic" >}}
{{< card link="portal-commands" title="Portal Commands" subtitle="Admin and player commands" >}}
{{< /cards >}}

View File

@@ -0,0 +1,147 @@
---
title: Portails
type: docs
weight: 6
---
Le systeme de portails fournit des mecaniques de voyage inter-dimensionnel entre les mondes, incluant les dispositifs de portail, les evenements du vide et les instances de fragment.
**Package:** `com.hypixel.hytale.builtin.portals`
## Architecture
```
Systeme Portails
├── Composants
│ ├── PortalDevice - Composant bloc portail
│ ├── VoidEvent - Evenement invasion du vide
│ └── VoidSpawner - Entite spawner du vide
├── Ressources
│ └── PortalWorld - Donnees monde portail
├── Interactions
│ ├── EnterPortalInteraction - Entrer portail
│ └── ReturnPortalInteraction - Retour du portail
├── Systemes
│ ├── PortalTrackerSystems - Suivi UI
│ ├── PortalInvalidDestinationSystem - Validation destination
│ ├── VoidEventStagesSystem - Progression evenement vide
│ └── VoidSpawnerSystems - Gestion spawners
└── Commandes
├── FragmentCommands - Gestion fragments
├── LeaveCommand - Quitter monde portail
└── VoidEventCommands - Controle evenement vide
```
## Concepts Cles
### Dispositif Portail
Un dispositif de portail est une entite basee sur un bloc qui peut transporter les joueurs vers des mondes fragments:
```java
// PortalDevice est un composant ChunkStore
PortalDevice portal = BlockModule.get().getComponent(
PortalDevice.getComponentType(),
world, x, y, z
);
// Obtenir monde de destination
World targetWorld = portal.getDestinationWorld();
```
### Monde Portail
Un monde portail est une instance temporaire creee quand les joueurs activent un portail:
```java
// PortalWorld est une ressource EntityStore
PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType());
// Verifier si c'est un monde portail
if (portalWorld.exists()) {
Transform spawn = portalWorld.getSpawnPoint();
double remaining = portalWorld.getRemainingSeconds(world);
}
```
### Instances Fragment
Les fragments sont des mondes temporaires avec:
- Limite de temps avant fermeture automatique
- Point de spawn pour les joueurs revenant
- Suivi des morts (les joueurs morts ne peuvent pas re-entrer)
- Evenements d'invasion du vide optionnels
## Fonctionnalites Cles
### Etats du Portail
Les portails ont trois etats visuels:
- **Eteint** - Portail inactif
- **Invocation** - Instance en creation
- **Actif** - Portail pret pour le voyage
### Limites de Temps
Les mondes portails ont des limites de temps configurables:
```java
int totalTime = portalWorld.getTimeLimitSeconds();
double elapsed = portalWorld.getElapsedSeconds(world);
double remaining = portalWorld.getRemainingSeconds(world);
```
### Suivi des Morts
Les joueurs morts dans un monde portail ne peuvent pas re-entrer:
```java
Set<UUID> deaths = portalWorld.getDiedInWorld();
if (deaths.contains(playerUuid)) {
// Joueur mort ici - afficher etat "DIED_IN_WORLD"
}
```
### Items Maudits
Les items peuvent devenir "maudits" dans les mondes portails et sont retires a la mort:
```java
// Retirer malediction des items au retour
CursedItems.uncurseAll(inventory.getCombinedEverything());
```
## Acces au Plugin
```java
PortalsPlugin portals = PortalsPlugin.getInstance();
// Types de composants
ComponentType<ChunkStore, PortalDevice> portalDeviceType =
portals.getPortalDeviceComponentType();
ComponentType<EntityStore, VoidEvent> voidEventType =
portals.getVoidEventComponentType();
ComponentType<EntityStore, VoidSpawner> voidSpawnerType =
portals.getVoidPortalComponentType();
// Type de ressource
ResourceType<EntityStore, PortalWorld> portalWorldType =
portals.getPortalResourceType();
```
## Limite Fragments Concurrents
Le systeme limite les fragments portails concurrents:
```java
public static final int MAX_CONCURRENT_FRAGMENTS = 4;
int active = portals.countActiveFragments();
if (active >= MAX_CONCURRENT_FRAGMENTS) {
// Impossible de creer nouveaux fragments
}
```
## Sujets Connexes
{{< cards >}}
{{< card link="portal-components" title="Composants Portail" subtitle="Composants PortalDevice, VoidEvent" >}}
{{< card link="portal-systems" title="Systemes Portail" subtitle="Systemes ECS pour logique portail" >}}
{{< card link="portal-commands" title="Commandes Portail" subtitle="Commandes admin et joueur" >}}
{{< /cards >}}

View File

@@ -0,0 +1,224 @@
---
title: Portal Commands
type: docs
weight: 3
---
Commands for managing portals, fragments, and void events.
**Package:** `com.hypixel.hytale.builtin.portals.commands`
## Player Commands
### /leave
Leave the current portal world and return to the origin world.
```
/leave
```
**Behavior:**
- Only works when inside a portal world
- Uncurses all items in inventory before leaving
- Teleports player back to the portal device location
**Implementation:**
```java
public class LeaveCommand extends AbstractPlayerCommand {
@Override
protected void execute(CommandContext context, Store<EntityStore> store,
Ref<EntityStore> ref, PlayerRef playerRef, World world) {
PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType());
if (!portalWorld.exists()) {
playerRef.sendMessage(MESSAGE_NOT_IN_PORTAL);
return;
}
// Remove curse from all items
CursedItems.uncurseAll(player.getInventory().getCombinedEverything());
// Exit the instance
InstancesPlugin.exitInstance(ref, store);
}
}
```
## Fragment Commands
### /fragment
Parent command for fragment management.
```
/fragment <subcommand>
```
| Subcommand | Description |
|------------|-------------|
| `timer` | Modify fragment time remaining |
### /fragment timer
Set the remaining time for the current portal fragment.
```
/fragment timer <seconds>
```
**Arguments:**
| Argument | Type | Description |
|----------|------|-------------|
| `seconds` | INTEGER | New remaining time in seconds |
**Example:**
```
/fragment timer 300 # Set 5 minutes remaining
/fragment timer 60 # Set 1 minute remaining
```
**Implementation:**
```java
public class TimerFragmentCommand extends PortalWorldCommandBase {
private final RequiredArg<Integer> remainingSecondsArg;
@Override
protected void execute(CommandContext context, World world,
PortalWorld portalWorld, Store<EntityStore> store) {
int before = (int) portalWorld.getRemainingSeconds(world);
int desired = remainingSecondsArg.get(context);
portalWorld.setRemainingSeconds(world, desired);
// Reports: "Changed from {before} to {after}"
}
}
```
## Void Event Commands
### /voidevent
Parent command for void event management.
```
/voidevent <subcommand>
```
| Subcommand | Description |
|------------|-------------|
| `start` | Start a void event |
### /voidevent start
Start a void invasion event in the current world.
```
/voidevent start [--override]
```
**Flags:**
| Flag | Description |
|------|-------------|
| `--override` | Force start even if not in a portal world |
**Behavior:**
- If already in a portal world with void event active: fails
- If not in portal world without `--override`: fails
- With `--override`: creates temporary portal world configuration
- Sets remaining time to 1 second to trigger void event
**Implementation:**
```java
public class StartVoidEventCommand extends AbstractWorldCommand {
private final FlagArg overrideWorld;
@Override
protected void execute(CommandContext context, World world,
Store<EntityStore> store) {
PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType());
// Check if already running
if (portalWorld.exists() && portalWorld.isVoidEventActive()) {
context.sendMessage("Void event already running");
return;
}
// If not in portal world, require --override
if (!portalWorld.exists()) {
if (!overrideWorld.get(context)) {
context.sendMessage("Not in portal world");
return;
}
// Set up temporary portal world config
portalWorld.init(portalType, timeLimit, removalCondition, config);
}
// Trigger void event by setting timer to 1 second
portalWorld.setRemainingSeconds(world, 1.0);
}
}
```
## Utility Commands
### /cursehelditem
Mark the currently held item as cursed (debug/testing command).
```
/cursehelditem
```
**Behavior:**
- Adds curse marker to item in main hand
- Cursed items are lost when player dies in portal world
## Command Base Classes
### PortalWorldCommandBase
Base class for commands that require a portal world:
```java
public abstract class PortalWorldCommandBase extends AbstractWorldCommand {
@Override
protected final void execute(CommandContext context, World world,
Store<EntityStore> store) {
PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType());
if (!portalWorld.exists()) {
context.sendMessage(MESSAGE_NOT_IN_PORTAL);
return;
}
execute(context, world, portalWorld, store);
}
protected abstract void execute(CommandContext context, World world,
PortalWorld portalWorld,
Store<EntityStore> store);
}
```
## Command Registration
Commands are registered in PortalsPlugin setup:
```java
this.getCommandRegistry().registerCommand(new LeaveCommand());
this.getCommandRegistry().registerCommand(new CursedHeldItemCommand());
this.getCommandRegistry().registerCommand(new VoidEventCommands());
this.getCommandRegistry().registerCommand(new FragmentCommands());
```
## Messages
Portal commands use translation keys for messages:
| Key | Description |
|-----|-------------|
| `server.commands.leave.notInPortal` | Not in a portal world |
| `server.commands.leave.uncursedTemp` | Items were uncursed |
| `server.commands.portals.notInPortal` | Not in portal world |
| `server.commands.voidevent.start.alreadyRunning` | Void event active |
| `server.commands.voidevent.start.success` | Void event started |
| `server.commands.fragment.timer.success` | Timer changed |

View File

@@ -0,0 +1,224 @@
---
title: Commandes Portail
type: docs
weight: 3
---
Commandes pour gerer les portails, fragments et evenements du vide.
**Package:** `com.hypixel.hytale.builtin.portals.commands`
## Commandes Joueur
### /leave
Quitter le monde portail actuel et retourner au monde d'origine.
```
/leave
```
**Comportement:**
- Fonctionne uniquement dans un monde portail
- Retire la malediction de tous les items avant de partir
- Teleporte le joueur vers l'emplacement du dispositif portail
**Implementation:**
```java
public class LeaveCommand extends AbstractPlayerCommand {
@Override
protected void execute(CommandContext context, Store<EntityStore> store,
Ref<EntityStore> ref, PlayerRef playerRef, World world) {
PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType());
if (!portalWorld.exists()) {
playerRef.sendMessage(MESSAGE_NOT_IN_PORTAL);
return;
}
// Retirer malediction de tous items
CursedItems.uncurseAll(player.getInventory().getCombinedEverything());
// Sortir de l'instance
InstancesPlugin.exitInstance(ref, store);
}
}
```
## Commandes Fragment
### /fragment
Commande parente pour gestion fragments.
```
/fragment <sous-commande>
```
| Sous-commande | Description |
|---------------|-------------|
| `timer` | Modifier temps restant fragment |
### /fragment timer
Definir le temps restant pour le fragment portail actuel.
```
/fragment timer <secondes>
```
**Arguments:**
| Argument | Type | Description |
|----------|------|-------------|
| `secondes` | INTEGER | Nouveau temps restant en secondes |
**Exemple:**
```
/fragment timer 300 # Definir 5 minutes restantes
/fragment timer 60 # Definir 1 minute restante
```
**Implementation:**
```java
public class TimerFragmentCommand extends PortalWorldCommandBase {
private final RequiredArg<Integer> remainingSecondsArg;
@Override
protected void execute(CommandContext context, World world,
PortalWorld portalWorld, Store<EntityStore> store) {
int before = (int) portalWorld.getRemainingSeconds(world);
int desired = remainingSecondsArg.get(context);
portalWorld.setRemainingSeconds(world, desired);
// Rapporte: "Change de {before} a {after}"
}
}
```
## Commandes Evenement Vide
### /voidevent
Commande parente pour gestion evenement vide.
```
/voidevent <sous-commande>
```
| Sous-commande | Description |
|---------------|-------------|
| `start` | Demarrer un evenement vide |
### /voidevent start
Demarrer un evenement invasion du vide dans le monde actuel.
```
/voidevent start [--override]
```
**Flags:**
| Flag | Description |
|------|-------------|
| `--override` | Forcer demarrage meme si pas dans monde portail |
**Comportement:**
- Si deja dans monde portail avec evenement vide actif: echoue
- Si pas dans monde portail sans `--override`: echoue
- Avec `--override`: cree configuration monde portail temporaire
- Met temps restant a 1 seconde pour declencher evenement vide
**Implementation:**
```java
public class StartVoidEventCommand extends AbstractWorldCommand {
private final FlagArg overrideWorld;
@Override
protected void execute(CommandContext context, World world,
Store<EntityStore> store) {
PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType());
// Verifier si deja en cours
if (portalWorld.exists() && portalWorld.isVoidEventActive()) {
context.sendMessage("Evenement vide deja en cours");
return;
}
// Si pas dans monde portail, requiert --override
if (!portalWorld.exists()) {
if (!overrideWorld.get(context)) {
context.sendMessage("Pas dans monde portail");
return;
}
// Configurer monde portail temporaire
portalWorld.init(portalType, timeLimit, removalCondition, config);
}
// Declencher evenement vide en mettant timer a 1 seconde
portalWorld.setRemainingSeconds(world, 1.0);
}
}
```
## Commandes Utilitaires
### /cursehelditem
Marquer l'item tenu comme maudit (commande debug/test).
```
/cursehelditem
```
**Comportement:**
- Ajoute marqueur malediction a l'item en main principale
- Items maudits sont perdus quand joueur meurt dans monde portail
## Classes de Base Commandes
### PortalWorldCommandBase
Classe de base pour commandes necessitant un monde portail:
```java
public abstract class PortalWorldCommandBase extends AbstractWorldCommand {
@Override
protected final void execute(CommandContext context, World world,
Store<EntityStore> store) {
PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType());
if (!portalWorld.exists()) {
context.sendMessage(MESSAGE_NOT_IN_PORTAL);
return;
}
execute(context, world, portalWorld, store);
}
protected abstract void execute(CommandContext context, World world,
PortalWorld portalWorld,
Store<EntityStore> store);
}
```
## Enregistrement Commandes
Les commandes sont enregistrees dans setup PortalsPlugin:
```java
this.getCommandRegistry().registerCommand(new LeaveCommand());
this.getCommandRegistry().registerCommand(new CursedHeldItemCommand());
this.getCommandRegistry().registerCommand(new VoidEventCommands());
this.getCommandRegistry().registerCommand(new FragmentCommands());
```
## Messages
Les commandes portail utilisent des cles de traduction pour les messages:
| Cle | Description |
|-----|-------------|
| `server.commands.leave.notInPortal` | Pas dans monde portail |
| `server.commands.leave.uncursedTemp` | Items ont ete decursifies |
| `server.commands.portals.notInPortal` | Pas dans monde portail |
| `server.commands.voidevent.start.alreadyRunning` | Evenement vide actif |
| `server.commands.voidevent.start.success` | Evenement vide demarre |
| `server.commands.fragment.timer.success` | Timer modifie |

View File

@@ -0,0 +1,270 @@
---
title: Portal Components
type: docs
weight: 1
---
Portal components define the data structures for portal devices, portal worlds, and void events.
**Package:** `com.hypixel.hytale.builtin.portals.components`
## PortalDevice
A chunk-level component representing a portal block device.
```java
public class PortalDevice implements Component<ChunkStore> {
// Configuration for portal states
private PortalDeviceConfig config;
// Block type key for the portal device
private String baseBlockTypeKey;
// UUID of the destination world (if active)
private UUID destinationWorldUuid;
}
```
### Accessing Portal Devices
```java
// Get portal device at block position
PortalDevice portal = BlockModule.get().getComponent(
PortalDevice.getComponentType(),
world, x, y, z
);
if (portal != null) {
// Portal exists at this location
BlockType blockType = portal.getBaseBlockType();
World destination = portal.getDestinationWorld();
}
```
### Destination World
```java
// Set destination when activating portal
portal.setDestinationWorld(targetWorld);
// Get destination (null if inactive or world closed)
World destination = portal.getDestinationWorld();
if (destination == null || !destination.isAlive()) {
// Portal destination invalid
}
```
## PortalDeviceConfig
Configuration for portal device visual states.
```java
public class PortalDeviceConfig {
// State when instance is being created
private String spawningState = "Spawning";
// State when portal is active
private String onState = "Active";
// State when portal is inactive
private String offState = "default";
// Block placed at spawn point in portal world
private String returnBlock;
}
```
### State Validation
```java
// Verify all states exist on block type
PortalDeviceConfig config = portal.getConfig();
if (config.areBlockStatesValid(baseBlockType)) {
// All states (on, off, spawning) are valid
}
```
### State Names
| State | Description | Default |
|-------|-------------|---------|
| `spawningState` | Transition from off to on | "Spawning" |
| `onState` | Portal is active | "Active" |
| `offState` | Portal is inactive | "default" |
## PortalWorld
A resource attached to worlds created by portals.
```java
public class PortalWorld implements Resource<EntityStore> {
// Portal type configuration ID
private String portalTypeId;
// Time limit in seconds
private int timeLimitSeconds;
// Removal condition handler
private PortalRemovalCondition worldRemovalCondition;
// Players who died in this world
private Set<UUID> diedInWorld;
// Players currently seeing the UI
private Set<UUID> seesUi;
// Spawn point transform
private Transform spawnPoint;
// Reference to active void event
private Ref<EntityStore> voidEventRef;
}
```
### Checking Portal World
```java
// Get portal world resource
PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType());
// Check if this is a valid portal world
if (portalWorld.exists()) {
PortalType type = portalWorld.getPortalType();
int timeLimit = portalWorld.getTimeLimitSeconds();
}
```
### Time Management
```java
// Get elapsed and remaining time
double elapsed = portalWorld.getElapsedSeconds(world);
double remaining = portalWorld.getRemainingSeconds(world);
// Modify remaining time (e.g., extend timer)
portalWorld.setRemainingSeconds(world, 300.0); // 5 minutes
```
### Death Tracking
```java
// Check if player died in this world
if (portalWorld.getDiedInWorld().contains(playerUuid)) {
// Cannot re-enter - died here
}
// Track a death
portalWorld.getDiedInWorld().add(playerUuid);
```
### UI State
```java
// Track players seeing the portal UI
portalWorld.getSeesUi().add(playerUuid);
portalWorld.getSeesUi().remove(playerUuid);
```
## VoidEvent
Component for void invasion events in portal worlds.
```java
public class VoidEvent implements Component<EntityStore> {
// Minimum distance between spawners
public static final double MIN_BLOCKS_BETWEEN_SPAWNERS = 62.0;
// Spatial grid of void spawners
private SpatialHashGrid<Ref<EntityStore>> voidSpawners;
// Current active stage
private VoidEventStage activeStage;
}
```
### Void Event Access
```java
// Check if void event is active
Ref<EntityStore> voidRef = portalWorld.getVoidEventRef();
if (portalWorld.isVoidEventActive()) {
VoidEvent voidEvent = store.getComponent(voidRef, VoidEvent.getComponentType());
VoidEventStage stage = voidEvent.getActiveStage();
}
```
### Void Spawner Grid
```java
// Get spawner grid
SpatialHashGrid<Ref<EntityStore>> spawners = voidEvent.getVoidSpawners();
// Spawners maintain minimum distance of 62 blocks
```
## VoidEventConfig
Configuration for void events.
```java
public class VoidEventConfig {
// Duration of void event in seconds
private int durationSeconds;
// Stages of the void event
private List<VoidEventStage> stages;
}
```
## VoidEventStage
Single stage in a void event progression.
```java
public class VoidEventStage {
// When this stage starts (seconds from event start)
private int startTime;
// Spawners to activate
private List<VoidSpawnerConfig> spawners;
}
```
## VoidSpawner
Entity component for void invasion spawners.
```java
public class VoidSpawner implements Component<EntityStore> {
// Spawner configuration
// Tracks active spawning state
}
```
## Component Registration
Components are registered in PortalsPlugin setup:
```java
// In PortalsPlugin.setup()
this.portalDeviceComponentType = this.getChunkStoreRegistry()
.registerComponent(PortalDevice.class, "Portal", PortalDevice.CODEC);
this.voidEventComponentType = this.getEntityStoreRegistry()
.registerComponent(VoidEvent.class, VoidEvent::new);
this.voidPortalComponentType = this.getEntityStoreRegistry()
.registerComponent(VoidSpawner.class, VoidSpawner::new);
```
## Serialization
PortalDevice uses BuilderCodec for persistence:
```java
public static final BuilderCodec<PortalDevice> CODEC = BuilderCodec.builder(...)
.append(new KeyedCodec<>("Config", PortalDeviceConfig.CODEC), ...)
.append(new KeyedCodec<>("BaseBlockType", Codec.STRING), ...)
.append(new KeyedCodec<>("DestinationWorld", Codec.UUID_BINARY), ...)
.build();
```

View File

@@ -0,0 +1,270 @@
---
title: Composants Portail
type: docs
weight: 1
---
Les composants portail definissent les structures de donnees pour les dispositifs portail, les mondes portail et les evenements du vide.
**Package:** `com.hypixel.hytale.builtin.portals.components`
## PortalDevice
Un composant au niveau chunk representant un bloc dispositif portail.
```java
public class PortalDevice implements Component<ChunkStore> {
// Configuration pour etats du portail
private PortalDeviceConfig config;
// Cle type de bloc pour le dispositif portail
private String baseBlockTypeKey;
// UUID du monde de destination (si actif)
private UUID destinationWorldUuid;
}
```
### Acceder aux Dispositifs Portail
```java
// Obtenir dispositif portail a position bloc
PortalDevice portal = BlockModule.get().getComponent(
PortalDevice.getComponentType(),
world, x, y, z
);
if (portal != null) {
// Portail existe a cet emplacement
BlockType blockType = portal.getBaseBlockType();
World destination = portal.getDestinationWorld();
}
```
### Monde de Destination
```java
// Definir destination lors activation portail
portal.setDestinationWorld(targetWorld);
// Obtenir destination (null si inactif ou monde ferme)
World destination = portal.getDestinationWorld();
if (destination == null || !destination.isAlive()) {
// Destination portail invalide
}
```
## PortalDeviceConfig
Configuration pour les etats visuels du dispositif portail.
```java
public class PortalDeviceConfig {
// Etat quand instance en creation
private String spawningState = "Spawning";
// Etat quand portail actif
private String onState = "Active";
// Etat quand portail inactif
private String offState = "default";
// Bloc place au point de spawn dans monde portail
private String returnBlock;
}
```
### Validation des Etats
```java
// Verifier que tous les etats existent sur type de bloc
PortalDeviceConfig config = portal.getConfig();
if (config.areBlockStatesValid(baseBlockType)) {
// Tous les etats (on, off, spawning) sont valides
}
```
### Noms des Etats
| Etat | Description | Defaut |
|------|-------------|--------|
| `spawningState` | Transition de eteint a actif | "Spawning" |
| `onState` | Portail actif | "Active" |
| `offState` | Portail inactif | "default" |
## PortalWorld
Une ressource attachee aux mondes crees par les portails.
```java
public class PortalWorld implements Resource<EntityStore> {
// ID configuration type portail
private String portalTypeId;
// Limite temps en secondes
private int timeLimitSeconds;
// Handler condition de suppression
private PortalRemovalCondition worldRemovalCondition;
// Joueurs morts dans ce monde
private Set<UUID> diedInWorld;
// Joueurs voyant actuellement l'UI
private Set<UUID> seesUi;
// Transform point de spawn
private Transform spawnPoint;
// Reference vers evenement vide actif
private Ref<EntityStore> voidEventRef;
}
```
### Verifier Monde Portail
```java
// Obtenir ressource monde portail
PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType());
// Verifier si c'est un monde portail valide
if (portalWorld.exists()) {
PortalType type = portalWorld.getPortalType();
int timeLimit = portalWorld.getTimeLimitSeconds();
}
```
### Gestion du Temps
```java
// Obtenir temps ecoule et restant
double elapsed = portalWorld.getElapsedSeconds(world);
double remaining = portalWorld.getRemainingSeconds(world);
// Modifier temps restant (ex: etendre timer)
portalWorld.setRemainingSeconds(world, 300.0); // 5 minutes
```
### Suivi des Morts
```java
// Verifier si joueur mort dans ce monde
if (portalWorld.getDiedInWorld().contains(playerUuid)) {
// Ne peut pas re-entrer - mort ici
}
// Enregistrer une mort
portalWorld.getDiedInWorld().add(playerUuid);
```
### Etat UI
```java
// Suivre joueurs voyant l'UI portail
portalWorld.getSeesUi().add(playerUuid);
portalWorld.getSeesUi().remove(playerUuid);
```
## VoidEvent
Composant pour evenements invasion du vide dans mondes portail.
```java
public class VoidEvent implements Component<EntityStore> {
// Distance minimum entre spawners
public static final double MIN_BLOCKS_BETWEEN_SPAWNERS = 62.0;
// Grille spatiale des spawners du vide
private SpatialHashGrid<Ref<EntityStore>> voidSpawners;
// Etape active actuelle
private VoidEventStage activeStage;
}
```
### Acces Evenement Vide
```java
// Verifier si evenement vide actif
Ref<EntityStore> voidRef = portalWorld.getVoidEventRef();
if (portalWorld.isVoidEventActive()) {
VoidEvent voidEvent = store.getComponent(voidRef, VoidEvent.getComponentType());
VoidEventStage stage = voidEvent.getActiveStage();
}
```
### Grille Spawners Vide
```java
// Obtenir grille spawners
SpatialHashGrid<Ref<EntityStore>> spawners = voidEvent.getVoidSpawners();
// Spawners maintiennent distance minimum de 62 blocs
```
## VoidEventConfig
Configuration pour evenements du vide.
```java
public class VoidEventConfig {
// Duree evenement vide en secondes
private int durationSeconds;
// Etapes de l'evenement vide
private List<VoidEventStage> stages;
}
```
## VoidEventStage
Etape unique dans progression evenement vide.
```java
public class VoidEventStage {
// Quand cette etape demarre (secondes depuis debut evenement)
private int startTime;
// Spawners a activer
private List<VoidSpawnerConfig> spawners;
}
```
## VoidSpawner
Composant entite pour spawners invasion du vide.
```java
public class VoidSpawner implements Component<EntityStore> {
// Configuration spawner
// Suit etat de spawn actif
}
```
## Enregistrement Composants
Les composants sont enregistres dans setup PortalsPlugin:
```java
// Dans PortalsPlugin.setup()
this.portalDeviceComponentType = this.getChunkStoreRegistry()
.registerComponent(PortalDevice.class, "Portal", PortalDevice.CODEC);
this.voidEventComponentType = this.getEntityStoreRegistry()
.registerComponent(VoidEvent.class, VoidEvent::new);
this.voidPortalComponentType = this.getEntityStoreRegistry()
.registerComponent(VoidSpawner.class, VoidSpawner::new);
```
## Serialisation
PortalDevice utilise BuilderCodec pour persistance:
```java
public static final BuilderCodec<PortalDevice> CODEC = BuilderCodec.builder(...)
.append(new KeyedCodec<>("Config", PortalDeviceConfig.CODEC), ...)
.append(new KeyedCodec<>("BaseBlockType", Codec.STRING), ...)
.append(new KeyedCodec<>("DestinationWorld", Codec.UUID_BINARY), ...)
.build();
```

View File

@@ -0,0 +1,280 @@
---
title: Portal Systems
type: docs
weight: 2
---
Portal systems handle the runtime logic for portal tracking, void events, and instance management.
**Package:** `com.hypixel.hytale.builtin.portals.systems`
## Portal Tracker Systems
### TrackerSystem
Tracks players entering and leaving portal worlds:
```java
public class TrackerSystem extends RefSystem<EntityStore> {
// Called when player enters portal world
@Override
public void onEntityAdded(Ref<EntityStore> ref, AddReason reason,
Store<EntityStore> store,
CommandBuffer<EntityStore> commandBuffer) {
PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType());
if (!portalWorld.exists()) return;
// Send portal UI to player
PlayerRef playerRef = commandBuffer.getComponent(ref, PlayerRef.getComponentType());
UpdatePortal packet = portalWorld.createFullPacket(world);
playerRef.getPacketHandler().write(packet);
}
// Called when player leaves portal world
@Override
public void onEntityRemove(Ref<EntityStore> ref, RemoveReason reason,
Store<EntityStore> store,
CommandBuffer<EntityStore> commandBuffer) {
// Clear UI state
playerRef.getPacketHandler().write(new UpdatePortal(null, null));
portalWorld.getSeesUi().remove(playerRef.getUuid());
}
@Override
public Query<EntityStore> getQuery() {
return Query.and(Player.getComponentType(), PlayerRef.getComponentType());
}
}
```
### UiTickingSystem
Updates portal UI every second:
```java
public class UiTickingSystem extends DelayedEntitySystem<EntityStore> {
public UiTickingSystem() {
super(1.0f); // Tick every 1 second
}
@Override
public void tick(float dt, int index, ArchetypeChunk<EntityStore> archetypeChunk,
Store<EntityStore> store, CommandBuffer<EntityStore> commandBuffer) {
PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType());
if (!portalWorld.exists()) return;
// Send updated time to player
UpdatePortal packet = portalWorld.createUpdatePacket(world);
playerRef.getPacketHandler().write(packet);
}
}
```
## Portal Destination Systems
### PortalInvalidDestinationSystem
Handles portals with invalid destinations:
```java
public class PortalInvalidDestinationSystem {
// Turns off portals when destination world closes
public static void turnOffPortalsInWorld(World world, World closedWorld) {
// Find all portals pointing to closedWorld
// Set their destination to null
// Update block state to "off"
}
}
```
### CloseWorldWhenBreakingDeviceSystems
Handles portal device destruction:
```java
// When portal device component is removed
public class ComponentRemoved extends System<ChunkStore> {
// Close the destination world
}
// When portal block entity is removed
public class EntityRemoved extends System<ChunkStore> {
// Close the destination world
}
```
## Void Event Systems
### VoidEventRefSystem
Manages void event entity references:
```java
public class VoidEventRefSystem extends System<EntityStore> {
// Tracks void event entity lifecycle
// Updates PortalWorld.voidEventRef
}
```
### VoidEventStagesSystem
Progresses through void event stages:
```java
public class VoidEventStagesSystem extends System<EntityStore> {
// Checks elapsed time
// Activates next stage when time threshold reached
// Updates VoidEvent.activeStage
}
```
### VoidInvasionPortalsSpawnSystem
Spawns invasion portals during void events:
```java
public class VoidInvasionPortalsSpawnSystem extends System<EntityStore> {
// Creates void spawner entities
// Uses spatial hash grid to maintain minimum distance
}
```
### VoidSpawnerSystems.Instantiate
Instantiates void spawner entities:
```java
public class Instantiate extends System<EntityStore> {
// Creates spawner entity from config
// Adds to VoidEvent's spatial grid
}
```
### StartVoidEventInFragmentSystem
Initiates void events in portal fragments:
```java
public class StartVoidEventInFragmentSystem extends System<EntityStore> {
// Checks if void invasion should start
// Creates VoidEvent entity
// Sets up first stage
}
```
## Curse Systems
### DiedInPortalSystem
Tracks player deaths in portal worlds:
```java
public class DiedInPortalSystem extends System<EntityStore> {
// On player death in portal world:
// - Add player UUID to diedInWorld set
// - Prevents re-entry
}
```
### CurseItemDropsSystem
Marks dropped items as cursed:
```java
public class CurseItemDropsSystem extends System<EntityStore> {
// Items dropped in portal world become cursed
// Cursed items are lost on death
}
```
### DeleteCursedItemsOnSpawnSystem
Removes cursed items when player spawns:
```java
public class DeleteCursedItemsOnSpawnSystem extends System<EntityStore> {
// When player respawns after dying in portal
// Remove all cursed items from inventory
}
```
## Portal Interactions
### EnterPortalInteraction
Handles entering a portal:
```java
public class EnterPortalInteraction extends SimpleBlockInteraction {
// Minimum time before allowing portal use
public static final Duration MINIMUM_TIME_IN_WORLD = Duration.ofMillis(3000L);
@Override
protected void interactWithBlock(...) {
// Check portal device exists
// Verify destination world is alive
// Check player hasn't died in target world
// Teleport player to instance
}
}
```
Target world states:
- `OKAY` - Can enter
- `WORLD_DEAD` - Destination closed
- `DIED_IN_WORLD` - Player died there
- `NO_SPAWN_AVAILABLE` - No spawn point
### ReturnPortalInteraction
Handles returning from a portal world:
```java
public class ReturnPortalInteraction extends SimpleBlockInteraction {
// Minimum time before allowing return
public static final Duration MINIMUM_TIME_IN_WORLD = Duration.ofSeconds(15L);
// Warning shown before timer expires
public static final Duration WARNING_TIME = Duration.ofSeconds(4L);
@Override
protected void interactWithBlock(...) {
// Check minimum time elapsed
// Uncurse all items
// Exit instance
}
}
```
## System Registration
All systems are registered in PortalsPlugin setup:
```java
// ChunkStore systems
this.getChunkStoreRegistry().registerSystem(new PortalInvalidDestinationSystem());
this.getChunkStoreRegistry().registerSystem(new CloseWorldWhenBreakingDeviceSystems.ComponentRemoved());
this.getChunkStoreRegistry().registerSystem(new CloseWorldWhenBreakingDeviceSystems.EntityRemoved());
// EntityStore systems
this.getEntityStoreRegistry().registerSystem(new PortalTrackerSystems.TrackerSystem());
this.getEntityStoreRegistry().registerSystem(new PortalTrackerSystems.UiTickingSystem());
this.getEntityStoreRegistry().registerSystem(new DiedInPortalSystem());
this.getEntityStoreRegistry().registerSystem(new CurseItemDropsSystem());
this.getEntityStoreRegistry().registerSystem(new DeleteCursedItemsOnSpawnSystem());
this.getEntityStoreRegistry().registerSystem(new VoidEventRefSystem());
this.getEntityStoreRegistry().registerSystem(new VoidInvasionPortalsSpawnSystem());
this.getEntityStoreRegistry().registerSystem(new VoidSpawnerSystems.Instantiate());
this.getEntityStoreRegistry().registerSystem(new StartVoidEventInFragmentSystem());
this.getEntityStoreRegistry().registerSystem(new VoidEventStagesSystem());
```
## Interaction Registration
Portal interactions are registered as codec types:
```java
this.getCodecRegistry(Interaction.CODEC)
.register("Portal", EnterPortalInteraction.class, EnterPortalInteraction.CODEC)
.register("PortalReturn", ReturnPortalInteraction.class, ReturnPortalInteraction.CODEC);
```

View File

@@ -0,0 +1,280 @@
---
title: Systemes Portail
type: docs
weight: 2
---
Les systemes portail gerent la logique d'execution pour le suivi des portails, les evenements du vide et la gestion des instances.
**Package:** `com.hypixel.hytale.builtin.portals.systems`
## Systemes de Suivi Portail
### TrackerSystem
Suit les joueurs entrant et sortant des mondes portail:
```java
public class TrackerSystem extends RefSystem<EntityStore> {
// Appele quand joueur entre dans monde portail
@Override
public void onEntityAdded(Ref<EntityStore> ref, AddReason reason,
Store<EntityStore> store,
CommandBuffer<EntityStore> commandBuffer) {
PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType());
if (!portalWorld.exists()) return;
// Envoyer UI portail au joueur
PlayerRef playerRef = commandBuffer.getComponent(ref, PlayerRef.getComponentType());
UpdatePortal packet = portalWorld.createFullPacket(world);
playerRef.getPacketHandler().write(packet);
}
// Appele quand joueur quitte monde portail
@Override
public void onEntityRemove(Ref<EntityStore> ref, RemoveReason reason,
Store<EntityStore> store,
CommandBuffer<EntityStore> commandBuffer) {
// Effacer etat UI
playerRef.getPacketHandler().write(new UpdatePortal(null, null));
portalWorld.getSeesUi().remove(playerRef.getUuid());
}
@Override
public Query<EntityStore> getQuery() {
return Query.and(Player.getComponentType(), PlayerRef.getComponentType());
}
}
```
### UiTickingSystem
Met a jour l'UI portail chaque seconde:
```java
public class UiTickingSystem extends DelayedEntitySystem<EntityStore> {
public UiTickingSystem() {
super(1.0f); // Tick chaque 1 seconde
}
@Override
public void tick(float dt, int index, ArchetypeChunk<EntityStore> archetypeChunk,
Store<EntityStore> store, CommandBuffer<EntityStore> commandBuffer) {
PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType());
if (!portalWorld.exists()) return;
// Envoyer temps mis a jour au joueur
UpdatePortal packet = portalWorld.createUpdatePacket(world);
playerRef.getPacketHandler().write(packet);
}
}
```
## Systemes Destination Portail
### PortalInvalidDestinationSystem
Gere les portails avec destinations invalides:
```java
public class PortalInvalidDestinationSystem {
// Eteint portails quand monde destination ferme
public static void turnOffPortalsInWorld(World world, World closedWorld) {
// Trouver tous portails pointant vers closedWorld
// Mettre leur destination a null
// Mettre a jour etat bloc a "off"
}
}
```
### CloseWorldWhenBreakingDeviceSystems
Gere la destruction du dispositif portail:
```java
// Quand composant dispositif portail supprime
public class ComponentRemoved extends System<ChunkStore> {
// Fermer le monde de destination
}
// Quand entite bloc portail supprimee
public class EntityRemoved extends System<ChunkStore> {
// Fermer le monde de destination
}
```
## Systemes Evenement Vide
### VoidEventRefSystem
Gere les references entites evenement vide:
```java
public class VoidEventRefSystem extends System<EntityStore> {
// Suit cycle de vie entite evenement vide
// Met a jour PortalWorld.voidEventRef
}
```
### VoidEventStagesSystem
Progresse a travers les etapes evenement vide:
```java
public class VoidEventStagesSystem extends System<EntityStore> {
// Verifie temps ecoule
// Active etape suivante quand seuil temps atteint
// Met a jour VoidEvent.activeStage
}
```
### VoidInvasionPortalsSpawnSystem
Fait apparaitre portails invasion pendant evenements vide:
```java
public class VoidInvasionPortalsSpawnSystem extends System<EntityStore> {
// Cree entites spawner vide
// Utilise grille hash spatiale pour maintenir distance minimum
}
```
### VoidSpawnerSystems.Instantiate
Instancie entites spawner vide:
```java
public class Instantiate extends System<EntityStore> {
// Cree entite spawner depuis config
// Ajoute a grille spatiale VoidEvent
}
```
### StartVoidEventInFragmentSystem
Initie evenements vide dans fragments portail:
```java
public class StartVoidEventInFragmentSystem extends System<EntityStore> {
// Verifie si invasion vide devrait demarrer
// Cree entite VoidEvent
// Configure premiere etape
}
```
## Systemes Malediction
### DiedInPortalSystem
Suit les morts joueurs dans mondes portail:
```java
public class DiedInPortalSystem extends System<EntityStore> {
// A la mort joueur dans monde portail:
// - Ajouter UUID joueur a set diedInWorld
// - Empeche re-entree
}
```
### CurseItemDropsSystem
Marque items laches comme maudits:
```java
public class CurseItemDropsSystem extends System<EntityStore> {
// Items laches dans monde portail deviennent maudits
// Items maudits sont perdus a la mort
}
```
### DeleteCursedItemsOnSpawnSystem
Supprime items maudits quand joueur spawn:
```java
public class DeleteCursedItemsOnSpawnSystem extends System<EntityStore> {
// Quand joueur reapparait apres mort dans portail
// Supprimer tous items maudits de l'inventaire
}
```
## Interactions Portail
### EnterPortalInteraction
Gere l'entree dans un portail:
```java
public class EnterPortalInteraction extends SimpleBlockInteraction {
// Temps minimum avant autoriser utilisation portail
public static final Duration MINIMUM_TIME_IN_WORLD = Duration.ofMillis(3000L);
@Override
protected void interactWithBlock(...) {
// Verifier dispositif portail existe
// Verifier monde destination vivant
// Verifier joueur pas mort dans monde cible
// Teleporter joueur vers instance
}
}
```
Etats monde cible:
- `OKAY` - Peut entrer
- `WORLD_DEAD` - Destination fermee
- `DIED_IN_WORLD` - Joueur mort la-bas
- `NO_SPAWN_AVAILABLE` - Pas de point de spawn
### ReturnPortalInteraction
Gere le retour d'un monde portail:
```java
public class ReturnPortalInteraction extends SimpleBlockInteraction {
// Temps minimum avant autoriser retour
public static final Duration MINIMUM_TIME_IN_WORLD = Duration.ofSeconds(15L);
// Avertissement affiche avant expiration timer
public static final Duration WARNING_TIME = Duration.ofSeconds(4L);
@Override
protected void interactWithBlock(...) {
// Verifier temps minimum ecoule
// Retirer malediction tous items
// Sortir instance
}
}
```
## Enregistrement Systemes
Tous les systemes sont enregistres dans setup PortalsPlugin:
```java
// Systemes ChunkStore
this.getChunkStoreRegistry().registerSystem(new PortalInvalidDestinationSystem());
this.getChunkStoreRegistry().registerSystem(new CloseWorldWhenBreakingDeviceSystems.ComponentRemoved());
this.getChunkStoreRegistry().registerSystem(new CloseWorldWhenBreakingDeviceSystems.EntityRemoved());
// Systemes EntityStore
this.getEntityStoreRegistry().registerSystem(new PortalTrackerSystems.TrackerSystem());
this.getEntityStoreRegistry().registerSystem(new PortalTrackerSystems.UiTickingSystem());
this.getEntityStoreRegistry().registerSystem(new DiedInPortalSystem());
this.getEntityStoreRegistry().registerSystem(new CurseItemDropsSystem());
this.getEntityStoreRegistry().registerSystem(new DeleteCursedItemsOnSpawnSystem());
this.getEntityStoreRegistry().registerSystem(new VoidEventRefSystem());
this.getEntityStoreRegistry().registerSystem(new VoidInvasionPortalsSpawnSystem());
this.getEntityStoreRegistry().registerSystem(new VoidSpawnerSystems.Instantiate());
this.getEntityStoreRegistry().registerSystem(new StartVoidEventInFragmentSystem());
this.getEntityStoreRegistry().registerSystem(new VoidEventStagesSystem());
```
## Enregistrement Interactions
Les interactions portail sont enregistrees comme types codec:
```java
this.getCodecRegistry(Interaction.CODEC)
.register("Portal", EnterPortalInteraction.class, EnterPortalInteraction.CODEC)
.register("PortalReturn", ReturnPortalInteraction.class, ReturnPortalInteraction.CODEC);
```

View File

@@ -0,0 +1,93 @@
---
title: Universe & Worlds
type: docs
weight: 1
---
## Universe
The Universe is a singleton managing all worlds:
```java
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import java.util.Map;
Universe universe = Universe.get();
// Get specific world
World world = universe.getWorld("default");
// Get all worlds
Map<String, World> worlds = universe.getWorlds();
// Check if world exists (hasWorld doesn't exist - use null check)
if (universe.getWorld("creative") != null) {
// World exists
}
```
## World
Each world contains chunks, entities, and players:
```java
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import java.util.Collection;
import java.util.List;
World world = Universe.get().getWorld("default");
// Get world name
String name = world.getName();
// Get all players (returns List<Player>)
List<Player> players = world.getPlayers();
// Get player refs
Collection<PlayerRef> playerRefs = world.getPlayerRefs();
```
{{< callout type="warning" >}}
**Note:** `world.getEntities()` does not exist on World. Entities are managed through the EntityStore component system.
{{< /callout >}}
## World Operations
```java
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.math.vector.Vector3i;
// Get block at position
BlockType blockType = world.getBlockType(x, y, z);
BlockType blockType = world.getBlockType(new Vector3i(x, y, z));
// Set block (takes String block type key, not BlockType)
world.setBlock(x, y, z, "stone");
// If you have a BlockType, use getId()
if (blockType != null) {
world.setBlock(x, y, z, blockType.getId());
}
```
{{< callout type="info" >}}
**Note:** `world.getSpawnPoint()` does not exist directly on World. Spawn configuration is accessed via `world.getWorldConfig().getSpawnProvider()`.
{{< /callout >}}
## World from Player Events
```java
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import java.util.logging.Level;
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef playerRef = event.getPlayerRef();
World world = event.getWorld(); // Can be null!
if (world != null) {
getLogger().at(Level.INFO).log("Player joined world: " + world.getName());
}
});
```

View File

@@ -0,0 +1,93 @@
---
title: Univers & Mondes
type: docs
weight: 1
---
## Universe
L'Universe est un singleton gérant tous les mondes :
```java
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import java.util.Map;
Universe universe = Universe.get();
// Obtenir un monde spécifique
World world = universe.getWorld("default");
// Obtenir tous les mondes
Map<String, World> worlds = universe.getWorlds();
// Vérifier si un monde existe (hasWorld n'existe pas - utiliser null check)
if (universe.getWorld("creative") != null) {
// Le monde existe
}
```
## World
Chaque monde contient des chunks, entités et joueurs :
```java
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import java.util.Collection;
import java.util.List;
World world = Universe.get().getWorld("default");
// Obtenir le nom du monde
String name = world.getName();
// Obtenir tous les joueurs (retourne List<Player>)
List<Player> players = world.getPlayers();
// Obtenir les références de joueurs
Collection<PlayerRef> playerRefs = world.getPlayerRefs();
```
{{< callout type="warning" >}}
**Note :** `world.getEntities()` n'existe pas sur World. Les entités sont gérées via le système de composants EntityStore.
{{< /callout >}}
## Opérations sur le Monde
```java
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.math.vector.Vector3i;
// Obtenir un bloc à une position
BlockType blockType = world.getBlockType(x, y, z);
BlockType blockType = world.getBlockType(new Vector3i(x, y, z));
// Définir un bloc (prend une clé String, pas un BlockType)
world.setBlock(x, y, z, "stone");
// Si vous avez un BlockType, utilisez getId()
if (blockType != null) {
world.setBlock(x, y, z, blockType.getId());
}
```
{{< callout type="info" >}}
**Note :** `world.getSpawnPoint()` n'existe pas directement sur World. La configuration du spawn est accessible via `world.getWorldConfig().getSpawnProvider()`.
{{< /callout >}}
## World depuis les Événements Player
```java
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import java.util.logging.Level;
getEventRegistry().register(PlayerConnectEvent.class, event -> {
PlayerRef playerRef = event.getPlayerRef();
World world = event.getWorld(); // Peut être null !
if (world != null) {
getLogger().at(Level.INFO).log("Joueur a rejoint le monde : " + world.getName());
}
});
```

View File

@@ -0,0 +1,115 @@
---
title: World Generation
type: docs
weight: 10
---
The World Generation system creates procedural terrain, caves, biomes, and structures for Hytale worlds.
**Packages:**
- `com.hypixel.hytale.server.worldgen`
- `com.hypixel.hytale.builtin.hytalegenerator`
{{< cards >}}
{{< card link="world-loader" title="World Loader" subtitle="Chunk loading and generation pipeline" >}}
{{< card link="chunk-generation" title="Chunk Generation" subtitle="Block placement and terrain" >}}
{{< card link="cave-generation" title="Cave Generation" subtitle="Underground cave systems" >}}
{{< card link="climate-biomes" title="Climate & Biomes" subtitle="Biome distribution and climate" >}}
{{< card link="prefabs" title="Prefabs" subtitle="Structure and building placement" >}}
{{< card link="zones" title="Zones" subtitle="Zone definitions and regions" >}}
{{< card link="hytale-generator" title="Hytale Generator" subtitle="Default procedural generator" >}}
{{< card link="density-functions" title="Density Functions" subtitle="Terrain shape definition" >}}
{{< card link="material-providers" title="Material Providers" subtitle="Block selection logic" >}}
{{< card link="prop-placement" title="Prop Placement" subtitle="Vegetation and object placement" >}}
{{< card link="generation-patterns" title="Generation Patterns" subtitle="Patterns and fields" >}}
{{< /cards >}}
## Architecture Overview
The world generation system operates in several phases:
```
World Generation Pipeline
├── Climate Generation
│ └── Biome Assignment
├── Terrain Generation
│ ├── Density Functions (shape)
│ ├── Material Providers (blocks)
│ └── Surface Decoration
├── Cave Generation
│ ├── Cave Carving
│ └── Cave Features
├── Structure Placement
│ ├── Prefab Selection
│ └── Prefab Positioning
└── Prop Placement
├── Vegetation
└── Objects
```
## Packages Overview
| Package | Files | Description |
|---------|-------|-------------|
| `loader/` | 75 | World loading system |
| `util/` | 35 | Generation utilities |
| `cave/` | 32 | Cave generation |
| `climate/` | 12 | Climate and biomes |
| `chunk/` | 12 | Chunk generation |
| `prefab/` | 8 | Structure placement |
| `zone/` | 7 | Zone definitions |
| `cache/` | 7 | Generation caching |
## Hytale Generator
The built-in Hytale generator provides the default world generation:
| Package | Files | Description |
|---------|-------|-------------|
| `assets/` | 232 | Generator asset definitions |
| `density/` | 76 | Density functions |
| `materialproviders/` | 29 | Block selection |
| `props/` | 24 | Prop placement |
| `patterns/` | 13 | Generation patterns |
| `fields/` | 8 | Field generation |
## Quick Example
```java
// Get world generator
WorldGenerator generator = world.getGenerator();
// Generate chunk
generator.generateChunk(chunkX, chunkZ);
// Get biome at position
Biome biome = generator.getBiome(position);
// Check if structure can generate
boolean canPlace = generator.canPlacePrefab(prefab, position);
```
## Configuration
World generation is configured through YAML assets:
```yaml
# worldgen/my_generator.yaml
Type: WorldGenerator
Id: my_generator
Seed: 12345
Climate:
Type: StandardClimate
Biomes:
- forest
- plains
- mountains
Density:
Type: MultipleDensity
Functions:
- terrain_base
- terrain_hills
Prefabs:
- Type: Village
Frequency: 0.01
```

View File

@@ -0,0 +1,115 @@
---
title: Génération de Monde
type: docs
weight: 10
---
Le système de génération de monde crée du terrain procédural, des grottes, des biomes et des structures pour les mondes Hytale.
**Packages:**
- `com.hypixel.hytale.server.worldgen`
- `com.hypixel.hytale.builtin.hytalegenerator`
{{< cards >}}
{{< card link="world-loader" title="World Loader" subtitle="Chargement de chunks et pipeline de génération" >}}
{{< card link="chunk-generation" title="Génération de Chunks" subtitle="Placement de blocs et terrain" >}}
{{< card link="cave-generation" title="Génération de Grottes" subtitle="Systèmes de grottes souterraines" >}}
{{< card link="climate-biomes" title="Climat & Biomes" subtitle="Distribution des biomes et climat" >}}
{{< card link="prefabs" title="Prefabs" subtitle="Placement de structures et bâtiments" >}}
{{< card link="zones" title="Zones" subtitle="Définitions de zones et régions" >}}
{{< card link="hytale-generator" title="Hytale Generator" subtitle="Générateur procédural par défaut" >}}
{{< card link="density-functions" title="Fonctions de Densité" subtitle="Définition de forme du terrain" >}}
{{< card link="material-providers" title="Material Providers" subtitle="Logique de sélection de blocs" >}}
{{< card link="prop-placement" title="Placement de Props" subtitle="Placement de végétation et objets" >}}
{{< card link="generation-patterns" title="Patterns de Génération" subtitle="Patterns et champs" >}}
{{< /cards >}}
## Vue d'Ensemble de l'Architecture
Le système de génération de monde opère en plusieurs phases :
```
Pipeline de Génération de Monde
├── Génération du Climat
│ └── Attribution des Biomes
├── Génération du Terrain
│ ├── Fonctions de Densité (forme)
│ ├── Material Providers (blocs)
│ └── Décoration de Surface
├── Génération des Grottes
│ ├── Creusement des Grottes
│ └── Caractéristiques des Grottes
├── Placement des Structures
│ ├── Sélection des Prefabs
│ └── Positionnement des Prefabs
└── Placement des Props
├── Végétation
└── Objets
```
## Vue d'Ensemble des Packages
| Package | Fichiers | Description |
|---------|----------|-------------|
| `loader/` | 75 | Système de chargement de monde |
| `util/` | 35 | Utilitaires de génération |
| `cave/` | 32 | Génération de grottes |
| `climate/` | 12 | Climat et biomes |
| `chunk/` | 12 | Génération de chunks |
| `prefab/` | 8 | Placement de structures |
| `zone/` | 7 | Définitions de zones |
| `cache/` | 7 | Cache de génération |
## Hytale Generator
Le générateur Hytale intégré fournit la génération de monde par défaut :
| Package | Fichiers | Description |
|---------|----------|-------------|
| `assets/` | 232 | Définitions d'assets générateur |
| `density/` | 76 | Fonctions de densité |
| `materialproviders/` | 29 | Sélection de blocs |
| `props/` | 24 | Placement de props |
| `patterns/` | 13 | Patterns de génération |
| `fields/` | 8 | Génération par champs |
## Exemple Rapide
```java
// Obtenir le générateur de monde
WorldGenerator generator = world.getGenerator();
// Générer un chunk
generator.generateChunk(chunkX, chunkZ);
// Obtenir le biome à une position
Biome biome = generator.getBiome(position);
// Vérifier si une structure peut être générée
boolean canPlace = generator.canPlacePrefab(prefab, position);
```
## Configuration
La génération de monde est configurée via des assets YAML :
```yaml
# worldgen/my_generator.yaml
Type: WorldGenerator
Id: my_generator
Seed: 12345
Climate:
Type: StandardClimate
Biomes:
- forest
- plains
- mountains
Density:
Type: MultipleDensity
Functions:
- terrain_base
- terrain_hills
Prefabs:
- Type: Village
Frequency: 0.01
```

View File

@@ -0,0 +1,252 @@
---
title: Cave Generation
type: docs
weight: 2
---
The cave generation system creates underground cave networks, caverns, and subterranean features.
**Package:** `com.hypixel.hytale.server.worldgen.cave`
## Cave Generator
The `CaveGenerator` carves out underground spaces:
```java
public class CaveGenerator {
// Generate caves for chunk
public void generateCaves(GenerationContainer container, int chunkX, int chunkZ);
// Check if position is in cave
public boolean isInCave(int x, int y, int z);
// Get cave density at position
public float getCaveDensity(int x, int y, int z);
}
```
## Cave Types
### Tunnel Caves
Winding tunnels through terrain:
```yaml
CaveType: Tunnel
Width: 3.0
Height: 4.0
Length: 50-200
Frequency: 0.02
MinY: 10
MaxY: 60
```
### Cavern Caves
Large open underground spaces:
```yaml
CaveType: Cavern
MinRadius: 10
MaxRadius: 30
MinHeight: 8
MaxHeight: 20
Frequency: 0.005
MinY: 5
MaxY: 40
```
### Ravine Caves
Vertical crack formations:
```yaml
CaveType: Ravine
Width: 5-15
Depth: 30-60
Length: 100-300
Frequency: 0.001
```
## Noise-Based Generation
Caves use 3D noise for natural shapes:
```java
public class CaveNoiseGenerator {
private SimplexNoise primaryNoise;
private SimplexNoise secondaryNoise;
private float threshold;
public boolean shouldCarve(int x, int y, int z) {
float noise1 = primaryNoise.noise3D(x * 0.05f, y * 0.1f, z * 0.05f);
float noise2 = secondaryNoise.noise3D(x * 0.02f, y * 0.05f, z * 0.02f);
float combined = (noise1 + noise2) / 2.0f;
return combined > threshold;
}
}
```
## Cave Carving
```java
public class CaveCarver {
// Carve sphere at position
public void carveSphere(GenerationContainer container, Vector3d center, float radius);
// Carve tunnel between points
public void carveTunnel(
GenerationContainer container,
Vector3d start,
Vector3d end,
float radius
);
// Carve with noise
public void carveWithNoise(
GenerationContainer container,
Vector3d position,
NoiseGenerator noise,
float threshold
);
}
```
## Cave Decoration
After carving, caves are decorated:
```java
public class CaveDecorator {
// Add stalactites/stalagmites
public void addStalactites(GenerationContainer container, int x, int y, int z);
// Add crystal formations
public void addCrystals(GenerationContainer container, int x, int y, int z);
// Add water/lava pools
public void addPool(GenerationContainer container, int x, int y, int z, FluidType type);
// Add cave vegetation
public void addMushrooms(GenerationContainer container, int x, int y, int z);
}
```
## Cave Biomes
Different cave biomes with unique features:
```yaml
# cave_biomes/crystal_cave.yaml
Type: CaveBiome
Id: crystal_cave
Features:
- Type: CrystalCluster
Frequency: 0.1
MinSize: 2
MaxSize: 8
- Type: GlowingMushroom
Frequency: 0.05
AmbientLight: 0.2
Blocks:
Floor: crystal_stone
Ceiling: crystal_stone
Walls: smooth_stone
```
## Cave Configuration
```yaml
# worldgen/cave_config.yaml
CaveGeneration:
Enabled: true
Types:
- Type: Tunnel
Weight: 0.6
MinY: 10
MaxY: 60
- Type: Cavern
Weight: 0.3
MinY: 5
MaxY: 40
- Type: Ravine
Weight: 0.1
MinY: 0
MaxY: 64
Decoration:
Stalactites: true
Pools: true
Vegetation: true
NoiseSettings:
Octaves: 4
Persistence: 0.5
Scale: 0.05
```
## Cave Systems
Connected cave networks:
```java
public class CaveSystem {
private List<CaveNode> nodes;
private List<CaveConnection> connections;
// Generate connected system
public void generate(GenerationContainer container) {
// Create nodes
for (int i = 0; i < nodeCount; i++) {
nodes.add(createCaveNode());
}
// Connect nodes
for (CaveNode node : nodes) {
CaveNode nearest = findNearest(node);
connections.add(new CaveConnection(node, nearest));
}
// Carve caves
for (CaveNode node : nodes) {
carver.carveCavern(container, node.position, node.radius);
}
// Carve tunnels
for (CaveConnection conn : connections) {
carver.carveTunnel(container, conn.start, conn.end, conn.radius);
}
}
}
```
## Integration with Terrain
```java
public class TerrainCaveIntegration {
// Prevent caves from breaking surface
public boolean shouldCarve(int x, int y, int z, int surfaceY) {
// Don't carve within 5 blocks of surface
if (y > surfaceY - 5) {
return false;
}
// Don't carve below bedrock level
if (y < 5) {
return false;
}
return caveNoise.shouldCarve(x, y, z);
}
}
```
## Best Practices
{{< callout type="info" >}}
**Cave Guidelines:**
- Balance cave frequency with world density
- Use noise for natural-looking shapes
- Connect cave systems for exploration
- Add decoration for visual interest
- Consider performance with large caverns
{{< /callout >}}

View File

@@ -0,0 +1,252 @@
---
title: Génération de Grottes
type: docs
weight: 2
---
Le système de génération de grottes crée des réseaux de grottes souterraines, des cavernes et des caractéristiques souterraines.
**Package:** `com.hypixel.hytale.server.worldgen.cave`
## Cave Generator
Le `CaveGenerator` creuse les espaces souterrains :
```java
public class CaveGenerator {
// Générer des grottes pour un chunk
public void generateCaves(GenerationContainer container, int chunkX, int chunkZ);
// Vérifier si une position est dans une grotte
public boolean isInCave(int x, int y, int z);
// Obtenir la densité de grotte à une position
public float getCaveDensity(int x, int y, int z);
}
```
## Types de Grottes
### Grottes Tunnel
Tunnels sinueux à travers le terrain :
```yaml
CaveType: Tunnel
Width: 3.0
Height: 4.0
Length: 50-200
Frequency: 0.02
MinY: 10
MaxY: 60
```
### Grottes Caverne
Grands espaces souterrains ouverts :
```yaml
CaveType: Cavern
MinRadius: 10
MaxRadius: 30
MinHeight: 8
MaxHeight: 20
Frequency: 0.005
MinY: 5
MaxY: 40
```
### Grottes Ravine
Formations de fissures verticales :
```yaml
CaveType: Ravine
Width: 5-15
Depth: 30-60
Length: 100-300
Frequency: 0.001
```
## Génération Basée sur le Bruit
Les grottes utilisent du bruit 3D pour des formes naturelles :
```java
public class CaveNoiseGenerator {
private SimplexNoise primaryNoise;
private SimplexNoise secondaryNoise;
private float threshold;
public boolean shouldCarve(int x, int y, int z) {
float noise1 = primaryNoise.noise3D(x * 0.05f, y * 0.1f, z * 0.05f);
float noise2 = secondaryNoise.noise3D(x * 0.02f, y * 0.05f, z * 0.02f);
float combined = (noise1 + noise2) / 2.0f;
return combined > threshold;
}
}
```
## Creusement de Grottes
```java
public class CaveCarver {
// Creuser une sphère à une position
public void carveSphere(GenerationContainer container, Vector3d center, float radius);
// Creuser un tunnel entre deux points
public void carveTunnel(
GenerationContainer container,
Vector3d start,
Vector3d end,
float radius
);
// Creuser avec du bruit
public void carveWithNoise(
GenerationContainer container,
Vector3d position,
NoiseGenerator noise,
float threshold
);
}
```
## Décoration de Grottes
Après le creusement, les grottes sont décorées :
```java
public class CaveDecorator {
// Ajouter stalactites/stalagmites
public void addStalactites(GenerationContainer container, int x, int y, int z);
// Ajouter des formations de cristaux
public void addCrystals(GenerationContainer container, int x, int y, int z);
// Ajouter des bassins d'eau/lave
public void addPool(GenerationContainer container, int x, int y, int z, FluidType type);
// Ajouter de la végétation de grotte
public void addMushrooms(GenerationContainer container, int x, int y, int z);
}
```
## Biomes de Grottes
Différents biomes de grottes avec des caractéristiques uniques :
```yaml
# cave_biomes/crystal_cave.yaml
Type: CaveBiome
Id: crystal_cave
Features:
- Type: CrystalCluster
Frequency: 0.1
MinSize: 2
MaxSize: 8
- Type: GlowingMushroom
Frequency: 0.05
AmbientLight: 0.2
Blocks:
Floor: crystal_stone
Ceiling: crystal_stone
Walls: smooth_stone
```
## Configuration des Grottes
```yaml
# worldgen/cave_config.yaml
CaveGeneration:
Enabled: true
Types:
- Type: Tunnel
Weight: 0.6
MinY: 10
MaxY: 60
- Type: Cavern
Weight: 0.3
MinY: 5
MaxY: 40
- Type: Ravine
Weight: 0.1
MinY: 0
MaxY: 64
Decoration:
Stalactites: true
Pools: true
Vegetation: true
NoiseSettings:
Octaves: 4
Persistence: 0.5
Scale: 0.05
```
## Systèmes de Grottes
Réseaux de grottes connectées :
```java
public class CaveSystem {
private List<CaveNode> nodes;
private List<CaveConnection> connections;
// Générer un système connecté
public void generate(GenerationContainer container) {
// Créer les nœuds
for (int i = 0; i < nodeCount; i++) {
nodes.add(createCaveNode());
}
// Connecter les nœuds
for (CaveNode node : nodes) {
CaveNode nearest = findNearest(node);
connections.add(new CaveConnection(node, nearest));
}
// Creuser les cavernes
for (CaveNode node : nodes) {
carver.carveCavern(container, node.position, node.radius);
}
// Creuser les tunnels
for (CaveConnection conn : connections) {
carver.carveTunnel(container, conn.start, conn.end, conn.radius);
}
}
}
```
## Intégration avec le Terrain
```java
public class TerrainCaveIntegration {
// Empêcher les grottes de percer la surface
public boolean shouldCarve(int x, int y, int z, int surfaceY) {
// Ne pas creuser à moins de 5 blocs de la surface
if (y > surfaceY - 5) {
return false;
}
// Ne pas creuser sous le niveau de bedrock
if (y < 5) {
return false;
}
return caveNoise.shouldCarve(x, y, z);
}
}
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives des Grottes :**
- Équilibrez la fréquence des grottes avec la densité du monde
- Utilisez le bruit pour des formes naturelles
- Connectez les systèmes de grottes pour l'exploration
- Ajoutez de la décoration pour l'intérêt visuel
- Considérez les performances avec les grandes cavernes
{{< /callout >}}

View File

@@ -0,0 +1,222 @@
---
title: Chunk Generation
type: docs
weight: 4
---
Chunk generation handles the block-by-block creation of terrain within each 16x16 chunk.
**Package:** `com.hypixel.hytale.server.worldgen.chunk`
## ChunkGenerator
The `ChunkGenerator` creates terrain blocks:
```java
public class ChunkGenerator {
// Generate terrain for chunk
public void generate(GenerationContainer container, int chunkX, int chunkZ);
// Get height at position
public int getHeight(int x, int z);
// Get block at position
public BlockType getBlock(int x, int y, int z);
}
```
## Generation Stages
Chunk generation occurs in stages:
```
Generation Stages
├── 1. Height Map
│ └── Calculate surface heights
├── 2. Base Terrain
│ └── Fill solid blocks
├── 3. Surface Layer
│ └── Apply biome surface blocks
├── 4. Carving
│ └── Remove blocks for caves/features
└── 5. Decoration
└── Add vegetation, ores, etc.
```
## Height Map Generation
```java
public class HeightMapGenerator {
private NoiseGenerator baseNoise;
private NoiseGenerator detailNoise;
public int getHeight(int x, int z) {
// Base terrain height
float base = baseNoise.noise2D(x * 0.01f, z * 0.01f);
// Detail variation
float detail = detailNoise.noise2D(x * 0.1f, z * 0.1f) * 0.3f;
// Convert to block height
int height = (int) ((base + detail + 1.0f) * 32.0f) + 64;
return Math.clamp(height, 1, 255);
}
}
```
## Terrain Fill
```java
public class TerrainFiller {
public void fill(GenerationContainer container, int x, int z, int height) {
Biome biome = container.getBiome(x, z);
for (int y = 0; y < height; y++) {
BlockType block;
if (y == 0) {
block = BlockTypes.BEDROCK;
} else if (y < height - 4) {
block = biome.getStoneBlock();
} else if (y < height - 1) {
block = biome.getSubsurfaceBlock();
} else {
block = biome.getSurfaceBlock();
}
container.setBlock(x, y, z, block);
}
}
}
```
## Surface Decoration
```java
public class SurfaceDecorator {
public void decorate(GenerationContainer container, int x, int z) {
int surfaceY = container.getHeight(x, z);
Biome biome = container.getBiome(x, z);
// Add surface features based on biome
for (SurfaceFeature feature : biome.getSurfaceFeatures()) {
if (random.nextFloat() < feature.getDensity()) {
feature.place(container, x, surfaceY + 1, z);
}
}
}
}
```
## Block Palette
Chunks use a block palette for efficient storage:
```java
public class BlockPalette {
private List<BlockType> palette;
private short[] blockData;
// Get block at local position
public BlockType getBlock(int localX, int localY, int localZ) {
int index = localX + localZ * 16 + localY * 256;
short paletteIndex = blockData[index];
return palette.get(paletteIndex);
}
// Set block at local position
public void setBlock(int localX, int localY, int localZ, BlockType type) {
int index = localX + localZ * 16 + localY * 256;
int paletteIndex = palette.indexOf(type);
if (paletteIndex == -1) {
paletteIndex = palette.size();
palette.add(type);
}
blockData[index] = (short) paletteIndex;
}
}
```
## Section-Based Generation
Chunks are divided into 16-block tall sections:
```java
public class ChunkSection {
private static final int SIZE = 16 * 16 * 16;
private BlockPalette palette;
private byte[] lightData;
public boolean isEmpty() {
return palette.size() == 1 && palette.get(0) == BlockTypes.AIR;
}
}
```
## Ore Generation
```java
public class OreGenerator {
public void generateOres(GenerationContainer container, int chunkX, int chunkZ) {
for (OreConfig ore : oreConfigs) {
int count = random.nextInt(ore.maxVeins - ore.minVeins) + ore.minVeins;
for (int i = 0; i < count; i++) {
int x = chunkX * 16 + random.nextInt(16);
int y = random.nextInt(ore.maxY - ore.minY) + ore.minY;
int z = chunkZ * 16 + random.nextInt(16);
generateOreVein(container, x, y, z, ore);
}
}
}
private void generateOreVein(GenerationContainer container, int x, int y, int z, OreConfig ore) {
int size = random.nextInt(ore.maxSize - ore.minSize) + ore.minSize;
// Generate blob of ore blocks
for (int i = 0; i < size; i++) {
int dx = random.nextInt(3) - 1;
int dy = random.nextInt(3) - 1;
int dz = random.nextInt(3) - 1;
if (container.getBlock(x + dx, y + dy, z + dz) == ore.replaceBlock) {
container.setBlock(x + dx, y + dy, z + dz, ore.oreBlock);
}
}
}
}
```
## Configuration
```yaml
# worldgen/chunk_config.yaml
ChunkGeneration:
HeightNoise:
Octaves: 6
Persistence: 0.5
Scale: 0.01
SeaLevel: 64
BedrockLayers: 5
Ores:
- Type: iron_ore
MinY: 0
MaxY: 64
VeinsPerChunk: 20
VeinSize: 9
- Type: diamond_ore
MinY: 0
MaxY: 16
VeinsPerChunk: 1
VeinSize: 8
```
## Best Practices
{{< callout type="info" >}}
**Chunk Guidelines:**
- Use noise for natural terrain variation
- Apply biome-specific blocks to surfaces
- Generate features after base terrain
- Use palettes for memory efficiency
- Consider section-based optimizations
{{< /callout >}}

View File

@@ -0,0 +1,222 @@
---
title: Génération de Chunks
type: docs
weight: 4
---
La génération de chunks gère la création bloc par bloc du terrain dans chaque chunk 16x16.
**Package:** `com.hypixel.hytale.server.worldgen.chunk`
## ChunkGenerator
Le `ChunkGenerator` crée les blocs de terrain :
```java
public class ChunkGenerator {
// Générer le terrain pour un chunk
public void generate(GenerationContainer container, int chunkX, int chunkZ);
// Obtenir la hauteur à une position
public int getHeight(int x, int z);
// Obtenir le bloc à une position
public BlockType getBlock(int x, int y, int z);
}
```
## Étapes de Génération
La génération de chunk se fait par étapes :
```
Étapes de Génération
├── 1. Carte de Hauteur
│ └── Calculer les hauteurs de surface
├── 2. Terrain de Base
│ └── Remplir les blocs solides
├── 3. Couche de Surface
│ └── Appliquer les blocs de surface du biome
├── 4. Creusement
│ └── Supprimer les blocs pour grottes/features
└── 5. Décoration
└── Ajouter végétation, minerais, etc.
```
## Génération de Carte de Hauteur
```java
public class HeightMapGenerator {
private NoiseGenerator baseNoise;
private NoiseGenerator detailNoise;
public int getHeight(int x, int z) {
// Hauteur de terrain de base
float base = baseNoise.noise2D(x * 0.01f, z * 0.01f);
// Variation de détail
float detail = detailNoise.noise2D(x * 0.1f, z * 0.1f) * 0.3f;
// Convertir en hauteur de bloc
int height = (int) ((base + detail + 1.0f) * 32.0f) + 64;
return Math.clamp(height, 1, 255);
}
}
```
## Remplissage du Terrain
```java
public class TerrainFiller {
public void fill(GenerationContainer container, int x, int z, int height) {
Biome biome = container.getBiome(x, z);
for (int y = 0; y < height; y++) {
BlockType block;
if (y == 0) {
block = BlockTypes.BEDROCK;
} else if (y < height - 4) {
block = biome.getStoneBlock();
} else if (y < height - 1) {
block = biome.getSubsurfaceBlock();
} else {
block = biome.getSurfaceBlock();
}
container.setBlock(x, y, z, block);
}
}
}
```
## Décoration de Surface
```java
public class SurfaceDecorator {
public void decorate(GenerationContainer container, int x, int z) {
int surfaceY = container.getHeight(x, z);
Biome biome = container.getBiome(x, z);
// Ajouter des features de surface selon le biome
for (SurfaceFeature feature : biome.getSurfaceFeatures()) {
if (random.nextFloat() < feature.getDensity()) {
feature.place(container, x, surfaceY + 1, z);
}
}
}
}
```
## Palette de Blocs
Les chunks utilisent une palette de blocs pour un stockage efficace :
```java
public class BlockPalette {
private List<BlockType> palette;
private short[] blockData;
// Obtenir le bloc à une position locale
public BlockType getBlock(int localX, int localY, int localZ) {
int index = localX + localZ * 16 + localY * 256;
short paletteIndex = blockData[index];
return palette.get(paletteIndex);
}
// Définir le bloc à une position locale
public void setBlock(int localX, int localY, int localZ, BlockType type) {
int index = localX + localZ * 16 + localY * 256;
int paletteIndex = palette.indexOf(type);
if (paletteIndex == -1) {
paletteIndex = palette.size();
palette.add(type);
}
blockData[index] = (short) paletteIndex;
}
}
```
## Génération par Sections
Les chunks sont divisés en sections de 16 blocs de haut :
```java
public class ChunkSection {
private static final int SIZE = 16 * 16 * 16;
private BlockPalette palette;
private byte[] lightData;
public boolean isEmpty() {
return palette.size() == 1 && palette.get(0) == BlockTypes.AIR;
}
}
```
## Génération de Minerais
```java
public class OreGenerator {
public void generateOres(GenerationContainer container, int chunkX, int chunkZ) {
for (OreConfig ore : oreConfigs) {
int count = random.nextInt(ore.maxVeins - ore.minVeins) + ore.minVeins;
for (int i = 0; i < count; i++) {
int x = chunkX * 16 + random.nextInt(16);
int y = random.nextInt(ore.maxY - ore.minY) + ore.minY;
int z = chunkZ * 16 + random.nextInt(16);
generateOreVein(container, x, y, z, ore);
}
}
}
private void generateOreVein(GenerationContainer container, int x, int y, int z, OreConfig ore) {
int size = random.nextInt(ore.maxSize - ore.minSize) + ore.minSize;
// Générer une grappe de blocs de minerai
for (int i = 0; i < size; i++) {
int dx = random.nextInt(3) - 1;
int dy = random.nextInt(3) - 1;
int dz = random.nextInt(3) - 1;
if (container.getBlock(x + dx, y + dy, z + dz) == ore.replaceBlock) {
container.setBlock(x + dx, y + dy, z + dz, ore.oreBlock);
}
}
}
}
```
## Configuration
```yaml
# worldgen/chunk_config.yaml
ChunkGeneration:
HeightNoise:
Octaves: 6
Persistence: 0.5
Scale: 0.01
SeaLevel: 64
BedrockLayers: 5
Ores:
- Type: iron_ore
MinY: 0
MaxY: 64
VeinsPerChunk: 20
VeinSize: 9
- Type: diamond_ore
MinY: 0
MaxY: 16
VeinsPerChunk: 1
VeinSize: 8
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives des Chunks :**
- Utilisez le bruit pour une variation de terrain naturelle
- Appliquez des blocs spécifiques au biome sur les surfaces
- Générez les features après le terrain de base
- Utilisez des palettes pour l'efficacité mémoire
- Considérez les optimisations basées sur les sections
{{< /callout >}}

View File

@@ -0,0 +1,238 @@
---
title: Climate & Biomes
type: docs
weight: 3
---
The climate and biome system determines biome distribution across the world based on temperature, humidity, and other factors.
**Package:** `com.hypixel.hytale.server.worldgen.climate`
## Climate System
The `ClimateGenerator` creates climate maps:
```java
public class ClimateGenerator {
// Get temperature at position
public float getTemperature(int x, int z);
// Get humidity at position
public float getHumidity(int x, int z);
// Get altitude factor
public float getAltitude(int x, int y, int z);
// Get climate data
public ClimateData getClimate(int x, int z);
}
```
## ClimateData
```java
public class ClimateData {
private float temperature; // -1.0 (cold) to 1.0 (hot)
private float humidity; // 0.0 (dry) to 1.0 (wet)
private float altitude; // Normalized height
private float continentality; // Distance from ocean
public Biome getBiome();
}
```
## Biome Selection
Biomes are selected based on climate parameters:
```java
public class BiomeSelector {
private Map<BiomeCondition, Biome> biomeMap;
public Biome selectBiome(ClimateData climate) {
for (Map.Entry<BiomeCondition, Biome> entry : biomeMap.entrySet()) {
if (entry.getKey().matches(climate)) {
return entry.getValue();
}
}
return defaultBiome;
}
}
public class BiomeCondition {
private Range temperatureRange;
private Range humidityRange;
private Range altitudeRange;
public boolean matches(ClimateData climate) {
return temperatureRange.contains(climate.temperature)
&& humidityRange.contains(climate.humidity)
&& altitudeRange.contains(climate.altitude);
}
}
```
## Biome Definition
```yaml
# biomes/forest.yaml
Type: Biome
Id: forest
DisplayName: "Forest"
Climate:
Temperature: [0.3, 0.7]
Humidity: [0.4, 0.8]
Terrain:
SurfaceBlock: grass
SubsurfaceBlock: dirt
StoneBlock: stone
Features:
- Type: Tree
Density: 0.3
Variants: [oak, birch]
- Type: Grass
Density: 0.8
- Type: Flower
Density: 0.1
Mobs:
- Type: deer
SpawnWeight: 10
- Type: rabbit
SpawnWeight: 20
AmbientSounds:
- birds
- wind_leaves
```
## Temperature Zones
```java
public enum TemperatureZone {
FROZEN(-1.0f, -0.5f), // Ice, snow
COLD(-0.5f, 0.0f), // Taiga, tundra
TEMPERATE(0.0f, 0.5f), // Forest, plains
WARM(0.5f, 0.8f), // Savanna, jungle
HOT(0.8f, 1.0f); // Desert, volcanic
private float min;
private float max;
}
```
## Humidity Zones
```java
public enum HumidityZone {
ARID(0.0f, 0.2f), // Desert
DRY(0.2f, 0.4f), // Savanna, steppe
MODERATE(0.4f, 0.6f), // Plains, forest
HUMID(0.6f, 0.8f), // Jungle, swamp
WET(0.8f, 1.0f); // Rainforest, marsh
}
```
## Biome Blending
Smooth transitions between biomes:
```java
public class BiomeBlender {
private int blendRadius;
public BiomeBlendData blend(int x, int z) {
Map<Biome, Float> weights = new HashMap<>();
// Sample surrounding biomes
for (int dx = -blendRadius; dx <= blendRadius; dx++) {
for (int dz = -blendRadius; dz <= blendRadius; dz++) {
Biome biome = getBiome(x + dx, z + dz);
float distance = (float) Math.sqrt(dx * dx + dz * dz);
float weight = 1.0f / (1.0f + distance);
weights.merge(biome, weight, Float::sum);
}
}
return new BiomeBlendData(weights);
}
}
```
## Climate Noise
```java
public class ClimateNoiseGenerator {
private SimplexNoise temperatureNoise;
private SimplexNoise humidityNoise;
public ClimateNoiseGenerator(long seed) {
temperatureNoise = new SimplexNoise(seed);
humidityNoise = new SimplexNoise(seed + 1);
}
public float getTemperature(int x, int z) {
// Large scale temperature variation
float base = temperatureNoise.noise2D(x * 0.001f, z * 0.001f);
// Small scale variation
float detail = temperatureNoise.noise2D(x * 0.01f, z * 0.01f) * 0.2f;
return base + detail;
}
public float getHumidity(int x, int z) {
return humidityNoise.noise2D(x * 0.002f, z * 0.002f);
}
}
```
## Altitude Effects
```java
public class AltitudeClimateModifier {
// Temperature decreases with altitude
public float modifyTemperature(float baseTemp, int altitude) {
float altitudeFactor = altitude / 256.0f;
return baseTemp - (altitudeFactor * 0.5f);
}
// Humidity varies with altitude
public float modifyHumidity(float baseHumidity, int altitude) {
if (altitude < 64) {
return baseHumidity * 1.2f; // More humid at low altitudes
} else if (altitude > 128) {
return baseHumidity * 0.6f; // Drier at high altitudes
}
return baseHumidity;
}
}
```
## Configuration
```yaml
# worldgen/climate_config.yaml
Climate:
NoiseScale: 0.001
TemperatureRange: [-1.0, 1.0]
HumidityRange: [0.0, 1.0]
BlendRadius: 8
AltitudeEffect: true
Biomes:
- Id: forest
Temperature: [0.3, 0.7]
Humidity: [0.4, 0.8]
- Id: desert
Temperature: [0.7, 1.0]
Humidity: [0.0, 0.3]
- Id: tundra
Temperature: [-1.0, -0.3]
Humidity: [0.2, 0.6]
```
## Best Practices
{{< callout type="info" >}}
**Climate Guidelines:**
- Use smooth noise for natural climate transitions
- Consider altitude when determining biomes
- Blend biomes for seamless transitions
- Test biome distribution across large areas
{{< /callout >}}

View File

@@ -0,0 +1,238 @@
---
title: Climat & Biomes
type: docs
weight: 3
---
Le système de climat et de biomes détermine la distribution des biomes à travers le monde basée sur la température, l'humidité et d'autres facteurs.
**Package:** `com.hypixel.hytale.server.worldgen.climate`
## Système de Climat
Le `ClimateGenerator` crée des cartes de climat :
```java
public class ClimateGenerator {
// Obtenir la température à une position
public float getTemperature(int x, int z);
// Obtenir l'humidité à une position
public float getHumidity(int x, int z);
// Obtenir le facteur d'altitude
public float getAltitude(int x, int y, int z);
// Obtenir les données climatiques
public ClimateData getClimate(int x, int z);
}
```
## ClimateData
```java
public class ClimateData {
private float temperature; // -1.0 (froid) à 1.0 (chaud)
private float humidity; // 0.0 (sec) à 1.0 (humide)
private float altitude; // Hauteur normalisée
private float continentality; // Distance de l'océan
public Biome getBiome();
}
```
## Sélection de Biome
Les biomes sont sélectionnés selon les paramètres climatiques :
```java
public class BiomeSelector {
private Map<BiomeCondition, Biome> biomeMap;
public Biome selectBiome(ClimateData climate) {
for (Map.Entry<BiomeCondition, Biome> entry : biomeMap.entrySet()) {
if (entry.getKey().matches(climate)) {
return entry.getValue();
}
}
return defaultBiome;
}
}
public class BiomeCondition {
private Range temperatureRange;
private Range humidityRange;
private Range altitudeRange;
public boolean matches(ClimateData climate) {
return temperatureRange.contains(climate.temperature)
&& humidityRange.contains(climate.humidity)
&& altitudeRange.contains(climate.altitude);
}
}
```
## Définition de Biome
```yaml
# biomes/forest.yaml
Type: Biome
Id: forest
DisplayName: "Forêt"
Climate:
Temperature: [0.3, 0.7]
Humidity: [0.4, 0.8]
Terrain:
SurfaceBlock: grass
SubsurfaceBlock: dirt
StoneBlock: stone
Features:
- Type: Tree
Density: 0.3
Variants: [oak, birch]
- Type: Grass
Density: 0.8
- Type: Flower
Density: 0.1
Mobs:
- Type: deer
SpawnWeight: 10
- Type: rabbit
SpawnWeight: 20
AmbientSounds:
- birds
- wind_leaves
```
## Zones de Température
```java
public enum TemperatureZone {
FROZEN(-1.0f, -0.5f), // Glace, neige
COLD(-0.5f, 0.0f), // Taïga, toundra
TEMPERATE(0.0f, 0.5f), // Forêt, plaines
WARM(0.5f, 0.8f), // Savane, jungle
HOT(0.8f, 1.0f); // Désert, volcanique
private float min;
private float max;
}
```
## Zones d'Humidité
```java
public enum HumidityZone {
ARID(0.0f, 0.2f), // Désert
DRY(0.2f, 0.4f), // Savane, steppe
MODERATE(0.4f, 0.6f), // Plaines, forêt
HUMID(0.6f, 0.8f), // Jungle, marais
WET(0.8f, 1.0f); // Forêt tropicale, marécage
}
```
## Mélange de Biomes
Transitions douces entre biomes :
```java
public class BiomeBlender {
private int blendRadius;
public BiomeBlendData blend(int x, int z) {
Map<Biome, Float> weights = new HashMap<>();
// Échantillonner les biomes environnants
for (int dx = -blendRadius; dx <= blendRadius; dx++) {
for (int dz = -blendRadius; dz <= blendRadius; dz++) {
Biome biome = getBiome(x + dx, z + dz);
float distance = (float) Math.sqrt(dx * dx + dz * dz);
float weight = 1.0f / (1.0f + distance);
weights.merge(biome, weight, Float::sum);
}
}
return new BiomeBlendData(weights);
}
}
```
## Bruit Climatique
```java
public class ClimateNoiseGenerator {
private SimplexNoise temperatureNoise;
private SimplexNoise humidityNoise;
public ClimateNoiseGenerator(long seed) {
temperatureNoise = new SimplexNoise(seed);
humidityNoise = new SimplexNoise(seed + 1);
}
public float getTemperature(int x, int z) {
// Variation de température à grande échelle
float base = temperatureNoise.noise2D(x * 0.001f, z * 0.001f);
// Variation à petite échelle
float detail = temperatureNoise.noise2D(x * 0.01f, z * 0.01f) * 0.2f;
return base + detail;
}
public float getHumidity(int x, int z) {
return humidityNoise.noise2D(x * 0.002f, z * 0.002f);
}
}
```
## Effets d'Altitude
```java
public class AltitudeClimateModifier {
// La température diminue avec l'altitude
public float modifyTemperature(float baseTemp, int altitude) {
float altitudeFactor = altitude / 256.0f;
return baseTemp - (altitudeFactor * 0.5f);
}
// L'humidité varie avec l'altitude
public float modifyHumidity(float baseHumidity, int altitude) {
if (altitude < 64) {
return baseHumidity * 1.2f; // Plus humide en basse altitude
} else if (altitude > 128) {
return baseHumidity * 0.6f; // Plus sec en haute altitude
}
return baseHumidity;
}
}
```
## Configuration
```yaml
# worldgen/climate_config.yaml
Climate:
NoiseScale: 0.001
TemperatureRange: [-1.0, 1.0]
HumidityRange: [0.0, 1.0]
BlendRadius: 8
AltitudeEffect: true
Biomes:
- Id: forest
Temperature: [0.3, 0.7]
Humidity: [0.4, 0.8]
- Id: desert
Temperature: [0.7, 1.0]
Humidity: [0.0, 0.3]
- Id: tundra
Temperature: [-1.0, -0.3]
Humidity: [0.2, 0.6]
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives Climatiques :**
- Utilisez un bruit lisse pour des transitions climatiques naturelles
- Considérez l'altitude lors de la détermination des biomes
- Mélangez les biomes pour des transitions fluides
- Testez la distribution des biomes sur de grandes zones
{{< /callout >}}

View File

@@ -0,0 +1,270 @@
---
title: Density Functions
type: docs
weight: 8
---
Density functions define terrain shape by calculating density values at each point in 3D space.
**Package:** `com.hypixel.hytale.builtin.hytalegenerator.density`
## Density Function Concept
Density functions return values that determine solid vs air:
- Positive values = solid terrain
- Negative values = air
- Zero = surface boundary
```java
public interface DensityFunction {
// Calculate density at position
float calculate(int x, int y, int z, GenerationContext context);
}
```
## Basic Density Functions
### ConstantDensity
Returns a constant value:
```yaml
Type: ConstantDensity
Value: 1.0
```
### NoiseDensity
Uses noise for terrain variation:
```yaml
Type: NoiseDensity
NoiseType: Simplex
Scale: 0.01
Amplitude: 1.0
Octaves: 4
Persistence: 0.5
```
### HeightGradient
Creates flat terrain at a specific height:
```yaml
Type: HeightGradient
BaseHeight: 64
Falloff: 32
```
```java
public class HeightGradient implements DensityFunction {
private int baseHeight;
private float falloff;
public float calculate(int x, int y, int z, GenerationContext ctx) {
return (baseHeight - y) / falloff;
}
}
```
## Composite Functions
### AddDensity
Combines multiple functions by addition:
```yaml
Type: AddDensity
Functions:
- terrain_base
- terrain_hills
- terrain_mountains
```
### MultiplyDensity
Multiplies function values:
```yaml
Type: MultiplyDensity
Functions:
- terrain_base
- mask_function
```
### BlendDensity
Blends between functions:
```yaml
Type: BlendDensity
FunctionA: plains_terrain
FunctionB: mountain_terrain
Blender: biome_blend
```
## Noise Functions
### SimplexNoise
Smooth, natural noise:
```java
public class SimplexNoiseDensity implements DensityFunction {
private SimplexNoise noise;
private float scale;
private int octaves;
private float persistence;
public float calculate(int x, int y, int z, GenerationContext ctx) {
float value = 0;
float amplitude = 1;
float frequency = scale;
for (int i = 0; i < octaves; i++) {
value += noise.noise3D(x * frequency, y * frequency, z * frequency) * amplitude;
amplitude *= persistence;
frequency *= 2;
}
return value;
}
}
```
### PerlinNoise
Classic Perlin noise:
```yaml
Type: PerlinNoiseDensity
Scale: 0.02
Octaves: 6
Persistence: 0.5
Lacunarity: 2.0
```
### VoronoiNoise
Cell-based noise:
```yaml
Type: VoronoiDensity
Scale: 0.005
Type: Distance # or Edge, Cell
```
## Terrain Shaping
### CliffFunction
Creates cliff formations:
```java
public class CliffFunction implements DensityFunction {
private float cliffHeight;
private float steepness;
public float calculate(int x, int y, int z, GenerationContext ctx) {
float baseNoise = ctx.noise2D(x, z, 0.01f);
float cliffFactor = Math.abs(baseNoise) > 0.3f ? steepness : 1.0f;
return (64 - y) * cliffFactor;
}
}
```
### TerraceFunction
Creates terraced terrain:
```yaml
Type: TerraceDensity
TerraceHeight: 8
Smoothing: 0.5
```
### OverhangFunction
Creates overhangs and arches:
```yaml
Type: OverhangDensity
BaseFunction: terrain_base
OverhangNoise: overhang_noise
Threshold: 0.3
```
## Biome-Specific Functions
```yaml
# density/forest_terrain.yaml
Type: BiomeDensity
Biome: forest
Function:
Type: AddDensity
Functions:
- Type: HeightGradient
BaseHeight: 68
Falloff: 24
- Type: NoiseDensity
Scale: 0.02
Amplitude: 0.3
```
## Mask Functions
Control where density applies:
```yaml
Type: MaskedDensity
Function: mountain_terrain
Mask:
Type: NoiseMask
Scale: 0.001
Threshold: 0.5
```
## Configuration
```yaml
# density/terrain_config.yaml
DensityFunctions:
terrain_base:
Type: HeightGradient
BaseHeight: 64
Falloff: 32
terrain_hills:
Type: NoiseDensity
Scale: 0.01
Amplitude: 16
terrain_combined:
Type: AddDensity
Functions: [terrain_base, terrain_hills]
```
## Custom Density Function
```java
public class MyDensityFunction implements DensityFunction {
@Override
public float calculate(int x, int y, int z, GenerationContext ctx) {
// Custom terrain logic
float baseHeight = 64 + ctx.noise2D(x, z, 0.01f) * 20;
return baseHeight - y;
}
}
// Register in plugin setup
densityRegistry.register("my_terrain", new MyDensityFunction());
```
## Best Practices
{{< callout type="info" >}}
**Density Function Guidelines:**
- Use composite functions for complex terrain
- Balance noise scales for natural appearance
- Test with visualization tools
- Consider performance with complex functions
- Use caching for expensive calculations
{{< /callout >}}

View File

@@ -0,0 +1,270 @@
---
title: Fonctions de Densité
type: docs
weight: 8
---
Les fonctions de densité définissent la forme du terrain en calculant des valeurs de densité à chaque point dans l'espace 3D.
**Package:** `com.hypixel.hytale.builtin.hytalegenerator.density`
## Concept de Fonction de Densité
Les fonctions de densité retournent des valeurs qui déterminent solide vs air :
- Valeurs positives = terrain solide
- Valeurs négatives = air
- Zéro = limite de surface
```java
public interface DensityFunction {
// Calculer la densité à une position
float calculate(int x, int y, int z, GenerationContext context);
}
```
## Fonctions de Densité de Base
### ConstantDensity
Retourne une valeur constante :
```yaml
Type: ConstantDensity
Value: 1.0
```
### NoiseDensity
Utilise le bruit pour la variation du terrain :
```yaml
Type: NoiseDensity
NoiseType: Simplex
Scale: 0.01
Amplitude: 1.0
Octaves: 4
Persistence: 0.5
```
### HeightGradient
Crée un terrain plat à une hauteur spécifique :
```yaml
Type: HeightGradient
BaseHeight: 64
Falloff: 32
```
```java
public class HeightGradient implements DensityFunction {
private int baseHeight;
private float falloff;
public float calculate(int x, int y, int z, GenerationContext ctx) {
return (baseHeight - y) / falloff;
}
}
```
## Fonctions Composites
### AddDensity
Combine plusieurs fonctions par addition :
```yaml
Type: AddDensity
Functions:
- terrain_base
- terrain_hills
- terrain_mountains
```
### MultiplyDensity
Multiplie les valeurs des fonctions :
```yaml
Type: MultiplyDensity
Functions:
- terrain_base
- mask_function
```
### BlendDensity
Mélange entre fonctions :
```yaml
Type: BlendDensity
FunctionA: plains_terrain
FunctionB: mountain_terrain
Blender: biome_blend
```
## Fonctions de Bruit
### SimplexNoise
Bruit lisse et naturel :
```java
public class SimplexNoiseDensity implements DensityFunction {
private SimplexNoise noise;
private float scale;
private int octaves;
private float persistence;
public float calculate(int x, int y, int z, GenerationContext ctx) {
float value = 0;
float amplitude = 1;
float frequency = scale;
for (int i = 0; i < octaves; i++) {
value += noise.noise3D(x * frequency, y * frequency, z * frequency) * amplitude;
amplitude *= persistence;
frequency *= 2;
}
return value;
}
}
```
### PerlinNoise
Bruit Perlin classique :
```yaml
Type: PerlinNoiseDensity
Scale: 0.02
Octaves: 6
Persistence: 0.5
Lacunarity: 2.0
```
### VoronoiNoise
Bruit basé sur les cellules :
```yaml
Type: VoronoiDensity
Scale: 0.005
Type: Distance # ou Edge, Cell
```
## Façonnage du Terrain
### CliffFunction
Crée des formations de falaises :
```java
public class CliffFunction implements DensityFunction {
private float cliffHeight;
private float steepness;
public float calculate(int x, int y, int z, GenerationContext ctx) {
float baseNoise = ctx.noise2D(x, z, 0.01f);
float cliffFactor = Math.abs(baseNoise) > 0.3f ? steepness : 1.0f;
return (64 - y) * cliffFactor;
}
}
```
### TerraceFunction
Crée un terrain en terrasses :
```yaml
Type: TerraceDensity
TerraceHeight: 8
Smoothing: 0.5
```
### OverhangFunction
Crée des surplombs et des arches :
```yaml
Type: OverhangDensity
BaseFunction: terrain_base
OverhangNoise: overhang_noise
Threshold: 0.3
```
## Fonctions Spécifiques aux Biomes
```yaml
# density/forest_terrain.yaml
Type: BiomeDensity
Biome: forest
Function:
Type: AddDensity
Functions:
- Type: HeightGradient
BaseHeight: 68
Falloff: 24
- Type: NoiseDensity
Scale: 0.02
Amplitude: 0.3
```
## Fonctions de Masque
Contrôle où la densité s'applique :
```yaml
Type: MaskedDensity
Function: mountain_terrain
Mask:
Type: NoiseMask
Scale: 0.001
Threshold: 0.5
```
## Configuration
```yaml
# density/terrain_config.yaml
DensityFunctions:
terrain_base:
Type: HeightGradient
BaseHeight: 64
Falloff: 32
terrain_hills:
Type: NoiseDensity
Scale: 0.01
Amplitude: 16
terrain_combined:
Type: AddDensity
Functions: [terrain_base, terrain_hills]
```
## Fonction de Densité Personnalisée
```java
public class MyDensityFunction implements DensityFunction {
@Override
public float calculate(int x, int y, int z, GenerationContext ctx) {
// Logique de terrain personnalisée
float baseHeight = 64 + ctx.noise2D(x, z, 0.01f) * 20;
return baseHeight - y;
}
}
// Enregistrer dans le setup du plugin
densityRegistry.register("my_terrain", new MyDensityFunction());
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives des Fonctions de Densité :**
- Utilisez des fonctions composites pour un terrain complexe
- Équilibrez les échelles de bruit pour une apparence naturelle
- Testez avec des outils de visualisation
- Considérez les performances avec les fonctions complexes
- Utilisez le cache pour les calculs coûteux
{{< /callout >}}

View File

@@ -0,0 +1,328 @@
---
title: Generation Patterns
type: docs
weight: 11
---
Generation patterns provide reusable algorithms for creating terrain features, structures, and decorations.
**Packages:**
- `com.hypixel.hytale.builtin.hytalegenerator.patterns`
- `com.hypixel.hytale.builtin.hytalegenerator.fields`
## Pattern System
Patterns encapsulate generation logic:
```java
public interface GenerationPattern {
// Apply pattern to container
void apply(GenerationContainer container, PatternContext context);
}
public class PatternContext {
private Vector3i origin;
private int size;
private Random random;
private Map<String, Object> parameters;
}
```
## Built-in Patterns
### SpherePattern
Creates spherical formations:
```yaml
Type: SpherePattern
Radius: 5
Block: stone
Hollow: false
```
```java
public class SpherePattern implements GenerationPattern {
private float radius;
private BlockType block;
private boolean hollow;
public void apply(GenerationContainer container, PatternContext ctx) {
int r = (int) radius;
for (int dx = -r; dx <= r; dx++) {
for (int dy = -r; dy <= r; dy++) {
for (int dz = -r; dz <= r; dz++) {
float dist = (float) Math.sqrt(dx*dx + dy*dy + dz*dz);
if (dist <= radius && (!hollow || dist > radius - 1)) {
container.setBlock(
ctx.origin.x + dx,
ctx.origin.y + dy,
ctx.origin.z + dz,
block
);
}
}
}
}
}
}
```
### CylinderPattern
Creates cylindrical formations:
```yaml
Type: CylinderPattern
Radius: 4
Height: 10
Block: stone
Hollow: true
```
### ArchPattern
Creates arch formations:
```yaml
Type: ArchPattern
Width: 8
Height: 6
Thickness: 2
Block: stone
```
### SpirePattern
Creates spire/stalagmite shapes:
```yaml
Type: SpirePattern
BaseRadius: 3
Height: 15
TaperRate: 0.2
Block: stone
```
## Field Generation
Fields generate values across 2D or 3D space:
### NoiseField
```java
public class NoiseField implements Field2D {
private SimplexNoise noise;
private float scale;
private int octaves;
public float getValue(int x, int z) {
return noise.noise2D(x * scale, z * scale);
}
}
```
### GradientField
```java
public class GradientField implements Field2D {
private Vector2f direction;
private float scale;
public float getValue(int x, int z) {
return (x * direction.x + z * direction.y) * scale;
}
}
```
### VoronoiField
```java
public class VoronoiField implements Field2D {
private List<Vector2i> points;
public float getValue(int x, int z) {
float minDist = Float.MAX_VALUE;
for (Vector2i point : points) {
float dist = (float) Math.sqrt(
(x - point.x) * (x - point.x) +
(z - point.y) * (z - point.y)
);
minDist = Math.min(minDist, dist);
}
return minDist;
}
}
```
## Composite Patterns
### RepeatPattern
Repeats a pattern in a grid:
```yaml
Type: RepeatPattern
Pattern: pillar_pattern
SpacingX: 10
SpacingZ: 10
Count: 16
```
### ScatterPattern
Places patterns at random positions:
```yaml
Type: ScatterPattern
Pattern: boulder_pattern
Density: 0.05
MinSpacing: 5
```
### ConditionalPattern
Applies pattern based on conditions:
```yaml
Type: ConditionalPattern
Pattern: snow_cap
Condition:
Type: HeightAbove
Height: 100
```
## Terrain Patterns
### RiverPattern
Carves river channels:
```java
public class RiverPattern implements GenerationPattern {
private List<Vector2f> path;
private float width;
private float depth;
public void apply(GenerationContainer container, PatternContext ctx) {
for (Vector2f point : path) {
carveRiverSection(container, point, width, depth);
}
}
}
```
### CliffPattern
Creates cliff formations:
```yaml
Type: CliffPattern
Height: 20
Steepness: 0.9
NoiseScale: 0.1
```
### TerracePattern
Creates terraced terrain:
```yaml
Type: TerracePattern
TerraceHeight: 5
TerraceCount: 4
Smoothing: 0.3
```
## Decoration Patterns
### ClusterPattern
Creates clusters of blocks:
```yaml
Type: ClusterPattern
Block: flower_red
Size: 3-7
Density: 0.8
```
### VeinPattern
Creates ore veins:
```yaml
Type: VeinPattern
Block: iron_ore
Length: 10-20
Branching: 0.3
```
### LayerPattern
Creates horizontal layers:
```yaml
Type: LayerPattern
Layers:
- Block: grass_block
Thickness: 1
- Block: dirt
Thickness: 3
- Block: stone
Thickness: -1 # Fill remaining
```
## Configuration
```yaml
# patterns/config.yaml
Patterns:
boulder:
Type: SpherePattern
Radius: 2-4
Block: stone
tree_trunk:
Type: CylinderPattern
Radius: 1
Height: 5-10
Block: oak_log
crystal_cluster:
Type: ClusterPattern
Block: crystal
Size: 3-8
```
## Custom Pattern
```java
public class MyPattern implements GenerationPattern {
@Override
public void apply(GenerationContainer container, PatternContext ctx) {
// Custom generation logic
int size = ctx.parameters.getInt("size", 5);
for (int i = 0; i < size; i++) {
int x = ctx.origin.x + ctx.random.nextInt(size);
int z = ctx.origin.z + ctx.random.nextInt(size);
int y = container.getHeight(x, z);
container.setBlock(x, y + 1, z, BlockTypes.STONE);
}
}
}
// Register
patternRegistry.register("my_pattern", new MyPattern());
```
## Best Practices
{{< callout type="info" >}}
**Pattern Guidelines:**
- Use patterns for reusable generation logic
- Combine patterns with fields for variety
- Test patterns at different scales
- Consider performance with complex patterns
- Document pattern parameters clearly
{{< /callout >}}

View File

@@ -0,0 +1,328 @@
---
title: Patterns de Génération
type: docs
weight: 11
---
Les patterns de génération fournissent des algorithmes réutilisables pour créer des caractéristiques de terrain, des structures et des décorations.
**Packages:**
- `com.hypixel.hytale.builtin.hytalegenerator.patterns`
- `com.hypixel.hytale.builtin.hytalegenerator.fields`
## Système de Patterns
Les patterns encapsulent la logique de génération :
```java
public interface GenerationPattern {
// Appliquer le pattern au conteneur
void apply(GenerationContainer container, PatternContext context);
}
public class PatternContext {
private Vector3i origin;
private int size;
private Random random;
private Map<String, Object> parameters;
}
```
## Patterns Intégrés
### SpherePattern
Crée des formations sphériques :
```yaml
Type: SpherePattern
Radius: 5
Block: stone
Hollow: false
```
```java
public class SpherePattern implements GenerationPattern {
private float radius;
private BlockType block;
private boolean hollow;
public void apply(GenerationContainer container, PatternContext ctx) {
int r = (int) radius;
for (int dx = -r; dx <= r; dx++) {
for (int dy = -r; dy <= r; dy++) {
for (int dz = -r; dz <= r; dz++) {
float dist = (float) Math.sqrt(dx*dx + dy*dy + dz*dz);
if (dist <= radius && (!hollow || dist > radius - 1)) {
container.setBlock(
ctx.origin.x + dx,
ctx.origin.y + dy,
ctx.origin.z + dz,
block
);
}
}
}
}
}
}
```
### CylinderPattern
Crée des formations cylindriques :
```yaml
Type: CylinderPattern
Radius: 4
Height: 10
Block: stone
Hollow: true
```
### ArchPattern
Crée des formations d'arches :
```yaml
Type: ArchPattern
Width: 8
Height: 6
Thickness: 2
Block: stone
```
### SpirePattern
Crée des formes de flèche/stalagmite :
```yaml
Type: SpirePattern
BaseRadius: 3
Height: 15
TaperRate: 0.2
Block: stone
```
## Génération par Champs
Les champs génèrent des valeurs à travers l'espace 2D ou 3D :
### NoiseField
```java
public class NoiseField implements Field2D {
private SimplexNoise noise;
private float scale;
private int octaves;
public float getValue(int x, int z) {
return noise.noise2D(x * scale, z * scale);
}
}
```
### GradientField
```java
public class GradientField implements Field2D {
private Vector2f direction;
private float scale;
public float getValue(int x, int z) {
return (x * direction.x + z * direction.y) * scale;
}
}
```
### VoronoiField
```java
public class VoronoiField implements Field2D {
private List<Vector2i> points;
public float getValue(int x, int z) {
float minDist = Float.MAX_VALUE;
for (Vector2i point : points) {
float dist = (float) Math.sqrt(
(x - point.x) * (x - point.x) +
(z - point.y) * (z - point.y)
);
minDist = Math.min(minDist, dist);
}
return minDist;
}
}
```
## Patterns Composites
### RepeatPattern
Répète un pattern dans une grille :
```yaml
Type: RepeatPattern
Pattern: pillar_pattern
SpacingX: 10
SpacingZ: 10
Count: 16
```
### ScatterPattern
Place des patterns à des positions aléatoires :
```yaml
Type: ScatterPattern
Pattern: boulder_pattern
Density: 0.05
MinSpacing: 5
```
### ConditionalPattern
Applique un pattern selon des conditions :
```yaml
Type: ConditionalPattern
Pattern: snow_cap
Condition:
Type: HeightAbove
Height: 100
```
## Patterns de Terrain
### RiverPattern
Creuse des canaux de rivière :
```java
public class RiverPattern implements GenerationPattern {
private List<Vector2f> path;
private float width;
private float depth;
public void apply(GenerationContainer container, PatternContext ctx) {
for (Vector2f point : path) {
carveRiverSection(container, point, width, depth);
}
}
}
```
### CliffPattern
Crée des formations de falaises :
```yaml
Type: CliffPattern
Height: 20
Steepness: 0.9
NoiseScale: 0.1
```
### TerracePattern
Crée un terrain en terrasses :
```yaml
Type: TerracePattern
TerraceHeight: 5
TerraceCount: 4
Smoothing: 0.3
```
## Patterns de Décoration
### ClusterPattern
Crée des groupes de blocs :
```yaml
Type: ClusterPattern
Block: flower_red
Size: 3-7
Density: 0.8
```
### VeinPattern
Crée des veines de minerai :
```yaml
Type: VeinPattern
Block: iron_ore
Length: 10-20
Branching: 0.3
```
### LayerPattern
Crée des couches horizontales :
```yaml
Type: LayerPattern
Layers:
- Block: grass_block
Thickness: 1
- Block: dirt
Thickness: 3
- Block: stone
Thickness: -1 # Remplir le reste
```
## Configuration
```yaml
# patterns/config.yaml
Patterns:
boulder:
Type: SpherePattern
Radius: 2-4
Block: stone
tree_trunk:
Type: CylinderPattern
Radius: 1
Height: 5-10
Block: oak_log
crystal_cluster:
Type: ClusterPattern
Block: crystal
Size: 3-8
```
## Pattern Personnalisé
```java
public class MyPattern implements GenerationPattern {
@Override
public void apply(GenerationContainer container, PatternContext ctx) {
// Logique de génération personnalisée
int size = ctx.parameters.getInt("size", 5);
for (int i = 0; i < size; i++) {
int x = ctx.origin.x + ctx.random.nextInt(size);
int z = ctx.origin.z + ctx.random.nextInt(size);
int y = container.getHeight(x, z);
container.setBlock(x, y + 1, z, BlockTypes.STONE);
}
}
}
// Enregistrer
patternRegistry.register("my_pattern", new MyPattern());
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives des Patterns :**
- Utilisez les patterns pour une logique de génération réutilisable
- Combinez les patterns avec les champs pour la variété
- Testez les patterns à différentes échelles
- Considérez les performances avec les patterns complexes
- Documentez clairement les paramètres des patterns
{{< /callout >}}

View File

@@ -0,0 +1,212 @@
---
title: Hytale Generator
type: docs
weight: 7
---
The Hytale Generator is the default procedural world generator that creates Hytale's terrain, structures, and features.
**Package:** `com.hypixel.hytale.builtin.hytalegenerator`
## Overview
The Hytale Generator combines multiple systems:
```
Hytale Generator
├── Density Functions (terrain shape)
├── Material Providers (block selection)
├── Prop Placement (vegetation/objects)
├── Cave Generation
├── Structure Placement
└── Biome-specific features
```
## Generator Structure
```java
public class HytaleGenerator implements WorldGenerator {
private DensityFunctionRegistry densityFunctions;
private MaterialProviderRegistry materialProviders;
private PropPlacementRegistry propPlacement;
private CaveGenerator caveGenerator;
private StructureGenerator structureGenerator;
@Override
public void generate(GenerationContainer container, int chunkX, int chunkZ) {
// 1. Generate base terrain
generateTerrain(container, chunkX, chunkZ);
// 2. Carve caves
caveGenerator.generate(container, chunkX, chunkZ);
// 3. Place structures
structureGenerator.generate(container, chunkX, chunkZ);
// 4. Place props
propPlacement.generate(container, chunkX, chunkZ);
// 5. Finalize
finalize(container);
}
}
```
## Asset Configuration
Generator assets define generation parameters:
```yaml
# hytalegenerator/world_generator.yaml
Type: HytaleGenerator
Id: default_generator
Seed: 0 # 0 = random
TerrainDensity:
Function: terrain_combined
Scale: 1.0
Materials:
Provider: biome_materials
Caves:
Enabled: true
Frequency: 0.5
Structures:
Enabled: true
Spacing: 32
Props:
Density: 1.0
```
## Framework Components
The generator framework provides core utilities:
```java
// Generation context
public class GenerationContext {
private long seed;
private int chunkX, chunkZ;
private Biome biome;
private Random random;
public float noise2D(float x, float z, float scale);
public float noise3D(float x, float y, float z, float scale);
}
// Generation phase
public enum GenerationPhase {
TERRAIN,
CARVING,
STRUCTURES,
DECORATION,
FINALIZATION
}
```
## Data Structures
Specialized data structures for generation:
```java
// Height map storage
public class HeightMap2D {
private float[] data;
private int width, height;
public float get(int x, int z);
public void set(int x, int z, float value);
}
// 3D density storage
public class DensityField3D {
private float[] data;
private int sizeX, sizeY, sizeZ;
public float get(int x, int y, int z);
public float sample(float x, float y, float z); // Interpolated
}
```
## New System Integration
The generator integrates newer systems:
```java
public class NewSystemIntegration {
// Register custom density function
public void registerDensityFunction(String id, DensityFunction function);
// Register custom material provider
public void registerMaterialProvider(String id, MaterialProvider provider);
// Register custom prop placer
public void registerPropPlacer(String id, PropPlacer placer);
}
```
## Subpackages
| Package | Files | Description |
|---------|-------|-------------|
| `assets/` | 232 | Asset definitions for generation |
| `density/` | 76 | Density function implementations |
| `newsystem/` | 29 | New generation system |
| `materialproviders/` | 29 | Material selection |
| `framework/` | 29 | Core framework |
| `props/` | 24 | Prop placement |
| `datastructures/` | 16 | Data structures |
| `positionproviders/` | 14 | Position calculation |
| `patterns/` | 13 | Generation patterns |
| `fields/` | 8 | Field generation |
## Custom Generator
Create a custom generator:
```java
public class MyGenerator extends HytaleGenerator {
@Override
protected void generateTerrain(GenerationContainer container, int chunkX, int chunkZ) {
// Custom terrain generation
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
int worldX = chunkX * 16 + x;
int worldZ = chunkZ * 16 + z;
float density = getCustomDensity(worldX, worldZ);
int height = (int) (density * 64) + 64;
fillColumn(container, x, z, height);
}
}
}
}
```
## Configuration
```yaml
# hytalegenerator/config.yaml
HytaleGenerator:
WorldType: normal
SeaLevel: 64
TerrainAmplitude: 1.0
CaveFrequency: 0.5
StructureSpacing: 32
BiomeScale: 256
Features:
Caves: true
Structures: true
Props: true
Ores: true
```
## Best Practices
{{< callout type="info" >}}
**Generator Guidelines:**
- Extend HytaleGenerator for custom worlds
- Use the asset system for configuration
- Register custom components in setup phase
- Test generation across different seeds
- Profile performance for custom functions
{{< /callout >}}

View File

@@ -0,0 +1,212 @@
---
title: Hytale Generator
type: docs
weight: 7
---
Le Hytale Generator est le générateur de monde procédural par défaut qui crée le terrain, les structures et les caractéristiques de Hytale.
**Package:** `com.hypixel.hytale.builtin.hytalegenerator`
## Vue d'Ensemble
Le Hytale Generator combine plusieurs systèmes :
```
Hytale Generator
├── Fonctions de Densité (forme du terrain)
├── Material Providers (sélection de blocs)
├── Placement de Props (végétation/objets)
├── Génération de Grottes
├── Placement de Structures
└── Caractéristiques spécifiques aux biomes
```
## Structure du Générateur
```java
public class HytaleGenerator implements WorldGenerator {
private DensityFunctionRegistry densityFunctions;
private MaterialProviderRegistry materialProviders;
private PropPlacementRegistry propPlacement;
private CaveGenerator caveGenerator;
private StructureGenerator structureGenerator;
@Override
public void generate(GenerationContainer container, int chunkX, int chunkZ) {
// 1. Générer le terrain de base
generateTerrain(container, chunkX, chunkZ);
// 2. Creuser les grottes
caveGenerator.generate(container, chunkX, chunkZ);
// 3. Placer les structures
structureGenerator.generate(container, chunkX, chunkZ);
// 4. Placer les props
propPlacement.generate(container, chunkX, chunkZ);
// 5. Finaliser
finalize(container);
}
}
```
## Configuration d'Assets
Les assets du générateur définissent les paramètres de génération :
```yaml
# hytalegenerator/world_generator.yaml
Type: HytaleGenerator
Id: default_generator
Seed: 0 # 0 = aléatoire
TerrainDensity:
Function: terrain_combined
Scale: 1.0
Materials:
Provider: biome_materials
Caves:
Enabled: true
Frequency: 0.5
Structures:
Enabled: true
Spacing: 32
Props:
Density: 1.0
```
## Composants du Framework
Le framework du générateur fournit des utilitaires de base :
```java
// Contexte de génération
public class GenerationContext {
private long seed;
private int chunkX, chunkZ;
private Biome biome;
private Random random;
public float noise2D(float x, float z, float scale);
public float noise3D(float x, float y, float z, float scale);
}
// Phase de génération
public enum GenerationPhase {
TERRAIN,
CARVING,
STRUCTURES,
DECORATION,
FINALIZATION
}
```
## Structures de Données
Structures de données spécialisées pour la génération :
```java
// Stockage de carte de hauteur
public class HeightMap2D {
private float[] data;
private int width, height;
public float get(int x, int z);
public void set(int x, int z, float value);
}
// Stockage de densité 3D
public class DensityField3D {
private float[] data;
private int sizeX, sizeY, sizeZ;
public float get(int x, int y, int z);
public float sample(float x, float y, float z); // Interpolé
}
```
## Intégration Nouveau Système
Le générateur intègre les systèmes plus récents :
```java
public class NewSystemIntegration {
// Enregistrer une fonction de densité personnalisée
public void registerDensityFunction(String id, DensityFunction function);
// Enregistrer un material provider personnalisé
public void registerMaterialProvider(String id, MaterialProvider provider);
// Enregistrer un placeur de props personnalisé
public void registerPropPlacer(String id, PropPlacer placer);
}
```
## Sous-packages
| Package | Fichiers | Description |
|---------|----------|-------------|
| `assets/` | 232 | Définitions d'assets pour la génération |
| `density/` | 76 | Implémentations de fonctions de densité |
| `newsystem/` | 29 | Nouveau système de génération |
| `materialproviders/` | 29 | Sélection de matériaux |
| `framework/` | 29 | Framework de base |
| `props/` | 24 | Placement de props |
| `datastructures/` | 16 | Structures de données |
| `positionproviders/` | 14 | Calcul de positions |
| `patterns/` | 13 | Patterns de génération |
| `fields/` | 8 | Génération par champs |
## Générateur Personnalisé
Créer un générateur personnalisé :
```java
public class MyGenerator extends HytaleGenerator {
@Override
protected void generateTerrain(GenerationContainer container, int chunkX, int chunkZ) {
// Génération de terrain personnalisée
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
int worldX = chunkX * 16 + x;
int worldZ = chunkZ * 16 + z;
float density = getCustomDensity(worldX, worldZ);
int height = (int) (density * 64) + 64;
fillColumn(container, x, z, height);
}
}
}
}
```
## Configuration
```yaml
# hytalegenerator/config.yaml
HytaleGenerator:
WorldType: normal
SeaLevel: 64
TerrainAmplitude: 1.0
CaveFrequency: 0.5
StructureSpacing: 32
BiomeScale: 256
Features:
Caves: true
Structures: true
Props: true
Ores: true
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives du Générateur :**
- Étendez HytaleGenerator pour des mondes personnalisés
- Utilisez le système d'assets pour la configuration
- Enregistrez les composants personnalisés dans la phase setup
- Testez la génération avec différentes seeds
- Profilez les performances des fonctions personnalisées
{{< /callout >}}

View File

@@ -0,0 +1,273 @@
---
title: Material Providers
type: docs
weight: 9
---
Material providers determine which blocks are placed at each position during terrain generation.
**Package:** `com.hypixel.hytale.builtin.hytalegenerator.materialproviders`
## Material Provider Concept
Material providers select blocks based on context:
```java
public interface MaterialProvider {
// Get block for position
BlockType getBlock(int x, int y, int z, MaterialContext context);
}
public class MaterialContext {
private Biome biome;
private float density;
private int surfaceHeight;
private int depth; // Depth below surface
private boolean underwater;
}
```
## Basic Providers
### SingleMaterialProvider
Returns a single block type:
```yaml
Type: SingleMaterial
Block: stone
```
### DepthMaterialProvider
Changes blocks based on depth:
```yaml
Type: DepthMaterial
Layers:
- Depth: 0
Block: grass_block
- Depth: 1
Block: dirt
- Depth: 4
Block: stone
```
```java
public class DepthMaterialProvider implements MaterialProvider {
private List<DepthLayer> layers;
public BlockType getBlock(int x, int y, int z, MaterialContext ctx) {
for (DepthLayer layer : layers) {
if (ctx.depth >= layer.minDepth) {
return layer.block;
}
}
return defaultBlock;
}
}
```
## Biome-Based Providers
### BiomeMaterialProvider
Selects materials based on biome:
```yaml
Type: BiomeMaterial
Biomes:
forest:
Surface: grass_block
Subsurface: dirt
Stone: stone
desert:
Surface: sand
Subsurface: sandstone
Stone: sandstone
tundra:
Surface: snow_block
Subsurface: permafrost
Stone: stone
```
### BiomeSurfaceProvider
Surface-specific provider:
```java
public class BiomeSurfaceProvider implements MaterialProvider {
private Map<String, SurfaceMaterials> biomeMaterials;
public BlockType getBlock(int x, int y, int z, MaterialContext ctx) {
SurfaceMaterials materials = biomeMaterials.get(ctx.biome.getId());
if (materials == null) {
materials = defaultMaterials;
}
if (ctx.depth == 0) {
return materials.surface;
} else if (ctx.depth < 4) {
return materials.subsurface;
} else {
return materials.stone;
}
}
}
```
## Noise-Based Providers
### NoiseMaterialProvider
Uses noise for variation:
```yaml
Type: NoiseMaterial
Noise:
Scale: 0.1
Threshold: 0.5
Materials:
Above: stone
Below: granite
```
### GradientMaterialProvider
Blends materials based on value:
```java
public class GradientMaterialProvider implements MaterialProvider {
private SimplexNoise noise;
private List<MaterialStop> stops;
public BlockType getBlock(int x, int y, int z, MaterialContext ctx) {
float value = noise.noise3D(x * 0.05f, y * 0.05f, z * 0.05f);
for (int i = stops.size() - 1; i >= 0; i--) {
if (value >= stops.get(i).threshold) {
return stops.get(i).block;
}
}
return defaultBlock;
}
}
```
## Composite Providers
### LayeredMaterialProvider
Stacks multiple providers:
```yaml
Type: LayeredMaterial
Layers:
- Provider: bedrock_layer
MaxY: 5
- Provider: stone_layer
MaxY: 60
- Provider: surface_layer
MaxY: 256
```
### ConditionalMaterialProvider
Conditional block selection:
```yaml
Type: ConditionalMaterial
Conditions:
- Condition:
Type: Underwater
Provider: sand_material
- Condition:
Type: NearCave
Distance: 2
Provider: cave_stone
- Default: default_material
```
## Special Providers
### OreMaterialProvider
Places ore veins:
```yaml
Type: OreMaterial
Ores:
- Type: iron_ore
MinY: 0
MaxY: 64
Frequency: 0.02
Size: 9
- Type: diamond_ore
MinY: 0
MaxY: 16
Frequency: 0.001
Size: 4
```
### CaveMaterialProvider
Materials for cave surfaces:
```yaml
Type: CaveMaterial
Floor: gravel
Ceiling: stone
Walls: mossy_stone
WaterLevel: 10
WaterBlock: water
```
## Configuration
```yaml
# materialproviders/config.yaml
MaterialProviders:
default:
Type: BiomeMaterial
Biomes:
forest:
Surface: grass_block
Subsurface: dirt
Stone: stone
cave:
Type: CaveMaterial
Floor: gravel
Walls: stone
```
## Custom Provider
```java
public class MyMaterialProvider implements MaterialProvider {
@Override
public BlockType getBlock(int x, int y, int z, MaterialContext ctx) {
// Custom material logic
if (ctx.underwater) {
return BlockTypes.SAND;
}
if (ctx.depth == 0) {
return BlockTypes.GRASS_BLOCK;
}
return BlockTypes.STONE;
}
}
// Register
materialRegistry.register("my_materials", new MyMaterialProvider());
```
## Best Practices
{{< callout type="info" >}}
**Material Provider Guidelines:**
- Use biome-aware providers for variety
- Layer providers for complex surfaces
- Add noise for natural variation
- Consider special cases (underwater, caves)
- Test material transitions between biomes
{{< /callout >}}

View File

@@ -0,0 +1,273 @@
---
title: Material Providers
type: docs
weight: 9
---
Les material providers déterminent quels blocs sont placés à chaque position pendant la génération du terrain.
**Package:** `com.hypixel.hytale.builtin.hytalegenerator.materialproviders`
## Concept de Material Provider
Les material providers sélectionnent les blocs selon le contexte :
```java
public interface MaterialProvider {
// Obtenir le bloc pour une position
BlockType getBlock(int x, int y, int z, MaterialContext context);
}
public class MaterialContext {
private Biome biome;
private float density;
private int surfaceHeight;
private int depth; // Profondeur sous la surface
private boolean underwater;
}
```
## Providers de Base
### SingleMaterialProvider
Retourne un seul type de bloc :
```yaml
Type: SingleMaterial
Block: stone
```
### DepthMaterialProvider
Change les blocs selon la profondeur :
```yaml
Type: DepthMaterial
Layers:
- Depth: 0
Block: grass_block
- Depth: 1
Block: dirt
- Depth: 4
Block: stone
```
```java
public class DepthMaterialProvider implements MaterialProvider {
private List<DepthLayer> layers;
public BlockType getBlock(int x, int y, int z, MaterialContext ctx) {
for (DepthLayer layer : layers) {
if (ctx.depth >= layer.minDepth) {
return layer.block;
}
}
return defaultBlock;
}
}
```
## Providers Basés sur les Biomes
### BiomeMaterialProvider
Sélectionne les matériaux selon le biome :
```yaml
Type: BiomeMaterial
Biomes:
forest:
Surface: grass_block
Subsurface: dirt
Stone: stone
desert:
Surface: sand
Subsurface: sandstone
Stone: sandstone
tundra:
Surface: snow_block
Subsurface: permafrost
Stone: stone
```
### BiomeSurfaceProvider
Provider spécifique à la surface :
```java
public class BiomeSurfaceProvider implements MaterialProvider {
private Map<String, SurfaceMaterials> biomeMaterials;
public BlockType getBlock(int x, int y, int z, MaterialContext ctx) {
SurfaceMaterials materials = biomeMaterials.get(ctx.biome.getId());
if (materials == null) {
materials = defaultMaterials;
}
if (ctx.depth == 0) {
return materials.surface;
} else if (ctx.depth < 4) {
return materials.subsurface;
} else {
return materials.stone;
}
}
}
```
## Providers Basés sur le Bruit
### NoiseMaterialProvider
Utilise le bruit pour la variation :
```yaml
Type: NoiseMaterial
Noise:
Scale: 0.1
Threshold: 0.5
Materials:
Above: stone
Below: granite
```
### GradientMaterialProvider
Mélange les matériaux selon une valeur :
```java
public class GradientMaterialProvider implements MaterialProvider {
private SimplexNoise noise;
private List<MaterialStop> stops;
public BlockType getBlock(int x, int y, int z, MaterialContext ctx) {
float value = noise.noise3D(x * 0.05f, y * 0.05f, z * 0.05f);
for (int i = stops.size() - 1; i >= 0; i--) {
if (value >= stops.get(i).threshold) {
return stops.get(i).block;
}
}
return defaultBlock;
}
}
```
## Providers Composites
### LayeredMaterialProvider
Empile plusieurs providers :
```yaml
Type: LayeredMaterial
Layers:
- Provider: bedrock_layer
MaxY: 5
- Provider: stone_layer
MaxY: 60
- Provider: surface_layer
MaxY: 256
```
### ConditionalMaterialProvider
Sélection conditionnelle de blocs :
```yaml
Type: ConditionalMaterial
Conditions:
- Condition:
Type: Underwater
Provider: sand_material
- Condition:
Type: NearCave
Distance: 2
Provider: cave_stone
- Default: default_material
```
## Providers Spéciaux
### OreMaterialProvider
Place des veines de minerai :
```yaml
Type: OreMaterial
Ores:
- Type: iron_ore
MinY: 0
MaxY: 64
Frequency: 0.02
Size: 9
- Type: diamond_ore
MinY: 0
MaxY: 16
Frequency: 0.001
Size: 4
```
### CaveMaterialProvider
Matériaux pour les surfaces de grottes :
```yaml
Type: CaveMaterial
Floor: gravel
Ceiling: stone
Walls: mossy_stone
WaterLevel: 10
WaterBlock: water
```
## Configuration
```yaml
# materialproviders/config.yaml
MaterialProviders:
default:
Type: BiomeMaterial
Biomes:
forest:
Surface: grass_block
Subsurface: dirt
Stone: stone
cave:
Type: CaveMaterial
Floor: gravel
Walls: stone
```
## Provider Personnalisé
```java
public class MyMaterialProvider implements MaterialProvider {
@Override
public BlockType getBlock(int x, int y, int z, MaterialContext ctx) {
// Logique de matériau personnalisée
if (ctx.underwater) {
return BlockTypes.SAND;
}
if (ctx.depth == 0) {
return BlockTypes.GRASS_BLOCK;
}
return BlockTypes.STONE;
}
}
// Enregistrer
materialRegistry.register("my_materials", new MyMaterialProvider());
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives des Material Providers :**
- Utilisez des providers conscients des biomes pour la variété
- Superposez les providers pour des surfaces complexes
- Ajoutez du bruit pour une variation naturelle
- Considérez les cas spéciaux (sous l'eau, grottes)
- Testez les transitions de matériaux entre biomes
{{< /callout >}}

View File

@@ -0,0 +1,220 @@
---
title: Prefabs
type: docs
weight: 5
---
Prefabs are pre-built structures that can be placed during world generation, such as buildings, ruins, and dungeons.
**Package:** `com.hypixel.hytale.server.worldgen.prefab`
## Prefab System
The prefab system handles structure placement:
```java
public class PrefabPlacer {
// Place prefab at position
public boolean place(Prefab prefab, World world, Vector3i position);
// Check if prefab can be placed
public boolean canPlace(Prefab prefab, World world, Vector3i position);
// Find valid position for prefab
public Vector3i findPlacement(Prefab prefab, World world, int chunkX, int chunkZ);
}
```
## Prefab Definition
```yaml
# prefabs/village_house.yaml
Type: Prefab
Id: village_house
Size: [7, 5, 9]
Origin: [3, 0, 4]
Palette:
W: oak_planks
S: stone_bricks
G: glass_pane
D: oak_door
A: air
Structure:
- "SSSSSSS"
- "SWWGWWS"
- "SWWAWWS"
- "SWWDWWS"
- "SSSSSSS"
# ... more layers
PlacementRules:
RequiresSolidGround: true
MinGroundLevel: 60
MaxGroundLevel: 100
AllowedBiomes: [plains, forest]
```
## Prefab Class
```java
public class Prefab {
private String id;
private Vector3i size;
private Vector3i origin;
private Map<Character, BlockType> palette;
private List<String[]> structure;
private PlacementRules rules;
// Get block at relative position
public BlockType getBlock(int x, int y, int z);
// Get size
public Vector3i getSize();
// Check placement rules
public boolean canPlace(World world, Vector3i position);
}
```
## Placement Rules
```java
public class PlacementRules {
private boolean requiresSolidGround;
private int minGroundLevel;
private int maxGroundLevel;
private Set<String> allowedBiomes;
private float spacing;
private int maxSlope;
public boolean evaluate(World world, Vector3i position) {
// Check ground level
if (position.y < minGroundLevel || position.y > maxGroundLevel) {
return false;
}
// Check biome
Biome biome = world.getBiome(position.x, position.z);
if (!allowedBiomes.contains(biome.getId())) {
return false;
}
// Check solid ground
if (requiresSolidGround && !hasSolidGround(world, position)) {
return false;
}
return true;
}
}
```
## Structure Placement
```java
public class StructurePlacer {
public void placeStructure(Prefab prefab, World world, Vector3i position, Rotation rotation) {
Vector3i size = prefab.getSize();
for (int y = 0; y < size.y; y++) {
for (int z = 0; z < size.z; z++) {
for (int x = 0; x < size.x; x++) {
BlockType block = prefab.getBlock(x, y, z);
if (block != null && block != BlockTypes.AIR) {
Vector3i rotated = rotate(x, y, z, rotation, size);
Vector3i worldPos = position.add(rotated);
world.setBlock(worldPos, block);
}
}
}
}
}
}
```
## Rotation Support
```java
public enum Rotation {
NONE(0),
CW_90(90),
CW_180(180),
CW_270(270);
public Vector3i rotate(Vector3i pos, Vector3i size) {
switch (this) {
case NONE:
return pos;
case CW_90:
return new Vector3i(size.z - pos.z - 1, pos.y, pos.x);
case CW_180:
return new Vector3i(size.x - pos.x - 1, pos.y, size.z - pos.z - 1);
case CW_270:
return new Vector3i(pos.z, pos.y, size.x - pos.x - 1);
}
return pos;
}
}
```
## Prefab Variants
Support for randomized variants:
```yaml
# prefabs/tree_oak.yaml
Type: PrefabGroup
Id: tree_oak
Variants:
- prefabs/tree_oak_small
- prefabs/tree_oak_medium
- prefabs/tree_oak_large
Weights: [0.5, 0.35, 0.15]
```
## Generation Integration
```java
public class PrefabGenerator {
private List<PrefabConfig> configs;
public void generatePrefabs(GenerationContainer container, int chunkX, int chunkZ) {
for (PrefabConfig config : configs) {
if (random.nextFloat() < config.frequency) {
Vector3i position = findValidPosition(config.prefab, container, chunkX, chunkZ);
if (position != null) {
Rotation rotation = Rotation.values()[random.nextInt(4)];
placePrefab(config.prefab, container, position, rotation);
}
}
}
}
}
```
## Entity Spawners
Prefabs can include entity spawn points:
```yaml
# prefabs/dungeon_room.yaml
Type: Prefab
Id: dungeon_room
EntitySpawns:
- Type: skeleton
Position: [5, 1, 5]
Count: 2
- Type: chest
Position: [7, 1, 7]
LootTable: dungeon_loot
```
## Best Practices
{{< callout type="info" >}}
**Prefab Guidelines:**
- Use palettes for easy block substitution
- Define clear placement rules
- Support rotation for variety
- Include variants for visual diversity
- Consider performance with complex structures
{{< /callout >}}

View File

@@ -0,0 +1,220 @@
---
title: Prefabs
type: docs
weight: 5
---
Les prefabs sont des structures pré-construites qui peuvent être placées pendant la génération de monde, comme des bâtiments, des ruines et des donjons.
**Package:** `com.hypixel.hytale.server.worldgen.prefab`
## Système de Prefabs
Le système de prefabs gère le placement des structures :
```java
public class PrefabPlacer {
// Placer un prefab à une position
public boolean place(Prefab prefab, World world, Vector3i position);
// Vérifier si un prefab peut être placé
public boolean canPlace(Prefab prefab, World world, Vector3i position);
// Trouver une position valide pour un prefab
public Vector3i findPlacement(Prefab prefab, World world, int chunkX, int chunkZ);
}
```
## Définition de Prefab
```yaml
# prefabs/village_house.yaml
Type: Prefab
Id: village_house
Size: [7, 5, 9]
Origin: [3, 0, 4]
Palette:
W: oak_planks
S: stone_bricks
G: glass_pane
D: oak_door
A: air
Structure:
- "SSSSSSS"
- "SWWGWWS"
- "SWWAWWS"
- "SWWDWWS"
- "SSSSSSS"
# ... plus de couches
PlacementRules:
RequiresSolidGround: true
MinGroundLevel: 60
MaxGroundLevel: 100
AllowedBiomes: [plains, forest]
```
## Classe Prefab
```java
public class Prefab {
private String id;
private Vector3i size;
private Vector3i origin;
private Map<Character, BlockType> palette;
private List<String[]> structure;
private PlacementRules rules;
// Obtenir le bloc à une position relative
public BlockType getBlock(int x, int y, int z);
// Obtenir la taille
public Vector3i getSize();
// Vérifier les règles de placement
public boolean canPlace(World world, Vector3i position);
}
```
## Règles de Placement
```java
public class PlacementRules {
private boolean requiresSolidGround;
private int minGroundLevel;
private int maxGroundLevel;
private Set<String> allowedBiomes;
private float spacing;
private int maxSlope;
public boolean evaluate(World world, Vector3i position) {
// Vérifier le niveau du sol
if (position.y < minGroundLevel || position.y > maxGroundLevel) {
return false;
}
// Vérifier le biome
Biome biome = world.getBiome(position.x, position.z);
if (!allowedBiomes.contains(biome.getId())) {
return false;
}
// Vérifier le sol solide
if (requiresSolidGround && !hasSolidGround(world, position)) {
return false;
}
return true;
}
}
```
## Placement de Structure
```java
public class StructurePlacer {
public void placeStructure(Prefab prefab, World world, Vector3i position, Rotation rotation) {
Vector3i size = prefab.getSize();
for (int y = 0; y < size.y; y++) {
for (int z = 0; z < size.z; z++) {
for (int x = 0; x < size.x; x++) {
BlockType block = prefab.getBlock(x, y, z);
if (block != null && block != BlockTypes.AIR) {
Vector3i rotated = rotate(x, y, z, rotation, size);
Vector3i worldPos = position.add(rotated);
world.setBlock(worldPos, block);
}
}
}
}
}
}
```
## Support de Rotation
```java
public enum Rotation {
NONE(0),
CW_90(90),
CW_180(180),
CW_270(270);
public Vector3i rotate(Vector3i pos, Vector3i size) {
switch (this) {
case NONE:
return pos;
case CW_90:
return new Vector3i(size.z - pos.z - 1, pos.y, pos.x);
case CW_180:
return new Vector3i(size.x - pos.x - 1, pos.y, size.z - pos.z - 1);
case CW_270:
return new Vector3i(pos.z, pos.y, size.x - pos.x - 1);
}
return pos;
}
}
```
## Variantes de Prefabs
Support pour des variantes aléatoires :
```yaml
# prefabs/tree_oak.yaml
Type: PrefabGroup
Id: tree_oak
Variants:
- prefabs/tree_oak_small
- prefabs/tree_oak_medium
- prefabs/tree_oak_large
Weights: [0.5, 0.35, 0.15]
```
## Intégration à la Génération
```java
public class PrefabGenerator {
private List<PrefabConfig> configs;
public void generatePrefabs(GenerationContainer container, int chunkX, int chunkZ) {
for (PrefabConfig config : configs) {
if (random.nextFloat() < config.frequency) {
Vector3i position = findValidPosition(config.prefab, container, chunkX, chunkZ);
if (position != null) {
Rotation rotation = Rotation.values()[random.nextInt(4)];
placePrefab(config.prefab, container, position, rotation);
}
}
}
}
}
```
## Spawners d'Entités
Les prefabs peuvent inclure des points de spawn d'entités :
```yaml
# prefabs/dungeon_room.yaml
Type: Prefab
Id: dungeon_room
EntitySpawns:
- Type: skeleton
Position: [5, 1, 5]
Count: 2
- Type: chest
Position: [7, 1, 7]
LootTable: dungeon_loot
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives des Prefabs :**
- Utilisez des palettes pour une substitution de blocs facile
- Définissez des règles de placement claires
- Supportez la rotation pour la variété
- Incluez des variantes pour la diversité visuelle
- Considérez les performances avec les structures complexes
{{< /callout >}}

View File

@@ -0,0 +1,281 @@
---
title: Prop Placement
type: docs
weight: 10
---
Prop placement handles the generation of vegetation, decorations, and small objects on terrain surfaces.
**Package:** `com.hypixel.hytale.builtin.hytalegenerator.props`
## Prop System
Props are small features placed after terrain generation:
```java
public interface PropPlacer {
// Place props in chunk
void place(GenerationContainer container, int chunkX, int chunkZ);
}
public class PropContext {
private int x, y, z;
private Biome biome;
private BlockType surfaceBlock;
private float slope;
private boolean nearWater;
}
```
## Prop Definition
```yaml
# props/oak_tree.yaml
Type: Prop
Id: oak_tree
Prefab: prefabs/trees/oak_medium
Density: 0.02
Placement:
SurfaceTypes: [grass_block, dirt]
MinSlope: 0
MaxSlope: 30
MinSpacing: 3
Requirements:
ClearanceAbove: 10
ClearanceRadius: 2
Variants:
- prefabs/trees/oak_small
- prefabs/trees/oak_medium
- prefabs/trees/oak_large
VariantWeights: [0.3, 0.5, 0.2]
```
## Position Providers
Determine where props can be placed:
### RandomPositionProvider
Random surface positions:
```yaml
Type: RandomPosition
Density: 0.1
GridSize: 4 # One attempt per 4x4 area
```
### GridPositionProvider
Regular grid with jitter:
```yaml
Type: GridPosition
Spacing: 8
Jitter: 3
```
### NoisePositionProvider
Noise-based clustering:
```yaml
Type: NoisePosition
NoiseScale: 0.05
Threshold: 0.3
Density: 0.5
```
## Placement Rules
```java
public class PlacementRules {
private Set<BlockType> validSurfaces;
private float minSlope;
private float maxSlope;
private int minSpacing;
private int clearanceAbove;
public boolean canPlace(PropContext ctx) {
// Check surface type
if (!validSurfaces.contains(ctx.surfaceBlock)) {
return false;
}
// Check slope
if (ctx.slope < minSlope || ctx.slope > maxSlope) {
return false;
}
// Check spacing from other props
if (!hasMinSpacing(ctx)) {
return false;
}
// Check clearance
if (!hasClearance(ctx)) {
return false;
}
return true;
}
}
```
## Vegetation Props
### TreePlacer
Places trees with biome variation:
```yaml
Type: TreePlacer
Biomes:
forest:
Density: 0.3
Types: [oak, birch, maple]
plains:
Density: 0.02
Types: [oak]
jungle:
Density: 0.5
Types: [jungle_tree, palm]
```
### GrassPlacer
Places grass and flowers:
```yaml
Type: GrassPlacer
Density: 0.8
Variants:
- Block: tall_grass
Weight: 0.7
- Block: fern
Weight: 0.2
- Block: flower_red
Weight: 0.05
- Block: flower_yellow
Weight: 0.05
```
### BushPlacer
Places bushes and shrubs:
```yaml
Type: BushPlacer
Density: 0.1
Prefabs: [bush_small, bush_medium]
NearTreesOnly: true
TreeDistance: 5
```
## Object Props
### RockPlacer
Places rocks and boulders:
```yaml
Type: RockPlacer
Density: 0.05
Sizes:
- Prefab: rock_small
Weight: 0.6
- Prefab: rock_medium
Weight: 0.3
- Prefab: rock_large
Weight: 0.1
BiomeModifiers:
mountains: 2.0
plains: 0.5
```
### DebrisPlacer
Places fallen logs, branches:
```yaml
Type: DebrisPlacer
Density: 0.03
Items:
- fallen_log
- branch
- leaf_pile
ForestOnly: true
```
## Biome Integration
```java
public class BiomePropPlacer implements PropPlacer {
private Map<String, List<PropConfig>> biomeProps;
public void place(GenerationContainer container, int chunkX, int chunkZ) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
int worldX = chunkX * 16 + x;
int worldZ = chunkZ * 16 + z;
Biome biome = container.getBiome(worldX, worldZ);
List<PropConfig> props = biomeProps.get(biome.getId());
for (PropConfig prop : props) {
if (shouldPlace(prop, worldX, worldZ)) {
placeProp(container, prop, worldX, worldZ);
}
}
}
}
}
}
```
## Configuration
```yaml
# props/config.yaml
PropPlacement:
Enabled: true
DensityMultiplier: 1.0
Props:
- Type: TreePlacer
Priority: 1
- Type: BushPlacer
Priority: 2
- Type: GrassPlacer
Priority: 3
- Type: RockPlacer
Priority: 4
```
## Custom Prop Placer
```java
public class MyPropPlacer implements PropPlacer {
@Override
public void place(GenerationContainer container, int chunkX, int chunkZ) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
if (random.nextFloat() < 0.1f) {
int surfaceY = container.getHeight(x, z);
// Place custom prop
placeMyProp(container, x, surfaceY + 1, z);
}
}
}
}
}
```
## Best Practices
{{< callout type="info" >}}
**Prop Placement Guidelines:**
- Use appropriate density for biome feel
- Ensure props don't overlap
- Add variety with multiple variants
- Consider performance with high density
- Test visual distribution across terrain
{{< /callout >}}

View File

@@ -0,0 +1,281 @@
---
title: Placement de Props
type: docs
weight: 10
---
Le placement de props gère la génération de végétation, de décorations et de petits objets sur les surfaces du terrain.
**Package:** `com.hypixel.hytale.builtin.hytalegenerator.props`
## Système de Props
Les props sont de petites caractéristiques placées après la génération du terrain :
```java
public interface PropPlacer {
// Placer des props dans un chunk
void place(GenerationContainer container, int chunkX, int chunkZ);
}
public class PropContext {
private int x, y, z;
private Biome biome;
private BlockType surfaceBlock;
private float slope;
private boolean nearWater;
}
```
## Définition de Prop
```yaml
# props/oak_tree.yaml
Type: Prop
Id: oak_tree
Prefab: prefabs/trees/oak_medium
Density: 0.02
Placement:
SurfaceTypes: [grass_block, dirt]
MinSlope: 0
MaxSlope: 30
MinSpacing: 3
Requirements:
ClearanceAbove: 10
ClearanceRadius: 2
Variants:
- prefabs/trees/oak_small
- prefabs/trees/oak_medium
- prefabs/trees/oak_large
VariantWeights: [0.3, 0.5, 0.2]
```
## Fournisseurs de Position
Déterminent où les props peuvent être placés :
### RandomPositionProvider
Positions de surface aléatoires :
```yaml
Type: RandomPosition
Density: 0.1
GridSize: 4 # Une tentative par zone 4x4
```
### GridPositionProvider
Grille régulière avec gigue :
```yaml
Type: GridPosition
Spacing: 8
Jitter: 3
```
### NoisePositionProvider
Regroupement basé sur le bruit :
```yaml
Type: NoisePosition
NoiseScale: 0.05
Threshold: 0.3
Density: 0.5
```
## Règles de Placement
```java
public class PlacementRules {
private Set<BlockType> validSurfaces;
private float minSlope;
private float maxSlope;
private int minSpacing;
private int clearanceAbove;
public boolean canPlace(PropContext ctx) {
// Vérifier le type de surface
if (!validSurfaces.contains(ctx.surfaceBlock)) {
return false;
}
// Vérifier la pente
if (ctx.slope < minSlope || ctx.slope > maxSlope) {
return false;
}
// Vérifier l'espacement des autres props
if (!hasMinSpacing(ctx)) {
return false;
}
// Vérifier le dégagement
if (!hasClearance(ctx)) {
return false;
}
return true;
}
}
```
## Props de Végétation
### TreePlacer
Place des arbres avec variation par biome :
```yaml
Type: TreePlacer
Biomes:
forest:
Density: 0.3
Types: [oak, birch, maple]
plains:
Density: 0.02
Types: [oak]
jungle:
Density: 0.5
Types: [jungle_tree, palm]
```
### GrassPlacer
Place de l'herbe et des fleurs :
```yaml
Type: GrassPlacer
Density: 0.8
Variants:
- Block: tall_grass
Weight: 0.7
- Block: fern
Weight: 0.2
- Block: flower_red
Weight: 0.05
- Block: flower_yellow
Weight: 0.05
```
### BushPlacer
Place des buissons et arbustes :
```yaml
Type: BushPlacer
Density: 0.1
Prefabs: [bush_small, bush_medium]
NearTreesOnly: true
TreeDistance: 5
```
## Props d'Objets
### RockPlacer
Place des rochers et blocs :
```yaml
Type: RockPlacer
Density: 0.05
Sizes:
- Prefab: rock_small
Weight: 0.6
- Prefab: rock_medium
Weight: 0.3
- Prefab: rock_large
Weight: 0.1
BiomeModifiers:
mountains: 2.0
plains: 0.5
```
### DebrisPlacer
Place des troncs tombés, branches :
```yaml
Type: DebrisPlacer
Density: 0.03
Items:
- fallen_log
- branch
- leaf_pile
ForestOnly: true
```
## Intégration des Biomes
```java
public class BiomePropPlacer implements PropPlacer {
private Map<String, List<PropConfig>> biomeProps;
public void place(GenerationContainer container, int chunkX, int chunkZ) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
int worldX = chunkX * 16 + x;
int worldZ = chunkZ * 16 + z;
Biome biome = container.getBiome(worldX, worldZ);
List<PropConfig> props = biomeProps.get(biome.getId());
for (PropConfig prop : props) {
if (shouldPlace(prop, worldX, worldZ)) {
placeProp(container, prop, worldX, worldZ);
}
}
}
}
}
}
```
## Configuration
```yaml
# props/config.yaml
PropPlacement:
Enabled: true
DensityMultiplier: 1.0
Props:
- Type: TreePlacer
Priority: 1
- Type: BushPlacer
Priority: 2
- Type: GrassPlacer
Priority: 3
- Type: RockPlacer
Priority: 4
```
## Prop Placer Personnalisé
```java
public class MyPropPlacer implements PropPlacer {
@Override
public void place(GenerationContainer container, int chunkX, int chunkZ) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
if (random.nextFloat() < 0.1f) {
int surfaceY = container.getHeight(x, z);
// Placer un prop personnalisé
placeMyProp(container, x, surfaceY + 1, z);
}
}
}
}
}
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives de Placement de Props :**
- Utilisez une densité appropriée pour l'ambiance du biome
- Assurez-vous que les props ne se chevauchent pas
- Ajoutez de la variété avec plusieurs variantes
- Considérez les performances avec une haute densité
- Testez la distribution visuelle sur le terrain
{{< /callout >}}

View File

@@ -0,0 +1,241 @@
---
title: World Loader
type: docs
weight: 1
---
The World Loader system manages chunk loading, generation scheduling, and world streaming.
**Package:** `com.hypixel.hytale.server.worldgen.loader`
## WorldLoader Class
The `WorldLoader` coordinates chunk loading and generation:
```java
public class WorldLoader {
// Load chunk at coordinates
public CompletableFuture<Chunk> loadChunk(int chunkX, int chunkZ);
// Unload chunk
public void unloadChunk(int chunkX, int chunkZ);
// Check if chunk is loaded
public boolean isChunkLoaded(int chunkX, int chunkZ);
// Get loaded chunk
public Chunk getChunk(int chunkX, int chunkZ);
// Request chunk generation
public void requestGeneration(int chunkX, int chunkZ, int priority);
}
```
## Loading Pipeline
The chunk loading process follows several stages:
```
Chunk Loading Pipeline
├── 1. Check Cache
│ └── Return if cached
├── 2. Load from Disk
│ └── Return if saved
├── 3. Queue for Generation
│ ├── Priority Assignment
│ └── Thread Pool Dispatch
├── 4. Generate Chunk
│ ├── Terrain Pass
│ ├── Cave Pass
│ ├── Structure Pass
│ └── Decoration Pass
└── 5. Cache and Return
```
## ChunkLoadRequest
```java
public class ChunkLoadRequest {
private int chunkX;
private int chunkZ;
private int priority;
private LoadReason reason;
private CompletableFuture<Chunk> future;
public enum LoadReason {
PLAYER_PROXIMITY,
ENTITY_SPAWN,
STRUCTURE_CHECK,
EXPLICIT_REQUEST
}
}
```
## Load Priority System
Chunks are loaded based on priority:
```java
public class LoadPriority {
// Highest priority - player's current chunk
public static final int IMMEDIATE = 0;
// High priority - chunks around player
public static final int PLAYER_VIEW = 100;
// Normal priority - entity needs
public static final int ENTITY = 200;
// Low priority - preloading
public static final int PRELOAD = 300;
// Lowest priority - background tasks
public static final int BACKGROUND = 400;
}
```
## Generation Threading
World generation uses a thread pool:
```java
public class GenerationThreadPool {
private int threadCount;
private PriorityBlockingQueue<ChunkLoadRequest> queue;
// Configure thread count
public void setThreadCount(int count);
// Get queue size
public int getPendingCount();
// Clear low-priority requests
public void clearLowPriority(int threshold);
}
```
## Chunk Cache
The loader maintains a chunk cache:
```java
public class ChunkCache {
private int maxSize;
private Map<ChunkPos, Chunk> cache;
// Get cached chunk
public Chunk get(int chunkX, int chunkZ);
// Add to cache
public void put(int chunkX, int chunkZ, Chunk chunk);
// Remove from cache
public void invalidate(int chunkX, int chunkZ);
// Clear cache
public void clear();
// Get cache statistics
public CacheStats getStats();
}
```
## View Distance Loading
```java
public class ViewDistanceLoader {
private int viewDistance;
private Player player;
// Update loaded chunks for player
public void updatePlayerChunks(Player player) {
Vector3d pos = player.getPosition();
int centerX = (int) pos.x >> 4;
int centerZ = (int) pos.z >> 4;
// Load chunks in view distance
for (int dx = -viewDistance; dx <= viewDistance; dx++) {
for (int dz = -viewDistance; dz <= viewDistance; dz++) {
int priority = Math.abs(dx) + Math.abs(dz);
worldLoader.requestGeneration(centerX + dx, centerZ + dz, priority);
}
}
}
}
```
## Container System
Generation containers manage intermediate data:
```java
public class GenerationContainer {
private int chunkX;
private int chunkZ;
private BlockBuffer blockBuffer;
private BiomeMap biomeMap;
private HeightMap heightMap;
// Set block in container
public void setBlock(int x, int y, int z, BlockType type);
// Get block from container
public BlockType getBlock(int x, int y, int z);
// Finalize to chunk
public Chunk toChunk();
}
```
## Event Hooks
```java
// Pre-generation event
getEventRegistry().register(ChunkPreGenerateEvent.class, event -> {
int chunkX = event.getChunkX();
int chunkZ = event.getChunkZ();
// Modify generation parameters
});
// Post-generation event
getEventRegistry().register(ChunkGeneratedEvent.class, event -> {
Chunk chunk = event.getChunk();
// Post-process chunk
});
// Chunk load event
getEventRegistry().register(ChunkLoadEvent.class, event -> {
Chunk chunk = event.getChunk();
// Handle chunk load
});
```
## Configuration
```yaml
# worldgen/loader_config.yaml
WorldLoader:
ThreadCount: 4
MaxQueueSize: 1000
CacheSize: 512
ViewDistance: 12
PreloadDistance: 2
UnloadDelay: 30000
```
## Best Practices
{{< callout type="info" >}}
**Loader Guidelines:**
- Use appropriate priorities for chunk requests
- Don't request too many chunks at once
- Let the cache system handle repeated requests
- Monitor queue size for performance issues
{{< /callout >}}
{{< callout type="warning" >}}
**Performance:** Chunk generation is expensive. Avoid:
- Requesting chunks far from players
- Bypassing the cache system
- Blocking on chunk futures on the main thread
{{< /callout >}}

View File

@@ -0,0 +1,241 @@
---
title: World Loader
type: docs
weight: 1
---
Le système World Loader gère le chargement des chunks, la planification de génération et le streaming du monde.
**Package:** `com.hypixel.hytale.server.worldgen.loader`
## Classe WorldLoader
Le `WorldLoader` coordonne le chargement et la génération des chunks :
```java
public class WorldLoader {
// Charger un chunk aux coordonnées
public CompletableFuture<Chunk> loadChunk(int chunkX, int chunkZ);
// Décharger un chunk
public void unloadChunk(int chunkX, int chunkZ);
// Vérifier si un chunk est chargé
public boolean isChunkLoaded(int chunkX, int chunkZ);
// Obtenir un chunk chargé
public Chunk getChunk(int chunkX, int chunkZ);
// Demander la génération d'un chunk
public void requestGeneration(int chunkX, int chunkZ, int priority);
}
```
## Pipeline de Chargement
Le processus de chargement de chunk suit plusieurs étapes :
```
Pipeline de Chargement de Chunk
├── 1. Vérifier le Cache
│ └── Retourner si en cache
├── 2. Charger depuis le Disque
│ └── Retourner si sauvegardé
├── 3. Mettre en Queue pour Génération
│ ├── Attribution de Priorité
│ └── Dispatch Thread Pool
├── 4. Générer le Chunk
│ ├── Passe Terrain
│ ├── Passe Grottes
│ ├── Passe Structures
│ └── Passe Décoration
└── 5. Mettre en Cache et Retourner
```
## ChunkLoadRequest
```java
public class ChunkLoadRequest {
private int chunkX;
private int chunkZ;
private int priority;
private LoadReason reason;
private CompletableFuture<Chunk> future;
public enum LoadReason {
PLAYER_PROXIMITY,
ENTITY_SPAWN,
STRUCTURE_CHECK,
EXPLICIT_REQUEST
}
}
```
## Système de Priorité de Chargement
Les chunks sont chargés selon leur priorité :
```java
public class LoadPriority {
// Priorité la plus haute - chunk actuel du joueur
public static final int IMMEDIATE = 0;
// Haute priorité - chunks autour du joueur
public static final int PLAYER_VIEW = 100;
// Priorité normale - besoins des entités
public static final int ENTITY = 200;
// Basse priorité - préchargement
public static final int PRELOAD = 300;
// Priorité la plus basse - tâches de fond
public static final int BACKGROUND = 400;
}
```
## Threading de Génération
La génération de monde utilise un pool de threads :
```java
public class GenerationThreadPool {
private int threadCount;
private PriorityBlockingQueue<ChunkLoadRequest> queue;
// Configurer le nombre de threads
public void setThreadCount(int count);
// Obtenir la taille de la queue
public int getPendingCount();
// Effacer les requêtes basse priorité
public void clearLowPriority(int threshold);
}
```
## Cache de Chunks
Le loader maintient un cache de chunks :
```java
public class ChunkCache {
private int maxSize;
private Map<ChunkPos, Chunk> cache;
// Obtenir un chunk en cache
public Chunk get(int chunkX, int chunkZ);
// Ajouter au cache
public void put(int chunkX, int chunkZ, Chunk chunk);
// Supprimer du cache
public void invalidate(int chunkX, int chunkZ);
// Vider le cache
public void clear();
// Obtenir les statistiques du cache
public CacheStats getStats();
}
```
## Chargement par Distance de Vue
```java
public class ViewDistanceLoader {
private int viewDistance;
private Player player;
// Mettre à jour les chunks chargés pour un joueur
public void updatePlayerChunks(Player player) {
Vector3d pos = player.getPosition();
int centerX = (int) pos.x >> 4;
int centerZ = (int) pos.z >> 4;
// Charger les chunks dans la distance de vue
for (int dx = -viewDistance; dx <= viewDistance; dx++) {
for (int dz = -viewDistance; dz <= viewDistance; dz++) {
int priority = Math.abs(dx) + Math.abs(dz);
worldLoader.requestGeneration(centerX + dx, centerZ + dz, priority);
}
}
}
}
```
## Système de Conteneurs
Les conteneurs de génération gèrent les données intermédiaires :
```java
public class GenerationContainer {
private int chunkX;
private int chunkZ;
private BlockBuffer blockBuffer;
private BiomeMap biomeMap;
private HeightMap heightMap;
// Définir un bloc dans le conteneur
public void setBlock(int x, int y, int z, BlockType type);
// Obtenir un bloc du conteneur
public BlockType getBlock(int x, int y, int z);
// Finaliser en chunk
public Chunk toChunk();
}
```
## Hooks d'Événements
```java
// Événement pré-génération
getEventRegistry().register(ChunkPreGenerateEvent.class, event -> {
int chunkX = event.getChunkX();
int chunkZ = event.getChunkZ();
// Modifier les paramètres de génération
});
// Événement post-génération
getEventRegistry().register(ChunkGeneratedEvent.class, event -> {
Chunk chunk = event.getChunk();
// Post-traiter le chunk
});
// Événement de chargement de chunk
getEventRegistry().register(ChunkLoadEvent.class, event -> {
Chunk chunk = event.getChunk();
// Gérer le chargement du chunk
});
```
## Configuration
```yaml
# worldgen/loader_config.yaml
WorldLoader:
ThreadCount: 4
MaxQueueSize: 1000
CacheSize: 512
ViewDistance: 12
PreloadDistance: 2
UnloadDelay: 30000
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives du Loader :**
- Utilisez des priorités appropriées pour les requêtes de chunks
- Ne demandez pas trop de chunks à la fois
- Laissez le système de cache gérer les requêtes répétées
- Surveillez la taille de la queue pour les problèmes de performance
{{< /callout >}}
{{< callout type="warning" >}}
**Performance :** La génération de chunks est coûteuse. Évitez :
- De demander des chunks loin des joueurs
- De contourner le système de cache
- De bloquer sur les futures de chunks sur le thread principal
{{< /callout >}}

View File

@@ -0,0 +1,226 @@
---
title: Zones
type: docs
weight: 6
---
Zones define distinct regions of the world with specific generation rules, biomes, and features.
**Package:** `com.hypixel.hytale.server.worldgen.zone`
## Zone System
Zones partition the world into distinct areas:
```java
public class ZoneManager {
// Get zone at position
public Zone getZone(int x, int z);
// Register zone
public void registerZone(Zone zone);
// Get all zones
public List<Zone> getZones();
}
```
## Zone Definition
```yaml
# zones/emerald_grove.yaml
Type: Zone
Id: emerald_grove
DisplayName: "Emerald Grove"
Shape: Circle
Center: [0, 0]
Radius: 1000
Biomes:
- forest
- dense_forest
Climate:
TemperatureOffset: 0.1
HumidityOffset: 0.2
Features:
- giant_trees
- fairy_circles
Prefabs:
- elf_village
- forest_shrine
MobSpawns:
- Type: deer
Weight: 20
- Type: forest_spirit
Weight: 5
```
## Zone Class
```java
public class Zone {
private String id;
private String displayName;
private ZoneShape shape;
private List<String> biomes;
private ClimateModifier climate;
private List<String> features;
private List<String> prefabs;
private List<MobSpawn> mobSpawns;
// Check if position is in zone
public boolean contains(int x, int z);
// Get zone-specific biome
public Biome getBiome(int x, int z);
// Modify generation
public void modifyGeneration(GenerationContainer container);
}
```
## Zone Shapes
```java
public interface ZoneShape {
boolean contains(int x, int z);
float getDistanceToEdge(int x, int z);
}
public class CircleZone implements ZoneShape {
private int centerX, centerZ;
private int radius;
public boolean contains(int x, int z) {
int dx = x - centerX;
int dz = z - centerZ;
return dx * dx + dz * dz <= radius * radius;
}
}
public class RectangleZone implements ZoneShape {
private int minX, minZ, maxX, maxZ;
public boolean contains(int x, int z) {
return x >= minX && x <= maxX && z >= minZ && z <= maxZ;
}
}
public class PolygonZone implements ZoneShape {
private List<Vector2i> vertices;
public boolean contains(int x, int z) {
// Point-in-polygon algorithm
return isPointInPolygon(x, z, vertices);
}
}
```
## Zone Transitions
Smooth transitions between zones:
```java
public class ZoneTransition {
private int transitionWidth;
public float getBlendFactor(Zone from, Zone to, int x, int z) {
float distFrom = from.getShape().getDistanceToEdge(x, z);
float distTo = to.getShape().getDistanceToEdge(x, z);
if (distFrom > transitionWidth) {
return 0.0f; // Fully in 'from' zone
}
if (distTo > transitionWidth) {
return 1.0f; // Fully in 'to' zone
}
return distFrom / (distFrom + distTo);
}
}
```
## Zone Layers
Zones can have vertical layers:
```yaml
# zones/underground_kingdom.yaml
Type: Zone
Id: underground_kingdom
Shape: Circle
Center: [5000, 5000]
Radius: 500
VerticalRange:
MinY: 0
MaxY: 40
Biomes:
- crystal_cave
- mushroom_forest
Features:
- glowing_crystals
- underground_lake
```
## Zone Priority
When zones overlap, priority determines which applies:
```java
public class ZonePriority {
public Zone getActiveZone(int x, int y, int z) {
List<Zone> containing = zones.stream()
.filter(z -> z.contains(x, z) && z.containsY(y))
.sorted(Comparator.comparing(Zone::getPriority).reversed())
.toList();
return containing.isEmpty() ? defaultZone : containing.get(0);
}
}
```
## Dynamic Zones
Zones that can change at runtime:
```java
public class DynamicZone extends Zone {
public void expand(int amount) {
if (shape instanceof CircleZone circle) {
circle.setRadius(circle.getRadius() + amount);
}
}
public void move(int dx, int dz) {
shape.translate(dx, dz);
}
}
```
## Configuration
```yaml
# worldgen/zone_config.yaml
Zones:
Default: wilderness
TransitionWidth: 32
List:
- Id: spawn_area
Priority: 100
Shape: Circle
Center: [0, 0]
Radius: 200
- Id: wilderness
Priority: 0
Shape: Infinite
```
## Best Practices
{{< callout type="info" >}}
**Zone Guidelines:**
- Use zones for distinct world regions
- Define smooth transitions between zones
- Consider zone priority for overlaps
- Use appropriate shapes for zone boundaries
- Test zone generation thoroughly
{{< /callout >}}

View File

@@ -0,0 +1,226 @@
---
title: Zones
type: docs
weight: 6
---
Les zones définissent des régions distinctes du monde avec des règles de génération, des biomes et des caractéristiques spécifiques.
**Package:** `com.hypixel.hytale.server.worldgen.zone`
## Système de Zones
Les zones partitionnent le monde en zones distinctes :
```java
public class ZoneManager {
// Obtenir la zone à une position
public Zone getZone(int x, int z);
// Enregistrer une zone
public void registerZone(Zone zone);
// Obtenir toutes les zones
public List<Zone> getZones();
}
```
## Définition de Zone
```yaml
# zones/emerald_grove.yaml
Type: Zone
Id: emerald_grove
DisplayName: "Bosquet d'Émeraude"
Shape: Circle
Center: [0, 0]
Radius: 1000
Biomes:
- forest
- dense_forest
Climate:
TemperatureOffset: 0.1
HumidityOffset: 0.2
Features:
- giant_trees
- fairy_circles
Prefabs:
- elf_village
- forest_shrine
MobSpawns:
- Type: deer
Weight: 20
- Type: forest_spirit
Weight: 5
```
## Classe Zone
```java
public class Zone {
private String id;
private String displayName;
private ZoneShape shape;
private List<String> biomes;
private ClimateModifier climate;
private List<String> features;
private List<String> prefabs;
private List<MobSpawn> mobSpawns;
// Vérifier si une position est dans la zone
public boolean contains(int x, int z);
// Obtenir le biome spécifique à la zone
public Biome getBiome(int x, int z);
// Modifier la génération
public void modifyGeneration(GenerationContainer container);
}
```
## Formes de Zones
```java
public interface ZoneShape {
boolean contains(int x, int z);
float getDistanceToEdge(int x, int z);
}
public class CircleZone implements ZoneShape {
private int centerX, centerZ;
private int radius;
public boolean contains(int x, int z) {
int dx = x - centerX;
int dz = z - centerZ;
return dx * dx + dz * dz <= radius * radius;
}
}
public class RectangleZone implements ZoneShape {
private int minX, minZ, maxX, maxZ;
public boolean contains(int x, int z) {
return x >= minX && x <= maxX && z >= minZ && z <= maxZ;
}
}
public class PolygonZone implements ZoneShape {
private List<Vector2i> vertices;
public boolean contains(int x, int z) {
// Algorithme point-dans-polygone
return isPointInPolygon(x, z, vertices);
}
}
```
## Transitions de Zones
Transitions douces entre zones :
```java
public class ZoneTransition {
private int transitionWidth;
public float getBlendFactor(Zone from, Zone to, int x, int z) {
float distFrom = from.getShape().getDistanceToEdge(x, z);
float distTo = to.getShape().getDistanceToEdge(x, z);
if (distFrom > transitionWidth) {
return 0.0f; // Complètement dans la zone 'from'
}
if (distTo > transitionWidth) {
return 1.0f; // Complètement dans la zone 'to'
}
return distFrom / (distFrom + distTo);
}
}
```
## Couches de Zones
Les zones peuvent avoir des couches verticales :
```yaml
# zones/underground_kingdom.yaml
Type: Zone
Id: underground_kingdom
Shape: Circle
Center: [5000, 5000]
Radius: 500
VerticalRange:
MinY: 0
MaxY: 40
Biomes:
- crystal_cave
- mushroom_forest
Features:
- glowing_crystals
- underground_lake
```
## Priorité des Zones
Quand les zones se chevauchent, la priorité détermine laquelle s'applique :
```java
public class ZonePriority {
public Zone getActiveZone(int x, int y, int z) {
List<Zone> containing = zones.stream()
.filter(z -> z.contains(x, z) && z.containsY(y))
.sorted(Comparator.comparing(Zone::getPriority).reversed())
.toList();
return containing.isEmpty() ? defaultZone : containing.get(0);
}
}
```
## Zones Dynamiques
Zones qui peuvent changer à l'exécution :
```java
public class DynamicZone extends Zone {
public void expand(int amount) {
if (shape instanceof CircleZone circle) {
circle.setRadius(circle.getRadius() + amount);
}
}
public void move(int dx, int dz) {
shape.translate(dx, dz);
}
}
```
## Configuration
```yaml
# worldgen/zone_config.yaml
Zones:
Default: wilderness
TransitionWidth: 32
List:
- Id: spawn_area
Priority: 100
Shape: Circle
Center: [0, 0]
Radius: 200
- Id: wilderness
Priority: 0
Shape: Infinite
```
## Bonnes Pratiques
{{< callout type="info" >}}
**Directives des Zones :**
- Utilisez les zones pour des régions distinctes du monde
- Définissez des transitions douces entre zones
- Considérez la priorité des zones pour les chevauchements
- Utilisez des formes appropriées pour les limites de zones
- Testez minutieusement la génération des zones
{{< /callout >}}