持久化世界数据
Saved Data(简称 SD)系统用于保存世界的额外数据。
如果数据只属于某个方块实体、区块或实体,可以考虑改用数据附件。
SavedData
每个 SD 实现都必须继承 SavedData。可以像普通对象一样定义自己的字段和方法,但如果需要将数据或修改保存到磁盘,必须调用 setDirty。它会通知游戏存在需要写入的修改。如果不调用,数据只会在当前维度保持加载期间存在;对于附加到 MinecraftServer 的数据,则只在整个世界保持加载期间存在。
// For some saved data implementation
public class ExampleSavedData extends SavedData {
public void foo() {
// Change data in saved data
// Call set dirty if data changes
this.setDirty();
}
}
SavedDataType
SavedData 本身只是对象,因此还需要关联标识符,并负责磁盘读写,这由 SavedDataType 处理。它接受数据标识符、数据不存在时使用的默认构造器,以及编码和解码数据的 codec。标识符会作为世界文件夹或维度文件夹内的路径:
- 服务端数据:
./<world_folder>/data/<identifier_namespace>/<identifier_path>.dat。 - 单独维度的数据:
./<world_folder>/dimensions/<level_namespace>/<level_path>/data/<identifier_namespace>/<identifier_path>.dat。
不存在的目录会自动创建,包括标识符路径中的目录。
提示 另有第四个参数
DataFixTypes。由于 NeoForge 不支持数据修复器,原版的各处调用已通过补丁允许传入null。
SavedDataType 构造方法有两个版本。一个接受用于构造实例的普通 Supplier 和处理磁盘数据的普通 Codec;如果需要保存当前 ServerLevel 或世界种子,可以使用 NeoForge 添加的重载,两者均接受获得 ServerLevel 的 SavedDataType.Factory。
// For some saved data implementation
public class NoContextExampleSavedData extends SavedData {
public static final SavedDataType<NoContextExampleSavedData> ID = new SavedDataType<>(
// The identifier of the saved data
// Used as the path within the `data` folder
Identifier.fromNamespaceAndPath("examplemod", "example"),
// The initial constructor
NoContextExampleSavedData::new,
// The codec used to serialize the data
RecordCodecBuilder.create(instance -> instance.group(
Codec.INT.fieldOf("val1").forGetter(sd -> sd.val1),
BuiltInRegistries.BLOCK.byNameCodec().fieldOf("val2").forGetter(sd -> sd.val2)
).apply(instance, NoContextExampleSavedData::new))
);
// Initial constructor
public NoContextExampleSavedData() {
// ...
}
// Data constructor
public NoContextExampleSavedData(int val1, Block val2) {
// ...
}
public void foo() {
// Change data in saved data
// Call set dirty if data changes
this.setDirty();
}
}
// For some saved data implementation
public class ContextExampleSavedData extends SavedData {
public static final SavedDataType<ContextExampleSavedData> ID = new SavedDataType<>(
// The identifier of the saved data
// Used as the path within the `data` folder
Identifier.fromNamespaceAndPath("examplemod", "example"),
// The initial constructor
ContextExampleSavedData::new,
// The codec used to serialize the data
level -> RecordCodecBuilder.create(instance -> instance.group(
RecordCodecBuilder.point(level),
Codec.INT.fieldOf("val1").forGetter(sd -> sd.val1),
BuiltInRegistries.BLOCK.byNameCodec().fieldOf("val2").forGetter(sd -> sd.val2)
).apply(instance, ContextExampleSavedData::new))
);
// Initial constructor
public ContextExampleSavedData(ServerLevel level) {
// ...
}
// Data constructor
public ContextExampleSavedData(ServerLevel level, int val1, Block val2) {
// ...
}
public void foo() {
// Change data in saved data
// Call set dirty if data changes
this.setDirty();
}
}
附加到世界
SavedData 都是在运行时动态加载或附加到维度或服务器的。因此,如果从未在某个维度或服务器中创建,它就不存在。
SavedData 通过 SavedDataStorage 创建和加载。可调用 ServerChunkCache#getDataStorage 或 ServerLevel#getDataStorage 获取存储,再将 SavedDataType 传给 SavedDataStorage#computeIfAbsent 获取或创建实例。如果已经存在就返回当前实例,否则创建新实例并加载可用数据。
// In some method with access to the SavedDataStorage
netherDataStorage.computeIfAbsent(ContextExampleSavedData.ID);
如果数据并不属于某个特定维度,应通过 MinecraftServer#getDataStorage 附加到 MinecraftServer。
来源: NeoForged/Documentation(26.1)。中文翻译依据提交
4a680cdd4d15。