Files
Applied-Energistics-2/src/main/java/appeng/fluids/helper/DualityFluidInterface.java
T
2022-09-17 08:08:02 -03:00

514 lines
18 KiB
Java

/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2018, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.fluids.helper;
import appeng.api.AEApi;
import appeng.api.config.Actionable;
import appeng.api.config.Upgrades;
import appeng.api.implementations.IUpgradeableHost;
import appeng.api.networking.GridFlags;
import appeng.api.networking.IGridNode;
import appeng.api.networking.energy.IEnergySource;
import appeng.api.networking.security.IActionHost;
import appeng.api.networking.security.IActionSource;
import appeng.api.networking.ticking.IGridTickable;
import appeng.api.networking.ticking.TickRateModulation;
import appeng.api.networking.ticking.TickingRequest;
import appeng.api.storage.*;
import appeng.api.storage.channels.IFluidStorageChannel;
import appeng.api.storage.channels.IItemStorageChannel;
import appeng.api.storage.data.IAEFluidStack;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IAEStack;
import appeng.api.util.AECableType;
import appeng.api.util.AEPartLocation;
import appeng.api.util.DimensionalCoord;
import appeng.api.util.IConfigManager;
import appeng.capabilities.Capabilities;
import appeng.core.settings.TickRates;
import appeng.fluids.util.AEFluidInventory;
import appeng.fluids.util.IAEFluidInventory;
import appeng.fluids.util.IAEFluidTank;
import appeng.me.GridAccessException;
import appeng.me.helpers.AENetworkProxy;
import appeng.me.helpers.MachineSource;
import appeng.me.storage.MEMonitorIFluidHandler;
import appeng.me.storage.MEMonitorPassThrough;
import appeng.me.storage.NullInventory;
import appeng.util.ConfigManager;
import appeng.util.IConfigManagerHost;
import appeng.util.Platform;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.items.IItemHandler;
import java.util.Optional;
public class DualityFluidInterface implements IGridTickable, IStorageMonitorable, IAEFluidInventory, IUpgradeableHost, IConfigManagerHost, IConfigurableFluidInventory {
public static final int NUMBER_OF_TANKS = 6;
public static final int TANK_CAPACITY = Fluid.BUCKET_VOLUME * 4;
private final ConfigManager cm = new ConfigManager(this);
private final AENetworkProxy gridProxy;
private final IFluidInterfaceHost iHost;
private final IActionSource mySource;
private final IActionSource interfaceRequestSource;
private boolean hasConfig = false;
private final IStorageMonitorableAccessor accessor = this::getMonitorable;
private final AEFluidInventory tanks = new AEFluidInventory(this, NUMBER_OF_TANKS, TANK_CAPACITY);
private final AEFluidInventory config = new AEFluidInventory(this, NUMBER_OF_TANKS);
private final IAEFluidStack[] requireWork;
private int isWorking = -1;
private int priority;
private final MEMonitorPassThrough<IAEItemStack> items = new MEMonitorPassThrough<>(new NullInventory<IAEItemStack>(), AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class));
private final MEMonitorPassThrough<IAEFluidStack> fluids = new MEMonitorPassThrough<>(new NullInventory<IAEFluidStack>(), AEApi.instance().storage().getStorageChannel(IFluidStorageChannel.class));
private boolean resetConfigCache = true;
private IMEMonitor<IAEFluidStack> configCachedHandler;
public DualityFluidInterface(final AENetworkProxy networkProxy, final IFluidInterfaceHost ih) {
this.gridProxy = networkProxy;
this.gridProxy.setFlags(GridFlags.REQUIRE_CHANNEL);
this.iHost = ih;
this.mySource = new MachineSource(this.iHost);
this.interfaceRequestSource = new InterfaceRequestSource(this.iHost);
this.fluids.setChangeSource(this.mySource);
this.items.setChangeSource(this.mySource);
this.requireWork = new IAEFluidStack[NUMBER_OF_TANKS];
for (int i = 0; i < NUMBER_OF_TANKS; ++i) {
this.requireWork[i] = null;
}
}
public IUpgradeableHost getHost() {
return this.iHost;
}
@Override
public <T extends IAEStack<T>> IMEMonitor<T> getInventory(IStorageChannel<T> channel) {
if (channel == AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class)) {
if (this.hasConfig()) {
return null;
}
return (IMEMonitor<T>) this.items;
} else if (channel == AEApi.instance().storage().getStorageChannel(IFluidStorageChannel.class)) {
if (this.hasConfig()) {
if (resetConfigCache) {
resetConfigCache = false;
configCachedHandler = new InterfaceInventory(this);
}
return (IMEMonitor<T>) configCachedHandler;
}
return (IMEMonitor<T>) this.fluids;
}
return null;
}
public IStorageMonitorable getMonitorable(final IActionSource src) {
if (Platform.canAccess(this.gridProxy, src)) {
return this;
}
return null;
}
@Override
public TickingRequest getTickingRequest(final IGridNode node) {
return new TickingRequest(TickRates.Interface.getMin(), TickRates.Interface.getMax(), !this.hasWorkToDo(), true);
}
@Override
public TickRateModulation tickingRequest(final IGridNode node, final int ticksSinceLastCall) {
if (!this.gridProxy.isActive()) {
return TickRateModulation.SLEEP;
}
final boolean couldDoWork = this.updateStorage();
return this.hasWorkToDo() ? (couldDoWork ? TickRateModulation.URGENT : TickRateModulation.SLOWER) : TickRateModulation.SLEEP;
}
public void notifyNeighbors() {
if (this.gridProxy.isActive()) {
try {
this.gridProxy.getTick().wakeDevice(this.gridProxy.getNode());
} catch (final GridAccessException e) {
// :P
}
}
final TileEntity te = this.iHost.getTileEntity();
if (te != null && te.getWorld() != null) {
Platform.notifyBlocksOfNeighbors(te.getWorld(), te.getPos());
}
}
public void gridChanged() {
try {
this.items.setInternal(this.gridProxy.getStorage().getInventory(AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class)));
this.fluids.setInternal(this.gridProxy.getStorage().getInventory(AEApi.instance().storage().getStorageChannel(IFluidStorageChannel.class)));
} catch (final GridAccessException gae) {
this.items.setInternal(new NullInventory<IAEItemStack>());
this.fluids.setInternal(new NullInventory<IAEFluidStack>());
}
this.notifyNeighbors();
}
public AECableType getCableConnectionType(final AEPartLocation dir) {
return AECableType.SMART;
}
public DimensionalCoord getLocation() {
return new DimensionalCoord(this.iHost.getTileEntity());
}
public boolean hasCapability(Capability<?> capabilityClass, EnumFacing facing) {
return capabilityClass == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY || capabilityClass == Capabilities.STORAGE_MONITORABLE_ACCESSOR;
}
@SuppressWarnings("unchecked")
public <T> T getCapability(Capability<T> capabilityClass, EnumFacing facing) {
if (capabilityClass == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) {
return (T) this.tanks;
} else if (capabilityClass == Capabilities.STORAGE_MONITORABLE_ACCESSOR) {
return (T) this.accessor;
}
return null;
}
private boolean hasConfig() {
return this.hasConfig;
}
private void readConfig() {
this.hasConfig = false;
for (int i = 0; i < this.config.getSlots(); i++) {
if (this.config.getFluidInSlot(i) != null) {
this.hasConfig = true;
break;
}
}
final boolean had = this.hasWorkToDo();
for (int x = 0; x < NUMBER_OF_TANKS; x++) {
this.updatePlan(x);
}
final boolean has = this.hasWorkToDo();
if (had != has) {
try {
if (has) {
this.gridProxy.getTick().alertDevice(this.gridProxy.getNode());
} else {
this.gridProxy.getTick().sleepDevice(this.gridProxy.getNode());
}
} catch (final GridAccessException e) {
// :P
}
}
this.notifyNeighbors();
}
private boolean updateStorage() {
boolean didSomething = false;
for (int x = 0; x < NUMBER_OF_TANKS; x++) {
if (this.requireWork[x] != null) {
didSomething = this.usePlan(x) || didSomething;
}
}
return didSomething;
}
private boolean hasWorkToDo() {
for (final IAEFluidStack requiredWork : this.requireWork) {
if (requiredWork != null) {
return true;
}
}
return false;
}
private void updatePlan(final int slot) {
final IAEFluidStack req = this.config.getFluidInSlot(slot);
final IAEFluidStack stored = this.tanks.getFluidInSlot(slot);
if (req == null && (stored != null && stored.getStackSize() > 0)) {
final IAEFluidStack work = stored.copy();
this.requireWork[slot] = work.setStackSize(-work.getStackSize());
return;
} else if (req != null) {
if (stored == null || stored.getStackSize() == 0) // need to add stuff!
{
this.requireWork[slot] = req.copy();
this.requireWork[slot].setStackSize(TANK_CAPACITY);
return;
} else if (req.equals(stored)) // same type ( qty different? )!
{
if (stored.getStackSize() < TANK_CAPACITY) {
this.requireWork[slot] = req.copy();
this.requireWork[slot].setStackSize(TANK_CAPACITY - stored.getStackSize());
return;
}
} else
// Stored != null; dispose!
{
final IAEFluidStack work = stored.copy();
this.requireWork[slot] = work.setStackSize(-work.getStackSize());
return;
}
}
this.requireWork[slot] = null;
}
private boolean usePlan(final int slot) {
IAEFluidStack work = this.requireWork[slot];
this.isWorking = slot;
boolean changed = false;
try {
final IMEInventory<IAEFluidStack> dest = this.gridProxy.getStorage()
.getInventory(AEApi.instance().storage().getStorageChannel(IFluidStorageChannel.class));
final IEnergySource src = this.gridProxy.getEnergy();
if (work.getStackSize() > 0) {
// make sure strange things didn't happen...
if (this.tanks.fill(slot, work.getFluidStack(), false) != work.getStackSize()) {
changed = true;
} else if (this.gridProxy.getStorage().getInventory(AEApi.instance().storage().getStorageChannel(IFluidStorageChannel.class)).getStorageList().findPrecise(work) != null) {
final IAEFluidStack acquired = Platform.poweredExtraction(src, dest, work, this.interfaceRequestSource);
if (acquired != null) {
changed = true;
final int filled = this.tanks.fill(slot, acquired.getFluidStack(), true);
if (filled != acquired.getStackSize()) {
throw new IllegalStateException("bad attempt at managing tanks. ( fill )");
}
}
}
} else if (work.getStackSize() < 0) {
IAEFluidStack toStore = work.copy();
toStore.setStackSize(-toStore.getStackSize());
// make sure strange things didn't happen...
final FluidStack canExtract = this.tanks.drain(slot, toStore.getFluidStack(), false);
if (canExtract == null || canExtract.amount != toStore.getStackSize()) {
changed = true;
} else {
IAEFluidStack notStored = Platform.poweredInsert(src, dest, toStore, this.interfaceRequestSource);
toStore.setStackSize(toStore.getStackSize() - (notStored == null ? 0 : notStored.getStackSize()));
if (toStore.getStackSize() > 0) {
// extract items!
changed = true;
final FluidStack removed = this.tanks.drain(slot, toStore.getFluidStack(), true);
if (removed == null || toStore.getStackSize() != removed.amount) {
throw new IllegalStateException("bad attempt at managing tanks. ( drain )");
}
}
}
}
} catch (final GridAccessException e) {
// :P
}
if (changed) {
this.updatePlan(slot);
}
this.isWorking = -1;
return changed;
}
@Override
public void onFluidInventoryChanged(final IAEFluidTank inventory, final int slot) {
if (this.isWorking == slot) {
return;
}
if (inventory == this.config) {
boolean cfg = hasConfig();
this.readConfig();
if (cfg != hasConfig) {
resetConfigCache = true;
this.notifyNeighbors();
}
} else if (inventory == this.tanks) {
this.saveChanges();
final boolean had = this.hasWorkToDo();
this.updatePlan(slot);
final boolean now = this.hasWorkToDo();
if (had != now) {
try {
if (now) {
this.gridProxy.getTick().alertDevice(this.gridProxy.getNode());
} else {
this.gridProxy.getTick().sleepDevice(this.gridProxy.getNode());
}
} catch (final GridAccessException e) {
// :P
}
}
}
}
public int getPriority() {
return this.priority;
}
public void setPriority(final int newValue) {
this.priority = newValue;
}
public void writeToNBT(final NBTTagCompound data) {
data.setInteger("priority", this.priority);
this.tanks.writeToNBT(data, "storage");
this.config.writeToNBT(data, "config");
}
public void readFromNBT(final NBTTagCompound data) {
this.config.readFromNBT(data, "config");
this.tanks.readFromNBT(data, "storage");
this.priority = data.getInteger("priority");
this.readConfig();
}
public IAEFluidTank getConfig() {
return this.config;
}
public IAEFluidTank getTanks() {
return this.tanks;
}
private class InterfaceRequestSource extends MachineSource {
private final InterfaceRequestContext context;
InterfaceRequestSource(IActionHost v) {
super(v);
this.context = new InterfaceRequestContext();
}
@Override
public <T> Optional<T> context(Class<T> key) {
if (key == InterfaceRequestContext.class) {
return (Optional<T>) Optional.of(this.context);
}
return super.context(key);
}
}
private class InterfaceRequestContext implements Comparable<Integer> {
@Override
public int compareTo(Integer o) {
return Integer.compare(DualityFluidInterface.this.priority, o);
}
}
private class InterfaceInventory extends MEMonitorIFluidHandler {
InterfaceInventory(final DualityFluidInterface tileInterface) {
super(tileInterface.tanks);
}
@Override
public IAEFluidStack injectItems(final IAEFluidStack input, final Actionable type, final IActionSource src) {
final Optional<InterfaceRequestContext> context = src.context(InterfaceRequestContext.class);
final boolean isInterface = context.isPresent();
if (isInterface) {
return input;
}
return super.injectItems(input, type, src);
}
@Override
public IAEFluidStack extractItems(final IAEFluidStack request, final Actionable type, final IActionSource src) {
final Optional<InterfaceRequestContext> context = src.context(InterfaceRequestContext.class);
final boolean hasLowerOrEqualPriority = context.map(c -> c.compareTo(DualityFluidInterface.this.priority) <= 0).orElse(false);
if (hasLowerOrEqualPriority) {
return null;
}
return super.extractItems(request, type, src);
}
}
public void saveChanges() {
this.iHost.saveChanges();
}
@Override
public IConfigManager getConfigManager() {
return this.cm;
}
@Override
public IItemHandler getInventoryByName(String name) {
return null;
}
@Override
public IFluidHandler getFluidInventoryByName(final String name) {
if (name.equals("config")) {
return this.config;
}
return null;
}
@Override
public int getInstalledUpgrades(Upgrades u) {
return 0;
}
@Override
public TileEntity getTile() {
return (TileEntity) (this.iHost instanceof TileEntity ? this.iHost : null);
}
@Override
public void updateSetting(IConfigManager manager, Enum settingName, Enum newValue) {
}
}