AgentSkillsCN

hytale-plugin-config

在 Hytale 插件中,利用 Config<T>、BuilderCodec 以及 withConfig() 创建并管理插件配置文件。适用于添加插件设置、创建配置类、加载/保存配置文件,或从其他类中访问配置数据时使用。触发条件:config、configuration、plugin config、Config、withConfig、插件设置、配置文件、保存配置、加载配置、配置类、插件选项。

SKILL.md
--- frontmatter
name: hytale-plugin-config
description: Creates and manages plugin configuration files in Hytale using Config<T>, BuilderCodec, and withConfig(). Use when adding plugin settings, creating config classes, loading/saving config files, or accessing config data from other classes. Triggers - config, configuration, plugin config, Config, withConfig, plugin settings, config file, save config, load config, configuration class, plugin options.

Hytale Plugin Configuration Files

This skill documents how to create, load, save, and use configuration files in Hytale plugins using the Config<T> API.

Related skills: For Codec/BuilderCodec serialization details, see hytale-persistent-data. For ECS component registration, see hytale-ecs.

Quick Reference

TaskApproach
Define config classPlain class with BuilderCodec<T> and default constructor
Register configthis.withConfig("Name", MyConfig.CODEC) in plugin field initializer
Save config to fileconfig.save() in setup() (creates file if missing)
Read config valueconfig.get().getSomeValue()
Modify config valueconfig.get().setSomeValue(newVal) then config.save()
Config file locationServer mods/ folder

Configuration Class

A configuration class is a plain Java class (NOT an ECS component) with a BuilderCodec<T> that defines how each field serializes/deserializes.

Critical Rules

  1. Codec keys MUST be capitalized — lowercase keys throw an error at load time.
  2. Must have a default (no-arg) constructor — the codec uses it as a factory.
  3. Does NOT implement Component — config classes are not ECS components.

Template

java
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;

public class MyConfig {

    // === Codec Definition ===
    // Keys MUST be capitalized (e.g. "SomeValue", not "someValue")
    public static final BuilderCodec<MyConfig> CODEC =
        BuilderCodec.builder(MyConfig.class, MyConfig::new)
            .append(new KeyedCodec<Integer>("SomeValue", Codec.INTEGER),
                    (config, value) -> config.someValue = value,   // setter
                    (config) -> config.someValue)                  // getter
            .add()
            .append(new KeyedCodec<String>("SomeString", Codec.STRING),
                    (config, value) -> config.someString = value,
                    (config) -> config.someString)
            .add()
            .build();

    // === Fields with defaults ===
    private int someValue = 12;
    private String someString = "My default string";

    // === Default Constructor (required) ===
    public MyConfig() {
    }

    // === Getters ===
    public int getSomeValue() {
        return someValue;
    }

    public String getSomeString() {
        return someString;
    }

    // === Setters ===
    public void setSomeValue(int someValue) {
        this.someValue = someValue;
    }

    public void setSomeString(String someString) {
        this.someString = someString;
    }
}

Registering and Loading the Config

Rules

  • withConfig(...) MUST be called as a field initializer (or in the constructor) — calling it after setup() throws an error.
  • Call config.save() in setup() to create the config file on disk if it doesn't already exist.

Plugin Setup Template

java
import com.hypixel.hytale.server.plugin.Config;
import com.hypixel.hytale.server.plugin.JavaPlugin;

public class ExamplePlugin extends JavaPlugin {

    // Register config — MUST be a field initializer (before setup())
    private final Config<MyConfig> config = this.withConfig("MyConfig", MyConfig.CODEC);

    @Override
    public void setup() {
        // Ensures the config file is created if it doesn't exist
        config.save();
    }
}

The first argument to withConfig() is the config file name (without extension). The file is created in the server's mods/ folder.


Accessing Config Values

From the Plugin Class

java
public void someMethod() {
    MyConfig myConfig = config.get();
    int value = myConfig.getSomeValue();
    String str = myConfig.getSomeString();
}

From Other Classes

Pass the plugin instance or expose a getter:

Option A — Pass plugin reference:

java
public class SomeOtherClass {
    private final ExamplePlugin plugin;

    public SomeOtherClass(ExamplePlugin plugin) {
        this.plugin = plugin;
    }

    public void someMethod() {
        MyConfig myConfig = plugin.getConfig().get();
        int value = myConfig.getSomeValue();
        String str = myConfig.getSomeString();
    }
}

Option B — Expose a typed getter on the plugin:

java
// In ExamplePlugin
public Config<MyConfig> getPluginConfig() {
    return config;
}

Modifying and Saving Config

Changes to config values persist across server restarts when saved:

java
MyConfig myConfig = config.get();

// Modify values
myConfig.setSomeValue(999);
myConfig.setSomeString("A new string");

// Save changes back to disk
config.save();

Common Codec Types for Config Fields

Use these Codec types in KeyedCodec for your config fields:

Java TypeCodecExample Key
intCodec.INTEGER"MaxPlayers"
floatCodec.FLOAT"SpawnRate"
doubleCodec.DOUBLE"DamageMultiplier"
booleanCodec.BOOLEAN"EnableFeature"
StringCodec.STRING"WelcomeMessage"
longCodec.LONG"CooldownMs"
List<T>ListCodec"AllowedWorlds"
Map<K,V>MapCodec"LevelThresholds"
Set<T>SetCodec"BannedItems"

For advanced codec usage (nested objects, collections, validators), see the hytale-persistent-data skill.


Common Mistakes

MistakeFix
Lowercase codec key ("someValue")Capitalize: "SomeValue"
Calling withConfig() inside setup()Move to field initializer or constructor
Forgetting config.save() in setup()Config file won't be created on first run
Forgetting .add() after .append(...)Codec builder chain is incomplete
Forgetting .build() at end of codecCodec is not finalized
Not saving after modifying valuesChanges are lost on restart — call config.save()

Complete Example

Config Class

java
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;

public class ServerConfig {

    public static final BuilderCodec<ServerConfig> CODEC =
        BuilderCodec.builder(ServerConfig.class, ServerConfig::new)
            .append(new KeyedCodec<Integer>("MaxPlayers", Codec.INTEGER),
                    (c, v) -> c.maxPlayers = v,
                    c -> c.maxPlayers)
            .add()
            .append(new KeyedCodec<Boolean>("PvpEnabled", Codec.BOOLEAN),
                    (c, v) -> c.pvpEnabled = v,
                    c -> c.pvpEnabled)
            .add()
            .append(new KeyedCodec<String>("Motd", Codec.STRING),
                    (c, v) -> c.motd = v,
                    c -> c.motd)
            .add()
            .append(new KeyedCodec<Double>("DamageMultiplier", Codec.DOUBLE),
                    (c, v) -> c.damageMultiplier = v,
                    c -> c.damageMultiplier)
            .add()
            .build();

    private int maxPlayers = 20;
    private boolean pvpEnabled = true;
    private String motd = "Welcome to the server!";
    private double damageMultiplier = 1.0;

    public ServerConfig() {
    }

    public int getMaxPlayers() { return maxPlayers; }
    public void setMaxPlayers(int maxPlayers) { this.maxPlayers = maxPlayers; }

    public boolean isPvpEnabled() { return pvpEnabled; }
    public void setPvpEnabled(boolean pvpEnabled) { this.pvpEnabled = pvpEnabled; }

    public String getMotd() { return motd; }
    public void setMotd(String motd) { this.motd = motd; }

    public double getDamageMultiplier() { return damageMultiplier; }
    public void setDamageMultiplier(double damageMultiplier) { this.damageMultiplier = damageMultiplier; }
}

Plugin Class

java
import com.hypixel.hytale.server.plugin.Config;
import com.hypixel.hytale.server.plugin.JavaPlugin;

public class MyPlugin extends JavaPlugin {

    private final Config<ServerConfig> config = this.withConfig("ServerConfig", ServerConfig.CODEC);

    @Override
    public void setup() {
        config.save();
    }

    @Override
    public void start() {
        ServerConfig cfg = config.get();
        getLogger().info("Max players: " + cfg.getMaxPlayers());
        getLogger().info("PvP enabled: " + cfg.isPvpEnabled());
        getLogger().info("MOTD: " + cfg.getMotd());
        getLogger().info("Damage multiplier: " + cfg.getDamageMultiplier());
    }

    public Config<ServerConfig> getServerConfig() {
        return config;
    }
}
code