Worlds
Declaring worlds and dimensions, per-world settings, game rules, the Linear format, importing existing worlds, and non-vanilla toggles.
A Basalt world is one dimension. A server loads as many as worlds.toml declares, each with its own chunks, region files and settings, and players move between them with /tp or through portals.
worlds/<name>/
region/ r.<x>.<z>.linear
poi/ points of interest (villager workstations, beds, portals)
settings.toml this world's settings, inheriting from worlds.toml
mobs.toml this world's mob settings, inheriting from worlds/mobs.tomlPlayer data is not stored here. It lives in players.db next to worlds/, so a world can be deleted, copied or reset without touching anyone's inventory.
Declaring worlds#
config/worlds.toml declares what to load. It never deletes anything:
- Add a table for a name with no directory and the world is created.
- Copy a world directory into
worlds/and add its table and the world is imported. - Remove a table and the world stops loading. Its files stay, and putting the table back loads it as it was.
- Deleting a world means deleting
worlds/<name>/yourself.
[worlds.world]
default = true
type = "normal"
dimension = "overworld"
[worlds.world_nether]
type = "normal"
dimension = "nether"
linked_to = "world"
[worlds.world_the_end]
type = "normal"
dimension = "end"
linked_to = "world"| Key | Type | Default | Meaning |
|---|---|---|---|
default |
boolean | false |
The world players join into. Exactly one world should be marked. With none marked the first declared is used and a warning is logged; more than one stops the start |
type |
string | "normal" |
normal, flat (vanilla superflat: bedrock, two dirt, grass), void (air with a small stone platform under the spawn, for lobbies), amplified, large_biomes or debug (every block state in a grid) |
dimension |
string | "overworld" |
overworld, nether or end. It decides the sky, height limits, the 1:8 portal ratio and whether it can rain |
seed |
integer | generated | Leave it out to get a random one on first creation. See Seeds |
linked_to |
string | the default world | For a Nether or End: the Overworld it belongs to. Portals lit in that Overworld come out here, and portals here lead back |
Several sets of dimensions#
linked_to is what lets one server run several Overworlds, each with its own Nether and End, without a plugin rewriting portal travel:
[worlds.skyblock]
type = "normal"
[worlds.skyblock_nether]
dimension = "nether"
linked_to = "skyblock"
[worlds.skyblock_the_end]
dimension = "end"
linked_to = "skyblock"The rules are checked at startup and a violation stops the server, naming the world, rather than sending portals somewhere nobody chose: only a Nether or End can have linked_to, it must name an Overworld that the file declares, and an Overworld has at most one Nether and one End. A Nether or End with no seed of its own shares the seed of its Overworld, as a vanilla save does. Removing a dimension's table makes portals to it stop working (the log says so); its files stay.
Seeds#
Once a world exists, the seed it was generated with wins over the one in worlds.toml. A mismatch is logged and nothing is regenerated, because changing a seed does not rewrite terrain already on disk. To use a new seed, delete worlds/<name>/ and start again. When a vanilla world is imported, its own seed is kept and a differing configured one only logs a warning.
Inherited settings#
Settings follow one chain: the world's own settings.toml, then the [defaults] of worlds.toml, then vanilla's values. A world only needs to list the keys it changes. The generated settings.toml is full of commented-out lines to uncomment.
In worlds.toml the keys sit under [defaults] (and rule tables under [defaults.<table>]). In settings.toml the same keys sit at the top (and rule tables as plain [<table>]). A world's settings are read when it loads, so changing them needs a restart. A settings.toml that does not parse or holds an invalid value leaves that world on the inherited defaults and logs a warning.
| Key | Type | Default | Meaning |
|---|---|---|---|
difficulty |
string | "easy" |
peaceful, easy, normal or hard |
view-distance |
integer | 10 |
Radius in chunks sent to a client around itself |
simulation-distance |
integer, 2 to 32 | 10 |
Radius in chunks around each player in which the world ticks. Never beyond view-distance. Lowering it is the strongest way to spare the tick on a busy server. |
save-interval-seconds |
integer | 300 |
How often changes are written to the region files, so a crash costs at most this much. 0 leaves chunks to save when they unload or on stop |
generate-structures |
boolean | true |
Off gives new chunks no villages, strongholds and the like. Chunks already generated keep theirs |
max-world-size |
integer, 1 to 29999984 | 29999984 |
Radius of the playable area in blocks; the world border can never go past it |
hardcore |
boolean | false |
The difficulty stays hard, hearts are drawn hardcore, and a player who dies comes back as a spectator |
open-end-exit-portal |
boolean | false |
NOT VANILLA. Opens the exit portal in the middle of the End from the moment the world is created |
loot-refill |
boolean | false |
NOT VANILLA. See Loot refill |
loot-refill-min-seconds |
integer | 43200 |
See below |
loot-refill-max-seconds |
integer | 172800 |
See below |
loot-max-refills |
integer | -1 |
See below |
loot-reloot-seconds |
integer | -1 |
See below |
Loot refill#
Not vanilla, off by default. With loot-refill = true, a loot container keeps its table after the first opening and fills again once a random time between loot-refill-min-seconds and loot-refill-max-seconds (12 hours and 48 hours by default) has passed since it was filled, for the next player who opens it. loot-max-refills is how many times a container may fill in all, the first fill included (-1 is unlimited). loot-reloot-seconds is how long a player must wait to loot a refilled container they already looted (-1 is never). The maximum is raised to the minimum if it is smaller.
Rules and game rules#
The same files carry tables that change how a world plays. Every value shipped is vanilla's, and anything different is marked NOT VANILLA in the generated comments. Rules are read when the world loads.
[gamerules]#
Initial game rules for a world that is being created, using the vanilla 26.3 names (keep_inventory, pvp, random_tick_speed, spawn_monsters, advance_time...). Values are true, false or a whole number. A world that already has level data keeps its own rules, and /gamerule still changes them afterwards.
[gamerules]
keep_inventory = true
random_tick_speed = 6[growth]#
How often plants try to grow, as a percentage of vanilla's chance. 100 is vanilla, 0 stops the plant, 200 doubles the rate. Range 0 to 100000. Anything but 100 is NOT VANILLA.
Keys, all default 100: crops (wheat, carrots, potatoes, beetroots, torchflower, pitcher plant), stems (pumpkin and melon), saplings, cactus, sugar-cane, bamboo, vines, kelp, cave-vines, twisting-vines, weeping-vines, nether-wart, sweet-berries, cocoa, mushrooms, chorus-flower.
[hopper]#
| Key | Type | Default | Meaning |
|---|---|---|---|
transfer-ticks |
integer, 1 or more | 8 |
Ticks a hopper rests after moving an item |
amount |
integer, 1 to 64 | 1 |
Items moved each time a hopper acts, from the same stack |
check-ticks |
integer, 1 or more | 1 |
A hopper only looks for work on ticks that are a multiple of this |
Anything but the defaults is NOT VANILLA.
[environment]#
| Key | Type | Default | Meaning |
|---|---|---|---|
thunder-chance |
integer, 1 or more | 100000 |
One in this many chances per chunk and tick, while it thunders, that lightning strikes there |
portal-search-radius |
integer, 1 to 512 | 128 |
How far a portal looks for an existing portal to come out of |
portal-create-radius |
integer, 1 to 128 | 16 |
How far from the wanted spot a new portal may be built when none is found |
void-damage |
boolean | true |
Whether falling out of the world hurts |
disable-thunder |
boolean | false |
NOT VANILLA. Rain still falls but it never thunders |
disable-explosion-knockback |
boolean | false |
NOT VANILLA. Explosions hurt but never push anything |
disable-ice-and-snow |
boolean | false |
NOT VANILLA. Weather no longer freezes still water or piles snow |
nether-ceiling-void-damage-height |
integer, 0 to 4096 | 0 |
NOT VANILLA when above 0. Players at or above this height in the Nether take void damage. 0 is off |
fishing-min-wait-ticks |
integer, 1 to 72000 | 100 |
A bobber waits for a fish a time drawn between the two values, before Lure shortens it |
fishing-max-wait-ticks |
integer, 1 to 72000 | 600 |
Raised to the minimum if smaller |
[border]#
The world border a world starts with. A world that already has a saved border keeps it, and /worldborder changes it afterwards.
| Key | Type | Default | Meaning |
|---|---|---|---|
size |
number, 1 to 59999968 | 59999968.0 |
Full width in blocks |
center-x, center-z |
number | 0.0 |
Centre of the border, within plus or minus 29999984 |
damage-per-block |
number | 0.2 |
Damage per second per block outside the buffer |
damage-buffer |
number | 5.0 |
Blocks beyond the border before damage starts |
warning-distance |
integer | 5 |
Blocks from the border at which the warning shows |
warning-time |
integer, seconds | 15 |
Seconds before a shrinking border arrives at which the warning shows |
[anti-xray]#
Not vanilla, off by default. Hides ores from clients that cheat by reading chunk data. Blocks that touch nothing see-through are replaced in the chunk packets, and whoever mines next to them is sent the real blocks. Bedrock players are not covered.
| Key | Type | Default | Meaning |
|---|---|---|---|
enabled |
boolean | false |
Turn it on |
engine-mode |
integer, 1 to 3 | 1 |
1: hidden blocks show as the plain rock of the dimension. 2: hidden and replacement blocks all show as random hidden blocks, fake ores included. 3: as 2 but a whole layer of a section gets the same fake block, which is much cheaper |
hidden-blocks |
list of strings | common ores, mossy_cobblestone, obsidian, clay |
Blocks that are hidden. In modes 2 and 3 they also make up the fake ores, so leave out blocks with an inventory |
replacement-blocks |
list of strings | ["stone", "oak_planks", "deepslate"] |
Modes 2 and 3 only: ordinary blocks that are swapped for fake ores too |
max-block-height |
integer, 0 to 4096 | 64 |
Only blocks below this height are touched, rounded down to a multiple of 16 |
update-radius |
integer, 0 to 2 | 2 |
When a block is broken, hidden blocks within this many blocks are sent for real |
lava-obscures |
boolean | false |
Treat lava as solid, so ores behind a lava lake stay hidden |
use-permission |
boolean | false |
Players with basalt.antixray.bypass see everything |
Tuning tables#
| Table | Key | Type | Default | Meaning |
|---|---|---|---|---|
[fire] |
tick-delay |
integer | 30 |
Ticks a fire waits before spreading or burning out, plus a random 0 to 9 |
[hunger] |
sprint-exhaustion |
number | 0.1 |
Exhaustion added by each action |
swim-exhaustion |
number | 0.01 |
||
jump-exhaustion |
number | 0.05 |
||
sprint-jump-exhaustion |
number | 0.2 |
||
combat-exhaustion |
number | 0.1 |
||
regen-exhaustion |
number | 6.0 |
Spent each time natural regeneration heals with a food bar of 18 or more | |
[growth-height] |
cactus |
integer, 1 to 64 | 3 |
How tall a plant may grow |
sugar-cane |
integer, 1 to 64 | 3 |
||
bamboo |
integer, 1 to 128 | 16 |
||
[sounds] |
dragon-death-radius, wither-spawn-radius |
number | 0.0 |
Blocks from the event within which players hear it. 0 is vanilla: the whole server hears it |
[chunks] |
keep-spawn-loaded-range |
integer, 0 to 32 | 1 |
Chunks around the world spawn that stay loaded without a player |
autosave-batch-size |
integer, 1 to 1024 | 8 |
Chunks snapshotted in one go while saving. Lower smooths the tick and makes a save take longer | |
unload-margin |
integer, 0 to 64 | 4 |
Chunks beyond a player's view distance that stay loaded before the unloading sweep | |
[combat] |
keep-sprint-on-attack |
boolean | false |
NOT VANILLA. Hitting with a knockback weapon does not stop the attacker sprinting |
[block-updates] |
disable-noteblock-updates, disable-tripwire-updates, disable-chorus-plant-updates, disable-mushroom-block-updates |
boolean | false |
NOT VANILLA. Stop those blocks reshaping when a neighbour changes |
[explosions] |
max-tnt-per-tick |
integer, -1 or more | -1 |
NOT VANILLA. Most primed TNT that may go off in one tick; the rest wait. -1 is no limit |
[maps] |
item-frame-cursor-limit |
integer | 256 |
Markers a map may track before an item frame refuses to show it |
item-frame-cursor-update-interval |
integer, 1 or more | 10 |
Ticks between checks that keep a map in an item frame current | |
[end] |
disable-credits |
boolean | false |
NOT VANILLA. Leaving the End for the first time takes the player straight home, without the credits |
[leash] |
max-distance |
number, 2 to 256 | 12.0 |
Blocks a leashed mob may get from its holder before the lead snaps |
[command-blocks] |
permission-level |
integer, 0 to 4 | 2 |
Level the commands of command blocks and command block minecarts run at |
[collisions] |
enable-player-collisions |
boolean | true |
false is NOT VANILLA: players no longer push each other |
Gameplay rules#
[gameplay.blocks], [gameplay.mechanics], [gameplay.player] and [gameplay.items] hold over a hundred finer rules. In worlds.toml they are [defaults.gameplay.<group>], in a world's settings.toml [gameplay.<group>]. Restart to apply. They are only listed in the generated file, where each key carries a comment stating its vanilla value:
| Table | Covers |
|---|---|
blocks |
Fluids (infinite water and lava sources, lava speed), sponges, pistons, rails, beds, respawn anchors and end crystals explosions, beacons, farmland trampling, plant growth ages, dripleaves, bone meal on cactus and sugar cane, hoe replanting, coloured text on signs and anvils |
mechanics |
Drowning, regeneration, poison and wither pulses, elytra wear, milk, raid and breeding cooldowns, day and night length, mob last-hurt time, entities using portals |
player |
Critical hit multiplier, totems, experience pickup, skipping the night, creative one-punch |
items |
Ender pearls, snowballs, projectile inaccuracy per weapon, minecart speed, trident loyalty, shulker box drops |
Every key defaults to vanilla's value, and any other value is marked NOT VANILLA next to the key.
Mob settings#
Mob caps, spawning, despawning, dropped-item lifetimes and per-type tweaks live in config/worlds/mobs.toml, and a world overrides any key in worlds/<name>/mobs.toml. Every value shipped is vanilla's, and a change applies when the world loads. Both files are generated with a comment on every key.
| Table | Covers |
|---|---|
[caps] |
Mob cap per category: monster 70, creature 10, ambient 15, axolotls 5, underground-water-creature 5, water-creature 5, water-ambient 20. 0 stops natural spawning of that category |
[spawn-interval-ticks] |
Ticks between spawn attempts per category: creature 400, every other 1 |
[spawning] |
per-player-mob-spawns (false, NOT VANILLA), range-chunks (8), monster-max-light-level (-1 keeps vanilla's rules, NOT VANILLA when 0 or more) |
[despawn] |
shape (sphere is vanilla, or cylinder), idle-ticks-before-random (600), random-odds (800), and soft-range and hard-range per category under [despawn.<category>] |
[items], [experience], [projectiles] |
Despawn time of dropped items (6000 ticks), merge radius of items and experience orbs (0.5), arrow and trident despawn (1200 ticks) |
[tracking-range] |
How far, in chunks, entities are shown to a player, per group: animals, monsters, misc, display, other. 0 keeps vanilla's own range |
[phantoms], [pillager-patrols], [wandering-trader] |
Timing and odds of those events |
[overrides.<entity_type>] |
Per-type changes: attributes (health, damage, speed...), experience and drops, behaviour switches (burn in daylight, no AI, persistent, ridable...), natural spawning limits, breeding, creeper blast radius, and AI goals to disable. Lists only the fields to change |
The Linear format#
Worlds are stored as Linear (.linear) region files, not Anvil (.mca). The format is invisible to players, so it is not a parity difference, and it is better in every way an operator notices:
- About half the disk space for the Overworld and Nether, and far less for the End.
- One compressed stream per region (zstd) instead of one per chunk, so a region is one read or write.
- No per-chunk padding to 4096 bytes and no separate files for very large chunks.
Files are worlds/<name>/region/r.<x>.<z>.linear.
Importing a world#
Moving an existing world to Basalt is "copy the folder and start", not "run a converter first".
- Copy the world into
worlds/<name>/. For a vanilla, Paper or Spigot world, copy itsregion/,entities/,poi/andlevel.dat. - Declare it in
worlds.tomlwith the matchingdimension. - Start the server.
When a region has only an Anvil file (region/r.<x>.<z>.mca), Basalt reads it and the entities that vanilla keeps in entities/, and writes it back as Linear. When the world has a vanilla level.dat, its seed, spawn point, time and difficulty are imported once and kept in the world.
A vanilla save holds three dimensions in one folder, while Basalt has one dimension per world. Import each as its own world: the Overworld's region/ is the root region/, the Nether's is DIM-1/region/ and the End's is DIM1/region/. Declare the second and third with dimension = "nether" and "end" and linked_to pointing at the Overworld, as in Declaring worlds.
A region that cannot be read is logged and left alone: it is never overwritten.
Old worlds
For a world saved by a much older game version, run it through Metamorph first. It brings chunks and entities to the current world version with the same fixes the official game applies when it opens an old world.
Metamorph#
Metamorph is the command line tool behind this conversion. It never modifies its input: the result goes to <world>.converted unless -o says otherwise, and an output folder that already holds files is refused.
metamorph convert <world> --to linear [-o <out>]
metamorph convert <world> --to anvil [-o <out>]
metamorph upgrade <world> [-o <out>]| Command | Result |
|---|---|
convert --to linear |
Every region file as .linear |
convert --to anvil |
Every region file as .mca, for taking a Basalt world back to another server |
upgrade |
Every chunk and entity file brought to the current world version, each keeping its format |
<world> is a folder with region files anywhere under it; dimension folders, entities/ and poi/ are found automatically. A chunk that cannot be read is reported and the command exits with an error.
Backups#
Basalt does not run backups; use your own tooling. What matters is what to copy and when:
- Copy the whole server directory, or at least
worlds/,config/,plugins/andplayers.db. Player data is inplayers.db, not in the worlds. - Region files are rewritten whole, so copy them while the server is quiet.
/save-offstops writing,/save-allflushes, then copy, then/save-on. Stopping the server first is always safe. - A world with its table removed from
worlds.tomlis simply not loaded; archiving it is a plain directory copy.