From 4f5cacc3d613b44db5d59c1f02727adc46b03a58 Mon Sep 17 00:00:00 2001 From: Netcrafts Date: Mon, 6 Apr 2026 00:44:26 +0200 Subject: [PATCH] feat: add item tracking for Chiseled Bookshelf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds item-insert and item-remove logging for ChiseledBookShelfBlock. Players inserting or removing books from a Chiseled Bookshelf are now tracked, with full rollback and restore support. Modelled on the existing ShelfBlockMixin pattern: - Inject into addBook() after setItem() fires ItemInsertCallback - ModifyExpressionValue on removeBook() to capture the removed stack and fire ItemRemoveCallback ChiseledBookShelfBlockEntity implements ListBackedContainer extends Container, so rollback resolves through the existing getInventory() path in ItemChangeActionType with no additional changes required. Note: preview rollback has no visual effect on Chiseled Bookshelf — this is a pre-existing limitation of ServerPlayerEntityMixin which intercepts ClientboundContainerSetContentPacket, a packet not sent for blocks using block-state slot properties. The same limitation exists for ShelfBlock. Actual rollback and restore work correctly. --- .../blocks/ChiseledBookShelfBlockMixin.java | 73 +++++++++++++++++++ src/main/resources/ledger.mixins.json | 1 + 2 files changed, 74 insertions(+) create mode 100644 src/main/java/com/github/quiltservertools/ledger/mixin/blocks/ChiseledBookShelfBlockMixin.java diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/ChiseledBookShelfBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/ChiseledBookShelfBlockMixin.java new file mode 100644 index 00000000..d7bfb711 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/ChiseledBookShelfBlockMixin.java @@ -0,0 +1,73 @@ +package com.github.quiltservertools.ledger.mixin.blocks; + +import com.github.quiltservertools.ledger.callbacks.ItemInsertCallback; +import com.github.quiltservertools.ledger.callbacks.ItemRemoveCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.ChiseledBookShelfBlock; +import net.minecraft.world.level.block.entity.ChiseledBookShelfBlockEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ChiseledBookShelfBlock.class) +public class ChiseledBookShelfBlockMixin { + + /** + * Fires ItemInsertCallback after a book is successfully placed into a chiseled bookshelf slot. + * Hooks into addBook(), which only runs server-side, after setItem() stores the book. + */ + @Inject( + method = "addBook", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity;setItem(ILnet/minecraft/world/item/ItemStack;)V", + shift = At.Shift.AFTER + ) + ) + private static void onAddBook( + Level level, + BlockPos pos, + Player player, + ChiseledBookShelfBlockEntity bookshelfBlock, + ItemStack itemStack, + int slot, + CallbackInfo ci + ) { + ItemStack insertedBook = bookshelfBlock.getItem(slot); + if (!insertedBook.isEmpty() && level instanceof ServerLevel serverLevel) { + ItemInsertCallback.EVENT.invoker().insert(insertedBook, pos, serverLevel, Sources.PLAYER, player); + } + } + + /** + * Fires ItemRemoveCallback when a book is removed from a chiseled bookshelf slot. + * Hooks into removeBook() to capture the removed ItemStack returned by removeItem(). + */ + @ModifyExpressionValue( + method = "removeBook", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity;removeItem(II)Lnet/minecraft/world/item/ItemStack;" + ) + ) + private static ItemStack onRemoveBook( + ItemStack removedStack, + Level level, + BlockPos pos, + Player player, + ChiseledBookShelfBlockEntity bookshelfBlock, + int slot + ) { + if (!removedStack.isEmpty() && level instanceof ServerLevel serverLevel) { + ItemRemoveCallback.EVENT.invoker().remove(removedStack, pos, serverLevel, Sources.PLAYER, player); + } + return removedStack; + } +} diff --git a/src/main/resources/ledger.mixins.json b/src/main/resources/ledger.mixins.json index 428a08b4..03232c9f 100644 --- a/src/main/resources/ledger.mixins.json +++ b/src/main/resources/ledger.mixins.json @@ -40,6 +40,7 @@ "blocks.CampfireBlockMixin", "blocks.CandleBlockMixin", "blocks.CarvedPumpkinBlockMixin", + "blocks.ChiseledBookShelfBlockMixin", "blocks.ChorusFlowerBlockMixin", "blocks.ChorusPlantBlockMixin", "blocks.ComparatorBlockMixin",