How to persist module state with DataFile
DataFile<T> wraps a YAML file with dirty-tracking and integrates with the auto-save loop
(core.auto-save-interval, default 5m).
Steps
1. Create a class extending DataFile<T>
T is the in-memory state type (a record, POJO, or AtomicReference<X>).
package de.kreiscraft.modules.myfeature;
import de.kreiscraft.core.config.DataFile;import java.nio.file.Path;import org.spongepowered.configurate.ConfigurationNode;import org.spongepowered.configurate.serialize.SerializationException;
public final class MyFeatureData extends DataFile<MyFeatureState> {
public MyFeatureData(Path dataFolder) { // dataFolder is ctx.plugin().getDataFolder().toPath() // Second arg is the path relative to the plugin data folder. super(dataFolder, "data/my-feature.yml"); }
@Override protected MyFeatureState empty() { return new MyFeatureState(/* defaults */); }
@Override protected MyFeatureState load(ConfigurationNode node) throws SerializationException { return node.get(MyFeatureState.class); }
@Override protected void save(MyFeatureState state, ConfigurationNode node) throws SerializationException { node.set(MyFeatureState.class, state); }}The state type must be @ConfigSerializable for the node.get()/node.set() calls above
to work with Configurate. Example:
@ConfigSerializablepublic record MyFeatureState(String lastValue, int counter) {}See SpawnData.java for a real example that stores a nullable Location via
AtomicReference<Location> and uses explicit node paths instead of a top-level record.
2. Load the file in enable()
@Overridepublic void enable(ModuleContext ctx) { MyFeatureData data = new MyFeatureData(ctx.plugin().getDataFolder().toPath()); try { data.load(); // reads file or calls empty() if the file doesn't exist yet } catch (IOException e) { throw new ModuleException("failed to load my-feature data", e); }
// Register with ConfigService so AutoSaveTask flushes it periodically. ctx.services().get(ConfigService.class).register(data);
// Pass data to services/listeners that need it. ctx.services().publish(MyFeatureService.class, new DefaultMyFeatureService(data));}3. Read state
MyFeatureState state = data.get();4. Mutate state
data.update(s -> s.setCounter(s.counter() + 1));update() calls mutator.accept(state) and marks the file dirty. The next
AutoSaveTask tick calls flushIfDirty(), which writes YAML only when dirty.
5. Auto-save
No action required. AutoSaveTask runs on the interval set by core.auto-save-interval
and calls flushIfDirty() on every registered DataFile.
6. Immediate flush (optional)
Call data.flushIfDirty() in disable() only if you want to guarantee the file is
written before the plugin unloads, rather than waiting for the next auto-save tick.
For most modules this is not necessary — AutoSaveTask is stopped before modules are
disabled.
Summary
| Operation | Call |
|---|---|
| Declare | extends DataFile<T>, implement empty(), load(node), save(state, node) |
| Load on startup | data.load() (throws IOException) |
| Register for auto-save | ctx.services().get(ConfigService.class).register(data) |
| Read | data.get() |
| Write (dirty-marks) | data.update(state -> ...) |
| Force flush | data.flushIfDirty() |