User Tools

Site Tools


zh_cn:tutorial:persistent_states

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
zh_cn:tutorial:persistent_states [2023/12/21 07:51] – translating player specified persistent states dreamuniversezh_cn:tutorial:persistent_states [2023/12/23 02:39] (current) – fix incorrect spelling&character dreamuniverse
Line 130: Line 130:
 </code> </code>
  
-注:在继承 ''**PersistentState**'' 类时,必须实现 ''**writeNbt**''。从功能上讲,我们通过 ''**NbtCompound**'' 将我们要存储到本地的数据进行打包。在本例中,我们将先前创建的 "**public Integer totalDirtBlocksBroken**移入了这个文件。+注:在继承 ''**PersistentState**'' 类时,必须实现 ''**writeNbt**''。从功能上讲,我们通过 ''**NbtCompound**'' 将我们要存储到本地的数据进行打包。在本例中,我们将先前创建的 ''**public Integer totalDirtBlocksBroken**'' 移入了这个文件。
  
   * ''**NbtCompound**'' 不只是保存**整数值**,它还保存着其他类型的数据,甚至是其他的 ''**NbtCompound**'' 数据或者任意的字节数据。   * ''**NbtCompound**'' 不只是保存**整数值**,它还保存着其他类型的数据,甚至是其他的 ''**NbtCompound**'' 数据或者任意的字节数据。
Line 322: Line 322:
 </code> </code>
  
-注:我们创建了一个关于 ''**UUID**'' 数据的 ''**HashMap**'' 并将其存储于 ''**PlayereData**'' 中。\\+注:我们创建了一个关于 ''**UUID**'' 数据的 ''**HashMap**'' 并将其存储于 ''**PlayerData**'' 中。\\
 Hashmap 即哈希表,简单而言,在本例中,您向表中给出一个特定的“键值”(key),表从 ''**PlayerData**'' 中返回对应“键值”的 ''**UUID**''。\\ Hashmap 即哈希表,简单而言,在本例中,您向表中给出一个特定的“键值”(key),表从 ''**PlayerData**'' 中返回对应“键值”的 ''**UUID**''。\\
 我们使用 ''**UUID**''进行记录的原因是每位连接到服务器的玩家的 ''**UUID**'' 必定是//唯一//的。这就使得我们得以区分不同玩家,并针对其返回相对应的数据。如对应玩家的数据不存在,则创建之。 我们使用 ''**UUID**''进行记录的原因是每位连接到服务器的玩家的 ''**UUID**'' 必定是//唯一//的。这就使得我们得以区分不同玩家,并针对其返回相对应的数据。如对应玩家的数据不存在,则创建之。
Line 396: Line 396:
 </code> </code>
  
-您同时需要将实现了 ''**ClientModInitializer**'' 的类做相应改动:+您同时需要将实现了 ''**ClientModInitializer**'' 的类做如下改动:
  
 <code java> <code java>
Line 563: Line 563:
 ===== 内联同步 ===== ===== 内联同步 =====
  
-What if it's important for our mod that as soon as a player joins they receive some or all the PlayerData associated with them? For this, we will crate a new packet ''**INITIAL_SYNC**'' which will send the player, their specific player data as soon as they join the world.+那么,当玩家加入服务器时,他们应当收到与他们相关的部分或全部玩家数据(''**PlayerData**''),而这一点对我们的模组至关重要。\\ 
 +对于这一点,当玩家加入世界时,我们会向玩家发送一个用于内联同步(''**INITIAL_SYNC**'')的数据包,从而指示服务器发送该玩家的数据。
  
-Modify your class which ''**implements ModInitializer**'' as follows:+现在,我们将实现了''**ModInitializer**'' 的类做如下改动:
  
 <code java> <code java>
Line 581: Line 582:
 public class ExampleMod implements ModInitializer { public class ExampleMod implements ModInitializer {
  
-    public static final String MOD_ID = "your_unique_mod_id_change_me_please";+    public static final String MOD_ID = "您的MOD_ID";
  
     public static final Identifier DIRT_BROKEN = new Identifier(MOD_ID, "dirt_broken");     public static final Identifier DIRT_BROKEN = new Identifier(MOD_ID, "dirt_broken");
Line 601: Line 602:
             if (state.getBlock() == Blocks.GRASS_BLOCK || state.getBlock() == Blocks.DIRT) {             if (state.getBlock() == Blocks.GRASS_BLOCK || state.getBlock() == Blocks.DIRT) {
                 StateSaverAndLoader serverState = StateSaverAndLoader.getServerState(world.getServer());                 StateSaverAndLoader serverState = StateSaverAndLoader.getServerState(world.getServer());
-                // Increment the amount of dirt blocks that have been broken+                // 当泥土方块被挖掘时增加计数
                 serverState.totalDirtBlocksBroken += 1;                 serverState.totalDirtBlocksBroken += 1;
  
Line 607: Line 608:
                 playerState.dirtBlocksBroken += 1;                 playerState.dirtBlocksBroken += 1;
  
-                // Send a packet to the client+                // 向客户端发送数据包
                 MinecraftServer server = world.getServer();                 MinecraftServer server = world.getServer();
  
Line 624: Line 625:
 </code> </code>
  
-Then modify your class which ''**implements ClientModInitializer**'' as follows:+随后,再将实现了 ''**ClientModInitializer**'' 的类做如下改动:
  
 <code java> <code java>
Line 642: Line 643:
  
             client.execute(() -> {             client.execute(() -> {
-                client.player.sendMessage(Text.literal("Total dirt blocks broken: " + totalDirtBlocksBroken)); +                client.player.sendMessage(Text.literal("总计被挖掘的泥土方块数:" + totalDirtBlocksBroken)); 
-                client.player.sendMessage(Text.literal("Player specific dirt blocks broken: " + playerData.dirtBlocksBroken));+                client.player.sendMessage(Text.literal("当前玩家挖掘的泥土方块数:" + playerData.dirtBlocksBroken));
             });             });
         });         });
Line 651: Line 652:
  
             client.execute(() -> {             client.execute(() -> {
-                client.player.sendMessage(Text.literal("Initial specific dirt blocks broken: " + playerData.dirtBlocksBroken));+                client.player.sendMessage(Text.literal("初始化的当前玩家挖掘的泥土方块数:" + playerData.dirtBlocksBroken));
             });             });
         });         });
Line 658: Line 659:
 </code> </code>
  
-As soon as you join the world/server you should see a message popup telling you the amount of dirt blocks you've specifically broken.+这样一来,当您进入本地世界或服务器时,您就会看到有一条消息提示您已经挖掘了多少泥土方块。
  
-  * Note: The ''**playerData**'' we created isn't the up-to-date one that lives on the server. We simply create our own copy of ''**PlayerData**'' client-side and update it as we receive packets. Since it'''**public static**'' that means you can access it from anywhere on the client.+  * 注①:我们在客户端侧所创建的 ''**PlayerData**'' 并非与服务器侧实时同步。我们只是从服务器侧接收并在客户端侧创建了相应副本,并在客户端侧刷新。由于其关键字为 ''**public static**'',这意味着您可以在客户端的任意类中访问。
  
-  * Note: each time you restart the minecraft client with fabric, you're assigned a new UUID, so it may seem like it's not working, but that's just because of the developer environment. (If you run the fabric multiplayer server, 'Minecraft Server' and use authme, you could verify it does indeed work as it's supposed to.)+  * 注②:每次您使用 Fabric 调试启动客户端时,您所分配到的 UUID 都是不一样的,所以您可能会认为代码没有正确工作,但这只是因为您使用的开发环境使然。(如果您在生产环境运行一个 Minecraft 服务器并使用 Authme,您就可以确认模组在按照我们的预期目的工作了。)
  
 ===== 更复杂的玩家数据 ===== ===== 更复杂的玩家数据 =====
  
-And just for good measure, let's see an example of how our ''**StateSaverAndLoader**'' class would look if our ''**PlayerData**'' has more than primitives, like lists, and even its own hashmap. How would that look?+现在我们已经不能满足单一方块的统计需求了,我们来看另一个例子:如果我们的 ''**PlayerData**'' 数据更多(包含列表甚至前文所提到的哈希表),我们的 ''**StateSaverAndLoader**'' 类需要怎么编写?
  
-Let's say this is our ''**PlayerData**''':+假设我们的 ''**PlayerData**'' 是这样的:
  
 <code java> <code java>
Line 684: Line 685:
 </code> </code>
  
-This would be our ''**StateSaverAndLoader**'':+我们的 ''**StateSaverAndLoader**'' 如下所示:
  
 <code java> <code java>
Line 755: Line 756:
  
     private static Type<StateSaverAndLoader> type = new Type<>(     private static Type<StateSaverAndLoader> type = new Type<>(
-            StateSaverAndLoader::new, // If there's no 'StateSaverAndLoader' yet create one +            StateSaverAndLoader::new, // 若不存在 'StateSaverAndLoader' 则创建 
-            StateSaverAndLoader::createFromNbt, // If there is a 'StateSaverAndLoader' NBT, parse it with 'createFromNbt' +            StateSaverAndLoader::createFromNbt, // 若存在 'StateSaverAndLoader' NBT, 则调用 'createFromNbt' 传入参数 
-            null // Supposed to be an 'DataFixTypes' enum, but we can just pass null+            null // 此处理论上应为 'DataFixTypes' 的枚举,但我们直接传递为空(null)也可以
     );     );
 + 
     public static StateSaverAndLoader getServerState(MinecraftServer server) {     public static StateSaverAndLoader getServerState(MinecraftServer server) {
-        // (Note: arbitrary choice to use 'World.OVERWORLD' instead of 'World.END' or 'World.NETHER'.  Any work)+        // (注:如需在任意维度生效,请使用 'World.OVERWORLD' ,不要使用 'World.END' 或 'World.NETHER')
         PersistentStateManager persistentStateManager = server.getWorld(World.OVERWORLD).getPersistentStateManager();         PersistentStateManager persistentStateManager = server.getWorld(World.OVERWORLD).getPersistentStateManager();
- +  
-        // The first time the following 'getOrCreate' function is called, it creates a brand new 'StateSaverAndLoader' and +        // 当第一次调用了方法 'getOrCreate' 后,它会创建新的 'StateSaverAndLoader' 并将其存储于  'PersistentStateManager' 中。 
-        // stores it inside the 'PersistentStateManager'. The subsequent calls to 'getOrCreate' pass in the saved +        //  'getOrCreate' 的后续调用将本地的 'StateSaverAndLoader' NBT 传递给 'StateSaverAndLoader::createFromNbt'
-        // 'StateSaverAndLoader' NBT on disk to our function 'StateSaverAndLoader::createFromNbt'.+
         StateSaverAndLoader state = persistentStateManager.getOrCreate(type, ExampleMod.MOD_ID);         StateSaverAndLoader state = persistentStateManager.getOrCreate(type, ExampleMod.MOD_ID);
- +  
-        // If state is not marked dirty, when Minecraft closes, 'writeNbt' won't be called and therefore nothing will be saved. +        // 若状态未标记为脏(dirty),当 Minecraft 关闭时, 'writeNbt' 不会被调用,相应地,没有数据会被保存。 
-        // Technically it's 'cleaner' if you only mark state as dirty when there was actually a change, but the vast majority +        // 从技术上讲,只有在事实上发生数据变更时才应当将状态标记为脏(dirty)。 
-        // of mod writers are just going to be confused when their data isn't being saved, and so it's best just to 'markDirty' for them. +        // 但大多数开发者和模组作者会对他们的数据未能保存而感到困惑,所以不妨直接使用 'markDirty'  
-        // Besides, it's literally just setting a bool to true, and the only time there's a 'cost' is when the file is written to disk when +        // 另外,这只将对应的布尔值设定为 TRUE,代价是文件写入磁盘时模组的状态不会有任何改变。(这种情况非常少见)
-        // there were no actual change to any of the mods state (INCREDIBLY RARE).+
         state.markDirty();         state.markDirty();
 + 
         return state;         return state;
     }     }
Line 782: Line 781:
         StateSaverAndLoader serverState = getServerState(player.getWorld().getServer());         StateSaverAndLoader serverState = getServerState(player.getWorld().getServer());
  
-        // Either get the player by the uuid, or we don't have data for him yet, make a new player state+        // 根据 UUID 获取对应玩家的状态,如果没有该玩家的数据,就创建一个新的玩家状态。
         PlayerData playerState = serverState.players.computeIfAbsent(player.getUuid(), uuid -> new PlayerData());         PlayerData playerState = serverState.players.computeIfAbsent(player.getUuid(), uuid -> new PlayerData());
  
Line 790: Line 789:
 </code> </code>
  
-Our classes which implement ''**ClientModInitializer**'' and ''**ModInitializer**'' would be the same as before, but we include them here so that this section of the article is easy to copy and paste into you program and experiment with persistent state.+对已经实现了 ''**ClientModInitializer**'' 和 ''**ModInitializer**'' 的类无需改动,但我们保留了其代码,这样一来您就可以直接复制粘贴,直观地体会状态持久化带来的效果。
  
 <code java> <code java>
Line 808: Line 807:
  
             client.execute(() -> {             client.execute(() -> {
-                client.player.sendMessage(Text.literal("Total dirt blocks broken: " + totalDirtBlocksBroken)); +                client.player.sendMessage(Text.literal("总计被挖掘的泥土方块数:" + totalDirtBlocksBroken)); 
-                client.player.sendMessage(Text.literal("Player specific dirt blocks broken: " + playerData.dirtBlocksBroken));+                client.player.sendMessage(Text.literal("当前玩家挖掘的泥土方块数:" + playerData.dirtBlocksBroken));
             });             });
         });         });
Line 817: Line 816:
  
             client.execute(() -> {             client.execute(() -> {
-                client.player.sendMessage(Text.literal("Initial specific dirt blocks broken: " + playerData.dirtBlocksBroken));+                client.player.sendMessage(Text.literal("初始化的当前玩家挖掘的泥土方块数:" + playerData.dirtBlocksBroken));
             });             });
         });         });
Line 839: Line 838:
 public class ExampleMod implements ModInitializer { public class ExampleMod implements ModInitializer {
  
-    public static final String MOD_ID = "your_unique_mod_id_change_me_please";+    public static final String MOD_ID = "您的MOD_ID";
  
     public static final Identifier DIRT_BROKEN = new Identifier(MOD_ID, "dirt_broken");     public static final Identifier DIRT_BROKEN = new Identifier(MOD_ID, "dirt_broken");
Line 859: Line 858:
             if (state.getBlock() == Blocks.GRASS_BLOCK || state.getBlock() == Blocks.DIRT) {             if (state.getBlock() == Blocks.GRASS_BLOCK || state.getBlock() == Blocks.DIRT) {
                 StateSaverAndLoader serverState = StateSaverAndLoader.getServerState(world.getServer());                 StateSaverAndLoader serverState = StateSaverAndLoader.getServerState(world.getServer());
-                // Increment the amount of dirt blocks that have been broken+                // 当泥土方块被挖掘时增加计数
                 serverState.totalDirtBlocksBroken += 1;                 serverState.totalDirtBlocksBroken += 1;
  
Line 865: Line 864:
                 playerState.dirtBlocksBroken += 1;                 playerState.dirtBlocksBroken += 1;
  
-                // Send a packet to the client+                // 向客户端发送数据包
                 MinecraftServer server = world.getServer();                 MinecraftServer server = world.getServer();
  
zh_cn/tutorial/persistent_states.1703145092.txt.gz · Last modified: 2023/12/21 07:51 by dreamuniverse