From 8b432e4443960da6724a13398cf20d0657d461b6 Mon Sep 17 00:00:00 2001 From: JR1811 Date: Fri, 1 Aug 2025 17:28:55 +0200 Subject: [PATCH 1/2] added Cardinal Components API for PedestalBlock --- build.gradle | 17 ++-- gradle.properties | 5 +- .../block/custom/PedestalBlock.java | 39 ++++---- .../entity/custom/PedestalBlockEntity.java | 88 +++++++++++++------ .../renderer/PedestalBlockEntityRenderer.java | 13 +-- .../cca/TutorialModCCAComponents.java | 19 ++++ .../component/SyncedPedestalComponent.java | 43 +++++++++ .../SyncedPedestalComponentImpl.java | 85 ++++++++++++++++++ src/main/resources/fabric.mod.json | 12 ++- 9 files changed, 263 insertions(+), 58 deletions(-) create mode 100644 src/main/java/net/kaupenjoe/tutorialmod/cca/TutorialModCCAComponents.java create mode 100644 src/main/java/net/kaupenjoe/tutorialmod/cca/component/SyncedPedestalComponent.java create mode 100644 src/main/java/net/kaupenjoe/tutorialmod/cca/implementation/SyncedPedestalComponentImpl.java diff --git a/build.gradle b/build.gradle index 344b827..589ab90 100644 --- a/build.gradle +++ b/build.gradle @@ -11,11 +11,11 @@ base { } repositories { - // Add repositories to retrieve artifacts from in here. - // You should only use this when depending on other mods because - // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. - // See https://docs.gradle.org/current/userguide/declaring_repositories.html - // for more information about repositories. + // Cardinal Components API (CCA) + maven { + name = "Ladysnake Mods" + url = 'https://maven.ladysnake.org/releases' + } } fabricApi { @@ -30,7 +30,12 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" - + + // Cardinal Components API (CCA) + // for even more providers, check out the wiki: https://ladysnake.org/wiki/cardinal-components-api/ + modImplementation "org.ladysnake.cardinal-components-api:cardinal-components-base:${project.cca_version}" + modImplementation "org.ladysnake.cardinal-components-api:cardinal-components-block:${project.cca_version}" + // to automatically include the api in the mod jar, check out the wiki too... } processResources { diff --git a/gradle.properties b/gradle.properties index 1dd9636..cb00b76 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,4 +14,7 @@ maven_group=net.kaupenjoe.tutorialmod archives_base_name=tutorialmod # Fabric API -fabric_version=0.103.0+1.21.1 \ No newline at end of file +fabric_version=0.103.0+1.21.1 + +# Dependencies +cca_version = 6.1.2 \ No newline at end of file diff --git a/src/main/java/net/kaupenjoe/tutorialmod/block/custom/PedestalBlock.java b/src/main/java/net/kaupenjoe/tutorialmod/block/custom/PedestalBlock.java index accdc7b..3bea9e0 100644 --- a/src/main/java/net/kaupenjoe/tutorialmod/block/custom/PedestalBlock.java +++ b/src/main/java/net/kaupenjoe/tutorialmod/block/custom/PedestalBlock.java @@ -50,9 +50,9 @@ protected BlockRenderType getRenderType(BlockState state) { @Override protected void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { - if(state.getBlock() != newState.getBlock()) { + if (state.getBlock() != newState.getBlock()) { BlockEntity blockEntity = world.getBlockEntity(pos); - if(blockEntity instanceof PedestalBlockEntity) { + if (blockEntity instanceof PedestalBlockEntity) { ItemScatterer.spawn(world, pos, ((PedestalBlockEntity) blockEntity)); world.updateComparators(pos, this); } @@ -63,25 +63,26 @@ protected void onStateReplaced(BlockState state, World world, BlockPos pos, Bloc @Override protected ItemActionResult onUseWithItem(ItemStack stack, BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { - if(world.getBlockEntity(pos) instanceof PedestalBlockEntity pedestalBlockEntity) { - if(pedestalBlockEntity.isEmpty() && !stack.isEmpty()) { - pedestalBlockEntity.setStack(0, stack.copyWithCount(1)); - world.playSound(player, pos, SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.BLOCKS, 1f, 2f); - stack.decrement(1); - - pedestalBlockEntity.markDirty(); - world.updateListeners(pos, state, state, 0); - } else if(stack.isEmpty() && !player.isSneaking()) { - ItemStack stackOnPedestal = pedestalBlockEntity.getStack(0); - player.setStackInHand(Hand.MAIN_HAND, stackOnPedestal); - world.playSound(player, pos, SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.BLOCKS, 1f, 1f); - pedestalBlockEntity.clear(); + if (!(world.getBlockEntity(pos) instanceof PedestalBlockEntity blockEntity)) { + return super.onUseWithItem(stack, state, world, pos, player, hand, hit); + } + if (blockEntity.isEmpty() && !stack.isEmpty()) { + blockEntity.setStack(0, stack.copyWithCount(1)); + world.playSound(player, pos, SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.BLOCKS, 1f, 2f); + stack.decrement(1); - pedestalBlockEntity.markDirty(); - world.updateListeners(pos, state, state, 0); - } + world.updateListeners(pos, state, state, 0); + return ItemActionResult.SUCCESS; } + if (blockEntity.getSyncedComponent().hasContent() && stack.isEmpty() && !player.isSneaking()) { + ItemStack stackOnPedestal = blockEntity.getStack(0); + player.setStackInHand(Hand.MAIN_HAND, stackOnPedestal); + world.playSound(player, pos, SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.BLOCKS, 1f, 1f); + blockEntity.clear(); - return ItemActionResult.SUCCESS; + world.updateListeners(pos, state, state, 0); + return ItemActionResult.SUCCESS; + } + return ItemActionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; } } 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 24d3dd7..c8c81f8 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,61 +1,97 @@ package net.kaupenjoe.tutorialmod.block.entity.custom; -import net.kaupenjoe.tutorialmod.block.entity.ImplementedInventory; import net.kaupenjoe.tutorialmod.block.entity.ModBlockEntities; +import net.kaupenjoe.tutorialmod.cca.component.SyncedPedestalComponent; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; -import net.minecraft.inventory.Inventories; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.SidedInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; -import net.minecraft.network.listener.ClientPlayPacketListener; -import net.minecraft.network.packet.Packet; -import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket; import net.minecraft.registry.RegistryWrapper; import net.minecraft.util.collection.DefaultedList; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; import org.jetbrains.annotations.Nullable; -public class PedestalBlockEntity extends BlockEntity implements ImplementedInventory { - private final DefaultedList inventory = DefaultedList.ofSize(1, ItemStack.EMPTY); - private float rotation = 0; +public class PedestalBlockEntity extends BlockEntity implements SidedInventory { + private final SyncedPedestalComponent syncedComponent; public PedestalBlockEntity(BlockPos pos, BlockState state) { super(ModBlockEntities.PEDESTAL_BE, pos, state); + this.syncedComponent = SyncedPedestalComponent.get(this); } - @Override - public DefaultedList getItems() { - return inventory; - } - - public float getRenderingRotation() { - rotation += 0.5f; - if(rotation >= 360) { - rotation = 0; - } - return rotation; + public SyncedPedestalComponent getSyncedComponent() { + return syncedComponent; } @Override protected void writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { super.writeNbt(nbt, registryLookup); - Inventories.writeNbt(nbt, inventory, registryLookup); } @Override protected void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { super.readNbt(nbt, registryLookup); - Inventories.readNbt(nbt, inventory, registryLookup); } - @Nullable @Override - public Packet toUpdatePacket() { - return BlockEntityUpdateS2CPacket.create(this); + public int[] getAvailableSlots(Direction side) { + int[] result = new int[syncedComponent.getInventory().size()]; + for (int i = 0; i < result.length; i++) { + result[i] = i; + } + return result; + } + + @Override + public boolean canInsert(int slot, ItemStack stack, @Nullable Direction dir) { + return dir == null || dir.equals(Direction.UP); + } + + @Override + public boolean canExtract(int slot, ItemStack stack, Direction dir) { + return dir == null || dir.equals(Direction.DOWN); + } + + @Override + public int size() { + return syncedComponent.getInventory().size(); + } + + @Override + public boolean isEmpty() { + return !syncedComponent.hasContent(); + } + + @Override + public ItemStack getStack(int slot) { + return syncedComponent.getInventory().get(slot); + } + + @Override + public ItemStack removeStack(int slot, int amount) { + return this.syncedComponent.remove(slot, amount, true); + } + + @Override + public ItemStack removeStack(int slot) { + return this.syncedComponent.remove(slot, null, true); + } + + @Override + public void setStack(int slot, ItemStack stack) { + this.syncedComponent.modifyInventory(itemStacks -> itemStacks.set(slot, stack), true); + } + + @Override + public boolean canPlayerUse(PlayerEntity player) { + return true; } @Override - public NbtCompound toInitialChunkDataNbt(RegistryWrapper.WrapperLookup registryLookup) { - return createNbt(registryLookup); + public void clear() { + this.syncedComponent.modifyInventory(DefaultedList::clear, true); } } diff --git a/src/main/java/net/kaupenjoe/tutorialmod/block/entity/renderer/PedestalBlockEntityRenderer.java b/src/main/java/net/kaupenjoe/tutorialmod/block/entity/renderer/PedestalBlockEntityRenderer.java index 8d7acab..0159ec8 100644 --- a/src/main/java/net/kaupenjoe/tutorialmod/block/entity/renderer/PedestalBlockEntityRenderer.java +++ b/src/main/java/net/kaupenjoe/tutorialmod/block/entity/renderer/PedestalBlockEntityRenderer.java @@ -10,6 +10,7 @@ import net.minecraft.client.render.item.ItemRenderer; import net.minecraft.client.render.model.json.ModelTransformationMode; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.client.world.ClientWorld; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RotationAxis; @@ -17,23 +18,25 @@ import net.minecraft.world.World; public class PedestalBlockEntityRenderer implements BlockEntityRenderer { + @SuppressWarnings("unused") public PedestalBlockEntityRenderer(BlockEntityRendererFactory.Context context) { } @Override - public void render(PedestalBlockEntity entity, float tickDelta, MatrixStack matrices, + public void render(PedestalBlockEntity blockEntity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) { + if (!(blockEntity.getWorld() instanceof ClientWorld clientWorld)) return; ItemRenderer itemRenderer = MinecraftClient.getInstance().getItemRenderer(); - ItemStack stack = entity.getStack(0); + ItemStack stack = blockEntity.getStack(0); matrices.push(); matrices.translate(0.5f, 1.15f, 0.5f); matrices.scale(0.5f, 0.5f, 0.5f); - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(entity.getRenderingRotation())); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(blockEntity.getSyncedComponent().getRenderingRotation(tickDelta))); - itemRenderer.renderItem(stack, ModelTransformationMode.GUI, getLightLevel(entity.getWorld(), - entity.getPos()), OverlayTexture.DEFAULT_UV, matrices, vertexConsumers, entity.getWorld(), 1); + itemRenderer.renderItem(stack, ModelTransformationMode.GUI, getLightLevel(clientWorld, + blockEntity.getPos()), OverlayTexture.DEFAULT_UV, matrices, vertexConsumers, blockEntity.getWorld(), 1); matrices.pop(); } diff --git a/src/main/java/net/kaupenjoe/tutorialmod/cca/TutorialModCCAComponents.java b/src/main/java/net/kaupenjoe/tutorialmod/cca/TutorialModCCAComponents.java new file mode 100644 index 0000000..40d195a --- /dev/null +++ b/src/main/java/net/kaupenjoe/tutorialmod/cca/TutorialModCCAComponents.java @@ -0,0 +1,19 @@ +package net.kaupenjoe.tutorialmod.cca; + +import net.kaupenjoe.tutorialmod.block.entity.custom.PedestalBlockEntity; +import net.kaupenjoe.tutorialmod.cca.component.SyncedPedestalComponent; +import net.kaupenjoe.tutorialmod.cca.implementation.SyncedPedestalComponentImpl; +import org.ladysnake.cca.api.v3.block.BlockComponentFactoryRegistry; +import org.ladysnake.cca.api.v3.block.BlockComponentInitializer; +import org.ladysnake.cca.api.v3.component.ComponentKey; +import org.ladysnake.cca.api.v3.component.ComponentRegistry; + +public class TutorialModCCAComponents implements BlockComponentInitializer { + public static final ComponentKey SYNCED_BLOCK_INVENTORY = + ComponentRegistry.getOrCreate(SyncedPedestalComponent.IDENTIFIER, SyncedPedestalComponent.class); + + @Override + public void registerBlockComponentFactories(BlockComponentFactoryRegistry registry) { + registry.registerFor(PedestalBlockEntity.class, SYNCED_BLOCK_INVENTORY, SyncedPedestalComponentImpl::new); + } +} diff --git a/src/main/java/net/kaupenjoe/tutorialmod/cca/component/SyncedPedestalComponent.java b/src/main/java/net/kaupenjoe/tutorialmod/cca/component/SyncedPedestalComponent.java new file mode 100644 index 0000000..9d44c60 --- /dev/null +++ b/src/main/java/net/kaupenjoe/tutorialmod/cca/component/SyncedPedestalComponent.java @@ -0,0 +1,43 @@ +package net.kaupenjoe.tutorialmod.cca.component; + +import net.kaupenjoe.tutorialmod.TutorialMod; +import net.kaupenjoe.tutorialmod.block.entity.custom.PedestalBlockEntity; +import net.kaupenjoe.tutorialmod.cca.TutorialModCCAComponents; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.collection.DefaultedList; +import org.jetbrains.annotations.Nullable; +import org.ladysnake.cca.api.v3.component.Component; +import org.ladysnake.cca.api.v3.component.tick.ClientTickingComponent; + +import java.util.function.Consumer; + +public interface SyncedPedestalComponent extends Component, ClientTickingComponent { + Identifier IDENTIFIER = Identifier.of(TutorialMod.MOD_ID, "synced_pedestal"); + + static SyncedPedestalComponent get(PedestalBlockEntity blockEntity) { + return TutorialModCCAComponents.SYNCED_BLOCK_INVENTORY.get(blockEntity); + } + + /** + * For proper Inventory modification, use {@link #modifyInventory(Consumer, boolean)} + */ + DefaultedList getInventory(); + + void modifyInventory(Consumer> consumer, boolean shouldSync); + + ItemStack remove(int slot, @Nullable Integer amount, boolean shouldSync); + + default boolean hasContent() { + for (ItemStack stack : getInventory()) { + if (!stack.isEmpty()) { + return true; + } + } + return false; + } + + float getRenderingRotation(float tickDelta); + + void sync(); +} diff --git a/src/main/java/net/kaupenjoe/tutorialmod/cca/implementation/SyncedPedestalComponentImpl.java b/src/main/java/net/kaupenjoe/tutorialmod/cca/implementation/SyncedPedestalComponentImpl.java new file mode 100644 index 0000000..cce3632 --- /dev/null +++ b/src/main/java/net/kaupenjoe/tutorialmod/cca/implementation/SyncedPedestalComponentImpl.java @@ -0,0 +1,85 @@ +package net.kaupenjoe.tutorialmod.cca.implementation; + +import net.kaupenjoe.tutorialmod.block.entity.custom.PedestalBlockEntity; +import net.kaupenjoe.tutorialmod.cca.TutorialModCCAComponents; +import net.kaupenjoe.tutorialmod.cca.component.SyncedPedestalComponent; +import net.minecraft.inventory.Inventories; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.registry.RegistryWrapper; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.collection.DefaultedList; +import org.jetbrains.annotations.Nullable; +import org.ladysnake.cca.api.v3.component.sync.AutoSyncedComponent; + +import java.util.function.Consumer; + +public class SyncedPedestalComponentImpl implements SyncedPedestalComponent, AutoSyncedComponent { + private final PedestalBlockEntity provider; + + private final DefaultedList inventory = DefaultedList.ofSize(1, ItemStack.EMPTY); + private float renderingRotation; + + + public SyncedPedestalComponentImpl(PedestalBlockEntity provider) { + this.provider = provider; + this.renderingRotation = 0; + } + + @Override + public DefaultedList getInventory() { + return inventory; + } + + @Override + public void modifyInventory(Consumer> consumer, boolean shouldSync) { + consumer.accept(this.inventory); + if (shouldSync) { + this.sync(); + } + } + + @Override + public ItemStack remove(int slot, @Nullable Integer amount, boolean shouldSync) { + ItemStack result; + if (amount == null) { + result = Inventories.removeStack(this.inventory, slot); + } else { + result = Inventories.splitStack(this.inventory, slot, amount); + } + if (shouldSync) { + this.sync(); + } + return result; + } + + @Override + public float getRenderingRotation(float tickDelta) { + return this.renderingRotation + tickDelta; + } + + @Override + public void clientTick() { + this.renderingRotation += 0.5f; + if (this.renderingRotation >= 360) { + this.renderingRotation = 0; + } + } + + @Override + public void sync() { + if (!(this.provider.getWorld() instanceof ServerWorld)) return; + TutorialModCCAComponents.SYNCED_BLOCK_INVENTORY.sync(this.provider); + } + + @Override + public void readFromNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { + this.inventory.clear(); + Inventories.readNbt(nbt, this.inventory, registryLookup); + } + + @Override + public void writeToNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { + Inventories.writeNbt(nbt, this.inventory, registryLookup); + } +} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 1d702d7..1768866 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -23,6 +23,14 @@ ], "client": [ "net.kaupenjoe.tutorialmod.TutorialModClient" + ], + "cardinal-components": [ + "net.kaupenjoe.tutorialmod.cca.TutorialModCCAComponents" + ] + }, + "custom": { + "cardinal-components": [ + "tutorialmod:synced_pedestal" ] }, "mixins": [ @@ -32,7 +40,9 @@ "fabricloader": ">=0.15.11", "minecraft": "~1.21", "java": ">=21", - "fabric-api": "*" + "fabric-api": "*", + "cardinal-components-base": "^6.1.2", + "cardinal-components-block": "^6.1.2" }, "suggests": { "another-mod": "*" From 55974c9dfbf2b7e7d4642913d6947e61aa223e33 Mon Sep 17 00:00:00 2001 From: JR1811 Date: Fri, 1 Aug 2025 17:41:47 +0200 Subject: [PATCH 2/2] added contribution and added branch explanation to README.md --- README.md | 27 ++++++++++++++++++++++++--- src/main/resources/fabric.mod.json | 3 ++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cea8ee2..10b5090 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,32 @@ +# Branch Information + +This branch specifically tackles the PedestalBlockRenderer desync issues and its boilerplate by making +use of [Cardinal Components API](https://ladysnake.org/wiki/cardinal-components-api/). + +The [Implemented Inventory Interface](./src/main/java/net/kaupenjoe/tutorialmod/block/entity/ImplementedInventory.java) +has been switched out with a direct `Sided Inventory` implementation in +the [BlockEntity](./src/main/java/net/kaupenjoe/tutorialmod/block/entity/custom/PedestalBlockEntity.java) to support +component syncing. If necessary, this can get cleaned up more. + +The [Pedestal Component](./src/main/java/net/kaupenjoe/tutorialmod/cca/component/SyncedPedestalComponent.java) also +takes +care of the rotation for the client side rendering now. + +For more information check out [Cardinal Component API's wiki](https://ladysnake.org/wiki/cardinal-components-api/). + +--- +

Logo

-# Fabric Modding Tutorials For Minecraft 1.21.X +# Fabric Modding Tutorials For Minecraft 1.21.X + This is the GitHub Repository for Kaupenjoe's Fabric Modding Tutorials For Minecraft 1.21.X -The Individual Tutorials are seperated into Branches for ease of access. +The Individual Tutorials are seperated into Branches for ease of access. -Watch the Tutorials here: YouTube Playlist +Watch the Tutorials +here: YouTube +Playlist diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 1768866..b79774e 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -5,7 +5,8 @@ "name": "Tutorial Mod", "description": "Tutorial Mod by Kaupenjoe for YouTube", "authors": [ - "Kaupenjoe!" + "Kaupenjoe!", + "ShiroJR (basic CCA implementation)" ], "contact": { "homepage": "https://kaupenjoe.net",