From 2d6d360da4a968792106427d53c9dad56abeb338 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Thu, 24 Sep 2020 13:39:40 +0200 Subject: [PATCH] Workstation transfer, Part II - Intermediate changes, probably doesn't even compile --- .../fluids/actors/SpoutRenderer.java | 14 +- .../fluids/actors/SpoutTileEntity.java | 112 ++------ .../processing/BasinTileEntity.java | 43 +-- .../foundation/fluid/CombinedTankWrapper.java | 143 ++++++++++ .../tileEntity/behaviour/BehaviourType.java | 14 + .../fluid/SmartFluidTankBehaviour.java | 261 ++++++++++++++++++ 6 files changed, 473 insertions(+), 114 deletions(-) create mode 100644 src/main/java/com/simibubi/create/foundation/fluid/CombinedTankWrapper.java create mode 100644 src/main/java/com/simibubi/create/foundation/tileEntity/behaviour/fluid/SmartFluidTankBehaviour.java diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/actors/SpoutRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/actors/SpoutRenderer.java index a97ee37b3..5b15356fd 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/actors/SpoutRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/actors/SpoutRenderer.java @@ -3,9 +3,9 @@ package com.simibubi.create.content.contraptions.fluids.actors; import com.mojang.blaze3d.matrix.MatrixStack; import com.simibubi.create.AllBlockPartials; import com.simibubi.create.foundation.fluid.FluidRenderer; +import com.simibubi.create.foundation.tileEntity.behaviour.fluid.SmartFluidTankBehaviour; +import com.simibubi.create.foundation.tileEntity.behaviour.fluid.SmartFluidTankBehaviour.TankSegment; import com.simibubi.create.foundation.tileEntity.renderer.SafeTileEntityRenderer; -import com.simibubi.create.foundation.utility.LerpedFloat; -import com.simibubi.create.foundation.utility.Pair; import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.RenderType; @@ -27,9 +27,13 @@ public class SpoutRenderer extends SafeTileEntityRenderer { protected void renderSafe(SpoutTileEntity te, float partialTicks, MatrixStack ms, IRenderTypeBuffer buffer, int light, int overlay) { - Pair fluid = te.getFluid(); - FluidStack fluidStack = fluid.getFirst(); - float level = fluid.getSecond() + SmartFluidTankBehaviour tank = te.tank; + if (tank == null) + return; + + TankSegment primaryTank = tank.getPrimaryTank(); + FluidStack fluidStack = primaryTank.getRenderedFluid(); + float level = primaryTank.getFluidLevel() .getValue(partialTicks); if (!fluidStack.isEmpty() && level != 0) { diff --git a/src/main/java/com/simibubi/create/content/contraptions/fluids/actors/SpoutTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/fluids/actors/SpoutTileEntity.java index b723a4849..3933cae55 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/fluids/actors/SpoutTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/fluids/actors/SpoutTileEntity.java @@ -7,16 +7,13 @@ import java.util.ArrayList; import java.util.List; import com.simibubi.create.content.contraptions.relays.belt.transport.TransportedItemStack; -import com.simibubi.create.foundation.fluid.SmartFluidTank; import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.belt.BeltProcessingBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.belt.BeltProcessingBehaviour.ProcessingResult; import com.simibubi.create.foundation.tileEntity.behaviour.belt.TransportedItemStackHandlerBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.belt.TransportedItemStackHandlerBehaviour.TransportedResult; -import com.simibubi.create.foundation.utility.LerpedFloat; -import com.simibubi.create.foundation.utility.LerpedFloat.Chaser; -import com.simibubi.create.foundation.utility.Pair; +import com.simibubi.create.foundation.tileEntity.behaviour.fluid.SmartFluidTankBehaviour; import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.item.ItemStack; @@ -32,58 +29,22 @@ import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; -import net.minecraftforge.fluids.capability.IFluidHandler; -import net.minecraftforge.fluids.capability.templates.FluidTank; - -// FIXME: Quite similar to FluidTankTileEntity, create a behaviour public class SpoutTileEntity extends SmartTileEntity { - protected FluidTank tank; - protected LazyOptional capability; - protected LerpedFloat fluidLevel; - protected FluidStack renderedFluid; - public static final int FILLING_TIME = 20; - protected int processingTicks; - - private static final int SYNC_RATE = 8; - protected int syncCooldown; - protected boolean queuedSync; - - protected boolean sendSplash; + protected BeltProcessingBehaviour beltProcessing; + protected int processingTicks; + protected boolean sendSplash; + + SmartFluidTankBehaviour tank; public SpoutTileEntity(TileEntityType tileEntityTypeIn) { super(tileEntityTypeIn); - tank = new SmartFluidTank(1000, this::onFluidStackChanged); - capability = LazyOptional.of(() -> tank); - fluidLevel = LerpedFloat.linear() - .startWithValue(0) - .chase(0, .25, Chaser.EXP); - renderedFluid = FluidStack.EMPTY; processingTicks = -1; } - protected void onFluidStackChanged(FluidStack newFluidStack) { - if (!hasWorld()) - return; - fluidLevel.chase(tank.getFluidAmount() / (float) tank.getCapacity(), .25, Chaser.EXP); - if (!world.isRemote) { - markDirty(); - sendData(); - } - } - - @Override - public void initialize() { - super.initialize(); - if (!world.isRemote) { - fluidLevel.forceNextSync(); - onFluidStackChanged(tank.getFluid()); - } - } - @Override public AxisAlignedBB getRenderBoundingBox() { return super.getRenderBoundingBox().expand(0, -2, 0); @@ -91,9 +52,13 @@ public class SpoutTileEntity extends SmartTileEntity { @Override public void addBehaviours(List behaviours) { + tank = SmartFluidTankBehaviour.single(this, 1000); + behaviours.add(tank); + beltProcessing = new BeltProcessingBehaviour(this).whenItemEnters(this::onItemReceived) .whileItemHeld(this::whenItemHeld); behaviours.add(beltProcessing); + } protected ProcessingResult onItemReceived(TransportedItemStack transported, @@ -102,7 +67,7 @@ public class SpoutTileEntity extends SmartTileEntity { return PASS; if (tank.isEmpty()) return HOLD; - if (FillingBySpout.getRequiredAmountForItem(world, transported.stack, tank.getFluid()) == -1) + if (FillingBySpout.getRequiredAmountForItem(world, transported.stack, getCurrentFluidInTank()) == -1) return PASS; return HOLD; } @@ -115,7 +80,7 @@ public class SpoutTileEntity extends SmartTileEntity { return PASS; if (tank.isEmpty()) return HOLD; - FluidStack fluid = tank.getFluid(); + FluidStack fluid = getCurrentFluidInTank(); int requiredAmountForItem = FillingBySpout.getRequiredAmountForItem(world, transported.stack, fluid.copy()); if (requiredAmountForItem == -1) return PASS; @@ -142,24 +107,21 @@ public class SpoutTileEntity extends SmartTileEntity { handler.handleProcessingOnItem(transported, TransportedResult.convertToAndLeaveHeld(outList, held)); } - tank.setFluid(fluid); + tank.getPrimaryHandler().setFluid(fluid); sendSplash = true; markDirty(); sendData(); return PASS; } - @Override - public void remove() { - capability.invalidate(); - super.remove(); + private FluidStack getCurrentFluidInTank() { + return tank.getPrimaryHandler().getFluid(); } @Override protected void write(CompoundNBT compound, boolean clientPacket) { super.write(compound, clientPacket); - compound.put("TankContent", tank.writeToNBT(new CompoundNBT())); - compound.put("Level", fluidLevel.writeNBT()); + compound.putInt("ProcessingTicks", processingTicks); if (sendSplash && clientPacket) { compound.putBoolean("Splash", true); @@ -170,61 +132,27 @@ public class SpoutTileEntity extends SmartTileEntity { @Override protected void read(CompoundNBT compound, boolean clientPacket) { super.read(compound, clientPacket); - tank.readFromNBT(compound.getCompound("TankContent")); - fluidLevel.readNBT(compound.getCompound("Level"), clientPacket); processingTicks = compound.getInt("ProcessingTicks"); - if (!tank.getFluid() - .isEmpty()) - renderedFluid = tank.getFluid(); - if (!clientPacket) return; if (compound.contains("Splash")) - spawnSplash(renderedFluid); + spawnSplash(tank.getPrimaryTank().getRenderedFluid()); } @Override public LazyOptional getCapability(Capability cap, Direction side) { if (cap == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY && side != Direction.DOWN) - return capability.cast(); + return tank.getCapability().cast(); return super.getCapability(cap, side); } - public Pair getFluid() { - return Pair.of(renderedFluid, fluidLevel); - } - - public void sendDataImmediately() { - syncCooldown = 0; - queuedSync = false; - sendData(); - } - - @Override + public void tick() { super.tick(); if (processingTicks >= 0) processingTicks--; if (processingTicks >= 8 && world.isRemote) - spawnProcessingParticles(renderedFluid); - if (syncCooldown > 0) { - syncCooldown--; - if (syncCooldown == 0 && queuedSync) - sendData(); - } - if (fluidLevel != null) - fluidLevel.tickChaser(); - } - - @Override - public void sendData() { - if (syncCooldown > 0) { - queuedSync = true; - return; - } - super.sendData(); - queuedSync = false; - syncCooldown = SYNC_RATE; + spawnProcessingParticles(tank.getPrimaryTank().getRenderedFluid()); } protected void spawnProcessingParticles(FluidStack fluid) { diff --git a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinTileEntity.java index 5dcbc472c..48565aa16 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinTileEntity.java @@ -5,18 +5,18 @@ import java.util.Optional; import javax.annotation.Nonnull; -import com.simibubi.create.content.contraptions.fluids.CombinedFluidHandler; +import com.simibubi.create.foundation.fluid.CombinedTankWrapper; import com.simibubi.create.foundation.item.SmartInventory; import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.ValueBoxTransform; import com.simibubi.create.foundation.tileEntity.behaviour.belt.DirectBeltInputBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour; +import com.simibubi.create.foundation.tileEntity.behaviour.fluid.SmartFluidTankBehaviour; import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.block.BlockState; import net.minecraft.nbt.CompoundNBT; -import net.minecraft.nbt.ListNBT; import net.minecraft.tileentity.ITickableTileEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityType; @@ -27,6 +27,7 @@ import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.wrapper.CombinedInvWrapper; @@ -34,20 +35,25 @@ import net.minecraftforge.items.wrapper.CombinedInvWrapper; public class BasinTileEntity extends SmartTileEntity implements ITickableTileEntity { public BasinInputInventory inputInventory; + public SmartFluidTankBehaviour inputTank; + protected SmartInventory outputInventory; + protected SmartFluidTankBehaviour outputTank; + protected LazyOptional itemCapability; - protected LazyOptional fluidCapability; - - private boolean contentsChanged; + protected LazyOptional fluidCapability; + private FilteringBehaviour filtering; + private boolean contentsChanged; public BasinTileEntity(TileEntityType type) { super(type); inputInventory = new BasinInputInventory(9, this); - inputInventory.withMaxStackSize(8).forbidExtraction(); + inputInventory.withMaxStackSize(8) + .forbidExtraction(); outputInventory = new SmartInventory(9, this).forbidInsertion(); itemCapability = LazyOptional.of(() -> new CombinedInvWrapper(inputInventory, outputInventory)); - fluidCapability = LazyOptional.of(() -> new CombinedFluidHandler(9, 1000)); + contentsChanged = true; } @@ -58,6 +64,16 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt .withCallback(newFilter -> contentsChanged = true) .forRecipes(); behaviours.add(filtering); + + inputTank = new SmartFluidTankBehaviour(SmartFluidTankBehaviour.INPUT, this, 2, 1000, true).forbidExtraction(); + outputTank = new SmartFluidTankBehaviour(SmartFluidTankBehaviour.OUTPUT, this, 2, 1000, true).forbidInsertion(); + behaviours.add(inputTank); + behaviours.add(outputTank); + fluidCapability = LazyOptional.of(() -> { + LazyOptional inputCap = inputTank.getCapability(); + LazyOptional outputCap = outputTank.getCapability(); + return new CombinedTankWrapper(inputCap.orElse(null), outputCap.orElse(null)); + }); } @Override @@ -65,9 +81,6 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt super.read(compound, clientPacket); inputInventory.deserializeNBT(compound.getCompound("InputItems")); outputInventory.deserializeNBT(compound.getCompound("OutputItems")); - if (compound.contains("fluids")) - fluidCapability - .ifPresent(combinedFluidHandler -> combinedFluidHandler.readFromNBT(compound.getList("fluids", 10))); } @Override @@ -75,10 +88,6 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt super.write(compound, clientPacket); compound.put("InputItems", inputInventory.serializeNBT()); compound.put("OutputItems", outputInventory.serializeNBT()); - fluidCapability.ifPresent(combinedFuidHandler -> { - ListNBT nbt = combinedFuidHandler.getListNBT(); - compound.put("fluids", nbt); - }); } public void onEmptied() { @@ -123,15 +132,15 @@ public class BasinTileEntity extends SmartTileEntity implements ITickableTileEnt public FilteringBehaviour getFilter() { return filtering; } - + public void notifyChangeOfContents() { contentsChanged = true; } - + public SmartInventory getInputInventory() { return inputInventory; } - + public SmartInventory getOutputInventory() { return outputInventory; } diff --git a/src/main/java/com/simibubi/create/foundation/fluid/CombinedTankWrapper.java b/src/main/java/com/simibubi/create/foundation/fluid/CombinedTankWrapper.java new file mode 100644 index 000000000..2a5bb5661 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/fluid/CombinedTankWrapper.java @@ -0,0 +1,143 @@ +package com.simibubi.create.foundation.fluid; + +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.items.wrapper.EmptyHandler; + +/** + * Combines multiple IFluidHandlers into one interface (See CombinedInvWrapper + * for items) + */ +public class CombinedTankWrapper implements IFluidHandler { + + protected final IFluidHandler[] itemHandler; + protected final int[] baseIndex; + protected final int tankCount; + protected boolean enforceVariety; + + public CombinedTankWrapper(IFluidHandler... fluidHandlers) { + this.itemHandler = fluidHandlers; + this.baseIndex = new int[fluidHandlers.length]; + int index = 0; + for (int i = 0; i < fluidHandlers.length; i++) { + index += fluidHandlers[i].getTanks(); + baseIndex[i] = index; + } + this.tankCount = index; + } + + public CombinedTankWrapper enforceVariety() { + enforceVariety = true; + return this; + } + + @Override + public int getTanks() { + return tankCount; + } + + @Override + public FluidStack getFluidInTank(int tank) { + int index = getIndexForSlot(tank); + IFluidHandler handler = getHandlerFromIndex(index); + tank = getSlotFromIndex(tank, index); + return handler.getFluidInTank(tank); + } + + @Override + public int getTankCapacity(int tank) { + int index = getIndexForSlot(tank); + IFluidHandler handler = getHandlerFromIndex(index); + int localSlot = getSlotFromIndex(tank, index); + return handler.getTankCapacity(localSlot); + } + + @Override + public boolean isFluidValid(int tank, FluidStack stack) { + int index = getIndexForSlot(tank); + IFluidHandler handler = getHandlerFromIndex(index); + int localSlot = getSlotFromIndex(tank, index); + return handler.isFluidValid(localSlot, stack); + } + + @Override + public int fill(FluidStack resource, FluidAction action) { + int filled = 0; + resource = resource.copy(); + + for (IFluidHandler iFluidHandler : itemHandler) { + boolean skipRest = false; + int filledIntoCurrent = iFluidHandler.fill(resource, action); + + for (int i = 0; i < iFluidHandler.getTanks(); i++) + if (iFluidHandler.getFluidInTank(i).isFluidEqual(resource) && enforceVariety) + skipRest = true; + + resource.shrink(filledIntoCurrent); + filled += filledIntoCurrent; + + if (resource.isEmpty() || skipRest) + break; + } + + return filled; + } + + @Override + public FluidStack drain(FluidStack resource, FluidAction action) { + FluidStack drained = FluidStack.EMPTY; + resource = resource.copy(); + + for (IFluidHandler iFluidHandler : itemHandler) { + FluidStack drainedFromCurrent = iFluidHandler.drain(resource, action); + int amount = drainedFromCurrent.getAmount(); + resource.shrink(amount); + + if (!drainedFromCurrent.isEmpty()) + drained = new FluidStack(drainedFromCurrent.getFluid(), amount + drained.getAmount()); + if (resource.isEmpty()) + break; + } + + return drained; + } + + @Override + public FluidStack drain(int maxDrain, FluidAction action) { + FluidStack drained = FluidStack.EMPTY; + + for (IFluidHandler iFluidHandler : itemHandler) { + FluidStack drainedFromCurrent = iFluidHandler.drain(maxDrain, action); + int amount = drainedFromCurrent.getAmount(); + maxDrain -= amount; + + if (!drainedFromCurrent.isEmpty()) + drained = new FluidStack(drainedFromCurrent.getFluid(), amount + drained.getAmount()); + if (maxDrain == 0) + break; + } + + return drained; + } + + protected int getIndexForSlot(int slot) { + if (slot < 0) + return -1; + for (int i = 0; i < baseIndex.length; i++) + if (slot - baseIndex[i] < 0) + return i; + return -1; + } + + protected IFluidHandler getHandlerFromIndex(int index) { + if (index < 0 || index >= itemHandler.length) + return (IFluidHandler) EmptyHandler.INSTANCE; + return itemHandler[index]; + } + + protected int getSlotFromIndex(int slot, int index) { + if (index <= 0 || index >= baseIndex.length) + return slot; + return slot - baseIndex[index - 1]; + } +} diff --git a/src/main/java/com/simibubi/create/foundation/tileEntity/behaviour/BehaviourType.java b/src/main/java/com/simibubi/create/foundation/tileEntity/behaviour/BehaviourType.java index 0942955f5..8b3f90827 100644 --- a/src/main/java/com/simibubi/create/foundation/tileEntity/behaviour/BehaviourType.java +++ b/src/main/java/com/simibubi/create/foundation/tileEntity/behaviour/BehaviourType.java @@ -4,4 +4,18 @@ import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; public class BehaviourType { + private String name; + + public BehaviourType(String name) { + this.name = name; + } + + public BehaviourType() { + this(""); + } + + public String getName() { + return name; + } + } diff --git a/src/main/java/com/simibubi/create/foundation/tileEntity/behaviour/fluid/SmartFluidTankBehaviour.java b/src/main/java/com/simibubi/create/foundation/tileEntity/behaviour/fluid/SmartFluidTankBehaviour.java new file mode 100644 index 000000000..971f7fd1e --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/tileEntity/behaviour/fluid/SmartFluidTankBehaviour.java @@ -0,0 +1,261 @@ +package com.simibubi.create.foundation.tileEntity.behaviour.fluid; + +import java.util.function.Consumer; + +import org.apache.commons.lang3.mutable.MutableInt; + +import com.simibubi.create.foundation.fluid.CombinedTankWrapper; +import com.simibubi.create.foundation.fluid.SmartFluidTank; +import com.simibubi.create.foundation.item.SmartInventory; +import com.simibubi.create.foundation.tileEntity.SmartTileEntity; +import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; +import com.simibubi.create.foundation.tileEntity.behaviour.BehaviourType; +import com.simibubi.create.foundation.utility.LerpedFloat; +import com.simibubi.create.foundation.utility.LerpedFloat.Chaser; +import com.simibubi.create.foundation.utility.NBTHelper; + +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraftforge.common.util.Constants.NBT; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.IFluidHandler; + +public class SmartFluidTankBehaviour extends TileEntityBehaviour { + + public static BehaviourType + + TYPE = new BehaviourType<>(), INPUT = new BehaviourType<>("Input"), OUTPUT = new BehaviourType<>("Output"); + + private static final int SYNC_RATE = 8; + + protected int syncCooldown; + protected boolean queuedSync; + protected TankSegment[] tanks; + protected LazyOptional capability; + protected boolean extractionAllowed; + protected boolean insertionAllowed; + + private BehaviourType behaviourType; + + public static SmartFluidTankBehaviour single(SmartTileEntity te, int capacity) { + return new SmartFluidTankBehaviour(TYPE, te, 1, capacity, false); + } + + public SmartFluidTankBehaviour(BehaviourType type, SmartTileEntity te, int tanks, + int tankCapacity, boolean enforceVariety) { + super(te); + insertionAllowed = true; + extractionAllowed = true; + behaviourType = type; + this.tanks = new TankSegment[tanks]; + IFluidHandler[] handlers = new IFluidHandler[tanks]; + for (int i = 0; i < tanks; i++) { + TankSegment tankSegment = new TankSegment(tankCapacity); + this.tanks[i] = tankSegment; + handlers[i] = tankSegment.tank; + } + capability = LazyOptional.of(() -> new InternalFluidHandler(handlers)); + } + + public SmartFluidTankBehaviour allowInsertion() { + insertionAllowed = true; + return this; + } + + public SmartFluidTankBehaviour allowExtraction() { + extractionAllowed = true; + return this; + } + + public SmartFluidTankBehaviour forbidInsertion() { + insertionAllowed = false; + return this; + } + + public SmartFluidTankBehaviour forbidExtraction() { + extractionAllowed = false; + return this; + } + + @Override + public void initialize() { + super.initialize(); + if (getWorld().isRemote) + return; + foreach(ts -> { + ts.fluidLevel.forceNextSync(); + ts.onFluidStackChanged(ts.tank.getFluid()); + }); + } + + @Override + public void tick() { + super.tick(); + + if (syncCooldown > 0) { + syncCooldown--; + if (syncCooldown == 0 && queuedSync) + tileEntity.sendData(); + } + + foreach(te -> { + LerpedFloat fluidLevel = te.getFluidLevel(); + if (fluidLevel != null) + fluidLevel.tickChaser(); + }); + } + + public void sendDataImmediately() { + syncCooldown = 0; + queuedSync = false; + tileEntity.sendData(); + } + + public void sendDataLazily() { + if (syncCooldown > 0) { + queuedSync = true; + return; + } + tileEntity.sendData(); + queuedSync = false; + syncCooldown = SYNC_RATE; + } + + @Override + public void remove() { + super.remove(); + capability.invalidate(); + } + + public SmartFluidTank getPrimaryHandler() { + return getPrimaryTank().tank; + } + + public TankSegment getPrimaryTank() { + return tanks[0]; + } + + public TankSegment[] getTanks() { + return tanks; + } + + public boolean isEmpty() { + for (TankSegment tankSegment : tanks) + if (!tankSegment.tank.isEmpty()) + return false; + return true; + } + + public void foreach(Consumer action) { + for (TankSegment tankSegment : tanks) + action.accept(tankSegment); + } + + public LazyOptional getCapability() { + return capability; + } + + @Override + public void write(CompoundNBT nbt, boolean clientPacket) { + super.write(nbt, clientPacket); + ListNBT tanksNBT = new ListNBT(); + foreach(ts -> tanksNBT.add(ts.writeNBT())); + nbt.put(getType().getName() + "Tanks", tanksNBT); + } + + @Override + public void read(CompoundNBT nbt, boolean clientPacket) { + super.read(nbt, clientPacket); + MutableInt index = new MutableInt(0); + NBTHelper.iterateCompoundList(nbt.getList(getType().getName() + "Tanks", NBT.TAG_COMPOUND), c -> { + if (index.intValue() >= tanks.length) + return; + tanks[index.intValue()].readNBT(c, clientPacket); + index.increment(); + }); + } + + class InternalFluidHandler extends CombinedTankWrapper { + + public InternalFluidHandler(IFluidHandler[] handlers) { + super(handlers); + if (enforceVariety) + enforceVariety(); + } + + @Override + public int fill(FluidStack resource, FluidAction action) { + if (!insertionAllowed) + return 0; + return super.fill(resource, action); + } + + @Override + public FluidStack drain(FluidStack resource, FluidAction action) { + if (!extractionAllowed) + return FluidStack.EMPTY; + return super.drain(resource, action); + } + + @Override + public FluidStack drain(int maxDrain, FluidAction action) { + if (!extractionAllowed) + return FluidStack.EMPTY; + return super.drain(maxDrain, action); + } + + } + + public class TankSegment { + + protected SmartFluidTank tank; + protected LerpedFloat fluidLevel; + protected FluidStack renderedFluid; + + public TankSegment(int capacity) { + tank = new SmartFluidTank(1000, f -> onFluidStackChanged(f)); + fluidLevel = LerpedFloat.linear() + .startWithValue(0) + .chase(0, .25, Chaser.EXP); + renderedFluid = FluidStack.EMPTY; + } + + protected void onFluidStackChanged(FluidStack newFluidStack) { + if (!tileEntity.hasWorld()) + return; + fluidLevel.chase(tank.getFluidAmount() / (float) tank.getCapacity(), .25, Chaser.EXP); + if (!getWorld().isRemote) + sendDataLazily(); + } + + public FluidStack getRenderedFluid() { + return renderedFluid; + } + + public LerpedFloat getFluidLevel() { + return fluidLevel; + } + + public CompoundNBT writeNBT() { + CompoundNBT compound = new CompoundNBT(); + compound.put("TankContent", tank.writeToNBT(new CompoundNBT())); + compound.put("Level", fluidLevel.writeNBT()); + return compound; + } + + public void readNBT(CompoundNBT compound, boolean clientPacket) { + tank.readFromNBT(compound.getCompound("TankContent")); + fluidLevel.readNBT(compound.getCompound("Level"), clientPacket); + if (!tank.getFluid() + .isEmpty()) + renderedFluid = tank.getFluid(); + } + + } + + @Override + public BehaviourType getType() { + return behaviourType; + } +}