MC百科社群

标题: 【Fabric模组开发】世界数据存储 [打印本页]

作者: QQ酱86010    时间: 2024-6-16 11:23
标题: 【Fabric模组开发】世界数据存储
世界数据存储   
[color=rgba(0, 0, 0, 0.87)]若问题欢迎讨论,感谢阅读!

[color=rgba(0, 0, 0, 0.87)]官方教程([color=var(--ttw-primary-color,#3c60ff)]教程:global_data [面料百科] (fabricmc.net))并没讲具体如何存储数据数据,不过看了原版后也容易写出,在此将教你保存世界数据(注意,这些操作几乎是在服务端完成的)。
PersistentState
[color=rgba(0, 0, 0, 0.87)]游戏每个世界中的数据是通过PersistentState保存的,该世界存档中保存的数据(*.dat)可以在游戏目录下saves\[world]\data文件夹看到,其中的文件都对应了一个PersistentState对象(如计分板的ScoreboardState类),这些类会实现“fromTag”和“toTag”方法来完成数据的解析与生成。接下来让我们编写自己的PersistentState类吧!
样例:
public class ExampleState extends PersistentState {           private static final Logger LOGGER = LogManager.getLogger();           private ExampleObject object;              public ExampleState() {                   super("example");           }              public void setObject(ExampleObject object) {                   this.object = object;           }              public ExampleObject getObject() {                   return object;           }              public void fromTag(CompoundTag tag) {                   object.fromTag(tag.getCompound("ExampleData"));           }              public CompoundTag toTag(CompoundTag tag) {                   if(this.handler == null) {                           LOGGER.warn("无法保存未创建的存储对象。");                   } else {                           tag.put("ExampleData", object.toTag());                   }                   return tag;           }   }  
[color=rgba(0, 0, 0, 0.87)]完成后,存储对象还需要注意下,为减少不必要的负担,防止该对象未更新数据而再次保存,需要实现void addUpdateListener(Runnable listener)并加入一个Synchronizer监听数据更改,稍等你将会用到它们,此外实现void runUpdateListeners()并在更新数据后调用。
样例:
//存储对象类   private Runnable[ updateListeners = new Runnable[0;      public void addUpdateListener(Runnable listener) {           this.updateListeners = Arrays.copyOf(this.updateListeners, this.updateListeners.length + );           this.updateListeners[this.updateListeners.length - 1 = listener;   }      protected void runUpdateListeners() {           Runnable[ var1 = this.updateListeners;           for(Runnable runnable : var1) {                   runnable.run();           }   } public class ExampleSynchronizer implements Runnable {           private final PersistentState compound;              public ExampleSynchronizer(PersistentState compound) {                   this.compound = compound;           }              public void run() {                   this.compound.markDirty();           }   }  
[color=rgba(0, 0, 0, 0.87)]接下来将教你将该 PersistentState 类加入到服务器中。
PersistentStateManager
[color=rgba(0, 0, 0, 0.87)]官方教程中讲到数据是按维度(代码中是World,使用DimensionType辨别维度)保存的,即每个维度都有自己的数据管理器——PersistentStateManager,我们可以使用serverWorld.getPersistentStateManager()获得 ,其中使用一个 map 存储着各个PersistentState,会在创建与保存时分别调用PersistentState的“fromTag”和“toTag”方法,我们可以在创建维度时使用persistentStateManager.getOrCreate(Supplier factory, String id)创建自己的PersistentState。接下来我们将使用 Mixin 向MinecraftServer中注入我们的代码。
样例:
@Mixin(MinecraftServer.class)   public abstract class MinecraftServerMixin {           private ExampleObject object;              @Inject(method = "(Ljava/io/File;Ljava/net/Proxy;Lcom/mojang/datafixers/DataFixer;Lnet/minecraft/server/command/CommandManager;Lcom/mojang/authlib/yggdrasil/YggdrasilAuthenticationService;Lcom/mojang/authlib/minecraft/MinecraftSessionService;Lcom/mojang/authlib/GameProfileRepository;Lnet/minecraft/util/UserCache;Lnet/minecraft/server/WorldGenerationProgressListenerFactory;Ljava/lang/String;)V", at = @At(value = "RETURN"))           private void initExampleObject(File gameDir, Proxy proxy, DataFixer dataFixer, CommandManager commandManager, YggdrasilAuthenticationService authService, MinecraftSessionService sessionService, GameProfileRepository gameProfileRepository, UserCache userCache, WorldGenerationProgressListenerFactory worldGenerationProgressListenerFactory, String levelName, CallbackInfo ci) {                   this.object = new ExampleObject();           }              private void initExampleObject(PersistentStateManager persistentStateManager) {                   ExampleState exampleState = persistentStateManager.getOrCreate(ExampleState::new, "example");                   exampleState.setObject(object);                   object.addUpdateListener(new ExampleSynchronizer(exampleState));           }              @Inject(method = "createWorlds(Lnet/minecraft/world/WorldSaveHandler;Lnet/minecraft/world/level/LevelProperties;Lnet/minecraft/world/level/LevelInfo;Lnet/minecraft/server/WorldGenerationProgressListener;)V", at = @At(value = "TAIL"), locals = LocalCapture.CAPTURE\_FAILHARD)           private void createTemperatureState(WorldSaveHandler worldSaveHandler, LevelProperties properties, LevelInfo levelInfo, WorldGenerationProgressListener worldGenerationProgressListener, CallbackInfo ci, ServerWorld serverWorld, ersistentStateManager persistentStateManager) {                   this.initExampleObject(persistentStateManager);           }   }  
[color=rgba(0, 0, 0, 0.87)]完成后,我们将学习如何在其它地方获取存储对象对其修改。
获取存储对象
[color=rgba(0, 0, 0, 0.87)]向 persistentStateManager 添加 PersistentState 后,我们应如何获取我们的存储对象?前面说到为了方便获取存储对象,在我们的 PersistentState 类中加入的“get”方法来获取存储对象,而已创建的 PersistentState 对象可以通过 persistentStateManager 的 get(Supplier factory, String id) 方法获取,因此可以写出以下代码:
server.getWorld(DimensionType.OVERWORLD).getPersistentStateManager().get(ExampleState::new, "example").getObject()
[color=rgba(0, 0, 0, 0.87)]完成后,赶快进入游戏测试吧!若成功你将在游戏目录(或开发环境的“run”目录)下saves\[world]\data文件夹看到你的数据文件,如果在 IDEA 安装的 MinecraftDev 插件,你可以方便地查看或编写这些数据文件。编写不易,你的支持是我最大的动力!





欢迎光临 MC百科社群 (https://bbs.mcmod.cn/) MC百科|最大的MineCraft中文模组百科