9.8 KiB
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
Universe universe = Universe.get();
Universe Properties
// 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
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
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
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
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
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
// 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:
// 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:
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:
// 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:
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:
// 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:
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
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:
// Custom teleportation with effects
TeleportManager.teleport(player, destination, new TeleportOptions()
.withEffect(TeleportEffect.PARTICLES)
.withSound(true));
Portals
The portals built-in module provides portal functionality:
// 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
@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
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
- Cache world references - Don't repeatedly call
Universe.get().getWorld()in hot paths - Handle world events - Listen for world lifecycle events for initialization/cleanup
- Use async chunk operations - Chunk loading/saving should be asynchronous
- Respect chunk boundaries - Be aware of chunk boundaries when modifying blocks
- Clean up on world removal - Handle
RemoveWorldEventto clean up resources - Use keyed events - Listen for world-specific events when possible
- Consider view distance - Be mindful of server view distance settings
- Test multi-world scenarios - Ensure your plugin works across multiple worlds