Opening a World
Before we get into chunks, entities and blocks, let's start with the basics: opening a world folder and reading the data that sits right at its root, like level.dat.
1. What is a world?
If you looked at the Introduction guide already, you know a world is not just a single file. It's a whole folder full of .dat files, region files and player data. Most of that data only matters once you start looking at a specific dimension or a specific chunk, which we'll cover in later guides.
For now, think of a world as two things:
- A handful of files that describe the world itself: its name, its difficulty, its spawn point, whether it's hardcore, and so on. This lives in
level.dat. - A
session.lockfile that Minecraft uses to make sure only one thing is writing to the world at a time.
minified-worlds wraps both of these in one class: WorldSave.
2. Add the dependency
repositories {
mavenCentral()
}
dependencies {
// Replace 3.3.0 with your preferred version
implementation 'io.github.dervarex.minified:minified-worlds:3.3.0'
}repositories {
mavenCentral()
}
dependencies {
// Replace 3.3.0 with your preferred version
implementation("io.github.dervarex.minified:minified-worlds:3.3.0")
}3. Opening a world
All you need is a path to the world folder, the same folder that contains the level.dat.
Path worldDirectory = Path.of("saves/My World");
WorldSave world = new WorldSave(worldDirectory);val worldDirectory = Path.of("saves/My World")
val world = WorldSave(worldDirectory)When you construct WorldSave, it immediately reads level.dat and a couple of other small .dat files from the data/ folder, so the constructor can throw if something is wrong. Two things it checks:
- The given path has to be an existing directory. If it isn't, you'll get a
RuntimeExceptionsaying the world directory doesn't exist. - That directory has to contain a
level.datfile. Without it,minified-worldshas no idea what world it's even looking at, so it throws too.
Both of these are unchecked exceptions, so you don't need a try/catch just to open a world, but it's worth catching them if you're opening a folder the user picked or one that might not be a real world.
4. Reading level.dat
Once you have a WorldSave, its getLevel() method gives you a Level object with the world's core settings already parsed for you.
Level level = world.getLevel();
System.out.println(level.getLevelName());
System.out.println(level.getDifficulty());
System.out.println(level.isHardcore());
System.out.println(level.getSpawn().getPos());val level = world.level
println(level.levelName)
println(level.difficulty)
println(level.hardcore)
println(level.spawn.pos)A few of the fields you'll find on Level:
Level fields The most commonly used ones
levelName
String
The world's name, as shown on the "Select World" screen.
difficulty
Difficulty
The world's difficulty: peaceful, easy, normal or hard.
hardcore
boolean
Whether hardcore mode is on. This is separate from difficulty, a hardcore world still reports hard there.
spawn
Spawn
The world spawn point: dimension, position, pitch and yaw.
version
Version
The Minecraft version this world was last saved with.
gameType
int
The default game mode: 0 survival, 1 creative, 2 adventure, 3 spectator.
enabledDatapacks
String[]
Which datapacks are turned on for this world. disabledDatapacks lists the rest.
allowCommands
boolean
Whether cheats are enabled.
time
int
The current world time, in ticks.
Level is a plain data class with getters and setters for everything, so if you want to change something, like renaming the world or nudging the spawn point, just call the matching setter.
Difficulty
Difficulty
peaceful
No hostile mobs spawn, and hunger doesn't drain.
easy
Hostile mobs deal less damage, hunger drains slowly.
normal
The default, balanced difficulty.
hard
Hostile mobs deal more damage, hunger drains faster. Also what hardcore worlds report here.
5. Saving your changes
WorldSave has a save() method that writes everything you changed back to disk: level.dat, the files in data/minecraft and the players, including their advancements and stats.
level.setLevelName("My Renamed World");
world.save();level.levelName = "My Renamed World"
world.save()A few things save() takes care of for you:
- Only files whose data actually changed get written, everything else stays exactly as it is.
- Each file is written to a temporary file first and then swapped in, so a crash or a full disk can't leave half a
level.datbehind. Like Minecraft, it keeps the previouslevel.dataslevel.dat_old, and the previous player files as<uuid>.dat_old. - Whatever
minified-worldsdoesn't know about (data added by mods,version_history, a player's ender chest, ...) is written back untouched. - It refuses to write while Minecraft has the world open and throws an
IOExceptioninstead, more on that below.
save() doesn't touch chunks, entities or point of interest data, since those live in their own region files per dimension. We'll get to saving those in the next guide.
6. Checking the session lock
Minecraft uses session.lock to prevent two processes (say, a running server and your tool) from writing to the same world at the same time. WorldSave reads this for you through getLock().
if (world.getLock().isLocked()) {
System.out.println("This world is currently open somewhere else!");
}if (world.lock.locked) {
println("This world is currently open somewhere else!")
}It's a good habit to check this before writing to a world your tool didn't open itself, editing a world while Minecraft has it loaded is a great way to lose progress.
Everything in minified-worlds that writes to a world takes the lock on its own, so writing while Minecraft has the world open fails with an IOException instead of corrupting it. isLocked() is just a snapshot though, Minecraft could still open the world right after you checked. If your tool keeps a world open for a while (an editor, for example), take the lock yourself for that time, Minecraft then refuses to open the world until you let go of it:
try (SessionLock.Handle lock = SessionLock.acquire(worldDirectory)) {
// load, edit and save in peace
}SessionLock.acquire(worldDirectory).use {
// load, edit and save in peace
}Saving while you hold it works just fine, save() sees that this program already has the lock.
7. Optional data
A couple of files in the data/ folder aren't always there, since Minecraft only creates them once they're actually used. WorldSave exposes those as Optional:
world.getGameRules().ifPresent(rules -> {
// do something with the game rules
});
world.getCustomBossEvents().ifPresent(bossEvents -> {
// do something with the boss bars
});world.gameRules.ifPresent { rules ->
// do something with the game rules
}
world.customBossEvents.ifPresent { bossEvents ->
// do something with the boss bars
}Next steps
Now that you can open a world and read its top level settings, it's time to look at the actual terrain.