diff --git a/.gitignore b/.gitignore index 31d2550..1207b6b 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,5 @@ run runs run-data -repo \ No newline at end of file +repo +.vscode \ No newline at end of file diff --git a/src/main/java/mrbysco/constructionstick/api/IContainerHandler.java b/src/main/java/mrbysco/constructionstick/api/IContainerHandler.java index 73a6506..e8516d5 100644 --- a/src/main/java/mrbysco/constructionstick/api/IContainerHandler.java +++ b/src/main/java/mrbysco/constructionstick/api/IContainerHandler.java @@ -2,11 +2,16 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import mrbysco.constructionstick.containers.ContainerTrace; -public interface IContainerHandler { +public interface IContainerHandler +{ boolean matches(Player player, ItemStack itemStack, ItemStack inventoryStack); - int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack); + // Ender Chest is 0 + int getSignature(Player player, ItemStack inventoryStack); - int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count); + int countItems(Player player, ContainerTrace trace, ItemStack itemStack, ItemStack inventoryStack); + + int useItems(Player player, ContainerTrace trace, ItemStack itemStack, ItemStack inventoryStack, int count); } diff --git a/src/main/java/mrbysco/constructionstick/basics/StickUtil.java b/src/main/java/mrbysco/constructionstick/basics/StickUtil.java index 6e825f5..cc12299 100644 --- a/src/main/java/mrbysco/constructionstick/basics/StickUtil.java +++ b/src/main/java/mrbysco/constructionstick/basics/StickUtil.java @@ -5,6 +5,7 @@ import mrbysco.constructionstick.api.IStickUpgrade; import mrbysco.constructionstick.config.ConstructionConfig; import mrbysco.constructionstick.containers.ContainerManager; +import mrbysco.constructionstick.containers.ContainerTrace; import mrbysco.constructionstick.items.stick.ItemStick; import mrbysco.constructionstick.stick.StickItemUseContext; import net.minecraft.ChatFormatting; @@ -13,6 +14,7 @@ import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.stats.Stats; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.Entity; @@ -187,18 +189,21 @@ public static int countItem(Player player, Item item) { if (player.isCreative()) return Integer.MAX_VALUE; int total = 0; - ContainerManager containerManager = ConstructionStick.containerManager; - List inventory = StickUtil.getFullInv(player); - - for (ItemStack stack : inventory) { - if (stack == null || stack.isEmpty()) continue; - - if (StickUtil.stackEquals(stack, item)) { - total += stack.getCount(); - } else { - int amount = containerManager.countItems(player, new ItemStack(item), stack); - if (amount == Integer.MAX_VALUE) return Integer.MAX_VALUE; - total += amount; + if(player instanceof ServerPlayer serverPlayer) { + ContainerManager containerManager = ConstructionStick.containerManager; + ContainerTrace trace = new ContainerTrace(serverPlayer); + List inventory = StickUtil.getFullInv(serverPlayer); + + for (ItemStack stack : inventory) { + if (stack == null || stack.isEmpty()) continue; + + if (StickUtil.stackEquals(stack, item)) { + total += stack.getCount(); + } else { + int amount = containerManager.countItems(serverPlayer, trace, new ItemStack(item), stack); + if (amount == Integer.MAX_VALUE) return Integer.MAX_VALUE; + total += amount; + } } } return total; diff --git a/src/main/java/mrbysco/constructionstick/client/RenderBlockPreview.java b/src/main/java/mrbysco/constructionstick/client/RenderBlockPreview.java index 9156805..60fb4e8 100644 --- a/src/main/java/mrbysco/constructionstick/client/RenderBlockPreview.java +++ b/src/main/java/mrbysco/constructionstick/client/RenderBlockPreview.java @@ -3,8 +3,8 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import mrbysco.constructionstick.basics.StickUtil; -import mrbysco.constructionstick.items.stick.ItemStick; -import mrbysco.constructionstick.stick.StickJob; +import mrbysco.constructionstick.network.ModMessages; +import mrbysco.constructionstick.network.PacketRequestPreview; import net.minecraft.client.Camera; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.MultiBufferSource; @@ -22,8 +22,10 @@ import java.util.Set; public class RenderBlockPreview { - private StickJob stickJob; + private BlockHitResult lastRayTraceResult = null; + private ItemStack lastStick = ItemStack.EMPTY; public Set undoBlocks; + public Set previewBlocks; @SubscribeEvent public void renderBlockHighlight(RenderHighlightEvent.Block event) { @@ -38,18 +40,21 @@ public void renderBlockHighlight(RenderHighlightEvent.Block event) { ItemStack stick = StickUtil.holdingStick(player); if (stick == null) return; - if (!KeybindHandler.KEY_SHOW_PREVIOUS.isDown()) { + if (KeybindHandler.KEY_SHOW_PREVIOUS.isDown()) { + blocks = undoBlocks; + colorG = 1; + } + else { // Use cached stickJob for previews of the same target pos/dir // Exception: always update if blockCount < 2 to prevent 1-block previews when block updates // from the last placement are lagging - if (stickJob == null || !compareRTR(stickJob.blockHitResult, target) || !(stickJob.stick.equals(stick)) - || stickJob.blockCount() < 2) { - stickJob = ItemStick.getStickJob(player, player.level(), target, stick); + if(lastRayTraceResult == null || !compareRTR(lastRayTraceResult, target) || !lastStick.equals(stick) + || previewBlocks == null || previewBlocks.size() < 2) { + lastRayTraceResult = target; + lastStick = stick; + ModMessages.sendToServer(new PacketRequestPreview(target, stick)); } - blocks = stickJob.getBlockPositions(); - } else { - blocks = undoBlocks; - colorG = 1; + blocks = previewBlocks; } if (blocks == null || blocks.isEmpty()) return; @@ -72,7 +77,9 @@ public void renderBlockHighlight(RenderHighlightEvent.Block event) { } public void reset() { - stickJob = null; + lastRayTraceResult = null; + lastStick = ItemStack.EMPTY; + previewBlocks = null; } private static boolean compareRTR(BlockHitResult rtr1, BlockHitResult rtr2) { diff --git a/src/main/java/mrbysco/constructionstick/containers/ContainerManager.java b/src/main/java/mrbysco/constructionstick/containers/ContainerManager.java index e0580b3..09a0894 100644 --- a/src/main/java/mrbysco/constructionstick/containers/ContainerManager.java +++ b/src/main/java/mrbysco/constructionstick/containers/ContainerManager.java @@ -5,10 +5,10 @@ import net.minecraft.world.item.ItemStack; import java.util.ArrayList; -import java.util.List; -public class ContainerManager { - private final List handlers; +public class ContainerManager +{ + private final ArrayList handlers; public ContainerManager() { handlers = new ArrayList(); @@ -18,19 +18,31 @@ public boolean register(IContainerHandler handler) { return handlers.add(handler); } - public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) { - for (IContainerHandler handler : handlers) { - if (handler.matches(player, itemStack, inventoryStack)) { - return handler.countItems(player, itemStack, inventoryStack); + public int countItems(Player player, ContainerTrace trace, ItemStack itemStack, ItemStack inventoryStack) { + for(IContainerHandler handler : handlers) { + if(handler.matches(player, itemStack, inventoryStack)) { + int sig = handler.getSignature(player, inventoryStack); + if (trace.push(sig)) { + int count = handler.countItems(player, trace, itemStack, inventoryStack); + return count; + } else { + return 0; + } } } return 0; } - public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) { - for (IContainerHandler handler : handlers) { - if (handler.matches(player, itemStack, inventoryStack)) { - return handler.useItems(player, itemStack, inventoryStack, count); + public int useItems(Player player, ContainerTrace trace, ItemStack itemStack, ItemStack inventoryStack, int count) { + for(IContainerHandler handler : handlers) { + if(handler.matches(player, itemStack, inventoryStack)) { + int sig = handler.getSignature(player, inventoryStack); + if (trace.push(sig)) { + int remaining = handler.useItems(player, trace, itemStack, inventoryStack, count); + return remaining; + } else { + return count; + } } } return count; diff --git a/src/main/java/mrbysco/constructionstick/containers/ContainerTrace.java b/src/main/java/mrbysco/constructionstick/containers/ContainerTrace.java new file mode 100644 index 0000000..9f79f2f --- /dev/null +++ b/src/main/java/mrbysco/constructionstick/containers/ContainerTrace.java @@ -0,0 +1,36 @@ +package mrbysco.constructionstick.containers; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.Nullable; + +import java.util.HashSet; + +public class ContainerTrace { + + private final HashSet visited = new HashSet<>(); + + public final ServerLevel level; + @Nullable + public final ServerPlayer player; + + public ContainerTrace(ServerLevel level) { + this.level = level; + this.player = null; + visited.add(-1); + } + + public ContainerTrace(ServerPlayer player) { + this.player = player; + this.level = player.serverLevel(); + visited.add(-1); + } + + public boolean push(int sig) { + if (visited.contains(sig)) { + return false; + } + visited.add(sig); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/mrbysco/constructionstick/containers/handlers/HandlerBundle.java b/src/main/java/mrbysco/constructionstick/containers/handlers/HandlerBundle.java index 001694b..cdbdf84 100644 --- a/src/main/java/mrbysco/constructionstick/containers/handlers/HandlerBundle.java +++ b/src/main/java/mrbysco/constructionstick/containers/handlers/HandlerBundle.java @@ -2,6 +2,7 @@ import mrbysco.constructionstick.api.IContainerHandler; import mrbysco.constructionstick.basics.StickUtil; +import mrbysco.constructionstick.containers.ContainerTrace; import net.minecraft.core.component.DataComponents; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; @@ -19,13 +20,18 @@ public boolean matches(Player player, ItemStack itemStack, ItemStack inventorySt } @Override - public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) { + public int getSignature(Player player, ItemStack inventoryStack) { + return inventoryStack.hashCode(); + } + + @Override + public int countItems(Player player, ContainerTrace trace, ItemStack itemStack, ItemStack inventoryStack) { return getContents(inventoryStack).filter((stack) -> StickUtil.stackEquals(stack, itemStack)) .map(ItemStack::getCount).reduce(0, Integer::sum); } @Override - public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) { + public int useItems(Player player, ContainerTrace trace, ItemStack itemStack, ItemStack inventoryStack, int count) { AtomicInteger newCount = new AtomicInteger(count); List itemStacks = getContents(inventoryStack).filter((stack -> { diff --git a/src/main/java/mrbysco/constructionstick/containers/handlers/HandlerCapability.java b/src/main/java/mrbysco/constructionstick/containers/handlers/HandlerCapability.java index 9664692..62e7482 100644 --- a/src/main/java/mrbysco/constructionstick/containers/handlers/HandlerCapability.java +++ b/src/main/java/mrbysco/constructionstick/containers/handlers/HandlerCapability.java @@ -2,6 +2,7 @@ import mrbysco.constructionstick.api.IContainerHandler; import mrbysco.constructionstick.basics.StickUtil; +import mrbysco.constructionstick.containers.ContainerTrace; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.neoforged.neoforge.capabilities.Capabilities; @@ -14,7 +15,12 @@ public boolean matches(Player player, ItemStack itemStack, ItemStack inventorySt } @Override - public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) { + public int getSignature(Player player, ItemStack inventoryStack) { + return inventoryStack.hashCode(); + } + + @Override + public int countItems(Player player, ContainerTrace trace, ItemStack itemStack, ItemStack inventoryStack) { IItemHandler itemHandler = inventoryStack.getCapability(Capabilities.ItemHandler.ITEM); if (itemHandler == null) return 0; @@ -30,7 +36,7 @@ public int countItems(Player player, ItemStack itemStack, ItemStack inventorySta } @Override - public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) { + public int useItems(Player player, ContainerTrace trace, ItemStack itemStack, ItemStack inventoryStack, int count) { IItemHandler itemHandler = inventoryStack.getCapability(Capabilities.ItemHandler.ITEM); if (itemHandler == null) return 0; diff --git a/src/main/java/mrbysco/constructionstick/containers/handlers/HandlerShulkerbox.java b/src/main/java/mrbysco/constructionstick/containers/handlers/HandlerShulkerbox.java index 4096453..7ce9b62 100644 --- a/src/main/java/mrbysco/constructionstick/containers/handlers/HandlerShulkerbox.java +++ b/src/main/java/mrbysco/constructionstick/containers/handlers/HandlerShulkerbox.java @@ -2,6 +2,7 @@ import mrbysco.constructionstick.api.IContainerHandler; import mrbysco.constructionstick.basics.StickUtil; +import mrbysco.constructionstick.containers.ContainerTrace; import net.minecraft.core.NonNullList; import net.minecraft.core.component.DataComponents; import net.minecraft.world.entity.player.Player; @@ -19,7 +20,12 @@ public boolean matches(Player player, ItemStack itemStack, ItemStack inventorySt } @Override - public int countItems(Player player, ItemStack itemStack, ItemStack inventoryStack) { + public int getSignature(Player player, ItemStack inventoryStack) { + return inventoryStack.hashCode(); + } + + @Override + public int countItems(Player player, ContainerTrace trace, ItemStack itemStack, ItemStack inventoryStack) { int count = 0; for (ItemStack stack : getItemList(inventoryStack)) { @@ -30,7 +36,7 @@ public int countItems(Player player, ItemStack itemStack, ItemStack inventorySta } @Override - public int useItems(Player player, ItemStack itemStack, ItemStack inventoryStack, int count) { + public int useItems(Player player, ContainerTrace trace, ItemStack itemStack, ItemStack inventoryStack, int count) { NonNullList itemList = getItemList(inventoryStack); boolean changed = false; diff --git a/src/main/java/mrbysco/constructionstick/network/ModMessages.java b/src/main/java/mrbysco/constructionstick/network/ModMessages.java index 5f4ed4d..25fc384 100644 --- a/src/main/java/mrbysco/constructionstick/network/ModMessages.java +++ b/src/main/java/mrbysco/constructionstick/network/ModMessages.java @@ -19,6 +19,8 @@ public static void registerPayloads(final RegisterPayloadHandlersEvent event) { registrar.playToClient(PacketUndoBlocks.ID, PacketUndoBlocks.CODEC, PacketUndoBlocks.Handler::handle); registrar.playToServer(PacketQueryUndo.ID, PacketQueryUndo.CODEC, PacketQueryUndo.Handler::handle); registrar.playToServer(PacketStickOption.ID, PacketStickOption.CODEC, PacketStickOption.Handler::handle); + registrar.playToServer(PacketRequestPreview.ID, PacketRequestPreview.CODEC, PacketRequestPreview.Handler::handle); + registrar.playToClient(PacketPreviewResult.ID, PacketPreviewResult.CODEC, PacketPreviewResult.Handler::handle); } public static void sendToServer(CustomPacketPayload message) { diff --git a/src/main/java/mrbysco/constructionstick/network/PacketPreviewResult.java b/src/main/java/mrbysco/constructionstick/network/PacketPreviewResult.java new file mode 100644 index 0000000..097cfb3 --- /dev/null +++ b/src/main/java/mrbysco/constructionstick/network/PacketPreviewResult.java @@ -0,0 +1,64 @@ +package mrbysco.constructionstick.network; + +import mrbysco.constructionstick.ConstructionStick; +import mrbysco.constructionstick.client.ClientHandler; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.network.handling.IPayloadContext; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Supplier; + + +public record PacketPreviewResult(HashSet previewBlocks) implements CustomPacketPayload { + public static final StreamCodec CODEC = CustomPacketPayload.codec( + PacketPreviewResult::encode, + PacketPreviewResult::new); + public static final Type ID = new Type<>(ConstructionStick.modLoc("preview_result")); + + public PacketPreviewResult(FriendlyByteBuf buffer) { + this(getSet(buffer)); + } + + public PacketPreviewResult(Set previewBlocks) { + this(new HashSet<>(previewBlocks)); + } + + private static HashSet getSet(FriendlyByteBuf buffer) { + HashSet previewBlocks = new HashSet<>(); + + while (buffer.isReadable()) { + previewBlocks.add(buffer.readBlockPos()); + } + return previewBlocks; + } + + public void encode(FriendlyByteBuf buffer) { + for (BlockPos pos : previewBlocks) { + buffer.writeBlockPos(pos); + } + } + + @Override + public Type type() { + return ID; + } + + public static class Handler { + public static void handle(final PacketPreviewResult msg, final IPayloadContext ctx) { + ctx.enqueueWork(() -> { + ClientHandler.renderBlockPreview.previewBlocks = msg.previewBlocks; + }) + .exceptionally(e -> { + ctx.disconnect(Component.translatable("constructionstick.networking.preview_result.failed", e.getMessage())); + return null; + }); + + } + } +} diff --git a/src/main/java/mrbysco/constructionstick/network/PacketRequestPreview.java b/src/main/java/mrbysco/constructionstick/network/PacketRequestPreview.java new file mode 100644 index 0000000..92f2943 --- /dev/null +++ b/src/main/java/mrbysco/constructionstick/network/PacketRequestPreview.java @@ -0,0 +1,57 @@ +package mrbysco.constructionstick.network; + +import mrbysco.constructionstick.ConstructionStick; +import mrbysco.constructionstick.items.stick.ItemStick; +import mrbysco.constructionstick.stick.StickJob; +import net.minecraft.core.BlockPos; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.BlockHitResult; +import net.neoforged.neoforge.network.handling.IPayloadContext; + +import java.util.Set; + +public record PacketRequestPreview(BlockHitResult rtr, ItemStack stick) implements CustomPacketPayload { + public static final StreamCodec CODEC = CustomPacketPayload.codec( + PacketRequestPreview::encode, + PacketRequestPreview::new); + public static final Type ID = new Type<>(ConstructionStick.modLoc("request_preview")); + + public PacketRequestPreview(RegistryFriendlyByteBuf buffer) { + this(buffer.readBlockHitResult(), ItemStack.STREAM_CODEC.decode(buffer)); + } + + public static void encode(PacketRequestPreview msg, RegistryFriendlyByteBuf buffer) { + buffer.writeBlockHitResult(msg.rtr); + ItemStack.STREAM_CODEC.encode(buffer, msg.stick); + } + + @Override + public Type type() { + return ID; + } + + public static class Handler + { + public static void handle(final PacketRequestPreview msg, final IPayloadContext ctx) { + ctx.enqueueWork(() -> { + if (ctx.flow().isServerbound() && ctx.player() instanceof ServerPlayer player) { + StickJob job = ItemStick.getStickJob(player, player.level(), msg.rtr, msg.stick); + if (job == null) return; + Set blocks = job.getBlockPositions(); + + ModMessages.sendToPlayer(new PacketPreviewResult(blocks), player); + } + }) + .exceptionally(e -> { + // Handle exception + ctx.disconnect(Component.translatable("constructionstick.networking.preview_request.failed", e.getMessage())); + return null; + }); + } + } +} diff --git a/src/main/java/mrbysco/constructionstick/stick/supplier/SupplierInventory.java b/src/main/java/mrbysco/constructionstick/stick/supplier/SupplierInventory.java index 7f9e737..c3e617d 100644 --- a/src/main/java/mrbysco/constructionstick/stick/supplier/SupplierInventory.java +++ b/src/main/java/mrbysco/constructionstick/stick/supplier/SupplierInventory.java @@ -8,8 +8,10 @@ import mrbysco.constructionstick.basics.pool.IPool; import mrbysco.constructionstick.basics.pool.OrderedPool; import mrbysco.constructionstick.containers.ContainerManager; +import mrbysco.constructionstick.containers.ContainerTrace; import mrbysco.constructionstick.stick.undo.PlaceSnapshot; import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.BlockItem; @@ -74,7 +76,7 @@ protected void addBlockItem(BlockItem item) { @Override @Nullable public PlaceSnapshot getPlaceSnapshot(Level level, BlockPos pos, BlockHitResult blockHitResult, - @Nullable BlockState supportingBlock) { + @Nullable BlockState supportingBlock) { if (!StickUtil.isPositionPlaceable(level, player, pos, options.replace.get())) return null; itemPool.reset(); @@ -122,20 +124,25 @@ public int takeItemStack(ItemStack stack) { } private int takeItemsInvList(int count, Item item, List inv, boolean container) { - ContainerManager containerManager = ConstructionStick.containerManager; + if (count == 0) return 0; + if (player instanceof ServerPlayer serverPlayer) { + ContainerManager containerManager = ConstructionStick.containerManager; + // In use, ContainerTrace is just a placeholder + ContainerTrace trace = new ContainerTrace(serverPlayer); - for (ItemStack stack : inv) { - if (count == 0) break; + for (ItemStack stack : inv) { + if (count == 0) break; - if (container) { - count = containerManager.useItems(player, new ItemStack(item), stack, count); - } + if (container) { + count = containerManager.useItems(serverPlayer, trace, new ItemStack(item), stack, count); + } - if (!container && StickUtil.stackEquals(stack, item)) { - int toTake = Math.min(count, stack.getCount()); - stack.shrink(toTake); - count -= toTake; - player.getInventory().setChanged(); + if (!container && StickUtil.stackEquals(stack, item)) { + int toTake = Math.min(count, stack.getCount()); + stack.shrink(toTake); + count -= toTake; + serverPlayer.getInventory().setChanged(); + } } } return count;