From 1372804bc92abae53361e56914e796be78ed2b95 Mon Sep 17 00:00:00 2001 From: TechnicianLP <9272536+TechnicianLP@users.noreply.github.com> Date: Thu, 14 Dec 2023 17:06:26 +0100 Subject: [PATCH] Optimize CPacketFluidUpdate (#177) --- build.gradle | 8 +- dependencies.gradle | 6 +- .../client/gui/GuiEssentiaTerminal.java | 11 +- .../github/client/gui/GuiFluidTerminal.java | 12 +- .../container/ContainerEssentiaMonitor.java | 194 ++++---- .../gui/container/ContainerFluidMonitor.java | 465 +++++++++--------- .../github/network/CPacketFluidUpdate.java | 61 ++- .../java/com/glodblock/github/util/Util.java | 6 +- 8 files changed, 377 insertions(+), 386 deletions(-) diff --git a/build.gradle b/build.gradle index 8507c7df..50c3291c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,4 @@ -//version: 1701530445 +//version: 1702141377 /* DO NOT CHANGE THIS FILE! Also, you may replace this file at any time if there is an update available. @@ -54,7 +54,7 @@ plugins { id 'com.diffplug.spotless' version '6.13.0' apply false // 6.13.0 is the last jvm8 supporting version id 'com.modrinth.minotaur' version '2.+' apply false id 'com.matthewprenger.cursegradle' version '1.4.0' apply false - id 'com.gtnewhorizons.retrofuturagradle' version '1.3.24' + id 'com.gtnewhorizons.retrofuturagradle' version '1.3.26' } print("You might want to check out './gradlew :faq' if your build fails.\n") @@ -613,7 +613,7 @@ repositories { } maven { name = "ic2" - url = getURL("https://maven.ic2.player.to/", "https://maven2.ic2.player.to/") + url = getURL("https://maven2.ic2.player.to/", "https://maven.ic2.player.to/") content { includeGroup "net.industrial-craft" } @@ -672,6 +672,8 @@ configurations.all { substitute module('com.github.GTNewHorizons:SpongePoweredMixin') using module(mixinProviderSpecNoClassifer) withClassifier("dev") because("Unimixins replaces other mixin mods") substitute module('com.github.GTNewHorizons:SpongeMixins') using module(mixinProviderSpecNoClassifer) withClassifier("dev") because("Unimixins replaces other mixin mods") substitute module('io.github.legacymoddingmc:unimixins') using module(mixinProviderSpecNoClassifer) withClassifier("dev") because("Our previous unimixins upload was missing the dev classifier") + + substitute module('org.scala-lang:scala-library:2.11.1') using module('org.scala-lang:scala-library:2.11.5') because('To allow mixing with Java 8 targets') } } diff --git a/dependencies.gradle b/dependencies.gradle index 3d2fe1c9..045b0dcb 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -45,14 +45,14 @@ dependencies { compileOnly('com.github.GTNewHorizons:ExtraCells2:2.5.34:dev') { transitive = false } compileOnly('com.github.GTNewHorizons:ForestryMC:4.7.0:dev') compileOnly('com.github.GTNewHorizons:EnderIO:2.5.6:dev') - compileOnly('com.github.GTNewHorizons:GT5-Unofficial:5.09.44.98:dev') { + compileOnly('com.github.GTNewHorizons:GT5-Unofficial:5.09.44.106:dev') { exclude group: 'com.github.GTNewHorizons', module: 'AE2FluidCraft-Rework' } compileOnly('thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev') compileOnly('com.gregoriust.gregtech:gregtech_1.7.10:6.14.23:dev') { transitive = false } compileOnly('com.github.GTNewHorizons:OpenComputers:1.9.19-GTNH:dev') { transitive = false } - compileOnly('com.github.GTNewHorizons:ThaumicEnergistics:1.5.3-GTNH:dev') { transitive = false } - compileOnly("com.github.GTNewHorizons:Hodgepodge:2.3.38:dev") { transitive = false } + compileOnly('com.github.GTNewHorizons:ThaumicEnergistics:1.5.4-GTNH:dev') { transitive = false } + compileOnly("com.github.GTNewHorizons:Hodgepodge:2.3.39:dev") { transitive = false } runtimeOnlyNonPublishable("com.github.GTNewHorizons:DuraDisplay:1.1.7:dev") } diff --git a/src/main/java/com/glodblock/github/client/gui/GuiEssentiaTerminal.java b/src/main/java/com/glodblock/github/client/gui/GuiEssentiaTerminal.java index f9ae8765..375ecfff 100644 --- a/src/main/java/com/glodblock/github/client/gui/GuiEssentiaTerminal.java +++ b/src/main/java/com/glodblock/github/client/gui/GuiEssentiaTerminal.java @@ -1,7 +1,5 @@ package com.glodblock.github.client.gui; -import java.util.HashMap; -import java.util.Map; import java.util.Objects; import net.minecraft.client.gui.GuiButton; @@ -80,13 +78,8 @@ public class GuiEssentiaTerminal extends GuiFluidMonitor { @Override protected void handleMouseClick(final Slot slot, final int slotIdx, final int ctrlDown, final int mouseButton) { if (slot instanceof SlotME && AspectUtil.isEssentiaContainer(player.inventory.getItemStack())) { - Map tmp = new HashMap<>(); - ItemStack itemStack = this.player.inventory.getItemStack().copy(); - tmp.put(0, ItemFluidDrop.getAeFluidStack(((SlotME) slot).getAEStack())); - if (!isShiftKeyDown()) { - itemStack.stackSize = 1; - } - FluidCraft.proxy.netHandler.sendToServer(new CPacketFluidUpdate(tmp, itemStack)); + IAEFluidStack fluid = ItemFluidDrop.getAeFluidStack(((SlotME) slot).getAEStack()); + FluidCraft.proxy.netHandler.sendToServer(new CPacketFluidUpdate(fluid, isShiftKeyDown())); } if (mouseButton == 3 && slot instanceof SlotME) { return; diff --git a/src/main/java/com/glodblock/github/client/gui/GuiFluidTerminal.java b/src/main/java/com/glodblock/github/client/gui/GuiFluidTerminal.java index 76a866d2..36331e26 100644 --- a/src/main/java/com/glodblock/github/client/gui/GuiFluidTerminal.java +++ b/src/main/java/com/glodblock/github/client/gui/GuiFluidTerminal.java @@ -1,8 +1,5 @@ package com.glodblock.github.client.gui; -import java.util.HashMap; -import java.util.Map; - import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.renderer.texture.TextureMap; @@ -121,13 +118,8 @@ public class GuiFluidTerminal extends GuiFluidMonitor { @Override protected void handleMouseClick(final Slot slot, final int slotIdx, final int ctrlDown, final int mouseButton) { if (slot instanceof SlotME && Util.FluidUtil.isFluidContainer(player.inventory.getItemStack())) { - Map tmp = new HashMap<>(); - ItemStack itemStack = this.player.inventory.getItemStack().copy(); - tmp.put(0, ItemFluidDrop.getAeFluidStack(((SlotME) slot).getAEStack())); - if (!isShiftKeyDown()) { - itemStack.stackSize = 1; - } - FluidCraft.proxy.netHandler.sendToServer(new CPacketFluidUpdate(tmp, itemStack)); + IAEFluidStack fluid = ItemFluidDrop.getAeFluidStack(((SlotME) slot).getAEStack()); + FluidCraft.proxy.netHandler.sendToServer(new CPacketFluidUpdate(fluid, isShiftKeyDown())); } if (mouseButton == 3 && slot instanceof SlotME) { return; diff --git a/src/main/java/com/glodblock/github/client/gui/container/ContainerEssentiaMonitor.java b/src/main/java/com/glodblock/github/client/gui/container/ContainerEssentiaMonitor.java index 002b0969..587eb23d 100644 --- a/src/main/java/com/glodblock/github/client/gui/container/ContainerEssentiaMonitor.java +++ b/src/main/java/com/glodblock/github/client/gui/container/ContainerEssentiaMonitor.java @@ -1,7 +1,6 @@ package com.glodblock.github.client.gui.container; import java.util.HashMap; -import java.util.Map; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; @@ -23,7 +22,6 @@ import appeng.api.storage.data.IAEFluidStack; import appeng.container.slot.SlotPlayerHotBar; import appeng.container.slot.SlotPlayerInv; import appeng.util.Platform; -import appeng.util.item.AEFluidStack; public class ContainerEssentiaMonitor extends ContainerFluidPortableCell { @@ -37,110 +35,118 @@ public class ContainerEssentiaMonitor extends ContainerFluidPortableCell { Slot clickSlot = (Slot) this.inventorySlots.get(idx); if ((clickSlot instanceof SlotPlayerInv || clickSlot instanceof SlotPlayerHotBar) && clickSlot.getHasStack() && AspectUtil.isEssentiaContainer(clickSlot.getStack())) { - ItemStack tis = clickSlot.getStack(); - Map tmp = new HashMap<>(); - tmp.put(0, null); int index = Util.findItemInPlayerInvSlot(p, clickSlot.getStack()); - FluidCraft.proxy.netHandler.sendToServer(new CPacketFluidUpdate(tmp, tis, index)); + FluidCraft.proxy.netHandler.sendToServer(new CPacketFluidUpdate(index)); } } return super.transferStackInSlot(p, idx); } @Override - public void postChange(Iterable change, ItemStack fluidContainer, EntityPlayer player, - int slotIndex) { - for (IAEFluidStack fluid : change) { - IAEFluidStack nfluid = this.monitor.getStorageList().findPrecise(fluid); - ItemStack out = fluidContainer.copy(); - out.stackSize = 1; - if (AspectUtil.isEmptyEssentiaContainer(fluidContainer) && AspectUtil.isEssentiaGas(fluid)) { - // add essentia to jars - if (nfluid.getStackSize() / AspectUtil.R <= 0) continue; - final IAEFluidStack toExtract = nfluid.copy(); - MutablePair fillStack = AspectUtil - .fillEssentiaFromGas(out, toExtract.getFluidStack()); - if (fillStack == null || fillStack.right == null || fillStack.left <= 0) continue; - toExtract.setStackSize((long) fillStack.left * fluidContainer.stackSize); - IAEFluidStack tmp = this.host.getFluidInventory() - .extractItems(toExtract, Actionable.SIMULATE, this.getActionSource()); - if (tmp == null) continue; - fillStack.right.stackSize = (int) (tmp.getStackSize() / fillStack.left); - this.dropItem(fillStack.right); - out.stackSize = fillStack.right.stackSize; - if (AspectUtil.isEssentiaContainer(fillStack.right)) { - this.host.getFluidInventory().extractItems(toExtract, Actionable.MODULATE, this.getActionSource()); - if ((int) (tmp.getStackSize() % fillStack.left) / AspectUtil.R > 0) { - this.dropItem( - AspectUtil.setAspectAmount( - fillStack.right, - (int) (tmp.getStackSize() % fillStack.left) / AspectUtil.R, - AspectUtil.getAspectFromGas(toExtract)), - 1); - out.stackSize++; - } - } - } else if (!AspectUtil.isEmptyEssentiaContainer(fluidContainer)) { - // add essentia to ae network - AEFluidStack fluidStack = AspectUtil.getAEGasFromContainer(fluidContainer); - if (fluidStack == null) { - continue; - } - final IAEFluidStack aeFluidStack = fluidStack.copy(); - // simulate result is incorrect. so I'm using other solution and ec2 both mod have same issues - final IAEFluidStack notInserted = this.host.getFluidInventory() - .injectItems(aeFluidStack, Actionable.MODULATE, this.getActionSource()); - MutablePair drainStack = AspectUtil - .drainEssentiaFromGas(out.copy(), aeFluidStack.getFluidStack()); - if (notInserted != null && notInserted.getStackSize() > 0 && drainStack != null) { - if (fluidStack.getStackSize() == notInserted.getStackSize()) continue; - aeFluidStack.decStackSize(notInserted.getStackSize()); + public void postChange(IAEFluidStack clientSelectedFluid, EntityPlayer player, int slotIndex, boolean shift) { + ItemStack targetStack; + if (slotIndex == -1) { + targetStack = player.inventory.getItemStack(); + } else { + targetStack = player.inventory.getStackInSlot(slotIndex); + } + final int containersRequested = shift ? targetStack.stackSize : 1; - if (drainStack.left > aeFluidStack.getStackSize() - && AspectUtil.isEssentiaContainer(drainStack.right)) { - aeFluidStack.setStackSize(drainStack.left - aeFluidStack.getStackSize()); - this.host.getFluidInventory() - .extractItems(aeFluidStack, Actionable.MODULATE, this.getActionSource()); - continue; - } - - // drop empty item - this.dropItem(drainStack.right, (int) (aeFluidStack.getStackSize() / drainStack.left)); - out.stackSize = (int) (notInserted.getStackSize() / drainStack.left); - if (AspectUtil.isEssentiaContainer(drainStack.right)) { - if (notInserted.getStackSize() % drainStack.left > 0) { - fluidStack.setStackSize((notInserted.getStackSize() % drainStack.left)); - AspectUtil.fillEssentiaFromGas(drainStack.right, fluidStack.getFluidStack()); - this.dropItem(drainStack.right, 1); - } - } - if (slotIndex == -1) out.stackSize = fluidContainer.stackSize - out.stackSize; - } else if (drainStack != null) { - if (slotIndex == -1) { - out.stackSize = fluidContainer.stackSize; - } else { - out.stackSize = (int) (fluidContainer.stackSize - - (aeFluidStack.getStackSize() / drainStack.left)); - } - this.dropItem(drainStack.right, fluidContainer.stackSize); // drop empty item - } - } else { - continue; + IAEFluidStack storedFluid = this.monitor.getStorageList().findPrecise(clientSelectedFluid); + ItemStack out = targetStack.copy(); + out.stackSize = 1; + if (AspectUtil.isEmptyEssentiaContainer(targetStack) && AspectUtil.isEssentiaGas(storedFluid)) { + // add essentia to jars + if (storedFluid.getStackSize() / AspectUtil.R <= 0) { + return; } - if (slotIndex == -1) { - player.inventory.getItemStack().stackSize = player.inventory.getItemStack().stackSize - out.stackSize; - if (player.inventory.getItemStack().stackSize > 0) { - FluidCraft.proxy.netHandler.sendTo( - new SPacketFluidUpdate(new HashMap<>(), player.inventory.getItemStack()), - (EntityPlayerMP) player); + final IAEFluidStack toExtract = storedFluid.copy(); + MutablePair fillStack = AspectUtil.fillEssentiaFromGas(out, toExtract.getFluidStack()); + if (fillStack == null || fillStack.right == null || fillStack.left <= 0) { + return; + } + toExtract.setStackSize((long) fillStack.left * containersRequested); + IAEFluidStack tmp = this.host.getFluidInventory() + .extractItems(toExtract, Actionable.SIMULATE, this.getActionSource()); + if (tmp == null) { + return; + } + fillStack.right.stackSize = (int) (tmp.getStackSize() / fillStack.left); + this.dropItem(fillStack.right); + out.stackSize = fillStack.right.stackSize; + if (AspectUtil.isEssentiaContainer(fillStack.right)) { + this.host.getFluidInventory().extractItems(toExtract, Actionable.MODULATE, this.getActionSource()); + if ((int) (tmp.getStackSize() % fillStack.left) / AspectUtil.R > 0) { + this.dropItem( + AspectUtil.setAspectAmount( + fillStack.right, + (int) (tmp.getStackSize() % fillStack.left) / AspectUtil.R, + AspectUtil.getAspectFromGas(toExtract)), + 1); + out.stackSize++; + } + } + } else if (!AspectUtil.isEmptyEssentiaContainer(targetStack)) { + // add essentia to ae network + ItemStack containerStack = targetStack.copy(); + containerStack.stackSize = containersRequested; + IAEFluidStack fluidStack = AspectUtil.getAEGasFromContainer(containerStack); + if (fluidStack == null) { + return; + } + final IAEFluidStack aeFluidStack = fluidStack.copy(); + // simulate result is incorrect. so I'm using other solution and ec2 both mod have same issues + final IAEFluidStack notInserted = this.host.getFluidInventory() + .injectItems(aeFluidStack, Actionable.MODULATE, this.getActionSource()); + MutablePair drainStack = AspectUtil + .drainEssentiaFromGas(out.copy(), aeFluidStack.getFluidStack()); + if (notInserted != null && notInserted.getStackSize() > 0 && drainStack != null) { + if (fluidStack.getStackSize() == notInserted.getStackSize()) { + return; + } + aeFluidStack.decStackSize(notInserted.getStackSize()); + + if (drainStack.left > aeFluidStack.getStackSize() && AspectUtil.isEssentiaContainer(drainStack.right)) { + aeFluidStack.setStackSize(drainStack.left - aeFluidStack.getStackSize()); + this.host.getFluidInventory() + .extractItems(aeFluidStack, Actionable.MODULATE, this.getActionSource()); + return; + } + + // drop empty item + this.dropItem(drainStack.right, (int) (aeFluidStack.getStackSize() / drainStack.left)); + out.stackSize = (int) (notInserted.getStackSize() / drainStack.left); + if (AspectUtil.isEssentiaContainer(drainStack.right)) { + if (notInserted.getStackSize() % drainStack.left > 0) { + fluidStack.setStackSize((notInserted.getStackSize() % drainStack.left)); + AspectUtil.fillEssentiaFromGas(drainStack.right, fluidStack.getFluidStack()); + this.dropItem(drainStack.right, 1); + } + } + if (slotIndex == -1) out.stackSize = targetStack.stackSize - out.stackSize; + } else if (drainStack != null) { + if (slotIndex == -1) { + out.stackSize = targetStack.stackSize; } else { - player.inventory.setItemStack(null); - FluidCraft.proxy.netHandler - .sendTo(new SPacketFluidUpdate(new HashMap<>()), (EntityPlayerMP) player); + out.stackSize = (int) (targetStack.stackSize - (aeFluidStack.getStackSize() / drainStack.left)); } - } else { - player.inventory.setInventorySlotContents(slotIndex, out.stackSize > 0 ? out : null); + this.dropItem(drainStack.right, targetStack.stackSize); // drop empty item } + } else { + return; + } + if (slotIndex == -1) { + player.inventory.getItemStack().stackSize = player.inventory.getItemStack().stackSize - out.stackSize; + if (player.inventory.getItemStack().stackSize > 0) { + FluidCraft.proxy.netHandler.sendTo( + new SPacketFluidUpdate(new HashMap<>(), player.inventory.getItemStack()), + (EntityPlayerMP) player); + } else { + player.inventory.setItemStack(null); + FluidCraft.proxy.netHandler.sendTo(new SPacketFluidUpdate(new HashMap<>()), (EntityPlayerMP) player); + } + } else { + player.inventory.setInventorySlotContents(slotIndex, out.stackSize > 0 ? out : null); } this.detectAndSendChanges(); } diff --git a/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidMonitor.java b/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidMonitor.java index ad1bb187..e73f55e3 100644 --- a/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidMonitor.java +++ b/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidMonitor.java @@ -3,7 +3,6 @@ package com.glodblock.github.client.gui.container; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Map; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; @@ -18,7 +17,7 @@ import net.minecraftforge.fluids.IFluidContainerItem; import com.glodblock.github.FluidCraft; import com.glodblock.github.client.gui.container.base.FCContainerMonitor; -import com.glodblock.github.common.item.ItemFluidDrop; +import com.glodblock.github.common.item.ItemFluidPacket; import com.glodblock.github.network.CPacketFluidUpdate; import com.glodblock.github.network.SPacketFluidUpdate; import com.glodblock.github.network.SPacketMEUpdateBuffer; @@ -44,7 +43,6 @@ import appeng.container.slot.SlotRestrictedInput; import appeng.me.helpers.ChannelPowerSrc; import appeng.util.Platform; import appeng.util.item.AEFluidStack; -import appeng.util.item.AEItemStack; public class ContainerFluidMonitor extends FCContainerMonitor { @@ -124,11 +122,8 @@ public class ContainerFluidMonitor extends FCContainerMonitor { Slot clickSlot = (Slot) this.inventorySlots.get(idx); if ((clickSlot instanceof SlotPlayerInv || clickSlot instanceof SlotPlayerHotBar) && clickSlot.getHasStack() && Util.FluidUtil.isFluidContainer(clickSlot.getStack())) { - ItemStack tis = clickSlot.getStack(); - Map tmp = new HashMap<>(); - tmp.put(0, ItemFluidDrop.getAeFluidStack(AEItemStack.create(tis))); int index = Util.findItemInPlayerInvSlot(p, clickSlot.getStack()); - FluidCraft.proxy.netHandler.sendToServer(new CPacketFluidUpdate(tmp, tis, index)); + FluidCraft.proxy.netHandler.sendToServer(new CPacketFluidUpdate(index)); } } return super.transferStackInSlot(p, idx); @@ -211,72 +206,26 @@ public class ContainerFluidMonitor extends FCContainerMonitor { * @param slotIndex The slot. If index == -1, the cursor held itemstack. Else, slotIndex indicates which slot was * shift clicked on. */ - public void postChange(Iterable change, ItemStack fluidContainer, EntityPlayer player, - int slotIndex) { - int cursorItems; + public void postChange(IAEFluidStack fluid, EntityPlayer player, int slotIndex, boolean shift) { + ItemStack targetStack; if (slotIndex == -1) { - cursorItems = player.inventory.getItemStack().stackSize; + targetStack = player.inventory.getItemStack(); } else { - cursorItems = player.inventory.getStackInSlot(slotIndex).stackSize; + targetStack = player.inventory.getStackInSlot(slotIndex); } - for (IAEFluidStack fluid : change) { - // The primary output itemstack - if (Util.FluidUtil.isEmpty(fluidContainer) && fluid != null) { - // Situation 1.a: Empty fluid container, and nonnull slot - extractFluid(fluid, fluidContainer, player, cursorItems); - } else if (!Util.FluidUtil.isEmpty(fluidContainer)) { - // Situation 2.a: We are holding a non-empty container. - insertFluid(fluidContainer, player, slotIndex, cursorItems); - // End of situation 2.a - } - // No op (Any other situation) + // The primary output itemstack + if (Util.FluidUtil.isEmpty(targetStack) && fluid != null) { + // Situation 1.a: Empty fluid container, and nonnull slot + extractFluid(fluid, player, slotIndex, shift); + } else if (!Util.FluidUtil.isEmpty(targetStack)) { + // Situation 2.a: We are holding a non-empty container. + insertFluid(player, slotIndex, shift); + // End of situation 2.a } + // No op (Any other situation) this.detectAndSendChanges(); } - /** - * Insert fluid into the ME system - * - * @param canSend the fluidstack to insert - * @param results a tuple of at least size 3 to indicate outputs (1), (2), (3). This will be mutated! - * @param numTanks number of tanks involved in the operation - * @param fluidInTank how much fluid is in the tank initially - * @return the amount of fluid remaining in output (3) - * @see #insertFluid(ItemStack, EntityPlayer, int, int) - */ - private int insertME(IAEFluidStack canSend, int[] results, int numTanks, int fluidInTank) { - final IAEFluidStack notInserted = this.host.getFluidInventory() - .injectItems(canSend, Actionable.MODULATE, this.getActionSource()); - - // Check if we need to handle partial insertion. - if (notInserted != null && notInserted.getStackSize() > 0) { - if (canSend.getStackSize() == notInserted.getStackSize()) { - // No-op for when system is full - results[REM_IDX] = numTanks; - return fluidInTank; - } - // Populate result list with outputs (1), (2), (3). - results[REM_IDX] = (int) (notInserted.getStackSize() / fluidInTank); - results[ACT_IDX] = numTanks - results[REM_IDX]; - - // If there is remaining fluid, set partial. - final int remainder = (int) (notInserted.getStackSize() % fluidInTank); - if (remainder > 0) { - results[PARTIAL_IDX] = 1; - results[ACT_IDX]--; - return remainder; - } else { - results[PARTIAL_IDX] = 0; - } - } else { - // Full operation successful - results[REM_IDX] = 0; - results[ACT_IDX] = numTanks; - results[PARTIAL_IDX] = 0; - } - return 0; - } - /** * The insert operation. For input, we have a filled container stack. For outputs, we have the following: *
    @@ -286,119 +235,153 @@ public class ContainerFluidMonitor extends FCContainerMonitor { *
* In order above, the itemstack at `slotIndex` is transformed into the output. */ - private void insertFluid(ItemStack fluidContainer, EntityPlayer player, int slotIndex, int heldContainers) { - IAEFluidStack canSend; - int fluidPerContainer; - ItemStack partialStack = null; - ItemStack emptyStack = null; - int[] insertionResults = new int[3]; + private void insertFluid(EntityPlayer player, int slotIndex, boolean shift) { + final ItemStack targetStack; + if (slotIndex == -1) { + targetStack = player.inventory.getItemStack(); + } else { + targetStack = player.inventory.getStackInSlot(slotIndex); + } + final int containersRequestedToInsert = shift ? targetStack.stackSize : 1; - // 2 types of containers: IFluidContainerItem and FluidContainerRegistry - // IFluidContainerItem - items are filled w/ volume (portable tanks) - // FluidContainerRegistry - items are filled, or not (buckets) - // The former is easy, the latter takes a bit more work. - if (fluidContainer.getItem() instanceof IFluidContainerItem fcItem) { - // Step 1: Find out how much fluid we can insert. - canSend = AEFluidStack.create(fcItem.getFluid(fluidContainer)); - fluidPerContainer = (int) canSend.getStackSize(); - canSend.setStackSize((long) fluidPerContainer * fluidContainer.stackSize); - - // Step 2: Find out how much fluid we can extract from the container. If this is null or 0, return. - ItemStack test = fluidContainer.copy(); + // Step 1: Determine container characteristics and verify fluid to be extractable + final int fluidPerContainer; + final FluidStack fluidStackPerContainer; + final boolean partialInsertSupported; + if (targetStack.getItem() instanceof IFluidContainerItem fcItem) { + ItemStack test = targetStack.copy(); test.stackSize = 1; - FluidStack fluidStack = fcItem.drain(test, fluidPerContainer, false); - if (fluidStack == null || fluidStack.amount == 0) { + fluidStackPerContainer = fcItem.drain(test, Integer.MAX_VALUE, false); + if (fluidStackPerContainer == null || fluidStackPerContainer.amount == 0) { return; } - // Step 3: Find out how much fluid we can insert into the ME system - int partialAmount = insertME(canSend, insertionResults, fluidContainer.stackSize, fluidPerContainer); - - // Step 4: Separate the outputs into no drain (1), fully drained (2), and partially drained (3) - // 4.1: Handle the partial stack - if (insertionResults[PARTIAL_IDX] > 0) { - partialStack = fluidContainer.copy(); - partialStack.stackSize = 1; - fcItem.drain(partialStack, fluidPerContainer - partialAmount, true); - } - - // 4.2: Handle empty output - if (insertionResults[ACT_IDX] > 0) { - emptyStack = fluidContainer.copy(); - emptyStack.stackSize = 1; - fcItem.drain(emptyStack, fluidPerContainer, true); - emptyStack.stackSize = insertionResults[ACT_IDX]; - } - } else if (FluidContainerRegistry.isContainer(fluidContainer)) { - // Step 1: Find out how much fluid we can insert. - canSend = AEFluidStack.create(FluidContainerRegistry.getFluidForFilledItem(fluidContainer)); - fluidPerContainer = (int) canSend.getStackSize(); - canSend.setStackSize((long) fluidPerContainer * fluidContainer.stackSize); - // Step 2: Find out how much fluid we can extract from the container. If this is null or 0, return. - ItemStack emptyTank = FluidContainerRegistry.drainFluidContainer(fluidContainer); + fluidPerContainer = fluidStackPerContainer.amount; + partialInsertSupported = true; + } else if (FluidContainerRegistry.isContainer(targetStack)) { + ItemStack emptyTank = FluidContainerRegistry.drainFluidContainer(targetStack); if (emptyTank == null) { return; } - // Step 3: Find out how much fluid we can insert into the ME system - int partialAmount = insertME(canSend, insertionResults, fluidContainer.stackSize, fluidPerContainer); - // Step 4: Separate the outputs into no drain (1), fully drained (2), and partially drained (3) - // 4.1: Handle the partial stack - if (partialAmount > 0) { - // We now extract the extra amount from the ME system. Blame simulate not working :P - int extract = FluidContainerRegistry.getContainerCapacity(fluidContainer) - partialAmount; - if (extract > 0) { - IAEFluidStack toExtract = canSend.copy().setStackSize(extract); - this.host.getFluidInventory().extractItems(toExtract, Actionable.MODULATE, this.getActionSource()); - } - insertionResults[PARTIAL_IDX] = 0; - } - - // 4.2: Handle empty output - if (insertionResults[ACT_IDX] > 0) { - emptyStack = FluidContainerRegistry.drainFluidContainer(fluidContainer); - emptyStack.stackSize = insertionResults[ACT_IDX]; - } + fluidStackPerContainer = FluidContainerRegistry.getFluidForFilledItem(targetStack); + fluidPerContainer = fluidStackPerContainer.amount; + partialInsertSupported = false; } else { return; } + // Step 2: determine network capacity + final IAEFluidStack totalFluid = AEFluidStack.create(fluidStackPerContainer); + totalFluid.setStackSize((long) fluidPerContainer * containersRequestedToInsert); + + final IAEFluidStack notInsertable = this.host.getFluidInventory() + .injectItems(totalFluid, Actionable.SIMULATE, this.getActionSource()); + + final long insertableFluid; + if (notInsertable == null || notInsertable.getStackSize() == 0) { + insertableFluid = totalFluid.getStackSize(); + } else { + long insertable = totalFluid.getStackSize() - notInsertable.getStackSize(); + if (partialInsertSupported) { + insertableFluid = insertable; + } else { + // avoid remainder + insertableFluid = insertable - (insertable % fluidPerContainer); + } + } + totalFluid.setStackSize(insertableFluid); + + // Step 3: perform insert + final long totalInserted; + final IAEFluidStack notInserted = this.host.getFluidInventory() + .injectItems(totalFluid, Actionable.MODULATE, this.getActionSource()); + if (notInserted != null && notInserted.getStackSize() > 0) { + // User has a setup that causes discrepancy between simulation and modulation. Likely double storage bus. + long total = totalFluid.getStackSize() - notInserted.getStackSize(); + if (partialInsertSupported) { + totalInserted = total; + } else { + // We cant have partially filled containers -> user will receive a fluid packet as last resort + long overflowAmount = fluidPerContainer - (total % fluidPerContainer); + IAEFluidStack overflow = AEFluidStack.create(fluidStackPerContainer); + overflow.setStackSize(overflowAmount); + dropItem(ItemFluidPacket.newStack(overflow)); + totalInserted = total + overflowAmount; + } + } else { + totalInserted = totalFluid.getStackSize(); + } + + // Step 4: calculate outputs + final int emptiedTanks = (int) (totalInserted / fluidPerContainer); + final int partialDrain = (int) (totalInserted % fluidPerContainer); + final int partialTanks = partialDrain > 0 && partialInsertSupported ? 1 : 0; + final int usedTanks = emptiedTanks + partialTanks; + final int untouchedTanks = targetStack.stackSize - usedTanks; + + final ItemStack emptiedTanksStack; + final ItemStack partialTanksStack; + + if (targetStack.getItem() instanceof IFluidContainerItem fcItem) { + if (emptiedTanks > 0) { + emptiedTanksStack = targetStack.copy(); + emptiedTanksStack.stackSize = 1; + fcItem.drain(emptiedTanksStack, fluidPerContainer, true); + emptiedTanksStack.stackSize = emptiedTanks; + } else { + emptiedTanksStack = null; + } + if (partialTanks > 0) { + partialTanksStack = targetStack.copy(); + partialTanksStack.stackSize = 1; + fcItem.drain(partialTanksStack, partialDrain, true); + } else { + partialTanksStack = null; + } + } else { + if (emptiedTanks > 0) { + emptiedTanksStack = FluidContainerRegistry.drainFluidContainer(targetStack); + emptiedTanksStack.stackSize = emptiedTanks; + } else { + emptiedTanksStack = null; + } + // Not possible > see Step 2 and Step 3 + partialTanksStack = null; + } + // Done. Put the output in the inventory or ground, and update stack size. boolean shouldSendStack = true; - int emptyTanks = insertionResults[ACT_IDX]; - int extraTanks = heldContainers - emptyTanks - insertionResults[PARTIAL_IDX]; if (slotIndex == -1) { // Item is in mouse hand - if (extraTanks > 0) { - ItemStack stack = player.inventory.getItemStack(); - stack.stackSize = extraTanks; - adjustStack(stack); - dropItem(emptyStack); - dropItem(partialStack); - } else if (emptyTanks != 0) { - adjustStack(emptyStack); - player.inventory.setItemStack(emptyStack); - dropItem(partialStack); - } else if (partialStack != null) { - player.inventory.setItemStack(partialStack); + if (untouchedTanks > 0) { + targetStack.stackSize = untouchedTanks; + adjustStack(targetStack); + dropItem(emptiedTanksStack); + dropItem(partialTanksStack); + } else if (emptiedTanksStack != null) { + adjustStack(emptiedTanksStack); + player.inventory.setItemStack(emptiedTanksStack); + dropItem(partialTanksStack); + } else if (partialTanksStack != null) { + player.inventory.setItemStack(partialTanksStack); } else { player.inventory.setItemStack(null); shouldSendStack = false; } } else { // Shift clicked in - ItemStack stack = player.inventory.getStackInSlot(slotIndex); - if (extraTanks > 0) { - stack.stackSize = extraTanks; - adjustStack(stack); - dropItem(emptyStack); - dropItem(partialStack); - } else if (emptyTanks != 0) { - adjustStack(emptyStack); - player.inventory.setInventorySlotContents(slotIndex, emptyStack); - dropItem(partialStack); - } else if (partialStack != null) { - player.inventory.setInventorySlotContents(slotIndex, partialStack); + if (untouchedTanks > 0) { + targetStack.stackSize = untouchedTanks; + adjustStack(targetStack); + dropItem(emptiedTanksStack); + dropItem(partialTanksStack); + } else if (emptiedTanksStack != null) { + adjustStack(emptiedTanksStack); + player.inventory.setInventorySlotContents(slotIndex, emptiedTanksStack); + dropItem(partialTanksStack); + } else if (partialTanksStack != null) { + player.inventory.setInventorySlotContents(slotIndex, partialTanksStack); } else { player.inventory.setItemStack(null); shouldSendStack = false; @@ -422,97 +405,119 @@ public class ContainerFluidMonitor extends FCContainerMonitor { * * In order above, the itemstack at `slotIndex` is transformed into the output. */ - private void extractFluid(IAEFluidStack fluid, ItemStack fluidContainer, EntityPlayer player, int heldContainers) { - // Step 1: Check if fluid can actually get filled into the fluidContainer - if (fluidContainer.getItem() instanceof IFluidContainerItem fcItem) { - ItemStack testStack = fluidContainer.copy(); + private void extractFluid(IAEFluidStack clientRequestedFluid, EntityPlayer player, int slotIndex, boolean shift) { + if (slotIndex != -1) { + // shift-click from inventory cant fill fluids + return; + } + final ItemStack targetStack = player.inventory.getItemStack(); + final int containersRequestedToExtract = shift ? targetStack.stackSize : 1; + + final FluidStack clientRequestedFluidStack = clientRequestedFluid.getFluidStack(); + clientRequestedFluidStack.amount = Integer.MAX_VALUE; + + // Step 1: Determine container characteristics and verify fluid to be insertable + final int fluidPerContainer; + final boolean partialInsertSupported; + if (targetStack.getItem() instanceof IFluidContainerItem fcItem) { + ItemStack testStack = targetStack.copy(); testStack.stackSize = 1; - int test = fcItem.fill(testStack, fluid.getFluidStack(), false); - if (test == 0) { - return; - } - } else if (FluidContainerRegistry.isContainer(fluidContainer)) { - ItemStack test = FluidContainerRegistry.fillFluidContainer(fluid.getFluidStack(), fluidContainer); - if (test == null) { + fluidPerContainer = fcItem.fill(testStack, clientRequestedFluidStack, false); + if (fluidPerContainer == 0) { return; } + partialInsertSupported = true; + } else if (FluidContainerRegistry.isContainer(targetStack)) { + fluidPerContainer = FluidContainerRegistry.getContainerCapacity(clientRequestedFluidStack, targetStack); + partialInsertSupported = false; } else { return; } - IAEFluidStack storedFluid = this.monitor.getStorageList().findPrecise(fluid); - if (storedFluid == null || storedFluid.getStackSize() <= 0) return; - int capacity = Util.FluidUtil.getCapacity(fluidContainer, storedFluid.getFluid()); - if (capacity == 0) return; - // The fluidstack that we will try to extract from the system. - final IAEFluidStack canExtract = storedFluid.copy(); - // Maximum amount of fluid we can extract with the given container(s). Guarantees that we won't - // run into issues w/ remainder fluid later. - canExtract.setStackSize((long) capacity * fluidContainer.stackSize); - IAEFluidStack actualExtract = this.host.getFluidInventory() - .extractItems(canExtract, Actionable.MODULATE, this.getActionSource()); - if (actualExtract == null) return; - // Calculate the number of full fluid containers we extracted - long toExtract = actualExtract.getStackSize(); - int filledTanks = (int) (toExtract / capacity); - int remainder = (int) (toExtract % capacity); - int emptyTanks = heldContainers - filledTanks; - // Fill the filled tanks - ItemStack filledStack = fluidContainer.copy(); - filledStack.stackSize = 1; - if (filledTanks > 0) { - if (filledStack.getItem() instanceof IFluidContainerItem) { - int amt = ((IFluidContainerItem) filledStack.getItem()) - .fill(filledStack, actualExtract.getFluidStack(), true); - assert amt == capacity; - } else { - // Should not be NPE since we affirmed that we have enough capacity. - filledStack = FluidContainerRegistry.fillFluidContainer(actualExtract.getFluidStack(), filledStack); - assert capacity == FluidContainerRegistry.getContainerCapacity(filledStack); - } - filledStack.stackSize = filledTanks; - } else { - filledStack.stackSize = 0; + + // Step 2: determine fluid in network + final IAEFluidStack totalRequestedFluid = clientRequestedFluid.copy(); + totalRequestedFluid.setStackSize((long) fluidPerContainer * containersRequestedToExtract); + + final IAEFluidStack availableFluid = this.host.getFluidInventory() + .extractItems(totalRequestedFluid, Actionable.SIMULATE, this.getActionSource()); + if (availableFluid == null || availableFluid.getStackSize() == 0) { + return; } - // Calculate the remaining fluid to extract, if any - ItemStack partialStack = fluidContainer.copy(); - if (remainder > 0) { - actualExtract.setStackSize(remainder); - partialStack.stackSize = 1; - if (partialStack.getItem() instanceof IFluidContainerItem) { - int amt = ((IFluidContainerItem) partialStack.getItem()) - .fill(partialStack, actualExtract.getFluidStack(), true); - assert amt == remainder; - emptyTanks--; + + if (availableFluid.getStackSize() != totalRequestedFluid.getStackSize() && !partialInsertSupported) { + availableFluid.decStackSize(availableFluid.getStackSize() % fluidPerContainer); + } + + // Step 3: perform extract + final IAEFluidStack extracted = this.host.getFluidInventory() + .extractItems(availableFluid, Actionable.MODULATE, this.getActionSource()); + final long totalExtracted = extracted != null ? extracted.getStackSize() : 0; + + // Step 4: calculate outputs + final int filledTanks = (int) (totalExtracted / fluidPerContainer); + final int partialFill = (int) (totalExtracted % fluidPerContainer); + final int partialTanks = partialFill > 0 && partialInsertSupported ? 1 : 0; + final int usedTanks = filledTanks + partialTanks; + final int untouchedTanks = targetStack.stackSize - usedTanks; + + final ItemStack filledTanksStack; + final ItemStack partialTanksStack; + + if (targetStack.getItem() instanceof IFluidContainerItem fcItem) { + if (filledTanks > 0) { + filledTanksStack = targetStack.copy(); + filledTanksStack.stackSize = 1; + FluidStack toInsert = extracted.getFluidStack().copy(); + toInsert.amount = fluidPerContainer; + fcItem.fill(filledTanksStack, toInsert, true); + filledTanksStack.stackSize = filledTanks; } else { - ItemStack dummy = FluidContainerRegistry - .fillFluidContainer(actualExtract.getFluidStack(), partialStack); - if (dummy == null) { - // Failed to fill partial... - partialStack.stackSize = 0; - } else { - partialStack = dummy; - emptyTanks--; - } + filledTanksStack = null; + } + if (partialTanks > 0) { + partialTanksStack = targetStack.copy(); + partialTanksStack.stackSize = 1; + FluidStack toInsert = extracted.getFluidStack().copy(); + toInsert.amount = partialFill; + fcItem.fill(partialTanksStack, toInsert, true); + } else { + partialTanksStack = null; } } else { - partialStack.stackSize = 0; + if (filledTanks > 0) { + FluidStack toInsert = extracted.getFluidStack().copy(); + toInsert.amount = fluidPerContainer; + filledTanksStack = FluidContainerRegistry.fillFluidContainer(toInsert, targetStack); + filledTanksStack.stackSize = filledTanks; + } else { + filledTanksStack = null; + } + if (partialFill > 0) { + // User has a setup that causes discrepancy between simulation and modulation. Likely double storage + // bus. + // We cant have partially filled containers -> user will receive a fluid packet as last resort + IAEFluidStack overflow = extracted.copy(); + overflow.setStackSize(partialFill); + dropItem(ItemFluidPacket.newStack(overflow)); + } + partialTanksStack = null; } // Done. Put the output in the inventory or ground, and update stack size. // We can assume slotIndex == -1, since we don't actually allow extraction via shift click. boolean shouldSendStack = true; - if (emptyTanks > 0) { + if (untouchedTanks > 0) { ItemStack emptyStack = player.inventory.getItemStack(); - emptyStack.stackSize = emptyTanks; + emptyStack.stackSize = untouchedTanks; adjustStack(emptyStack); - dropItem(filledStack); - dropItem(partialStack); - } else if (filledStack.stackSize != 0) { - adjustStack(filledStack); - player.inventory.setItemStack(filledStack); - dropItem(partialStack); - } else if (partialStack.stackSize != 0) { - player.inventory.setItemStack(partialStack); + dropItem(filledTanksStack); + dropItem(partialTanksStack); + } else if (filledTanksStack != null) { + adjustStack(filledTanksStack); + player.inventory.setItemStack(filledTanksStack); + dropItem(partialTanksStack); + } else if (partialTanksStack != null) { + player.inventory.setItemStack(partialTanksStack); } else { player.inventory.setItemStack(null); shouldSendStack = false; diff --git a/src/main/java/com/glodblock/github/network/CPacketFluidUpdate.java b/src/main/java/com/glodblock/github/network/CPacketFluidUpdate.java index 6e6d0ca9..6461e24e 100644 --- a/src/main/java/com/glodblock/github/network/CPacketFluidUpdate.java +++ b/src/main/java/com/glodblock/github/network/CPacketFluidUpdate.java @@ -1,19 +1,14 @@ package com.glodblock.github.network; import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.Container; -import net.minecraft.item.ItemStack; import com.glodblock.github.client.gui.container.ContainerFluidMonitor; -import com.glodblock.github.util.Util; import appeng.api.storage.data.IAEFluidStack; -import appeng.util.item.AEItemStack; +import appeng.util.item.AEFluidStack; import cpw.mods.fml.common.network.simpleimpl.IMessage; import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; import cpw.mods.fml.common.network.simpleimpl.MessageContext; @@ -21,37 +16,42 @@ import io.netty.buffer.ByteBuf; public class CPacketFluidUpdate implements IMessage { - private Map list; - private ItemStack itemStack; + private IAEFluidStack fluid; + private boolean shift; private int slotIndex; public CPacketFluidUpdate() {} - public CPacketFluidUpdate(Map data, ItemStack itemStack) { - this.list = data; - this.itemStack = itemStack; - this.slotIndex = -1; + /** + * Used by transferStackInSlot. No clicked fluid, always shift. + */ + public CPacketFluidUpdate(int slotIndex) { + this(null, slotIndex, true); } - public CPacketFluidUpdate(Map data) { - this.list = data; + /** + * Used by handleMouseClick. Always uses mouse stack. + */ + public CPacketFluidUpdate(IAEFluidStack fluid, boolean shift) { + this(fluid, -1, shift); } - public CPacketFluidUpdate(Map data, ItemStack itemStack, int slotIndex) { - this.list = data; - this.itemStack = itemStack; + private CPacketFluidUpdate(IAEFluidStack fluid, int slotIndex, boolean shift) { + this.fluid = fluid; this.slotIndex = slotIndex; + this.shift = shift; } @Override public void fromBytes(ByteBuf buf) { - this.list = new HashMap<>(); try { - Util.readFluidMapFromBuf(this.list, buf); if (buf.readBoolean()) { - this.itemStack = AEItemStack.loadItemStackFromPacket(buf).getItemStack(); - this.slotIndex = buf.readInt(); + this.fluid = AEFluidStack.loadFluidStackFromPacket(buf); + } else { + this.fluid = null; } + this.slotIndex = buf.readInt(); + this.shift = buf.readBoolean(); } catch (IOException e) { e.printStackTrace(); } @@ -60,14 +60,14 @@ public class CPacketFluidUpdate implements IMessage { @Override public void toBytes(ByteBuf buf) { try { - Util.writeFluidMapToBuf(this.list, buf); - if (this.itemStack != null) { - buf.writeBoolean(true); - AEItemStack.create(itemStack).writeToPacket(buf); - buf.writeInt(this.slotIndex); - } else { + if (fluid == null) { buf.writeBoolean(false); + } else { + buf.writeBoolean(true); + fluid.writeToPacket(buf); } + buf.writeInt(this.slotIndex); + buf.writeBoolean(this.shift); } catch (IOException e) { e.printStackTrace(); } @@ -80,12 +80,7 @@ public class CPacketFluidUpdate implements IMessage { Container container = ctx.getServerHandler().playerEntity.openContainer; EntityPlayer player = ctx.getServerHandler().playerEntity; if (container instanceof ContainerFluidMonitor) { - ItemStack item = player.inventory.getItemStack(); - ((ContainerFluidMonitor) container).postChange( - new ArrayList<>(message.list.values()), - message.itemStack == null ? item : message.itemStack, - player, - message.slotIndex); + ((ContainerFluidMonitor) container).postChange(message.fluid, player, message.slotIndex, message.shift); } return null; } diff --git a/src/main/java/com/glodblock/github/util/Util.java b/src/main/java/com/glodblock/github/util/Util.java index 795c1e02..2bed4fba 100644 --- a/src/main/java/com/glodblock/github/util/Util.java +++ b/src/main/java/com/glodblock/github/util/Util.java @@ -229,8 +229,7 @@ public final class Util { } public static AEFluidStack getAEFluidFromItem(ItemStack stack) { - if (stack != null - && (stack.getItem() instanceof IFluidContainerItem || FluidContainerRegistry.isContainer(stack))) { + if (stack != null) { if (stack.getItem() instanceof IFluidContainerItem) { FluidStack fluid = ((IFluidContainerItem) stack.getItem()).getFluid(stack); if (fluid != null) { @@ -252,8 +251,7 @@ public final class Util { } public static FluidStack getFluidFromItem(ItemStack stack) { - if (stack != null - && (stack.getItem() instanceof IFluidContainerItem || FluidContainerRegistry.isContainer(stack))) { + if (stack != null) { if (stack.getItem() instanceof IFluidContainerItem) { FluidStack fluid = ((IFluidContainerItem) stack.getItem()).getFluid(stack); if (fluid != null) {