Java server development on Paper · 3 min read

Validating plugin YAML: types, limits and atomic application

Loaded YAML does not mean valid settings. Let's check cooldown-seconds as an integer 0–3600 and apply the new Settings only after a complete check; an erroneous reload will leave the same snapshot.

LonelyCraft editors · Updated

Desktop with computer and world Minecraft on screen
Illustration: desktop with a computer and world Minecraft on the screen. This is an artistic scene, not a snapshot of a specific server.

Define the field contract

In the Paper 26.3 and Java 25 project, create src/main/resources/config.yml with the line cooldown-seconds: 30. Zero means no wait, no field means 30. The line "30" is wrong: it is not an integer.

In plugin.yml add permissions: → trailnotes.config: → default: op. command /checkconfig is registered with code; Administrative rights are not issued to an ordinary player. The resource is copied for the first time via saveDefaultConfig.

YAML path through parser, model checking and working configuration replacement
Until the last stage, the previous Settings remains valid; any mistake saves it.Scroll the diagram horizontally.Open full-size diagram

Parse the candidate separately from live state

Replace the main class with the full code. Unlike the convenient static loader, the YamlConfiguration instance and its load allow you to handle a read error. Javadoc YAML also warns that this object is not synchronized: it is not passed between threads here.

package ru.example.trailnotes;

import java.io.File;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;

public final class TrailNotesPlugin extends JavaPlugin {
    private record Settings(int cooldownSeconds) {}
    private Settings settings = new Settings(30);

    private Settings readSettings() throws Exception {
        YamlConfiguration yaml = new YamlConfiguration();
        yaml.load(new File(getDataFolder(), "config.yml"));
        Object raw = yaml.contains("cooldown-seconds")
            ? yaml.get("cooldown-seconds") : Integer.valueOf(30);
        if (!(raw instanceof Integer value) || value < 0 || value > 3600)
            throw new IllegalArgumentException("cooldown-seconds: нужен integer 0..3600");
        return new Settings(value);
    }
    @Override public void onEnable() {
        saveDefaultConfig();
        try {
            settings = readSettings();
            registerCommand("checkconfig", (source, args) -> checkConfig(source.getSender()));
        } catch (Exception error) {
            getLogger().severe("config.yml не прошёл проверку: " + error.getClass().getSimpleName());
            getServer().getPluginManager().disablePlugin(this);
        }
    }
    private void checkConfig(CommandSender sender) {
        if (!sender.hasPermission("trailnotes.config")) {
            sender.sendMessage("Нет права trailnotes.config.");
            return;
        }
        try {
            Settings candidate = readSettings();
            settings = candidate;
            sender.sendMessage("Применено: " + settings.cooldownSeconds() + " секунд.");
        } catch (Exception error) {
            sender.sendMessage("Отказ. Проверьте cooldown-seconds: integer 0..3600.");
            sender.sendMessage("Прежнее значение: " + settings.cooldownSeconds() + " секунд.");
        }
        return;
    }
}

Verify that an invalid reload changes nothing

After assembly and installation, execute checkconfig in the console, change the runtime file plugins/TrailNotes/config.yml and repeat command. The resource inside the source does not change the existing server file.

FileResult
cooldown-seconds: 0Applied 0
cooldown-seconds: "30"Refusal; remains 0
cooldown-seconds: -1Refusal; remains 0
cooldown-seconds: 3601Refusal; remains 0
cooldown-seconds: 60Applied 60
Field missingdefault 30 applied

Check component settings before replacing

For the arena, check min-players <= max-players, the presence of a world, the uniqueness of the ID and all links to rewards. Collect messages with field paths, then either replace one immutable snapshot or leave the same one entirely. A round that has already started may retain its old model until completion.

When changing a schema, keep the schema-version number and make a copy of the file before migration. The new template should not erase custom values. Do not include connection passwords in diagnostic messages.

The state model shows when it is acceptable to apply a new set. Save the settings of the started round.

The binary version and its old data file must be checked together. Check the config after replacing the JAR.

Frequently asked questions

Why can getInt hide the wrong type?

Convenient reading with default does not prove that the user specified an integer. In this contract, the original Object is first verified.

Do I need to rewrite the file every time there is an error?

No. Save it for correction and leave the previous working model; automatic rewriting will eliminate the cause of the error.

Can 0 be considered a missing value?

Only if it is expressly provided for in the contract. In our example, 0 has a separate meaning and should be preserved.

Views: … · One view per browser per day

Was this useful?Total score: …

One reaction per browser. Click again to remove it, or choose the other reaction.

Sources and documentation

Minecraftplugin developmentPaper

Read on

YAML Plugin Validation: Type, Range and Atomic Application - LonelyCraft — Lonely Craft