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/).
+
+---
+
-# 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/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..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",
@@ -23,6 +24,14 @@
],
"client": [
"net.kaupenjoe.tutorialmod.TutorialModClient"
+ ],
+ "cardinal-components": [
+ "net.kaupenjoe.tutorialmod.cca.TutorialModCCAComponents"
+ ]
+ },
+ "custom": {
+ "cardinal-components": [
+ "tutorialmod:synced_pedestal"
]
},
"mixins": [
@@ -32,7 +41,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": "*"