Update script to write to vendor/hytale-server
This commit is contained in:
396
docs/06-world-management.md
Normal file
396
docs/06-world-management.md
Normal file
@@ -0,0 +1,396 @@
|
||||
# World Management
|
||||
|
||||
The Hytale server supports multiple worlds within a single universe. This guide covers world management, chunk handling, and related systems.
|
||||
|
||||
## Universe
|
||||
|
||||
The `Universe` is the top-level container for all worlds and global game state.
|
||||
|
||||
### Accessing the Universe
|
||||
|
||||
```java
|
||||
Universe universe = Universe.get();
|
||||
```
|
||||
|
||||
### Universe Properties
|
||||
|
||||
```java
|
||||
// Get all loaded worlds
|
||||
Collection<World> worlds = universe.getWorlds();
|
||||
|
||||
// Get a specific world
|
||||
World world = universe.getWorld("worldName");
|
||||
|
||||
// Get player count
|
||||
int playerCount = universe.getPlayerCount();
|
||||
|
||||
// Get all players
|
||||
Collection<Player> players = universe.getPlayers();
|
||||
|
||||
// Get player by name
|
||||
Player player = universe.getPlayer("PlayerName");
|
||||
|
||||
// Get player by UUID
|
||||
Player player = universe.getPlayer(uuid);
|
||||
```
|
||||
|
||||
## Worlds
|
||||
|
||||
Each `World` represents a separate game environment with its own terrain, entities, and rules.
|
||||
|
||||
### World Properties
|
||||
|
||||
```java
|
||||
World world = universe.getWorld("default");
|
||||
|
||||
// Basic properties
|
||||
String name = world.getName();
|
||||
WorldConfig config = world.getConfig();
|
||||
|
||||
// State
|
||||
boolean isPaused = world.isPaused();
|
||||
long tick = world.getTick();
|
||||
|
||||
// Stores
|
||||
EntityStore entityStore = world.getEntityStore();
|
||||
ChunkStore chunkStore = world.getChunkStore();
|
||||
```
|
||||
|
||||
### World Configuration
|
||||
|
||||
```java
|
||||
WorldConfig config = WorldConfig.builder()
|
||||
.generator(myWorldGenProvider) // World generation
|
||||
.storage(myChunkStorageProvider) // Chunk persistence
|
||||
.resourceStorage(myResourceProvider) // Resource storage
|
||||
.worldMap(myWorldMapProvider) // World map provider
|
||||
.spawn(mySpawnProvider) // Spawn point provider
|
||||
.gameplay(gameplayConfig) // Gameplay settings
|
||||
.build();
|
||||
```
|
||||
|
||||
## Chunks
|
||||
|
||||
Worlds are divided into chunks for efficient loading and rendering.
|
||||
|
||||
### Chunk Access
|
||||
|
||||
```java
|
||||
World world = universe.getWorld("default");
|
||||
|
||||
// Get chunk at world coordinates
|
||||
Chunk chunk = world.getChunk(x, y, z);
|
||||
|
||||
// Get chunk column (all chunks at x,z)
|
||||
ChunkColumn column = world.getChunkColumn(x, z);
|
||||
|
||||
// Check if chunk is loaded
|
||||
boolean loaded = world.isChunkLoaded(x, y, z);
|
||||
```
|
||||
|
||||
### Chunk Store
|
||||
|
||||
```java
|
||||
ChunkStore chunkStore = world.getChunkStore();
|
||||
|
||||
// Get chunk data
|
||||
ChunkData data = chunkStore.getChunk(chunkX, chunkY, chunkZ);
|
||||
|
||||
// Iterate loaded chunks
|
||||
chunkStore.forEachLoaded((chunkPos, chunk) -> {
|
||||
// Process chunk
|
||||
});
|
||||
```
|
||||
|
||||
### Block Access
|
||||
|
||||
```java
|
||||
World world = universe.getWorld("default");
|
||||
|
||||
// Get block at position
|
||||
BlockState block = world.getBlock(x, y, z);
|
||||
|
||||
// Set block at position
|
||||
world.setBlock(x, y, z, newBlockState);
|
||||
|
||||
// Get block type
|
||||
BlockType type = block.getType();
|
||||
```
|
||||
|
||||
## World Events
|
||||
|
||||
### Listening for World Events
|
||||
|
||||
```java
|
||||
// World added
|
||||
getEventRegistry().register(AddWorldEvent.class, event -> {
|
||||
World world = event.getWorld();
|
||||
getLogger().info("World added: " + world.getName());
|
||||
});
|
||||
|
||||
// World removed
|
||||
getEventRegistry().register(RemoveWorldEvent.class, event -> {
|
||||
World world = event.getWorld();
|
||||
getLogger().info("World removed: " + world.getName());
|
||||
});
|
||||
|
||||
// World started
|
||||
getEventRegistry().register(StartWorldEvent.class, event -> {
|
||||
World world = event.getWorld();
|
||||
getLogger().info("World started: " + world.getName());
|
||||
});
|
||||
|
||||
// All worlds loaded
|
||||
getEventRegistry().register(AllWorldsLoadedEvent.class, event -> {
|
||||
getLogger().info("All worlds have been loaded!");
|
||||
});
|
||||
|
||||
// Player added to world
|
||||
getEventRegistry().register(AddPlayerToWorldEvent.class, event -> {
|
||||
Player player = event.getPlayer();
|
||||
World world = event.getWorld();
|
||||
getLogger().info(player.getName() + " entered " + world.getName());
|
||||
});
|
||||
|
||||
// Player removed from world
|
||||
getEventRegistry().register(DrainPlayerFromWorldEvent.class, event -> {
|
||||
Player player = event.getPlayer();
|
||||
World world = event.getWorld();
|
||||
getLogger().info(player.getName() + " left " + world.getName());
|
||||
});
|
||||
```
|
||||
|
||||
### Keyed World Events
|
||||
|
||||
Some events are keyed by world name:
|
||||
|
||||
```java
|
||||
// Listen for events in a specific world
|
||||
getEventRegistry().register(AddPlayerToWorldEvent.class, "spawn_world", event -> {
|
||||
// Only triggered for "spawn_world"
|
||||
event.getPlayer().sendMessage("Welcome to spawn!");
|
||||
});
|
||||
```
|
||||
|
||||
## World Generation
|
||||
|
||||
### World Generation Provider
|
||||
|
||||
Implement `IWorldGenProvider` for custom world generation:
|
||||
|
||||
```java
|
||||
public class MyWorldGenProvider implements IWorldGenProvider {
|
||||
|
||||
@Override
|
||||
public void generate(ChunkData chunk, int chunkX, int chunkY, int chunkZ) {
|
||||
// Generate terrain for this chunk
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
int height = calculateHeight(chunkX * 16 + x, chunkZ * 16 + z);
|
||||
for (int y = 0; y < 16; y++) {
|
||||
int worldY = chunkY * 16 + y;
|
||||
BlockState block = getBlockForPosition(worldY, height);
|
||||
chunk.setBlock(x, y, z, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int calculateHeight(int worldX, int worldZ) {
|
||||
// Use noise functions for terrain generation
|
||||
return 64 + (int)(Math.sin(worldX * 0.1) * 10);
|
||||
}
|
||||
|
||||
private BlockState getBlockForPosition(int y, int surfaceHeight) {
|
||||
if (y > surfaceHeight) {
|
||||
return BlockStates.AIR;
|
||||
} else if (y == surfaceHeight) {
|
||||
return BlockStates.GRASS;
|
||||
} else if (y > surfaceHeight - 3) {
|
||||
return BlockStates.DIRT;
|
||||
} else {
|
||||
return BlockStates.STONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Procedural Generation Utilities
|
||||
|
||||
The `procedurallib` package provides utilities for procedural generation:
|
||||
|
||||
```java
|
||||
// Noise generation
|
||||
PerlinNoise noise = new PerlinNoise(seed);
|
||||
double value = noise.getValue(x, y, z);
|
||||
|
||||
// Multi-octave noise
|
||||
FractalNoise fractal = FractalNoise.builder()
|
||||
.octaves(4)
|
||||
.persistence(0.5)
|
||||
.lacunarity(2.0)
|
||||
.build();
|
||||
double terrain = fractal.getValue(x, z);
|
||||
```
|
||||
|
||||
## Spawn System
|
||||
|
||||
### Spawn Provider
|
||||
|
||||
Implement `ISpawnProvider` for custom spawn logic:
|
||||
|
||||
```java
|
||||
public class MySpawnProvider implements ISpawnProvider {
|
||||
|
||||
@Override
|
||||
public Vector3d getSpawnLocation(Player player, World world) {
|
||||
// Calculate spawn location for player
|
||||
return new Vector3d(0, 64, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSpawnYaw(Player player, World world) {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Built-in Spawning
|
||||
|
||||
The `spawning` module provides entity spawning capabilities:
|
||||
|
||||
```java
|
||||
// Spawn entities based on spawn rules
|
||||
SpawnManager spawner = world.getSpawnManager();
|
||||
|
||||
// Trigger spawn checks in area
|
||||
spawner.checkSpawns(position, radius);
|
||||
```
|
||||
|
||||
## Chunk Storage
|
||||
|
||||
### Storage Provider
|
||||
|
||||
Implement `IChunkStorageProvider` for custom chunk persistence:
|
||||
|
||||
```java
|
||||
public class MyChunkStorageProvider implements IChunkStorageProvider {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkData> load(int x, int y, int z) {
|
||||
// Load chunk from storage
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
return loadFromDatabase(x, y, z);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> save(int x, int y, int z, ChunkData data) {
|
||||
// Save chunk to storage
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
saveToDatabase(x, y, z, data);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Teleportation
|
||||
|
||||
### Teleporting Players
|
||||
|
||||
```java
|
||||
Player player = getPlayer();
|
||||
|
||||
// Teleport to position in same world
|
||||
Vector3d destination = new Vector3d(100, 64, 200);
|
||||
player.teleport(destination);
|
||||
|
||||
// Teleport with rotation
|
||||
player.teleport(destination, yaw, pitch);
|
||||
|
||||
// Teleport to another world
|
||||
World targetWorld = universe.getWorld("nether");
|
||||
player.teleport(targetWorld, destination);
|
||||
```
|
||||
|
||||
### Teleportation Events
|
||||
|
||||
The `teleport` built-in module handles teleportation:
|
||||
|
||||
```java
|
||||
// Custom teleportation with effects
|
||||
TeleportManager.teleport(player, destination, new TeleportOptions()
|
||||
.withEffect(TeleportEffect.PARTICLES)
|
||||
.withSound(true));
|
||||
```
|
||||
|
||||
## Portals
|
||||
|
||||
The `portals` built-in module provides portal functionality:
|
||||
|
||||
```java
|
||||
// Create a portal
|
||||
Portal portal = Portal.builder()
|
||||
.position(sourcePosition)
|
||||
.destination(targetWorld, destPosition)
|
||||
.size(2, 3) // width, height
|
||||
.build();
|
||||
|
||||
// Register portal with world
|
||||
world.addPortal(portal);
|
||||
```
|
||||
|
||||
## Multi-World Management
|
||||
|
||||
### Creating Multiple Worlds
|
||||
|
||||
```java
|
||||
@Override
|
||||
protected void start() {
|
||||
Universe universe = Universe.get();
|
||||
|
||||
// Create custom world configurations
|
||||
WorldConfig spawnConfig = WorldConfig.builder()
|
||||
.generator(new FlatWorldGenerator())
|
||||
.gameplay(GameplayConfig.CREATIVE)
|
||||
.build();
|
||||
|
||||
WorldConfig survivalConfig = WorldConfig.builder()
|
||||
.generator(new HytaleWorldGenerator())
|
||||
.gameplay(GameplayConfig.SURVIVAL)
|
||||
.build();
|
||||
|
||||
// Register worlds (if supported)
|
||||
// universe.createWorld("spawn", spawnConfig);
|
||||
// universe.createWorld("survival", survivalConfig);
|
||||
}
|
||||
```
|
||||
|
||||
### World Transitions
|
||||
|
||||
```java
|
||||
public void sendToWorld(Player player, String worldName) {
|
||||
World targetWorld = Universe.get().getWorld(worldName);
|
||||
if (targetWorld == null) {
|
||||
player.sendMessage("World not found: " + worldName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get spawn location for target world
|
||||
Vector3d spawn = targetWorld.getSpawnLocation();
|
||||
|
||||
// Teleport player
|
||||
player.teleport(targetWorld, spawn);
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Cache world references** - Don't repeatedly call `Universe.get().getWorld()` in hot paths
|
||||
2. **Handle world events** - Listen for world lifecycle events for initialization/cleanup
|
||||
3. **Use async chunk operations** - Chunk loading/saving should be asynchronous
|
||||
4. **Respect chunk boundaries** - Be aware of chunk boundaries when modifying blocks
|
||||
5. **Clean up on world removal** - Handle `RemoveWorldEvent` to clean up resources
|
||||
6. **Use keyed events** - Listen for world-specific events when possible
|
||||
7. **Consider view distance** - Be mindful of server view distance settings
|
||||
8. **Test multi-world scenarios** - Ensure your plugin works across multiple worlds
|
||||
Reference in New Issue
Block a user