Files
hytale-server/docs/06-world-management.md

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

  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