GameStateSavedData
GameStateSavedData<T> is a thin base over vanilla SavedData that packages the recurring four-piece boilerplate — SavedDataType field, computeIfAbsent getter, codec wiring, and a setDirty helper — into a single subclass-able shape. Use it for per-instance markers and simple replay state (“round started”, “round index”). Mid-game state restoration (eliminated set, target picks, timers) stays in the consumer mod’s own state object.
When to use it
Section titled “When to use it”- You need durable per-instance state (the slot’s saved data) without writing the vanilla boilerplate.
- You want a one-call mutator that marks dirty automatically.
- You want your state class to be loadable with a single static call.
package me.zlex.conduit.state;
public abstract class GameStateSavedData<T extends GameStateSavedData<T>> extends SavedData {
protected static <T extends GameStateSavedData<T>> SavedDataType<T> type( String namespace, String path, Supplier<T> factory, Codec<T> codec);
protected static <T extends GameStateSavedData<T>> T load( ServerLevel level, SavedDataType<T> type);
protected T mutated(Function<T, T> mutator); // mutate + setDirty in one call}Example
Section titled “Example”import com.mojang.serialization.Codec;import com.mojang.serialization.codecs.RecordCodecBuilder;import me.zlex.conduit.state.GameStateSavedData;import net.minecraft.server.level.ServerLevel;import net.minecraft.world.level.saveddata.SavedDataType;
public final class MyGameState extends GameStateSavedData<MyGameState> {
public boolean started; public int roundIndex;
private static final Codec<MyGameState> CODEC = RecordCodecBuilder.create(inst -> inst.group( Codec.BOOL.optionalFieldOf("started", false).forGetter(s -> s.started), Codec.INT .optionalFieldOf("round", 0) .forGetter(s -> s.roundIndex) ).apply(inst, MyGameState::reconstruct));
private static final SavedDataType<MyGameState> TYPE = type( "my-game", "game-state", MyGameState::new, CODEC);
public static MyGameState get(ServerLevel level) { return load(level, TYPE); }
private static MyGameState reconstruct(boolean started, int round) { MyGameState s = new MyGameState(); s.started = started; s.roundIndex = round; return s; }}Mutate via mutated:
state.mutated(s -> { s.roundIndex++; return s; });- Saved data is per-
ServerLevel. For per-instance state, callget(inst.level())— each instance dimension has its own savefile. - Scope is the same as the original
ShuffleGameStatepattern — markers and replay state, not mid-game tactical state. - The codec lives on your subclass. Use
RecordCodecBuilderfor typed fields, orCodec.unboundedMapfor KV blobs.