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
tutorial:persistent_states [2024/01/11 06:44] – typo jmanc3tutorial:persistent_states [2025/03/13 14:52] (current) – world.getServer() only once, harmonized MOD_ID lakazatong
Line 19: Line 19:
 First, since the data will be saved on the 'server', (NOTE: that there is always a 'server' running even when you play offline, so don't be scared about that word), let's send a simple packet to the player when the server detects the player breaks a dirt block, and print it to the chat. First, since the data will be saved on the 'server', (NOTE: that there is always a 'server' running even when you play offline, so don't be scared about that word), let's send a simple packet to the player when the server detects the player breaks a dirt block, and print it to the chat.
  
-Modify your class which ''**implements ModInitializer**'' as follows:+For that, we need to define a Payload which ''**implements CustomPayload**'' as follows: 
 + 
 +<code java> 
 +import net.minecraft.network.PacketByteBuf; 
 +import net.minecraft.network.codec.PacketCodec; 
 +import net.minecraft.network.codec.PacketCodecs; 
 +import net.minecraft.network.packet.CustomPayload; 
 +import net.minecraft.util.Identifier; 
 + 
 +public record DirtBrokenPayload(Integer totalDirtBlocksBroken) implements CustomPayload { 
 +    public static final Identifier DIRT_BROKEN_ID = Identifier.of(MOD_ID, "dirt_broken"); 
 +    public static final CustomPayload.Id<DirtBrokenPayload> ID = new CustomPayload.Id<>(DIRT_BROKEN_ID); 
 +    public static final PacketCodec<PacketByteBuf, DirtBrokenPayload> CODEC = PacketCodec.tuple( 
 +            PacketCodecs.INTEGER, DirtBrokenPayload::totalDirtBlocksBroken, 
 +            DirtBrokenPayload::new); 
 + 
 +    @Override 
 +    public Id<? extends CustomPayload> getId() { 
 +        return ID; 
 +    } 
 +
 +</code> 
 + 
 +Next, modify your class which ''**implements ModInitializer**'' as follows:
  
 <code java> <code java>
 import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
 import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents;
-import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
-import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;+
 import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
 import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
-import net.minecraft.network.PacketByteBuf; 
 import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
 import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
-import net.minecraft.util.Identifier; 
  
 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 = "examplemod";
- +
-    public static final Identifier DIRT_BROKEN = new Identifier(MOD_ID, "dirt_broken");+
  
     private Integer totalDirtBlocksBroken = 0;     private Integer totalDirtBlocksBroken = 0;
  
-    @Override 
     public void onInitialize() {     public void onInitialize() {
 +        PayloadTypeRegistry.playS2C().register(DirtBrokenPayload.ID, DirtBrokenPayload.CODEC);
 +
         PlayerBlockBreakEvents.AFTER.register((world, player, pos, state, entity) -> {         PlayerBlockBreakEvents.AFTER.register((world, player, pos, state, entity) -> {
             if (state.getBlock() == Blocks.GRASS_BLOCK || state.getBlock() == Blocks.DIRT) {             if (state.getBlock() == Blocks.GRASS_BLOCK || state.getBlock() == Blocks.DIRT) {
-                // Increment the amount of dirt blocks that have been broken 
-                totalDirtBlocksBroken += 1; 
- 
                 // Send a packet to the client                 // Send a packet to the client
                 MinecraftServer server = world.getServer();                 MinecraftServer server = world.getServer();
 +                assert server != null;
  
-                PacketByteBuf data = PacketByteBufs.create(); +                // Increment the amount of dirt blocks that have been broken 
-                data.writeInt(totalDirtBlocksBroken);+                totalDirtBlocksBroken += 1;
  
                 ServerPlayerEntity playerEntity = server.getPlayerManager().getPlayer(player.getUuid());                 ServerPlayerEntity playerEntity = server.getPlayerManager().getPlayer(player.getUuid());
                 server.execute(() -> {                 server.execute(() -> {
-                    ServerPlayNetworking.send(playerEntity, DIRT_BROKEN, data);+                    assert playerEntity != null; 
 +                    ServerPlayNetworking.send(playerEntity, new DirtBrokenPayload(totalDirtBlocksBroken));
                 });                 });
             }             }
Line 67: Line 85:
     * To send a packet we use: ''**ServerPlayNetworking.send**''. It takes three arguments; The ''**ServerPlayerEntity**''. An ''**Identifer**'' which is needed when sending a packet between server and client or client and server, and a ''**PacketByteBuf**'' which we are able to fill with arbritary data (that is: bools, ints, arrays, strings, and more). The ''**PacketByteBuf**'' is where we stuff the data we want to send across the line. In this case we send the total amount of dirt blocks that the server has seen broken.     * To send a packet we use: ''**ServerPlayNetworking.send**''. It takes three arguments; The ''**ServerPlayerEntity**''. An ''**Identifer**'' which is needed when sending a packet between server and client or client and server, and a ''**PacketByteBuf**'' which we are able to fill with arbritary data (that is: bools, ints, arrays, strings, and more). The ''**PacketByteBuf**'' is where we stuff the data we want to send across the line. In this case we send the total amount of dirt blocks that the server has seen broken.
  
-Next modify your class which ''**implements ClientModInitializer**'' as follows:+Finally, modify your class which ''**implements ClientModInitializer**'' as follows:
  
 <code java> <code java>
 import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
 import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
-import net.minecraft.text.Text; +import net.minecraft.client.network.ClientPlayerEntity; 
 +import net.minecraft.text.Text;
  
 public class ExampleModClient implements ClientModInitializer { public class ExampleModClient implements ClientModInitializer {
 +    private static void handleDirtBrokenPayload(DirtBrokenPayload payload, ClientPlayNetworking.Context context) {
 +        ClientPlayerEntity player = context.client().player;
 +        assert player != null;
 +        player.sendMessage(Text.literal("Total dirt blocks broken: " + payload.totalDirtBlocksBroken()), false);
 +    }
  
     @Override     @Override
     public void onInitializeClient() {     public void onInitializeClient() {
-        ClientPlayNetworking.registerGlobalReceiver(ExampleMod.DIRT_BROKEN(client, handler, buf, responseSender) -> { +        ClientPlayNetworking.registerGlobalReceiver(DirtBrokenPayload.IDExampleModClient::handleDirtBrokenPayload);
-            int totalDirtBlocksBroken = buf.readInt(); +
-            client.execute(() -> { +
-                client.player.sendMessage(Text.literal("Total dirt blocks broken" + totalDirtBlocksBroken)); +
-            }); +
-        });+
     }     }
 } }
Line 100: Line 119:
 <code java> <code java>
 import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
 +import net.minecraft.registry.RegistryWrapper;
 import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
 +import net.minecraft.server.world.ServerWorld;
 import net.minecraft.world.PersistentState; import net.minecraft.world.PersistentState;
-import net.minecraft.world.PersistentStateManager; 
 import net.minecraft.world.World; import net.minecraft.world.World;
  
Line 110: Line 130:
  
     @Override     @Override
-    public NbtCompound writeNbt(NbtCompound nbt) {+    public NbtCompound writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registries) {
         nbt.putInt("totalDirtBlocksBroken", totalDirtBlocksBroken);         nbt.putInt("totalDirtBlocksBroken", totalDirtBlocksBroken);
         return nbt;         return nbt;
Line 121: Line 141:
   * ''**NbtCompound**'' doesn't just store ''**Integers**''. It has functions for strings, arrays, bools, floats, importantly other ''**NbtCompound**'''s as you'll see soon enough, and even arbitrary bytes. That means, if you want to store some ''**SuperCustomClass**'', what you should do is create a ''**new NbtCompound**'' and pack that new ''**NbtCompound**'' with the fields of your ''**SuperCustomClass**'' and then store it in the main ''**NbtCompound**'' you get passed in. (We're about to do just that!)   * ''**NbtCompound**'' doesn't just store ''**Integers**''. It has functions for strings, arrays, bools, floats, importantly other ''**NbtCompound**'''s as you'll see soon enough, and even arbitrary bytes. That means, if you want to store some ''**SuperCustomClass**'', what you should do is create a ''**new NbtCompound**'' and pack that new ''**NbtCompound**'' with the fields of your ''**SuperCustomClass**'' and then store it in the main ''**NbtCompound**'' you get passed in. (We're about to do just that!)
  
-Next add the following function to that same file:+Next add the following functions to that same file:
  
 <code java> <code java>
Line 128: Line 148:
     // ... (Previously written code)     // ... (Previously written code)
  
-    public static StateSaverAndLoader createFromNbt(NbtCompound tag) {+    public static StateSaverAndLoader createFromNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) {
         StateSaverAndLoader state = new StateSaverAndLoader();         StateSaverAndLoader state = new StateSaverAndLoader();
         state.totalDirtBlocksBroken = tag.getInt("totalDirtBlocksBroken");         state.totalDirtBlocksBroken = tag.getInt("totalDirtBlocksBroken");
 +        return state;
 +    }
 +
 +    public static StateSaverAndLoader createNew() {
 +        StateSaverAndLoader state = new StateSaverAndLoader();
 +        state.totalDirtBlocksBroken = 0;
         return state;         return state;
     }     }
Line 136: Line 162:
 </code> </code>
  
-This function does the opposite of ''**writeNbt**''. It takes in an ''**NbtCompound**'' (the same one we wrote in ''**writeNbt**''), creates a brand ''**new StateSaverAndLoader**'' and stuffs it with the data inside the ''**NbtCompound**''.+First function does the opposite of ''**writeNbt**''. It takes in an ''**NbtCompound**'' (the same one we wrote in ''**writeNbt**''and a ''**RegistryWrapper.WrapperLookup**'', creates a brand ''**new StateSaverAndLoader**'' and stuffs it with the data inside the ''**NbtCompound**''.
  
   * Note: how we pull out the int we stored earlier with ''**getInt**'' and how the string we pass in is the same one we used in ''**writeNbt**''.   * Note: how we pull out the int we stored earlier with ''**getInt**'' and how the string we pass in is the same one we used in ''**writeNbt**''.
  
-Now we just need to add one more utility function which hooks everything up together. This function will take a ''**MinecraftServer**'' and from it, get the ''**PersistentStateManager**''. ''**PersistentStateManager**'' has a function ''**getOrCreate**'' which will use our ''**MOD_ID**'' as a key to see if it has an instance of our ''**StateSaverAndLoader**'' or if it needs to create one. If it needs to create one, it'll call the function we just wrote ''**createFromNbt**'' passing in the previously saved-to-disk ''**NbtCompound**''. Ultimately the function returns the ''**StateSaverAndLoader**'' for the given ''**MinecraftServer**''.+Second function refreshing variables ''**totalDirtBlocksBroken**'' to avoid transfering data from another world to new one. 
 + 
 +Now we just need to add one more utility function which hooks everything up together. This function will take a ''**MinecraftServer**'' and from it, get the ''**PersistentStateManager**''. ''**PersistentStateManager**'' has a function ''**getOrCreate**'' which will use our ''**MOD_ID**'' as a key to see if it has an instance of our ''**StateSaverAndLoader**'' or if it needs to create one. If it needs to create one, it'll call the function we just wrote ''**createFromNbt**'' passing in the previously saved-to-disk ''**NbtCompound**'' and a ''**RegistryWrapper.WrapperLookup**''. Ultimately the function returns the ''**StateSaverAndLoader**'' for the given ''**MinecraftServer**''.
  
 <code java> <code java>
Line 147: Line 175:
     // ... (Previously written code)     // ... (Previously written code)
  
-    private static Type<StateSaverAndLoader> type = new Type<>+    private static final Type<StateSaverAndLoader> type = new Type<>
-            StateSaverAndLoader::new, // If there's no 'StateSaverAndLoader' yet create one+            StateSaverAndLoader::createNew, // If there's no 'StateSaverAndLoader' yet create one and refresh variables
             StateSaverAndLoader::createFromNbt, // If there is a 'StateSaverAndLoader' NBT, parse it with 'createFromNbt'             StateSaverAndLoader::createFromNbt, // If there is a 'StateSaverAndLoader' NBT, parse it with 'createFromNbt'
             null // Supposed to be an 'DataFixTypes' enum, but we can just pass null             null // Supposed to be an 'DataFixTypes' enum, but we can just pass null
Line 155: Line 183:
     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)         // (Note: arbitrary choice to use 'World.OVERWORLD' instead of 'World.END' or 'World.NETHER' Any work)
-        PersistentStateManager persistentStateManager = server.getWorld(World.OVERWORLD).getPersistentStateManager();+        ServerWorld serverWorld = server.getWorld(World.OVERWORLD)
 +        assert serverWorld != null;
  
         // The first time the following 'getOrCreate' function is called, it creates a brand new 'StateSaverAndLoader' and         // The first time the following 'getOrCreate' function is called, it creates a brand new 'StateSaverAndLoader' and
         // stores it inside the 'PersistentStateManager'. The subsequent calls to 'getOrCreate' pass in the saved         // stores it inside the 'PersistentStateManager'. The subsequent calls to 'getOrCreate' pass in the saved
         // 'StateSaverAndLoader' NBT on disk to our function 'StateSaverAndLoader::createFromNbt'.         // 'StateSaverAndLoader' NBT on disk to our function 'StateSaverAndLoader::createFromNbt'.
-        StateSaverAndLoader state = persistentStateManager.getOrCreate(type, ExampleMod.MOD_ID);+        StateSaverAndLoader state = serverWorld.getPersistentStateManager().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.         // If state is not marked dirty, when Minecraft closes, 'writeNbt' won't be called and therefore nothing will be saved.
Line 178: Line 207:
 <code java> <code java>
 import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
 +import net.minecraft.registry.RegistryWrapper;
 import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
 +import net.minecraft.server.world.ServerWorld;
 import net.minecraft.world.PersistentState; import net.minecraft.world.PersistentState;
-import net.minecraft.world.PersistentStateManager; 
 import net.minecraft.world.World; import net.minecraft.world.World;
  
Line 188: Line 218:
  
     @Override     @Override
-    public NbtCompound writeNbt(NbtCompound nbt) {+    public NbtCompound writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registries) {
         nbt.putInt("totalDirtBlocksBroken", totalDirtBlocksBroken);         nbt.putInt("totalDirtBlocksBroken", totalDirtBlocksBroken);
         return nbt;         return nbt;
     }     }
  
-    public static StateSaverAndLoader createFromNbt(NbtCompound tag) {+    public static StateSaverAndLoader createFromNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) {
         StateSaverAndLoader state = new StateSaverAndLoader();         StateSaverAndLoader state = new StateSaverAndLoader();
         state.totalDirtBlocksBroken = tag.getInt("totalDirtBlocksBroken");         state.totalDirtBlocksBroken = tag.getInt("totalDirtBlocksBroken");
Line 199: Line 229:
     }     }
  
-    private static Type<StateSaverAndLoader> type = new Type<>+    public static StateSaverAndLoader createNew() { 
-            StateSaverAndLoader::new, // If there's no 'StateSaverAndLoader' yet create one+        StateSaverAndLoader state = new StateSaverAndLoader(); 
 +        state.totalDirtBlocksBroken = 0; 
 +        return state; 
 +    } 
 + 
 +    private static final Type<StateSaverAndLoader> type = new Type<>
 +            StateSaverAndLoader::createNew, // If there's no 'StateSaverAndLoader' yet create one and refresh variables
             StateSaverAndLoader::createFromNbt, // If there is a 'StateSaverAndLoader' NBT, parse it with 'createFromNbt'             StateSaverAndLoader::createFromNbt, // If there is a 'StateSaverAndLoader' NBT, parse it with 'createFromNbt'
             null // Supposed to be an 'DataFixTypes' enum, but we can just pass null             null // Supposed to be an 'DataFixTypes' enum, but we can just pass null
Line 207: Line 243:
     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)         // (Note: arbitrary choice to use 'World.OVERWORLD' instead of 'World.END' or 'World.NETHER' Any work)
-        PersistentStateManager persistentStateManager = server.getWorld(World.OVERWORLD).getPersistentStateManager();+        ServerWorld serverWorld = server.getWorld(World.OVERWORLD)
 +        assert serverWorld != null;
  
         // The first time the following 'getOrCreate' function is called, it creates a brand new 'StateSaverAndLoader' and         // The first time the following 'getOrCreate' function is called, it creates a brand new 'StateSaverAndLoader' and
         // stores it inside the 'PersistentStateManager'. The subsequent calls to 'getOrCreate' pass in the saved         // stores it inside the 'PersistentStateManager'. The subsequent calls to 'getOrCreate' pass in the saved
         // 'StateSaverAndLoader' NBT on disk to our function 'StateSaverAndLoader::createFromNbt'.         // 'StateSaverAndLoader' NBT on disk to our function 'StateSaverAndLoader::createFromNbt'.
-        StateSaverAndLoader state = persistentStateManager.getOrCreate(type, ExampleMod.MOD_ID);+        StateSaverAndLoader state = serverWorld.getPersistentStateManager().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.         // If state is not marked dirty, when Minecraft closes, 'writeNbt' won't be called and therefore nothing will be saved.
Line 231: Line 268:
 import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
 import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents;
-import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
-import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;+
 import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
 import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
-import net.minecraft.network.PacketByteBuf; 
 import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
 import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
-import net.minecraft.util.Identifier; 
  
 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 = "examplemod";
  
-    public static final Identifier DIRT_BROKEN = new Identifier(MOD_ID"dirt_broken");+    public void onInitialize() { 
 +        PayloadTypeRegistry.playS2C().register(DirtBrokenPayload.IDDirtBrokenPayload.CODEC);
  
-    @Override 
-    public void onInitialize() { 
         PlayerBlockBreakEvents.AFTER.register((world, player, pos, state, entity) -> {         PlayerBlockBreakEvents.AFTER.register((world, player, pos, state, entity) -> {
             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()); 
-                // Increment the amount of dirt blocks that have been broken 
-                serverState.totalDirtBlocksBroken += 1; 
- 
                 // Send a packet to the client                 // Send a packet to the client
                 MinecraftServer server = world.getServer();                 MinecraftServer server = world.getServer();
 +                assert server != null;
  
-                PacketByteBuf data PacketByteBufs.create(); +                // Increment the amount of dirt blocks that have been broken 
-                data.writeInt(serverState.totalDirtBlocksBroken);+                StateSaverAndLoader serverState StateSaverAndLoader.getServerState(server); 
 +                serverState.totalDirtBlocksBroken += 1;
  
                 ServerPlayerEntity playerEntity = server.getPlayerManager().getPlayer(player.getUuid());                 ServerPlayerEntity playerEntity = server.getPlayerManager().getPlayer(player.getUuid());
                 server.execute(() -> {                 server.execute(() -> {
-                    ServerPlayNetworking.send(playerEntity, DIRT_BROKEN, data);+                    assert playerEntity != null; 
 +                    ServerPlayNetworking.send(playerEntity, new DirtBrokenPayload(serverState.totalDirtBlocksBroken));
                 });                 });
             }             }
Line 346: Line 378:
 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 = "examplemod";
  
     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 354: Line 386:
         PlayerBlockBreakEvents.AFTER.register((world, player, pos, state, entity) -> {         PlayerBlockBreakEvents.AFTER.register((world, player, pos, state, entity) -> {
             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());+                // Send a packet to the client 
 +                MinecraftServer server = world.getServer(); 
 +                assert server != null; 
 +                 
 +                StateSaverAndLoader serverState = StateSaverAndLoader.getServerState(server);
                 // Increment the amount of dirt blocks that have been broken                 // Increment the amount of dirt blocks that have been broken
                 serverState.totalDirtBlocksBroken += 1;                 serverState.totalDirtBlocksBroken += 1;
Line 360: Line 396:
                 PlayerData playerState = StateSaverAndLoader.getPlayerState(player);                 PlayerData playerState = StateSaverAndLoader.getPlayerState(player);
                 playerState.dirtBlocksBroken += 1;                 playerState.dirtBlocksBroken += 1;
- 
-                // Send a packet to the client 
-                MinecraftServer server = world.getServer(); 
  
                 PacketByteBuf data = PacketByteBufs.create();                 PacketByteBuf data = PacketByteBufs.create();
Line 402: Line 435:
 </code> </code>
  
-If you ran the client now, it would seem as if everything is working, but we are forgetting a crucial step: We haven't updated our ''**writeNbt**'' and ''**createFromNbt**'' to save and load our hashmap.+If you ran the client now, it would seem as if everything is working, but we are forgetting a crucial step: We haven't updated our ''**writeNbt**''''**createFromNbt**'' and ''**createNew**'' to save and load our hashmap.
  
 The updated functions are as follows: The updated functions are as follows:
Line 428: Line 461:
     }     }
  
-    public static StateSaverAndLoader createFromNbt(NbtCompound tag) {+    public static StateSaverAndLoader createFromNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) {
         StateSaverAndLoader state = new StateSaverAndLoader();         StateSaverAndLoader state = new StateSaverAndLoader();
         state.totalDirtBlocksBroken = tag.getInt("totalDirtBlocksBroken");         state.totalDirtBlocksBroken = tag.getInt("totalDirtBlocksBroken");
Line 442: Line 475:
         });         });
  
 +        return state;
 +    }
 +    
 +    public static StateSaverAndLoader createNew() {
 +        StateSaverAndLoader state = new StateSaverAndLoader();
 +        state.totalDirtBlocksBroken = 0;
 +        state.players = new HashMap<>();
         return state;         return state;
     }     }
Line 486: Line 526:
     }     }
  
-    public static StateSaverAndLoader createFromNbt(NbtCompound tag) {+    public static StateSaverAndLoader createFromNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) {
         StateSaverAndLoader state = new StateSaverAndLoader();         StateSaverAndLoader state = new StateSaverAndLoader();
         state.totalDirtBlocksBroken = tag.getInt("totalDirtBlocksBroken");         state.totalDirtBlocksBroken = tag.getInt("totalDirtBlocksBroken");
Line 500: Line 540:
         });         });
  
 +        return state;
 +    }
 +    
 +    public static StateSaverAndLoader createNew() {
 +        StateSaverAndLoader state = new StateSaverAndLoader();
 +        state.totalDirtBlocksBroken = 0;
 +        state.players = new HashMap<>();
         return state;         return state;
     }     }
  
     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::createNew, // If there's no 'StateSaverAndLoader' yet create one and refresh variables
             StateSaverAndLoader::createFromNbt, // If there is a 'StateSaverAndLoader' NBT, parse it with 'createFromNbt'             StateSaverAndLoader::createFromNbt, // If there is a 'StateSaverAndLoader' NBT, parse it with 'createFromNbt'
             null // Supposed to be an 'DataFixTypes' enum, but we can just pass null             null // Supposed to be an 'DataFixTypes' enum, but we can just pass null
Line 567: Line 614:
 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 = "examplemod";
  
     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 586: Line 633:
         PlayerBlockBreakEvents.AFTER.register((world, player, pos, state, entity) -> {         PlayerBlockBreakEvents.AFTER.register((world, player, pos, state, entity) -> {
             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());+                // Send a packet to the client 
 +                MinecraftServer server = world.getServer(); 
 +                assert server != null; 
 +                 
 +                StateSaverAndLoader serverState = StateSaverAndLoader.getServerState(server);
                 // Increment the amount of dirt blocks that have been broken                 // Increment the amount of dirt blocks that have been broken
                 serverState.totalDirtBlocksBroken += 1;                 serverState.totalDirtBlocksBroken += 1;
Line 592: Line 643:
                 PlayerData playerState = StateSaverAndLoader.getPlayerState(player);                 PlayerData playerState = StateSaverAndLoader.getPlayerState(player);
                 playerState.dirtBlocksBroken += 1;                 playerState.dirtBlocksBroken += 1;
- 
-                // Send a packet to the client 
-                MinecraftServer server = world.getServer(); 
  
                 PacketByteBuf data = PacketByteBufs.create();                 PacketByteBuf data = PacketByteBufs.create();
Line 714: Line 762:
     }     }
  
-    public static StateSaverAndLoader createFromNbt(NbtCompound tag) {+    public static StateSaverAndLoader createFromNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) {
         StateSaverAndLoader state = new StateSaverAndLoader();         StateSaverAndLoader state = new StateSaverAndLoader();
         state.totalDirtBlocksBroken = tag.getInt("totalDirtBlocksBroken");         state.totalDirtBlocksBroken = tag.getInt("totalDirtBlocksBroken");
Line 739: Line 787:
         });         });
  
 +        return state;
 +    }
 +    
 +    public static StateSaverAndLoader createNew() {
 +        StateSaverAndLoader state = new StateSaverAndLoader();
 +        state.totalDirtBlocksBroken = 0;
 +        state.players = new HashMap<>();
         return state;         return state;
     }     }
  
     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::createNew, // If there's no 'StateSaverAndLoader' yet create one and refresh variables
             StateSaverAndLoader::createFromNbt, // If there is a 'StateSaverAndLoader' NBT, parse it with 'createFromNbt'             StateSaverAndLoader::createFromNbt, // If there is a 'StateSaverAndLoader' NBT, parse it with 'createFromNbt'
             null // Supposed to be an 'DataFixTypes' enum, but we can just pass null             null // Supposed to be an 'DataFixTypes' enum, but we can just pass null
Line 827: Line 882:
 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 = "examplemod";
  
     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 846: Line 901:
         PlayerBlockBreakEvents.AFTER.register((world, player, pos, state, entity) -> {         PlayerBlockBreakEvents.AFTER.register((world, player, pos, state, entity) -> {
             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());+                // Send a packet to the client 
 +                MinecraftServer server = world.getServer(); 
 +                assert server != null; 
 +                 
 +                StateSaverAndLoader serverState = StateSaverAndLoader.getServerState(server);
                 // Increment the amount of dirt blocks that have been broken                 // Increment the amount of dirt blocks that have been broken
                 serverState.totalDirtBlocksBroken += 1;                 serverState.totalDirtBlocksBroken += 1;
Line 852: Line 911:
                 PlayerData playerState = StateSaverAndLoader.getPlayerState(player);                 PlayerData playerState = StateSaverAndLoader.getPlayerState(player);
                 playerState.dirtBlocksBroken += 1;                 playerState.dirtBlocksBroken += 1;
- 
-                // Send a packet to the client 
-                MinecraftServer server = world.getServer(); 
  
                 PacketByteBuf data = PacketByteBufs.create();                 PacketByteBuf data = PacketByteBufs.create();
tutorial/persistent_states.1704955448.txt.gz · Last modified: 2024/01/11 06:44 by jmanc3