diff --git a/src/main/java/appeng/core/network/InitNetwork.java b/src/main/java/appeng/core/network/InitNetwork.java index 04d52872d3b..d74b5b906b5 100644 --- a/src/main/java/appeng/core/network/InitNetwork.java +++ b/src/main/java/appeng/core/network/InitNetwork.java @@ -33,6 +33,7 @@ import appeng.core.network.serverbound.HotkeyPacket; import appeng.core.network.serverbound.InventoryActionPacket; import appeng.core.network.serverbound.MEInteractionPacket; +import appeng.core.network.serverbound.PullItemToPlayerPacket; import appeng.core.network.serverbound.MouseWheelPacket; import appeng.core.network.serverbound.PartLeftClickPacket; import appeng.core.network.serverbound.QuickMovePatternPacket; @@ -82,6 +83,7 @@ public static void init(RegisterPayloadHandlersEvent event) { serverbound(registrar, SwapSlotsPacket.TYPE, SwapSlotsPacket.STREAM_CODEC); serverbound(registrar, SwitchGuisPacket.TYPE, SwitchGuisPacket.STREAM_CODEC); serverbound(registrar, UpdateHoldingCtrlPacket.TYPE, UpdateHoldingCtrlPacket.STREAM_CODEC); + serverbound(registrar, PullItemToPlayerPacket.TYPE, PullItemToPlayerPacket.STREAM_CODEC); // Bidirectional bidirectional(registrar, ConfigValuePacket.TYPE, ConfigValuePacket.STREAM_CODEC); diff --git a/src/main/java/appeng/core/network/serverbound/PullItemToPlayerPacket.java b/src/main/java/appeng/core/network/serverbound/PullItemToPlayerPacket.java new file mode 100644 index 00000000000..3b7c8c374c4 --- /dev/null +++ b/src/main/java/appeng/core/network/serverbound/PullItemToPlayerPacket.java @@ -0,0 +1,57 @@ +package appeng.core.network.serverbound; + +import net.minecraft.core.NonNullList; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; + +import appeng.core.network.CustomAppEngPayload; +import appeng.core.network.ServerboundPacket; +import appeng.menu.me.common.MEStorageMenu; + +public record PullItemToPlayerPacket(int containerId, NonNullList stacks, long toPull) implements ServerboundPacket { + + public static final StreamCodec STREAM_CODEC = StreamCodec.ofMember( + PullItemToPlayerPacket::write, + PullItemToPlayerPacket::decode); + + public static final Type TYPE = CustomAppEngPayload.createType("me_pull"); + + @Override + public Type type() { + return TYPE; + } + + public static PullItemToPlayerPacket decode(RegistryFriendlyByteBuf buffer) { + int containerId = buffer.readInt(); + NonNullList stacks = NonNullList.withSize(buffer.readInt(), ItemStack.EMPTY); + for (int i = 0; i < stacks.size(); i++) { + stacks.set(i, ItemStack.OPTIONAL_STREAM_CODEC.decode(buffer)); + } + long toPull = buffer.readVarLong(); + return new PullItemToPlayerPacket(containerId, stacks, toPull); + } + + public void write(RegistryFriendlyByteBuf data) { + data.writeInt(containerId); + data.writeInt(stacks.size()); + for (ItemStack stack : stacks) { + ItemStack.OPTIONAL_STREAM_CODEC.encode(data, stack); + } + data.writeVarLong(toPull); + } + + @Override + public void handleOnServer(ServerPlayer player) { + if (player.containerMenu instanceof MEStorageMenu aeMenu) { + // The open screen has changed since the client sent the packet + if (player.containerMenu.containerId != containerId) { + return; + } + + aeMenu.transferMatchingStacksToPlayer(stacks, toPull); + } + } + +} diff --git a/src/main/java/appeng/integration/modules/emi/AppEngEmiPlugin.java b/src/main/java/appeng/integration/modules/emi/AppEngEmiPlugin.java index a2fbbf0fe9b..f943216006a 100644 --- a/src/main/java/appeng/integration/modules/emi/AppEngEmiPlugin.java +++ b/src/main/java/appeng/integration/modules/emi/AppEngEmiPlugin.java @@ -62,6 +62,7 @@ public void register(EmiRegistry registry) { registry.addGenericExclusionArea(new EmiAeBaseScreenExclusionZones()); registry.addGenericStackProvider(new EmiAeBaseScreenStackProvider()); registry.addGenericDragDropHandler(new EmiAeBaseScreenDragDropHandler()); + registry.addGenericStackPuller(new EmiAeBaseScreenStackPuller()); // Additional Workstations registerWorkstations(registry); diff --git a/src/main/java/appeng/integration/modules/emi/EmiAeBaseScreenStackPuller.java b/src/main/java/appeng/integration/modules/emi/EmiAeBaseScreenStackPuller.java new file mode 100644 index 00000000000..71c3d62c518 --- /dev/null +++ b/src/main/java/appeng/integration/modules/emi/EmiAeBaseScreenStackPuller.java @@ -0,0 +1,31 @@ +package appeng.integration.modules.emi; + +import java.util.List; + +import net.minecraft.core.NonNullList; +import dev.emi.emi.api.EmiStackPuller; +import net.neoforged.neoforge.network.PacketDistributor; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.item.ItemStack; + +import appeng.menu.me.common.MEStorageMenu; +import appeng.core.network.ServerboundPacket; +import appeng.core.network.serverbound.PullItemToPlayerPacket; + +public class EmiAeBaseScreenStackPuller implements EmiStackPuller { + + @Override + public boolean pullStack(AbstractContainerMenu menu, List stacks, long toPull) { + if (!(menu instanceof MEStorageMenu aeMenu)) { + return false; + } + + NonNullList nonNullStacks = NonNullList.copyOf(stacks); + + ServerboundPacket message = new PullItemToPlayerPacket(menu.containerId, nonNullStacks, toPull); + PacketDistributor.sendToServer(message); + + return true; + } + +} diff --git a/src/main/java/appeng/menu/me/common/MEStorageMenu.java b/src/main/java/appeng/menu/me/common/MEStorageMenu.java index 74e86adf7b0..676d2d7be05 100644 --- a/src/main/java/appeng/menu/me/common/MEStorageMenu.java +++ b/src/main/java/appeng/menu/me/common/MEStorageMenu.java @@ -32,6 +32,7 @@ import org.jetbrains.annotations.Nullable; +import net.minecraft.core.NonNullList; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.MenuType; @@ -636,6 +637,42 @@ private boolean moveOneStackToPlayer(AEItemKey what) { return false; } + public void transferMatchingStacksToPlayer(NonNullList stacks, long toPull) { + for (ItemStack stack : stacks) { + AEItemKey key = AEItemKey.of(stack); + + long potentialAmount = storage.extract(key, toPull, Actionable.SIMULATE, getActionSource()); + if (potentialAmount <= 0) { + continue; + } + + var destinationSlots = getQuickMoveDestinationSlots(key.toStack(), false); + + for (var destinationSlot : destinationSlots) { + var amount = Math.min(toPull, getPlaceableAmount(destinationSlot, key)); + if (amount <= 0) { + continue; + } + + var extracted = StorageHelper.poweredExtraction(energySource, storage, key, amount, getActionSource()); + if (extracted == 0) { + break; // No items available, try next matching type + } + + var currentItem = destinationSlot.getItem(); + if (!currentItem.isEmpty()) { + destinationSlot.setByPlayer(currentItem.copyWithCount(currentItem.getCount() + (int) extracted)); + } else { + destinationSlot.setByPlayer(key.toStack((int) extracted)); + } + + toPull -= extracted; + } + + if (toPull <= 0) return; + } + } + @Nullable protected final AEKey getStackBySerial(long serial) { return updateHelper.getBySerial(serial);