diff --git a/src/main/java/io/fabricatedatelier/mayor/block/AbstractVillageContainerBlock.java b/src/main/java/io/fabricatedatelier/mayor/block/AbstractVillageContainerBlock.java index cb5492f..7b9c2ac 100644 --- a/src/main/java/io/fabricatedatelier/mayor/block/AbstractVillageContainerBlock.java +++ b/src/main/java/io/fabricatedatelier/mayor/block/AbstractVillageContainerBlock.java @@ -2,6 +2,7 @@ import com.mojang.serialization.MapCodec; import io.fabricatedatelier.mayor.util.ConnectedBlockUtil; +import io.fabricatedatelier.mayor.util.HandledInventory; import net.minecraft.block.Block; import net.minecraft.block.BlockRenderType; import net.minecraft.block.BlockState; @@ -19,6 +20,7 @@ import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.ItemActionResult; +import net.minecraft.util.ItemScatterer; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; @@ -56,7 +58,7 @@ protected ActionResult onUse(BlockState state, World world, BlockPos pos, Player BlockPos originPos = getOrigin(world, pos).orElse(pos); if (world.getBlockEntity(originPos) instanceof AbstractVillageContainerBlockEntity blockEntity && !world.isClient()) { // extract - Optional removedStack = blockEntity.extract(hit.getSide()); + Optional removedStack = blockEntity.extractFromOrigin(hit.getSide()); if (removedStack.isPresent() && !removedStack.get().isEmpty()) { player.getInventory().offerOrDrop(removedStack.get()); return ActionResult.SUCCESS; @@ -68,9 +70,15 @@ protected ActionResult onUse(BlockState state, World world, BlockPos pos, Player @Override protected ItemActionResult onUseWithItem(ItemStack stack, BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { BlockPos originPos = getOrigin(world, pos).orElse(pos); - if (world.getBlockEntity(originPos) instanceof AbstractVillageContainerBlockEntity blockEntity && !world.isClient()) { - // inset - if (blockEntity.insert(player.getStackInHand(hand).copyAndEmpty(), hit.getSide())) { + + if (world.isClient()) + return super.onUseWithItem(stack, state, world, pos, player, hand, hit); + if (!(world.getBlockEntity(originPos) instanceof AbstractVillageContainerBlockEntity blockEntity)) + return super.onUseWithItem(stack, state, world, pos, player, hand, hit); + + if (blockEntity.canInsert(player.getStackInHand(hand).copy(), hit.getSide())) { + if (blockEntity.insertIntoOrigin(player.getStackInHand(hand).copy(), hit.getSide())) { + player.getStackInHand(hand).decrementUnlessCreative(player.getStackInHand(hand).getCount(), player); return ItemActionResult.SUCCESS; } } @@ -91,13 +99,26 @@ public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable Livi if (!box.hasHoles() && box.isSquare()) { state = state.with(POSITION, getPositionFromConnectedWalls(world, pos)); world.setBlockState(pos, state); - if (world.getBlockEntity(pos) instanceof AbstractVillageContainerBlockEntity blockEntity) { // TODO: only if origin + if (world.getBlockEntity(pos) instanceof AbstractVillageContainerBlockEntity blockEntity) { box.getConnectedPosList().forEach(blockEntity::addConnectedBlocks); } } + if (world.getBlockEntity(pos) instanceof AbstractVillageContainerBlockEntity blockEntity) { + blockEntity.setStructureOriginPos(getOrigin(world, pos).orElse(pos)); + } super.onPlaced(world, pos, state, placer, itemStack); } + @Override + public BlockState onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player) { + if (!world.isClient()) { + if (world.getBlockEntity(pos) instanceof HandledInventory inventory && !inventory.isEmpty()) { + ItemScatterer.spawn(world, pos, inventory); + } + } + return super.onBreak(world, pos, state, player); + } + @Override public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) { if (state.get(Properties.WATERLOGGED)) { @@ -110,6 +131,9 @@ public BlockState getStateForNeighborUpdate(BlockState state, Direction directio } else { state = state.with(POSITION, MayorProperties.Position.SINGLE); } + if (world.getBlockEntity(pos) instanceof AbstractVillageContainerBlockEntity blockEntity) { + blockEntity.setStructureOriginPos(originPos); + } return state; } diff --git a/src/main/java/io/fabricatedatelier/mayor/block/AbstractVillageContainerBlockEntity.java b/src/main/java/io/fabricatedatelier/mayor/block/AbstractVillageContainerBlockEntity.java index a49eaf6..7c839c1 100644 --- a/src/main/java/io/fabricatedatelier/mayor/block/AbstractVillageContainerBlockEntity.java +++ b/src/main/java/io/fabricatedatelier/mayor/block/AbstractVillageContainerBlockEntity.java @@ -1,12 +1,13 @@ package io.fabricatedatelier.mayor.block; -import io.fabricatedatelier.mayor.util.AbstractStorageCallback; import io.fabricatedatelier.mayor.util.HandledInventory; import io.fabricatedatelier.mayor.util.NbtKeys; +import io.fabricatedatelier.mayor.util.StorageCallback; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntityType; import net.minecraft.inventory.Inventories; +import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.network.listener.ClientPlayPacketListener; import net.minecraft.network.packet.Packet; @@ -14,7 +15,7 @@ import net.minecraft.registry.RegistryWrapper; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; +import net.minecraft.util.math.Direction; import org.jetbrains.annotations.Nullable; import java.util.HashSet; @@ -24,45 +25,44 @@ public abstract class AbstractVillageContainerBlockEntity extends BlockEntity implements HandledInventory { private BlockPos structureOriginPos; private final HashSet connectedBlocks = new HashSet<>(); - private AbstractStorageCallback callback; + private StorageCallback callback = null; public AbstractVillageContainerBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { super(type, pos, state); } - public void registerCallback(AbstractStorageCallback callback) { + /** + * If you want to listen to the {@link AbstractVillageContainerBlockEntity StorageBlockEntity} + * {@link StorageCallback callbacks}, you will need to register the callback first.
+ * In the class, where you want to listen to those signals, implement the {@link StorageCallback} interface.
+ * Then wherever you get access to the current {@link AbstractVillageContainerBlockEntity StorageBlockEntity}, + * call its {@link AbstractVillageContainerBlockEntity#registerCallback(StorageCallback) registerCallback()} method + * with your current class's instance as the parameter (usually just a this call) + * + * @param callback Your current class, which implements the {@link StorageCallback} interface + */ + public void registerCallback(StorageCallback callback) { this.callback = callback; } @Override - protected void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { - super.readNbt(nbt, registryLookup); - Inventories.readNbt(nbt, this.getItems(), registryLookup); - if (nbt.contains(NbtKeys.BLOCK_ENTITY_ORIGIN_POS)) { - this.setStructureOriginPos(BlockPos.fromLong(nbt.getLong(NbtKeys.BLOCK_ENTITY_ORIGIN_POS))); - } + public boolean canInsert(int slot, ItemStack stack, @Nullable Direction dir) { + if (!this.isStructureOrigin()) return false; + return HandledInventory.super.canInsert(slot, stack, dir); + } - this.connectedBlocks.clear(); - NbtCompound blockPosListNbt = nbt.getCompound(NbtKeys.CONNECTED_BLOCKS); - for (String index : blockPosListNbt.getKeys()) { - BlockPos connectedPos = BlockPos.fromLong(blockPosListNbt.getLong(index)); - this.connectedBlocks.add(connectedPos); - } + /** + * Use this method if you are not concerned about a specific slot. + * It will test for the condition on the first stack in the inventory. + */ + public boolean canInsert(ItemStack stack, @Nullable Direction direction) { + return canInsert(0, stack, direction); } @Override - protected void writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { - super.writeNbt(nbt, registryLookup); - Inventories.writeNbt(nbt, this.getItems(), registryLookup); - this.getStructureOriginPos().ifPresent(originPos -> nbt.putLong(NbtKeys.BLOCK_ENTITY_ORIGIN_POS, originPos.asLong())); - - List blockPosList = connectedBlocks.stream().toList(); - NbtCompound blockPosListNbt = new NbtCompound(); - for (int i = 0; i < blockPosList.size(); i++) { - BlockPos connectedPos = blockPosList.get(i); - blockPosListNbt.putLong(String.valueOf(i), connectedPos.asLong()); - } - nbt.put(NbtKeys.CONNECTED_BLOCKS, blockPosListNbt); + public boolean canExtract(int slot, ItemStack stack, Direction dir) { + if (!this.isStructureOrigin()) return false; + return HandledInventory.super.canExtract(slot, stack, dir); } public boolean isStructureOrigin() { @@ -74,11 +74,33 @@ public Optional getStructureOriginPos() { } public void setStructureOriginPos(BlockPos newStructureOriginPos) { - callback.onOriginChanged(this, new BlockPos(this.structureOriginPos), new BlockPos(newStructureOriginPos)); + if (callback != null) { + callback.onOriginChanged(this, new BlockPos(this.structureOriginPos), new BlockPos(newStructureOriginPos)); + } this.structureOriginPos = newStructureOriginPos; markDirty(); } + public boolean insertIntoOrigin(ItemStack stack, @Nullable Direction direction) { + if (this.getWorld() == null || this.getWorld().isClient()) return false; + if (this.getStructureOriginPos().isEmpty()) return false; + if (!(this.getWorld().getBlockEntity(this.getStructureOriginPos().get()) instanceof AbstractVillageContainerBlockEntity blockEntity)) + return false; + boolean inserted = blockEntity.insert(stack, direction); + if (inserted) blockEntity.markDirty(); + return inserted; + } + + public Optional extractFromOrigin(@Nullable Direction direction) { + if (this.getWorld() == null || this.getWorld().isClient()) return Optional.empty(); + if (this.getStructureOriginPos().isEmpty()) return Optional.empty(); + if (!(this.getWorld().getBlockEntity(this.getStructureOriginPos().get()) instanceof AbstractVillageContainerBlockEntity blockEntity)) + return Optional.empty(); + Optional extractedStack = blockEntity.extract(direction); + if (extractedStack.isPresent()) blockEntity.markDirty(); + return extractedStack; + } + public HashSet getConnectedBlocks() { return this.connectedBlocks; } @@ -88,11 +110,13 @@ public void addConnectedBlocks(BlockPos... pos) { if (entry.equals(this.pos)) continue; this.connectedBlocks.add(entry); } - callback.onConnectedBlocksChanged(this); + if (callback != null) { + callback.onConnectedBlocksChanged(this); + } markDirty(); } -// Network +// --- Network & Data --- @Override public void markDirty() { @@ -113,26 +137,34 @@ public Packet toUpdatePacket() { return BlockEntityUpdateS2CPacket.create(this); } + @Override + protected void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { + super.readNbt(nbt, registryLookup); + Inventories.readNbt(nbt, this.getItems(), registryLookup); + if (nbt.contains(NbtKeys.BLOCK_ENTITY_ORIGIN_POS)) { + this.setStructureOriginPos(BlockPos.fromLong(nbt.getLong(NbtKeys.BLOCK_ENTITY_ORIGIN_POS))); + } -// Util - - public StructureDimensions getMaxStructureDimensions() { - return new StructureDimensions(3); - } - - public static Optional> getConnectedBlocksFromOrigin(AbstractVillageContainerBlockEntity blockEntity) { - World world = blockEntity.getWorld(); - if (world == null) return Optional.empty(); - if (blockEntity.getStructureOriginPos().isEmpty()) return Optional.empty(); - if (!(world.getBlockEntity(blockEntity.getStructureOriginPos().get()) instanceof AbstractVillageContainerBlockEntity originBlockEntity)) - return Optional.empty(); - return Optional.ofNullable(originBlockEntity.getConnectedBlocks()); + this.connectedBlocks.clear(); + NbtCompound blockPosListNbt = nbt.getCompound(NbtKeys.CONNECTED_BLOCKS); + for (String index : blockPosListNbt.getKeys()) { + BlockPos connectedPos = BlockPos.fromLong(blockPosListNbt.getLong(index)); + this.connectedBlocks.add(connectedPos); + } } + @Override + protected void writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { + super.writeNbt(nbt, registryLookup); + Inventories.writeNbt(nbt, this.getItems(), registryLookup); + this.getStructureOriginPos().ifPresent(originPos -> nbt.putLong(NbtKeys.BLOCK_ENTITY_ORIGIN_POS, originPos.asLong())); - public record StructureDimensions(int width, int height, int length) { - public StructureDimensions(int length) { - this(length, length, length); + List blockPosList = connectedBlocks.stream().toList(); + NbtCompound blockPosListNbt = new NbtCompound(); + for (int i = 0; i < blockPosList.size(); i++) { + BlockPos connectedPos = blockPosList.get(i); + blockPosListNbt.putLong(String.valueOf(i), connectedPos.asLong()); } + nbt.put(NbtKeys.CONNECTED_BLOCKS, blockPosListNbt); } } diff --git a/src/main/java/io/fabricatedatelier/mayor/init/Items.java b/src/main/java/io/fabricatedatelier/mayor/init/Items.java index 2bf013b..71511de 100644 --- a/src/main/java/io/fabricatedatelier/mayor/init/Items.java +++ b/src/main/java/io/fabricatedatelier/mayor/init/Items.java @@ -1,8 +1,7 @@ package io.fabricatedatelier.mayor.init; import io.fabricatedatelier.mayor.Mayor; -import io.fabricatedatelier.mayor.item.LumberStorageBlockItem; -import io.fabricatedatelier.mayor.item.StoneStorageBlockItem; +import io.fabricatedatelier.mayor.item.StorageBlockItem; import net.minecraft.item.Item; import net.minecraft.registry.Registries; import net.minecraft.registry.Registry; @@ -11,11 +10,11 @@ import java.util.List; public class Items { - public static final LumberStorageBlockItem LUMBER_STORAGE_BLOCK = register("lumber_storage_block", - new LumberStorageBlockItem(Blocks.LUMBER_STORAGE, new Item.Settings()), List.of(ItemGroups.MAYOR_BLOCKS)); + public static final StorageBlockItem LUMBER_STORAGE_BLOCK = register("lumber_storage_block", + new StorageBlockItem(Blocks.LUMBER_STORAGE, new Item.Settings()), List.of(ItemGroups.MAYOR_BLOCKS)); - public static final StoneStorageBlockItem STONE_STORAGE_BLOCK = register("stone_storage_block", - new StoneStorageBlockItem(Blocks.STONE_STORAGE, new Item.Settings()), List.of(ItemGroups.MAYOR_BLOCKS)); + public static final StorageBlockItem STONE_STORAGE_BLOCK = register("stone_storage_block", + new StorageBlockItem(Blocks.STONE_STORAGE, new Item.Settings()), List.of(ItemGroups.MAYOR_BLOCKS)); private static T register(String name, T item, @Nullable List itemGroups) { diff --git a/src/main/java/io/fabricatedatelier/mayor/item/LumberStorageBlockItem.java b/src/main/java/io/fabricatedatelier/mayor/item/LumberStorageBlockItem.java deleted file mode 100644 index 3fff468..0000000 --- a/src/main/java/io/fabricatedatelier/mayor/item/LumberStorageBlockItem.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.fabricatedatelier.mayor.item; - -import net.minecraft.block.Block; -import net.minecraft.item.BlockItem; - -public class LumberStorageBlockItem extends BlockItem { - public LumberStorageBlockItem(Block block, Settings settings) { - super(block, settings); - } -} diff --git a/src/main/java/io/fabricatedatelier/mayor/item/StoneStorageBlockItem.java b/src/main/java/io/fabricatedatelier/mayor/item/StorageBlockItem.java similarity index 55% rename from src/main/java/io/fabricatedatelier/mayor/item/StoneStorageBlockItem.java rename to src/main/java/io/fabricatedatelier/mayor/item/StorageBlockItem.java index 1fb8bc7..2559a8f 100644 --- a/src/main/java/io/fabricatedatelier/mayor/item/StoneStorageBlockItem.java +++ b/src/main/java/io/fabricatedatelier/mayor/item/StorageBlockItem.java @@ -3,9 +3,8 @@ import net.minecraft.block.Block; import net.minecraft.item.BlockItem; -public class StoneStorageBlockItem extends BlockItem { - public StoneStorageBlockItem(Block block, Settings settings) { +public class StorageBlockItem extends BlockItem { + public StorageBlockItem(Block block, Settings settings) { super(block, settings); } - } diff --git a/src/main/java/io/fabricatedatelier/mayor/mixin/BlockItemMixin.java b/src/main/java/io/fabricatedatelier/mayor/mixin/BlockItemMixin.java new file mode 100644 index 0000000..da94e93 --- /dev/null +++ b/src/main/java/io/fabricatedatelier/mayor/mixin/BlockItemMixin.java @@ -0,0 +1,45 @@ +package io.fabricatedatelier.mayor.mixin; + +import io.fabricatedatelier.mayor.block.custom.LumberStorageBlock; +import io.fabricatedatelier.mayor.block.custom.StoneStorageBlock; +import io.fabricatedatelier.mayor.datagen.TagProvider; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.item.BlockItem; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemUsageContext; +import net.minecraft.util.ActionResult; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(BlockItem.class) +public abstract class BlockItemMixin { + + @Shadow + public abstract Block getBlock(); + + // Note: This is probably a scuffed implementation, so change it if there is something better! + // The issue was that BlockItems which were placed in storage blocks with right click sometimes + // flashed up for a split second as if they were placed in the world before being inserted in the inventory + @Inject(method = "useOnBlock", at = @At("HEAD"), cancellable = true) + private void avoidPlacementIfUsedOnStorage(ItemUsageContext context, CallbackInfoReturnable cir) { + BlockState targetBlockState = context.getWorld().getBlockState(context.getBlockPos()); + ItemStack blockItemStack = new ItemStack((BlockItem) (Object) this); + + if (blockItemStack.isIn(TagProvider.ItemTags.LUMBER_STORAGE_STORABLE)) { + if (targetBlockState.getBlock() instanceof LumberStorageBlock) { + cir.setReturnValue(ActionResult.PASS); + return; + } + } + if (blockItemStack.isIn(TagProvider.ItemTags.STONE_STORAGE_STORABLE)) { + if (targetBlockState.getBlock() instanceof StoneStorageBlock) { + cir.setReturnValue(ActionResult.PASS); + return; + } + } + } +} diff --git a/src/main/java/io/fabricatedatelier/mayor/util/HandledInventory.java b/src/main/java/io/fabricatedatelier/mayor/util/HandledInventory.java index 188001f..88c6b6a 100644 --- a/src/main/java/io/fabricatedatelier/mayor/util/HandledInventory.java +++ b/src/main/java/io/fabricatedatelier/mayor/util/HandledInventory.java @@ -58,7 +58,7 @@ default Optional extract(@Nullable Direction direction) { @Override default boolean isEmpty() { - return size() <= 0; + return size() <= 0 || getItems().stream().allMatch(ItemStack::isEmpty); } @Override diff --git a/src/main/java/io/fabricatedatelier/mayor/util/AbstractStorageCallback.java b/src/main/java/io/fabricatedatelier/mayor/util/StorageCallback.java similarity index 89% rename from src/main/java/io/fabricatedatelier/mayor/util/AbstractStorageCallback.java rename to src/main/java/io/fabricatedatelier/mayor/util/StorageCallback.java index 934248a..517ae6a 100644 --- a/src/main/java/io/fabricatedatelier/mayor/util/AbstractStorageCallback.java +++ b/src/main/java/io/fabricatedatelier/mayor/util/StorageCallback.java @@ -3,7 +3,7 @@ import io.fabricatedatelier.mayor.block.AbstractVillageContainerBlockEntity; import net.minecraft.util.math.BlockPos; -public interface AbstractStorageCallback { +public interface StorageCallback { void onOriginChanged(AbstractVillageContainerBlockEntity blockEntity, BlockPos oldPos, BlockPos newPos); void onConnectedBlocksChanged(AbstractVillageContainerBlockEntity blockEntity); diff --git a/src/main/resources/mayor.mixins.json b/src/main/resources/mayor.mixins.json index 1a1d3a2..62a324a 100644 --- a/src/main/resources/mayor.mixins.json +++ b/src/main/resources/mayor.mixins.json @@ -4,32 +4,33 @@ "compatibilityLevel": "JAVA_21", "mixins": [ "BlockEntityMixin", + "BlockItemMixin", + "CarvedPumpkinBlockMixin", "EntityMixin", + "FindPointOfInterestTaskMixin", + "GoToWorkTaskMixin", "PlayerEntityMixin", + "ServerPlayerEntityMixin", "ServerWorldMixin", + "SinglePoolElementMixin", "StructureStartMixin", "StructureTemplateMixin", - "SinglePoolElementMixin", - "ServerPlayerEntityMixin", - "VillagerEntityMixin", - "CarvedPumpkinBlockMixin", "TakeJobSiteTaskMixin", - "FindPointOfInterestTaskMixin", + "VillagerEntityMixin", "VillagerTaskListProviderMixin", - "GoToWorkTaskMixin", "access.JigsawStructureAccess" ], "injectors": { "defaultRequire": 1 }, "client": [ + "access.BlockRenderManagerAccess", + "client.BuiltinModelItemRendererMixin", "client.CameraMixin", + "client.InGameHudMixin", + "client.KeyBindingMixin", "client.MinecraftClientMixin", "client.MouseMixin", - "client.KeyBindingMixin", - "client.InGameHudMixin", - "client.PlayerEntityRendererMixin", - "client.BuiltinModelItemRendererMixin", - "access.BlockRenderManagerAccess" + "client.PlayerEntityRendererMixin" ] } \ No newline at end of file