Init
This commit is contained in:
35
content/world/_index.en.md
Normal file
35
content/world/_index.en.md
Normal 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();
|
||||
```
|
||||
35
content/world/_index.fr.md
Normal file
35
content/world/_index.fr.md
Normal 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
329
content/world/blocks.en.md
Normal 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
329
content/world/blocks.fr.md
Normal 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
172
content/world/chunks.en.md
Normal 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
172
content/world/chunks.fr.md
Normal 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);
|
||||
}
|
||||
```
|
||||
53
content/world/entities/_index.en.md
Normal file
53
content/world/entities/_index.en.md
Normal 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 >}}
|
||||
53
content/world/entities/_index.fr.md
Normal file
53
content/world/entities/_index.fr.md
Normal 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 >}}
|
||||
828
content/world/entities/entity-components.en.md
Normal file
828
content/world/entities/entity-components.en.md
Normal 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 |
|
||||
828
content/world/entities/entity-components.fr.md
Normal file
828
content/world/entities/entity-components.fr.md
Normal 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 |
|
||||
259
content/world/entities/entity-hierarchy.en.md
Normal file
259
content/world/entities/entity-hierarchy.en.md
Normal 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 >}}
|
||||
|
||||
259
content/world/entities/entity-hierarchy.fr.md
Normal file
259
content/world/entities/entity-hierarchy.fr.md
Normal 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 >}}
|
||||
|
||||
302
content/world/entities/flocking-behavior.en.md
Normal file
302
content/world/entities/flocking-behavior.en.md
Normal 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)
|
||||
);
|
||||
```
|
||||
302
content/world/entities/flocking-behavior.fr.md
Normal file
302
content/world/entities/flocking-behavior.fr.md
Normal 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)
|
||||
);
|
||||
```
|
||||
57
content/world/entities/inventory/_index.en.md
Normal file
57
content/world/entities/inventory/_index.en.md
Normal 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
|
||||
```
|
||||
57
content/world/entities/inventory/_index.fr.md
Normal file
57
content/world/entities/inventory/_index.fr.md
Normal 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
|
||||
```
|
||||
668
content/world/entities/inventory/containers.en.md
Normal file
668
content/world/entities/inventory/containers.en.md
Normal 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 |
|
||||
668
content/world/entities/inventory/containers.fr.md
Normal file
668
content/world/entities/inventory/containers.fr.md
Normal 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 |
|
||||
354
content/world/entities/inventory/itemstacks.en.md
Normal file
354
content/world/entities/inventory/itemstacks.en.md
Normal 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 |
|
||||
354
content/world/entities/inventory/itemstacks.fr.md
Normal file
354
content/world/entities/inventory/itemstacks.fr.md
Normal 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 |
|
||||
430
content/world/entities/inventory/transactions.en.md
Normal file
430
content/world/entities/inventory/transactions.en.md
Normal 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 |
|
||||
430
content/world/entities/inventory/transactions.fr.md
Normal file
430
content/world/entities/inventory/transactions.fr.md
Normal 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 |
|
||||
291
content/world/entities/mounts.en.md
Normal file
291
content/world/entities/mounts.en.md
Normal 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);
|
||||
```
|
||||
291
content/world/entities/mounts.fr.md
Normal file
291
content/world/entities/mounts.fr.md
Normal 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);
|
||||
```
|
||||
86
content/world/entities/npc/_index.en.md
Normal file
86
content/world/entities/npc/_index.en.md
Normal 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 |
|
||||
86
content/world/entities/npc/_index.fr.md
Normal file
86
content/world/entities/npc/_index.fr.md
Normal 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 |
|
||||
373
content/world/entities/npc/npc-ai.en.md
Normal file
373
content/world/entities/npc/npc-ai.en.md
Normal 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 >}}
|
||||
373
content/world/entities/npc/npc-ai.fr.md
Normal file
373
content/world/entities/npc/npc-ai.fr.md
Normal 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 >}}
|
||||
217
content/world/entities/npc/npc-basics.en.md
Normal file
217
content/world/entities/npc/npc-basics.en.md
Normal 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 >}}
|
||||
217
content/world/entities/npc/npc-basics.fr.md
Normal file
217
content/world/entities/npc/npc-basics.fr.md
Normal 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 >}}
|
||||
374
content/world/entities/npc/npc-commands.en.md
Normal file
374
content/world/entities/npc/npc-commands.en.md
Normal 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 >}}
|
||||
374
content/world/entities/npc/npc-commands.fr.md
Normal file
374
content/world/entities/npc/npc-commands.fr.md
Normal 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 >}}
|
||||
348
content/world/entities/npc/npc-components.en.md
Normal file
348
content/world/entities/npc/npc-components.en.md
Normal 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 >}}
|
||||
348
content/world/entities/npc/npc-components.fr.md
Normal file
348
content/world/entities/npc/npc-components.fr.md
Normal 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 >}}
|
||||
365
content/world/entities/npc/npc-movement.en.md
Normal file
365
content/world/entities/npc/npc-movement.en.md
Normal 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 >}}
|
||||
365
content/world/entities/npc/npc-movement.fr.md
Normal file
365
content/world/entities/npc/npc-movement.fr.md
Normal 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 >}}
|
||||
904
content/world/entities/player-api.en.md
Normal file
904
content/world/entities/player-api.en.md
Normal 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 >}}
|
||||
|
||||
904
content/world/entities/player-api.fr.md
Normal file
904
content/world/entities/player-api.fr.md
Normal 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 >}}
|
||||
|
||||
266
content/world/entities/spawning-entities.en.md
Normal file
266
content/world/entities/spawning-entities.en.md
Normal 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());
|
||||
}
|
||||
```
|
||||
266
content/world/entities/spawning-entities.fr.md
Normal file
266
content/world/entities/spawning-entities.fr.md
Normal 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());
|
||||
}
|
||||
```
|
||||
112
content/world/entities/spawning/_index.en.md
Normal file
112
content/world/entities/spawning/_index.en.md
Normal 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 >}}
|
||||
112
content/world/entities/spawning/_index.fr.md
Normal file
112
content/world/entities/spawning/_index.fr.md
Normal 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 >}}
|
||||
212
content/world/entities/spawning/local-spawning.en.md
Normal file
212
content/world/entities/spawning/local-spawning.en.md
Normal 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
|
||||
212
content/world/entities/spawning/local-spawning.fr.md
Normal file
212
content/world/entities/spawning/local-spawning.fr.md
Normal 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
|
||||
249
content/world/entities/spawning/spawn-suppression.en.md
Normal file
249
content/world/entities/spawning/spawn-suppression.en.md
Normal 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 |
|
||||
249
content/world/entities/spawning/spawn-suppression.fr.md
Normal file
249
content/world/entities/spawning/spawn-suppression.fr.md
Normal 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 |
|
||||
304
content/world/entities/spawning/spawner-assets.en.md
Normal file
304
content/world/entities/spawning/spawner-assets.en.md
Normal 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();
|
||||
```
|
||||
304
content/world/entities/spawning/spawner-assets.fr.md
Normal file
304
content/world/entities/spawning/spawner-assets.fr.md
Normal 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();
|
||||
```
|
||||
260
content/world/entities/spawning/world-spawning.en.md
Normal file
260
content/world/entities/spawning/world-spawning.en.md
Normal 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
|
||||
```
|
||||
260
content/world/entities/spawning/world-spawning.fr.md
Normal file
260
content/world/entities/spawning/world-spawning.fr.md
Normal 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
|
||||
```
|
||||
255
content/world/interactions/_index.en.md
Normal file
255
content/world/interactions/_index.en.md
Normal 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.
|
||||
255
content/world/interactions/_index.fr.md
Normal file
255
content/world/interactions/_index.fr.md
Normal 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.
|
||||
270
content/world/interactions/selectors.en.md
Normal file
270
content/world/interactions/selectors.en.md
Normal 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 >}}
|
||||
270
content/world/interactions/selectors.fr.md
Normal file
270
content/world/interactions/selectors.fr.md
Normal 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 >}}
|
||||
215
content/world/player-refs.en.md
Normal file
215
content/world/player-refs.en.md
Normal 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
|
||||
// });
|
||||
```
|
||||
215
content/world/player-refs.fr.md
Normal file
215
content/world/player-refs.fr.md
Normal 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
|
||||
// });
|
||||
```
|
||||
147
content/world/portals/_index.en.md
Normal file
147
content/world/portals/_index.en.md
Normal 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 >}}
|
||||
147
content/world/portals/_index.fr.md
Normal file
147
content/world/portals/_index.fr.md
Normal 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 >}}
|
||||
224
content/world/portals/portal-commands.en.md
Normal file
224
content/world/portals/portal-commands.en.md
Normal 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 |
|
||||
224
content/world/portals/portal-commands.fr.md
Normal file
224
content/world/portals/portal-commands.fr.md
Normal 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 |
|
||||
270
content/world/portals/portal-components.en.md
Normal file
270
content/world/portals/portal-components.en.md
Normal 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();
|
||||
```
|
||||
270
content/world/portals/portal-components.fr.md
Normal file
270
content/world/portals/portal-components.fr.md
Normal 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();
|
||||
```
|
||||
280
content/world/portals/portal-systems.en.md
Normal file
280
content/world/portals/portal-systems.en.md
Normal 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);
|
||||
```
|
||||
280
content/world/portals/portal-systems.fr.md
Normal file
280
content/world/portals/portal-systems.fr.md
Normal 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);
|
||||
```
|
||||
93
content/world/universe-and-worlds.en.md
Normal file
93
content/world/universe-and-worlds.en.md
Normal 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());
|
||||
}
|
||||
});
|
||||
```
|
||||
93
content/world/universe-and-worlds.fr.md
Normal file
93
content/world/universe-and-worlds.fr.md
Normal 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());
|
||||
}
|
||||
});
|
||||
```
|
||||
115
content/world/worldgen/_index.en.md
Normal file
115
content/world/worldgen/_index.en.md
Normal 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
|
||||
```
|
||||
115
content/world/worldgen/_index.fr.md
Normal file
115
content/world/worldgen/_index.fr.md
Normal 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
|
||||
```
|
||||
252
content/world/worldgen/cave-generation.en.md
Normal file
252
content/world/worldgen/cave-generation.en.md
Normal 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 >}}
|
||||
252
content/world/worldgen/cave-generation.fr.md
Normal file
252
content/world/worldgen/cave-generation.fr.md
Normal 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 >}}
|
||||
222
content/world/worldgen/chunk-generation.en.md
Normal file
222
content/world/worldgen/chunk-generation.en.md
Normal 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 >}}
|
||||
222
content/world/worldgen/chunk-generation.fr.md
Normal file
222
content/world/worldgen/chunk-generation.fr.md
Normal 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 >}}
|
||||
238
content/world/worldgen/climate-biomes.en.md
Normal file
238
content/world/worldgen/climate-biomes.en.md
Normal 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 >}}
|
||||
238
content/world/worldgen/climate-biomes.fr.md
Normal file
238
content/world/worldgen/climate-biomes.fr.md
Normal 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 >}}
|
||||
270
content/world/worldgen/density-functions.en.md
Normal file
270
content/world/worldgen/density-functions.en.md
Normal 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 >}}
|
||||
270
content/world/worldgen/density-functions.fr.md
Normal file
270
content/world/worldgen/density-functions.fr.md
Normal 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 >}}
|
||||
328
content/world/worldgen/generation-patterns.en.md
Normal file
328
content/world/worldgen/generation-patterns.en.md
Normal 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 >}}
|
||||
328
content/world/worldgen/generation-patterns.fr.md
Normal file
328
content/world/worldgen/generation-patterns.fr.md
Normal 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 >}}
|
||||
212
content/world/worldgen/hytale-generator.en.md
Normal file
212
content/world/worldgen/hytale-generator.en.md
Normal 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 >}}
|
||||
212
content/world/worldgen/hytale-generator.fr.md
Normal file
212
content/world/worldgen/hytale-generator.fr.md
Normal 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 >}}
|
||||
273
content/world/worldgen/material-providers.en.md
Normal file
273
content/world/worldgen/material-providers.en.md
Normal 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 >}}
|
||||
273
content/world/worldgen/material-providers.fr.md
Normal file
273
content/world/worldgen/material-providers.fr.md
Normal 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 >}}
|
||||
220
content/world/worldgen/prefabs.en.md
Normal file
220
content/world/worldgen/prefabs.en.md
Normal 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 >}}
|
||||
220
content/world/worldgen/prefabs.fr.md
Normal file
220
content/world/worldgen/prefabs.fr.md
Normal 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 >}}
|
||||
281
content/world/worldgen/prop-placement.en.md
Normal file
281
content/world/worldgen/prop-placement.en.md
Normal 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 >}}
|
||||
281
content/world/worldgen/prop-placement.fr.md
Normal file
281
content/world/worldgen/prop-placement.fr.md
Normal 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 >}}
|
||||
241
content/world/worldgen/world-loader.en.md
Normal file
241
content/world/worldgen/world-loader.en.md
Normal 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 >}}
|
||||
241
content/world/worldgen/world-loader.fr.md
Normal file
241
content/world/worldgen/world-loader.fr.md
Normal 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 >}}
|
||||
226
content/world/worldgen/zones.en.md
Normal file
226
content/world/worldgen/zones.en.md
Normal 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 >}}
|
||||
226
content/world/worldgen/zones.fr.md
Normal file
226
content/world/worldgen/zones.fr.md
Normal 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 >}}
|
||||
Reference in New Issue
Block a user