From e0360ea1c3f38ca53e1bacfa1a140b2228b82c05 Mon Sep 17 00:00:00 2001 From: LeBogo Date: Sun, 23 Mar 2025 22:04:08 +0100 Subject: [PATCH] fix pedestal block multiplayer sync issues Co-authored-by: DZultra --- .../kaupenjoe/tutorialmod/TutorialMod.java | 3 + .../tutorialmod/TutorialModClient.java | 5 ++ .../entity/custom/PedestalBlockEntity.java | 45 +++++++++++++++ .../tutorialmod/networking/ModPayloads.java | 12 ++++ .../SyncPedestalBlockEntityS2CPayload.java | 56 +++++++++++++++++++ 5 files changed, 121 insertions(+) create mode 100644 src/main/java/net/kaupenjoe/tutorialmod/networking/ModPayloads.java create mode 100644 src/main/java/net/kaupenjoe/tutorialmod/networking/custom/SyncPedestalBlockEntityS2CPayload.java diff --git a/src/main/java/net/kaupenjoe/tutorialmod/TutorialMod.java b/src/main/java/net/kaupenjoe/tutorialmod/TutorialMod.java index 508a461..3c8d18d 100644 --- a/src/main/java/net/kaupenjoe/tutorialmod/TutorialMod.java +++ b/src/main/java/net/kaupenjoe/tutorialmod/TutorialMod.java @@ -17,6 +17,7 @@ import net.kaupenjoe.tutorialmod.entity.custom.MantisEntity; import net.kaupenjoe.tutorialmod.item.ModItemGroups; import net.kaupenjoe.tutorialmod.item.ModItems; +import net.kaupenjoe.tutorialmod.networking.ModPayloads; import net.kaupenjoe.tutorialmod.particle.ModParticles; import net.kaupenjoe.tutorialmod.potion.ModPotions; import net.kaupenjoe.tutorialmod.recipe.ModRecipes; @@ -70,6 +71,8 @@ public void onInitialize() { ModBlockEntities.registerBlockEntities(); ModScreenHandlers.registerScreenHandlers(); + ModPayloads.registerModPayloads(); + ModRecipes.registerRecipes(); FuelRegistry.INSTANCE.add(ModItems.STARLIGHT_ASHES, 600); diff --git a/src/main/java/net/kaupenjoe/tutorialmod/TutorialModClient.java b/src/main/java/net/kaupenjoe/tutorialmod/TutorialModClient.java index 1d9adaf..e14115e 100644 --- a/src/main/java/net/kaupenjoe/tutorialmod/TutorialModClient.java +++ b/src/main/java/net/kaupenjoe/tutorialmod/TutorialModClient.java @@ -2,14 +2,17 @@ import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry; import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry; import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry; import net.kaupenjoe.tutorialmod.block.ModBlocks; import net.kaupenjoe.tutorialmod.block.entity.ModBlockEntities; +import net.kaupenjoe.tutorialmod.block.entity.custom.PedestalBlockEntity; import net.kaupenjoe.tutorialmod.block.entity.renderer.PedestalBlockEntityRenderer; import net.kaupenjoe.tutorialmod.entity.ModEntities; import net.kaupenjoe.tutorialmod.entity.client.*; +import net.kaupenjoe.tutorialmod.networking.custom.SyncPedestalBlockEntityS2CPayload; import net.kaupenjoe.tutorialmod.particle.ModParticles; import net.kaupenjoe.tutorialmod.particle.PinkGarnetParticle; import net.kaupenjoe.tutorialmod.screen.ModScreenHandlers; @@ -49,5 +52,7 @@ public void onInitializeClient() { HandledScreens.register(ModScreenHandlers.PEDESTAL_SCREEN_HANDLER, PedestalScreen::new); HandledScreens.register(ModScreenHandlers.GROWTH_CHAMBER_SCREEN_HANDLER, GrowthChamberScreen::new); + + ClientPlayNetworking.registerGlobalReceiver(SyncPedestalBlockEntityS2CPayload.ID, PedestalBlockEntity::onSyncPacket); } } diff --git a/src/main/java/net/kaupenjoe/tutorialmod/block/entity/custom/PedestalBlockEntity.java b/src/main/java/net/kaupenjoe/tutorialmod/block/entity/custom/PedestalBlockEntity.java index 21a115c..3974ade 100644 --- a/src/main/java/net/kaupenjoe/tutorialmod/block/entity/custom/PedestalBlockEntity.java +++ b/src/main/java/net/kaupenjoe/tutorialmod/block/entity/custom/PedestalBlockEntity.java @@ -1,11 +1,17 @@ package net.kaupenjoe.tutorialmod.block.entity.custom; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.fabric.api.networking.v1.PlayerLookup; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; import net.kaupenjoe.tutorialmod.block.entity.ImplementedInventory; import net.kaupenjoe.tutorialmod.block.entity.ModBlockEntities; +import net.kaupenjoe.tutorialmod.networking.custom.SyncPedestalBlockEntityS2CPayload; import net.kaupenjoe.tutorialmod.screen.custom.PedestalScreenHandler; +import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; +import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.Inventories; @@ -17,9 +23,11 @@ import net.minecraft.registry.RegistryWrapper; import net.minecraft.screen.ScreenHandler; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; import net.minecraft.text.Text; import net.minecraft.util.collection.DefaultedList; import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; import org.jetbrains.annotations.Nullable; public class PedestalBlockEntity extends BlockEntity implements ImplementedInventory, ExtendedScreenHandlerFactory { @@ -30,6 +38,31 @@ public PedestalBlockEntity(BlockPos pos, BlockState state) { super(ModBlockEntities.PEDESTAL_BE, pos, state); } + public static void sendSyncPacket(World world, BlockPos blockpos, DefaultedList inventory) { + if (world.isClient()) return; + SyncPedestalBlockEntityS2CPayload payload = new SyncPedestalBlockEntityS2CPayload(blockpos, inventory); + + for (ServerPlayerEntity serverPlayerEntity : PlayerLookup.world((ServerWorld) world)) { + ServerPlayNetworking.send(serverPlayerEntity, payload); + } + } + + public static void onSyncPacket(SyncPedestalBlockEntityS2CPayload payload, ClientPlayNetworking.Context context) { + ClientWorld world = context.client().world; + + if (world == null) return; // Ensure the world is not null + + // Retrieve the BlockEntity at the specified BlockPos + BlockEntity blockEntity = world.getBlockEntity(payload.blockPos()); + if (blockEntity instanceof PedestalBlockEntity pedestalBlockEntity) { + // Update the BlockEntity's inventory with the payload data + pedestalBlockEntity.setStack(0, payload.inventory().getFirst()); + + // Mark the BlockEntity for re-rendering (optional, if needed) + world.updateListeners(payload.blockPos(), blockEntity.getCachedState(), blockEntity.getCachedState(), Block.NOTIFY_ALL); + } + } + @Override public DefaultedList getItems() { return inventory; @@ -82,4 +115,16 @@ public Packet toUpdatePacket() { public NbtCompound toInitialChunkDataNbt(RegistryWrapper.WrapperLookup registryLookup) { return createNbt(registryLookup); } + + @Override + public void markDirty() { + super.markDirty(); + syncInventory(); + } + + public void syncInventory() { + if (this.world != null && !this.world.isClient) { + sendSyncPacket(this.world, this.pos, this.inventory); + } + } } diff --git a/src/main/java/net/kaupenjoe/tutorialmod/networking/ModPayloads.java b/src/main/java/net/kaupenjoe/tutorialmod/networking/ModPayloads.java new file mode 100644 index 0000000..7e00c8f --- /dev/null +++ b/src/main/java/net/kaupenjoe/tutorialmod/networking/ModPayloads.java @@ -0,0 +1,12 @@ +package net.kaupenjoe.tutorialmod.networking; + +import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; +import net.kaupenjoe.tutorialmod.TutorialMod; +import net.kaupenjoe.tutorialmod.networking.custom.SyncPedestalBlockEntityS2CPayload; + +public class ModPayloads { + public static void registerModPayloads() { + PayloadTypeRegistry.playS2C().register(SyncPedestalBlockEntityS2CPayload.ID, SyncPedestalBlockEntityS2CPayload.CODEC); + TutorialMod.LOGGER.info("Registering Mod Payloads for " + TutorialMod.MOD_ID); + } +} diff --git a/src/main/java/net/kaupenjoe/tutorialmod/networking/custom/SyncPedestalBlockEntityS2CPayload.java b/src/main/java/net/kaupenjoe/tutorialmod/networking/custom/SyncPedestalBlockEntityS2CPayload.java new file mode 100644 index 0000000..af0dd8f --- /dev/null +++ b/src/main/java/net/kaupenjoe/tutorialmod/networking/custom/SyncPedestalBlockEntityS2CPayload.java @@ -0,0 +1,56 @@ +package net.kaupenjoe.tutorialmod.networking.custom; + +import net.kaupenjoe.tutorialmod.TutorialMod; +import net.minecraft.item.ItemStack; +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.network.codec.PacketCodec; +import net.minecraft.network.packet.CustomPayload; +import net.minecraft.util.Identifier; +import net.minecraft.util.collection.DefaultedList; +import net.minecraft.util.math.BlockPos; + +public record SyncPedestalBlockEntityS2CPayload(BlockPos blockPos, DefaultedList inventory) implements CustomPayload { + public static final Identifier SYNC_PEDESTAL_BLOCK_ENTITY_PAYLOAD_ID = Identifier.of(TutorialMod.MOD_ID, "sync_pedestal_block_entity"); + public static final CustomPayload.Id ID = new CustomPayload.Id<>(SYNC_PEDESTAL_BLOCK_ENTITY_PAYLOAD_ID); + + // Define a PacketCodec for DefaultedList + public static final PacketCodec> ITEM_STACK_LIST_CODEC = new PacketCodec<>() { + @Override + public DefaultedList decode(RegistryByteBuf buf) { + // Read the size of the list + int size = buf.readVarInt(); + // Create a DefaultedList with the specified size + DefaultedList list = DefaultedList.ofSize(size, ItemStack.EMPTY); + // Read each ItemStack from the buffer + for (int i = 0; i < size; i++) { + // Decode the ItemStack, allowing empty stacks + ItemStack stack = ItemStack.OPTIONAL_PACKET_CODEC.decode(buf); + list.set(i, stack); + } + return list; + } + + @Override + public void encode(RegistryByteBuf buf, DefaultedList list) { + // Write the size of the list + buf.writeVarInt(list.size()); + // Write each ItemStack to the buffer + for (ItemStack stack : list) { + // Encode the ItemStack, allowing empty stacks + ItemStack.OPTIONAL_PACKET_CODEC.encode(buf, stack); + } + } + }; + + // Define the PacketCodec for SyncPedestalBlockEntityS2CPayload + public static final PacketCodec CODEC = PacketCodec.tuple( + BlockPos.PACKET_CODEC, SyncPedestalBlockEntityS2CPayload::blockPos, // Handle BlockPos + ITEM_STACK_LIST_CODEC, SyncPedestalBlockEntityS2CPayload::inventory, // Handle DefaultedList + SyncPedestalBlockEntityS2CPayload::new // Constructor + ); + + @Override + public Id getId() { + return ID; + } +}