Creating Worlds
Package: com.hypixel.hytale.server.core.universe
The Universe singleton manages all loaded worlds. Each world runs on its own thread, has its own config, and can be created, loaded, or removed at runtime.
Universe universe = Universe.get();Creating Worlds
Section titled “Creating Worlds”Simple Creation
Section titled “Simple Creation”// create a world with default configCompletableFuture<World> future = universe.addWorld("arena");future.thenAccept(world -> { // world is running and ready});addWorld() throws IllegalArgumentException if the name is already taken (in memory or on disk). The world is saved to {universe}/worlds/{name}/.
A deprecated overload accepts generator and storage type names:
// create with a specific generatoruniverse.addWorld("flatworld", "Flat", null);Full Control
Section titled “Full Control”For complete control over the world, build a WorldConfig and call makeWorld():
WorldConfig config = new WorldConfig();config.setDisplayName("PvP Arena");config.setPvpEnabled(true);config.setGameMode(GameMode.ADVENTURE);config.setDeleteOnRemove(true); // clean up files when removed
Path savePath = universe.getPath().resolve("worlds").resolve("pvp-arena");CompletableFuture<World> future = universe.makeWorld("pvp-arena", savePath, config);makeWorld() dispatches AddWorldEvent before registering the world — if a listener cancels it, the future completes exceptionally with WorldLoadCancelledException.
A start parameter (defaults to true) controls whether the world thread starts immediately:
// create without starting — useful for setup before ticking beginsuniverse.makeWorld("staging", savePath, config, false);Lifecycle
Section titled “Lifecycle”When a world is created:
WorldConfigvalidated (required mods checked)Worldobject constructed (stores, thread created)- AddWorldEvent dispatched (cancellable)
- World registered in the universe maps
world.init()— world generator loadedworld.start()— ticking thread starts, ECS stores initialized- StartWorldEvent dispatched — safe to access stores and entities
Loading Worlds
Section titled “Loading Worlds”Load a world that exists on disk but isn’t currently in memory:
// check if a world can be loadedif (universe.isWorldLoadable("saved-world")) { CompletableFuture<World> future = universe.loadWorld("saved-world");}isWorldLoadable() returns true if the save directory contains config.bson or config.json. loadWorld() reads the config from disk and delegates to makeWorld().
Load-or-Create Pattern
Section titled “Load-or-Create Pattern”The built-in hub system uses this pattern to handle worlds that may or may not exist:
World world = universe.getWorld(worldName);if (world != null) { // already loaded — use directly teleportPlayer(playerRef, world);} else if (universe.isWorldLoadable(worldName)) { // exists on disk — load it universe.loadWorld(worldName).thenAccept(w -> teleportPlayer(playerRef, w));} else { // doesn't exist — create it universe.addWorld(worldName).thenAccept(w -> teleportPlayer(playerRef, w));}Removing Worlds
Section titled “Removing Worlds”// remove a world (returns false if cancelled or not found)boolean removed = universe.removeWorld("arena");Removal dispatches RemoveWorldEvent (cancellable unless the reason is EXCEPTIONAL). Before the world thread stops, all players are drained to the default world.
Draining Players
Section titled “Draining Players”When a non-default world is removed, its players are moved automatically. You can also drain manually:
// move all players from one world to anotherworld.drainPlayersTo(targetWorld);Each player drain dispatches DrainPlayerFromWorldEvent (keyed by the source world name), which allows listeners to redirect the target world or modify the spawn transform:
eventRegistry.registerGlobal(DrainPlayerFromWorldEvent.class, event -> { // redirect to a specific world instead of the default World lobby = Universe.get().getWorld("lobby"); if (lobby != null) { event.setWorld(lobby); }});File Cleanup
Section titled “File Cleanup”Two WorldConfig flags control what happens to world files:
| Flag | Default | Effect |
|---|---|---|
DeleteOnRemove | false | Delete world files when the world is removed at runtime |
DeleteOnUniverseStart | false | Delete world files on server startup |
These are useful for ephemeral worlds like game arenas or instances.
World Generators
Section titled “World Generators”World generators are registered as codec types. Set them on WorldConfig before creating the world, or pass the type name to addWorld().
| Type | Class | Description |
|---|---|---|
"Flat" | FlatWorldGenProvider | Flat world with configurable layers, block types, and environment |
"Void" | VoidWorldGenProvider | Empty world — no blocks, configurable tint and environment |
"Dummy" | DummyWorldGenProvider | Single layer of blocks at Y=0 |
"Hytale" | (WorldGenPlugin) | Full terrain generation with biomes, caves, structures |
"Void" is the default when no generator is specified. The "Hytale" generator is loaded by the built-in WorldGenPlugin.
// create a flat worlduniverse.addWorld("flatworld", "Flat", null);
// or configure via WorldConfigWorldConfig config = new WorldConfig();config.setWorldGenProvider(new FlatWorldGenProvider());universe.makeWorld("flatworld", savePath, config);Spawn Providers
Section titled “Spawn Providers”Spawn providers control where new players appear in the world. Set them on WorldConfig via setSpawnProvider().
| Type | Behavior |
|---|---|
GlobalSpawnProvider | Single static spawn point for all players |
IndividualSpawnProvider | Selects from a list of spawn points per player (hash-based on UUID) |
FitToHeightMapSpawnProvider | Wraps another provider, adjusts Y to the heightmap if below 0 |
If no spawn provider is set, the world generator’s default is used.
Chunk Storage Types
Section titled “Chunk Storage Types”| Type | Description |
|---|---|
"Hytale" (default) | Standard chunk persistence (delegates to IndexedStorageChunkStorageProvider internally) |
"Empty" | No persistence — chunks are never saved |
"Migration" | Migration from older formats |
"IndexedStorage" | Indexed storage format (same implementation as "Hytale") |
Set via WorldConfig.setChunkStorageProvider() or the chunkStorageType parameter on addWorld().
Instances
Section titled “Instances”The built-in instance system (InstancesPlugin) demonstrates runtime world creation for ephemeral gameplay areas. Instances are template-based worlds that auto-clean up.
How Instances Work
Section titled “How Instances Work”- Template files stored in
Server/Instances/{name}/(same format as world save) spawnInstance()copies the template to a temporary world directory- Loads config from
instance.bson(same schema as WorldConfig) - Creates the world with
universe.makeWorld()using a unique key:instance-{safeName}-{uuid} - Auto-removal conditions handle cleanup when the instance is no longer needed
Auto-Removal
Section titled “Auto-Removal”Instances register removal conditions that automatically call removeWorld():
| Condition | Behavior |
|---|---|
WorldEmptyCondition | Remove when no players remain |
IdleTimeoutCondition | Remove after idle with no players for N seconds |
TimeoutCondition | Remove after N seconds regardless of players |
Discovery Events
Section titled “Discovery Events”When a player enters an instance for the first time, DiscoverInstanceEvent.Display fires as an ECS event (cancellable). This triggers the event title display. See Event Titles for how to listen to and suppress instance discovery titles.
Custom WorldConfigProvider
Section titled “Custom WorldConfigProvider”The WorldConfigProvider interface controls how world configs are loaded and saved. By default, it reads from JSON/BSON files in the world’s save directory.
Replace it during PrepareUniverseEvent by calling setWorldConfigProvider() on the event:
eventRegistry.registerGlobal(PrepareUniverseEvent.class, event -> { event.setWorldConfigProvider(new MyCustomConfigProvider());});