Files
Applied-Energistics-2/src/main/java/appeng/helpers/DualityInterface.java
T
Sebastian Hartte fee5b7fdfc Utils ported
2020-06-28 03:41:44 +02:00

1121 lines
39 KiB
Java

/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, 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.helpers;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
import alexiil.mc.lib.attributes.item.FixedItemInv;
import com.google.common.collect.ImmutableSet;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.inventory.CraftingInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListNBT;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.text.Text;
import net.minecraft.text.LiteralText;
import net.minecraft.text.TranslatableText;
import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.wrapper.RangedWrapper;
import appeng.api.AEApi;
import appeng.api.config.Actionable;
import appeng.api.config.Settings;
import appeng.api.config.Upgrades;
import appeng.api.config.YesNo;
import appeng.api.implementations.ICraftingPatternItem;
import appeng.api.implementations.IUpgradeableHost;
import appeng.api.implementations.tiles.ICraftingMachine;
import appeng.api.networking.GridFlags;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridNode;
import appeng.api.networking.crafting.ICraftingLink;
import appeng.api.networking.crafting.ICraftingPatternDetails;
import appeng.api.networking.crafting.ICraftingProvider;
import appeng.api.networking.crafting.ICraftingProviderHelper;
import appeng.api.networking.energy.IEnergySource;
import appeng.api.networking.events.MENetworkCraftingPatternChange;
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.parts.IPart;
import appeng.api.storage.IMEInventory;
import appeng.api.storage.IMEMonitor;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.IStorageMonitorable;
import appeng.api.storage.IStorageMonitorableAccessor;
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.me.GridAccessException;
import appeng.me.helpers.AENetworkProxy;
import appeng.me.helpers.MachineSource;
import appeng.me.storage.MEMonitorIInventory;
import appeng.me.storage.MEMonitorPassThrough;
import appeng.me.storage.NullInventory;
import appeng.parts.automation.StackUpgradeInventory;
import appeng.parts.automation.UpgradeInventory;
import appeng.tile.inventory.AppEngInternalAEInventory;
import appeng.tile.inventory.AppEngInternalInventory;
import appeng.util.ConfigManager;
import appeng.util.IConfigManagerHost;
import appeng.util.InventoryAdaptor;
import appeng.util.Platform;
import appeng.util.inv.AdaptorFixedInv;
import appeng.util.inv.IAEAppEngInventory;
import appeng.util.inv.IInventoryDestination;
import appeng.util.inv.InvOperation;
import appeng.util.item.AEItemStack;
public class DualityInterface implements IGridTickable, IStorageMonitorable, IInventoryDestination, IAEAppEngInventory,
IConfigManagerHost, ICraftingProvider, IUpgradeableHost {
public static final int NUMBER_OF_STORAGE_SLOTS = 9;
public static final int NUMBER_OF_CONFIG_SLOTS = 9;
public static final int NUMBER_OF_PATTERN_SLOTS = 9;
private static final Collection<Block> BAD_BLOCKS = new HashSet<>(100);
private final IAEItemStack[] requireWork = { null, null, null, null, null, null, null, null, null };
private final MultiCraftingTracker craftingTracker;
private final AENetworkProxy gridProxy;
private final IInterfaceHost iHost;
private final IActionSource mySource;
private final IActionSource interfaceRequestSource;
private final ConfigManager cm = new ConfigManager(this);
private final AppEngInternalAEInventory config = new AppEngInternalAEInventory(this, NUMBER_OF_CONFIG_SLOTS);
private final AppEngInternalInventory storage = new AppEngInternalInventory(this, NUMBER_OF_STORAGE_SLOTS);
private final AppEngInternalInventory patterns = new AppEngInternalInventory(this, NUMBER_OF_PATTERN_SLOTS);
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 final UpgradeInventory upgrades;
private boolean hasConfig = false;
private int priority;
private List<ICraftingPatternDetails> craftingList = null;
private List<ItemStack> waitingToSend = null;
private IMEInventory<IAEItemStack> destination;
private int isWorking = -1;
private final Accessor accessor = new Accessor();
public DualityInterface(final AENetworkProxy networkProxy, final IInterfaceHost ih) {
this.gridProxy = networkProxy;
this.gridProxy.setFlags(GridFlags.REQUIRE_CHANNEL);
this.upgrades = new StackUpgradeInventory(this.gridProxy.getMachineRepresentation(), this, 1);
this.cm.registerSetting(Settings.BLOCK, YesNo.NO);
this.cm.registerSetting(Settings.INTERFACE_TERMINAL, YesNo.YES);
this.iHost = ih;
this.craftingTracker = new MultiCraftingTracker(this.iHost, 9);
final MachineSource actionSource = new MachineSource(this.iHost);
this.mySource = actionSource;
this.fluids.setChangeSource(actionSource);
this.items.setChangeSource(actionSource);
this.interfaceRequestSource = new InterfaceRequestSource(this.iHost);
}
@Override
public void saveChanges() {
this.iHost.saveChanges();
}
@Override
public void onChangeInventory(final FixedItemInv inv, final int slot, final InvOperation mc,
final ItemStack removed, final ItemStack added) {
if (this.isWorking == slot) {
return;
}
if (inv == this.config) {
this.readConfig();
} else if (inv == this.patterns && (!removed.isEmpty() || !added.isEmpty())) {
this.updateCraftingList();
} else if (inv == this.storage && slot >= 0) {
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 void writeToNBT(final CompoundTag data) {
this.config.writeToNBT(data, "config");
this.patterns.writeToNBT(data, "patterns");
this.storage.writeToNBT(data, "storage");
this.upgrades.writeToNBT(data, "upgrades");
this.cm.writeToNBT(data);
this.craftingTracker.writeToNBT(data);
data.putInt("priority", this.priority);
final ListNBT waitingToSend = new ListNBT();
if (this.waitingToSend != null) {
for (final ItemStack is : this.waitingToSend) {
final CompoundTag item = new CompoundTag();
is.toTag(item);
waitingToSend.add(item);
}
}
data.put("waitingToSend", waitingToSend);
}
public void readFromNBT(final CompoundTag data) {
this.waitingToSend = null;
final ListNBT waitingList = data.getList("waitingToSend", 10);
if (waitingList != null) {
for (int x = 0; x < waitingList.size(); x++) {
final CompoundTag c = waitingList.getCompound(x);
if (c != null) {
final ItemStack is = ItemStack.fromTag(c);
this.addToSendList(is);
}
}
}
this.craftingTracker.readFromNBT(data);
this.upgrades.readFromNBT(data, "upgrades");
this.config.readFromNBT(data, "config");
this.patterns.readFromNBT(data, "patterns");
this.storage.readFromNBT(data, "storage");
this.priority = data.getInt("priority");
this.cm.readFromNBT(data);
this.readConfig();
this.updateCraftingList();
}
private void addToSendList(final ItemStack is) {
if (is.isEmpty()) {
return;
}
if (this.waitingToSend == null) {
this.waitingToSend = new ArrayList<>();
}
this.waitingToSend.add(is);
try {
this.gridProxy.getTick().wakeDevice(this.gridProxy.getNode());
} catch (final GridAccessException e) {
// :P
}
}
private void readConfig() {
this.hasConfig = false;
for (final ItemStack p : this.config) {
if (!p.isEmpty()) {
this.hasConfig = true;
break;
}
}
final boolean had = this.hasWorkToDo();
for (int x = 0; x < NUMBER_OF_CONFIG_SLOTS; 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 void updateCraftingList() {
final Boolean[] accountedFor = { false, false, false, false, false, false, false, false, false }; // 9...
assert (accountedFor.length == this.patterns.getSlots());
if (!this.gridProxy.isReady()) {
return;
}
if (this.craftingList != null) {
final Iterator<ICraftingPatternDetails> i = this.craftingList.iterator();
while (i.hasNext()) {
final ICraftingPatternDetails details = i.next();
boolean found = false;
for (int x = 0; x < accountedFor.length; x++) {
final ItemStack is = this.patterns.getStackInSlot(x);
if (details.getPattern() == is) {
accountedFor[x] = found = true;
}
}
if (!found) {
i.remove();
}
}
}
for (int x = 0; x < accountedFor.length; x++) {
if (!accountedFor[x]) {
this.addToCraftingList(this.patterns.getStackInSlot(x));
}
}
try {
this.gridProxy.getGrid().postEvent(new MENetworkCraftingPatternChange(this, this.gridProxy.getNode()));
} catch (final GridAccessException e) {
// :P
}
}
private boolean hasWorkToDo() {
if (this.hasItemsToSend()) {
return true;
} else {
for (final IAEItemStack requiredWork : this.requireWork) {
if (requiredWork != null) {
return true;
}
}
return false;
}
}
private void updatePlan(final int slot) {
IAEItemStack req = this.config.getAEStackInSlot(slot);
if (req != null && req.getStackSize() <= 0) {
this.config.setStackInSlot(slot, ItemStack.EMPTY);
req = null;
}
final ItemStack stored = this.storage.getStackInSlot(slot);
if (req == null && !stored.isEmpty()) {
final IAEItemStack work = AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class)
.createStack(stored);
this.requireWork[slot] = work.setStackSize(-work.getStackSize());
return;
} else if (req != null) {
if (stored.isEmpty()) // need to add stuff!
{
this.requireWork[slot] = req.copy();
return;
} else if (req.isSameType(stored)) // same type ( qty different? )!
{
if (req.getStackSize() != stored.getCount()) {
this.requireWork[slot] = req.copy();
this.requireWork[slot].setStackSize(req.getStackSize() - stored.getCount());
return;
}
} else
// Stored != null; dispose!
{
final IAEItemStack work = AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class)
.createStack(stored);
this.requireWork[slot] = work.setStackSize(-work.getStackSize());
return;
}
}
// else
this.requireWork[slot] = null;
}
public void notifyNeighbors() {
if (this.gridProxy.isActive()) {
try {
this.gridProxy.getGrid().postEvent(new MENetworkCraftingPatternChange(this, this.gridProxy.getNode()));
this.gridProxy.getTick().wakeDevice(this.gridProxy.getNode());
} catch (final GridAccessException e) {
// :P
}
}
final BlockEntity te = this.iHost.getBlockEntity();
if (te != null && te.getWorld() != null) {
Platform.notifyBlocksOfNeighbors(te.getWorld(), te.getPos());
}
}
private void addToCraftingList(final ItemStack is) {
if (is.isEmpty()) {
return;
}
if (is.getItem() instanceof ICraftingPatternItem) {
final ICraftingPatternItem cpi = (ICraftingPatternItem) is.getItem();
final ICraftingPatternDetails details = cpi.getPatternForItem(is, this.iHost.getBlockEntity().getWorld());
if (details != null) {
if (this.craftingList == null) {
this.craftingList = new ArrayList<>();
}
this.craftingList.add(details);
}
}
}
private boolean hasItemsToSend() {
return this.waitingToSend != null && !this.waitingToSend.isEmpty();
}
@Override
public boolean canInsert(final ItemStack stack) {
final IAEItemStack out = this.destination.injectItems(
AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class).createStack(stack),
Actionable.SIMULATE, null);
if (out == null) {
return true;
}
return out.getStackSize() != stack.getCount();
// ItemStack after = adaptor.simulateAdd( stack );
// if ( after == null )
// return true;
// return after.stackSize != stack.stackSize;
}
public FixedItemInv getConfig() {
return this.config;
}
public FixedItemInv getPatterns() {
return this.patterns;
}
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.getBlockEntity());
}
public FixedItemInv getInternalInventory() {
return this.storage;
}
@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;
}
if (this.hasItemsToSend()) {
this.pushItemsOut(this.iHost.getTargets());
}
final boolean couldDoWork = this.updateStorage();
return this.hasWorkToDo() ? (couldDoWork ? TickRateModulation.URGENT : TickRateModulation.SLOWER)
: TickRateModulation.SLEEP;
}
private void pushItemsOut(final EnumSet<Direction> possibleDirections) {
if (!this.hasItemsToSend()) {
return;
}
final BlockEntity tile = this.iHost.getBlockEntity();
final World w = tile.getWorld();
final Iterator<ItemStack> i = this.waitingToSend.iterator();
while (i.hasNext()) {
ItemStack whatToSend = i.next();
for (final Direction s : possibleDirections) {
final BlockEntity te = w.getBlockEntity(tile.getPos().offset(s));
if (te == null) {
continue;
}
final InventoryAdaptor ad = InventoryAdaptor.getAdaptor(te, s.getOpposite());
if (ad != null) {
final ItemStack result = ad.addItems(whatToSend);
if (result.isEmpty()) {
whatToSend = ItemStack.EMPTY;
} else {
whatToSend.setCount(whatToSend.getCount() - (whatToSend.getCount() - result.getCount()));
}
if (whatToSend.isEmpty()) {
break;
}
}
}
if (whatToSend.isEmpty()) {
i.remove();
}
}
if (this.waitingToSend.isEmpty()) {
this.waitingToSend = null;
}
}
private boolean updateStorage() {
boolean didSomething = false;
for (int x = 0; x < NUMBER_OF_STORAGE_SLOTS; x++) {
if (this.requireWork[x] != null) {
didSomething = this.usePlan(x, this.requireWork[x]) || didSomething;
}
}
return didSomething;
}
private boolean usePlan(final int x, final IAEItemStack itemStack) {
final InventoryAdaptor adaptor = this.getAdaptor(x);
this.isWorking = x;
boolean changed = false;
try {
this.destination = this.gridProxy.getStorage()
.getInventory(AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class));
final IEnergySource src = this.gridProxy.getEnergy();
if (this.craftingTracker.isBusy(x)) {
changed = this.handleCrafting(x, adaptor, itemStack) || changed;
} else if (itemStack.getStackSize() > 0) {
// make sure strange things didn't happen...
if (!adaptor.simulateAdd(itemStack.createItemStack()).isEmpty()) {
changed = true;
throw new GridAccessException();
}
final IAEItemStack acquired = Platform.poweredExtraction(src, this.destination, itemStack,
this.interfaceRequestSource);
if (acquired != null) {
changed = true;
final ItemStack issue = adaptor.addItems(acquired.createItemStack());
if (!issue.isEmpty()) {
throw new IllegalStateException("bad attempt at managing inventory. ( addItems )");
}
} else {
changed = this.handleCrafting(x, adaptor, itemStack) || changed;
}
} else if (itemStack.getStackSize() < 0) {
IAEItemStack toStore = itemStack.copy();
toStore.setStackSize(-toStore.getStackSize());
long diff = toStore.getStackSize();
// make sure strange things didn't happen...
// TODO: check if OK
final ItemStack canExtract = adaptor.simulateRemove((int) diff, toStore.getDefinition(), null);
if (canExtract.isEmpty() || canExtract.getCount() != diff) {
changed = true;
throw new GridAccessException();
}
toStore = Platform.poweredInsert(src, this.destination, toStore, this.interfaceRequestSource);
if (toStore != null) {
diff -= toStore.getStackSize();
}
if (diff != 0) {
// extract items!
changed = true;
final ItemStack removed = adaptor.removeItems((int) diff, ItemStack.EMPTY, null);
if (removed.isEmpty()) {
throw new IllegalStateException("bad attempt at managing inventory. ( removeItems )");
} else if (removed.getCount() != diff) {
throw new IllegalStateException("bad attempt at managing inventory. ( removeItems )");
}
}
}
// else wtf?
} catch (final GridAccessException e) {
// :P
}
if (changed) {
this.updatePlan(x);
}
this.isWorking = -1;
return changed;
}
private InventoryAdaptor getAdaptor(final int slot) {
return new AdaptorFixedInv(new RangedWrapper(this.storage, slot, slot + 1));
}
private boolean handleCrafting(final int x, final InventoryAdaptor d, final IAEItemStack itemStack) {
try {
if (this.getInstalledUpgrades(Upgrades.CRAFTING) > 0 && itemStack != null) {
return this.craftingTracker.handleCrafting(x, itemStack.getStackSize(), itemStack, d,
this.iHost.getBlockEntity().getWorld(), this.gridProxy.getGrid(), this.gridProxy.getCrafting(),
this.mySource);
}
} catch (final GridAccessException e) {
// :P
}
return false;
}
@Override
public int getInstalledUpgrades(final Upgrades u) {
if (this.upgrades == null) {
return 0;
}
return this.upgrades.getInstalledUpgrades(u);
}
@Override
public BlockEntity getTile() {
return (BlockEntity) (this.iHost instanceof BlockEntity ? this.iHost : null);
}
@Override
public <T extends IAEStack<T>> IMEMonitor<T> getInventory(IStorageChannel<T> channel) {
if (channel == AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class)) {
if (this.hasConfig()) {
return (IMEMonitor<T>) new InterfaceInventory(this);
}
return (IMEMonitor<T>) this.items;
} else if (channel == AEApi.instance().storage().getStorageChannel(IFluidStorageChannel.class)) {
if (this.hasConfig()) {
return null;
}
return (IMEMonitor<T>) this.fluids;
}
return null;
}
private boolean hasConfig() {
return this.hasConfig;
}
@Override
public FixedItemInv getInventoryByName(final String name) {
if (name.equals("storage")) {
return this.storage;
}
if (name.equals("patterns")) {
return this.patterns;
}
if (name.equals("config")) {
return this.config;
}
if (name.equals("upgrades")) {
return this.upgrades;
}
return null;
}
public FixedItemInv getStorage() {
return this.storage;
}
@Override
public appeng.api.util.IConfigManager getConfigManager() {
return this.cm;
}
@Override
public void updateSetting(final IConfigManager manager, final Settings settingName, final Enum<?> newValue) {
if (this.getInstalledUpgrades(Upgrades.CRAFTING) == 0) {
this.cancelCrafting();
}
this.iHost.saveChanges();
}
private void cancelCrafting() {
this.craftingTracker.cancel();
}
public IStorageMonitorable getMonitorable(final IActionSource src, final IStorageMonitorable myInterface) {
if (Platform.canAccess(this.gridProxy, src)) {
return myInterface;
}
final DualityInterface di = this;
return new IStorageMonitorable() {
@Override
public <T extends IAEStack<T>> IMEMonitor<T> getInventory(IStorageChannel<T> channel) {
if (channel == AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class)) {
return (IMEMonitor<T>) new InterfaceInventory(di);
}
return null;
}
};
}
@Override
public boolean pushPattern(final ICraftingPatternDetails patternDetails, final CraftingInventory table) {
if (this.hasItemsToSend() || !this.gridProxy.isActive() || !this.craftingList.contains(patternDetails)) {
return false;
}
final BlockEntity tile = this.iHost.getBlockEntity();
final World w = tile.getWorld();
final EnumSet<Direction> possibleDirections = this.iHost.getTargets();
for (final Direction s : possibleDirections) {
final BlockEntity te = w.getBlockEntity(tile.getPos().offset(s));
if (te instanceof IInterfaceHost) {
try {
if (((IInterfaceHost) te).getInterfaceDuality().sameGrid(this.gridProxy.getGrid())) {
continue;
}
} catch (final GridAccessException e) {
continue;
}
}
if (te instanceof ICraftingMachine) {
final ICraftingMachine cm = (ICraftingMachine) te;
if (cm.acceptsPlans()) {
if (cm.pushPattern(patternDetails, table, s.getOpposite())) {
return true;
}
continue;
}
}
final InventoryAdaptor ad = InventoryAdaptor.getAdaptor(te, s.getOpposite());
if (ad != null) {
if (this.isBlocking()) {
if (!ad.simulateRemove(1, ItemStack.EMPTY, null).isEmpty()) {
continue;
}
}
if (this.acceptsItems(ad, table)) {
for (int x = 0; x < table.size(); x++) {
final ItemStack is = table.getStack(x);
if (!is.isEmpty()) {
final ItemStack added = ad.addItems(is);
this.addToSendList(added);
}
}
this.pushItemsOut(possibleDirections);
return true;
}
}
}
return false;
}
@Override
public boolean isBusy() {
if (this.hasItemsToSend()) {
return true;
}
boolean busy = false;
if (this.isBlocking()) {
final EnumSet<Direction> possibleDirections = this.iHost.getTargets();
final BlockEntity tile = this.iHost.getBlockEntity();
final World w = tile.getWorld();
boolean allAreBusy = true;
for (final Direction s : possibleDirections) {
final BlockEntity te = w.getBlockEntity(tile.getPos().offset(s));
final InventoryAdaptor ad = InventoryAdaptor.getAdaptor(te, s.getOpposite());
if (ad != null) {
if (ad.simulateRemove(1, ItemStack.EMPTY, null).isEmpty()) {
allAreBusy = false;
break;
}
}
}
busy = allAreBusy;
}
return busy;
}
private boolean sameGrid(final IGrid grid) throws GridAccessException {
return grid == this.gridProxy.getGrid();
}
private boolean isBlocking() {
return this.cm.getSetting(Settings.BLOCK) == YesNo.YES;
}
private boolean acceptsItems(final InventoryAdaptor ad, final CraftingInventory table) {
for (int x = 0; x < table.size(); x++) {
final ItemStack is = table.getStack(x);
if (is.isEmpty()) {
continue;
}
if (!ad.simulateAdd(is.copy()).isEmpty()) {
return false;
}
}
return true;
}
@Override
public void provideCrafting(final ICraftingProviderHelper craftingTracker) {
if (this.gridProxy.isActive() && this.craftingList != null) {
for (final ICraftingPatternDetails details : this.craftingList) {
details.setPriority(this.priority);
craftingTracker.addCraftingOption(this, details);
}
}
}
public void addDrops(final List<ItemStack> drops) {
if (this.waitingToSend != null) {
for (final ItemStack is : this.waitingToSend) {
if (!is.isEmpty()) {
drops.add(is);
}
}
}
for (final ItemStack is : this.upgrades) {
if (!is.isEmpty()) {
drops.add(is);
}
}
for (final ItemStack is : this.storage) {
if (!is.isEmpty()) {
drops.add(is);
}
}
for (final ItemStack is : this.patterns) {
if (!is.isEmpty()) {
drops.add(is);
}
}
}
public IUpgradeableHost getHost() {
if (this.getPart() instanceof IUpgradeableHost) {
return (IUpgradeableHost) this.getPart();
}
if (this.getTile() instanceof IUpgradeableHost) {
return (IUpgradeableHost) this.getTile();
}
return null;
}
private IPart getPart() {
return (IPart) (this.iHost instanceof IPart ? this.iHost : null);
}
public ImmutableSet<ICraftingLink> getRequestedJobs() {
return this.craftingTracker.getRequestedJobs();
}
public IAEItemStack injectCraftedItems(final ICraftingLink link, final IAEItemStack acquired,
final Actionable mode) {
final int slot = this.craftingTracker.getSlot(link);
if (acquired != null && slot >= 0 && slot <= this.requireWork.length) {
final InventoryAdaptor adaptor = this.getAdaptor(slot);
if (mode == Actionable.SIMULATE) {
return AEItemStack.fromItemStack(adaptor.simulateAdd(acquired.createItemStack()));
} else {
final IAEItemStack is = AEItemStack.fromItemStack(adaptor.addItems(acquired.createItemStack()));
this.updatePlan(slot);
return is;
}
}
return acquired;
}
public void jobStateChange(final ICraftingLink link) {
this.craftingTracker.jobStateChange(link);
}
public Text getTermName() {
final BlockEntity hostTile = this.iHost.getBlockEntity();
final World hostWorld = hostTile.getWorld();
if (((ICustomNameObject) this.iHost).hasCustomInventoryName()) {
return ((ICustomNameObject) this.iHost).getCustomInventoryName();
}
final EnumSet<Direction> possibleDirections = this.iHost.getTargets();
for (final Direction direction : possibleDirections) {
final BlockPos targ = hostTile.getPos().offset(direction);
final BlockEntity directedTile = hostWorld.getBlockEntity(targ);
if (directedTile == null) {
continue;
}
if (directedTile instanceof IInterfaceHost) {
try {
if (((IInterfaceHost) directedTile).getInterfaceDuality().sameGrid(this.gridProxy.getGrid())) {
continue;
}
} catch (final GridAccessException e) {
continue;
}
}
final InventoryAdaptor adaptor = InventoryAdaptor.getAdaptor(directedTile, direction.getOpposite());
if (directedTile instanceof ICraftingMachine || adaptor != null) {
if (adaptor != null && !adaptor.hasSlots()) {
continue;
}
final BlockState directedBlockState = hostWorld.getBlockState(targ);
final Block directedBlock = directedBlockState.getBlock();
ItemStack what = new ItemStack(directedBlock, 1);
try {
Vec3d from = new Vec3d(hostTile.getPos().getX() + 0.5, hostTile.getPos().getY() + 0.5,
hostTile.getPos().getZ() + 0.5);
from = from.add(direction.getOffsetX() * 0.501, direction.getOffsetY() * 0.501,
direction.getOffsetZ() * 0.501);
final Vec3d to = from.add(direction.getOffsetX(), direction.getOffsetY(), direction.getZOffset());
final BlockHitResult hit = null;// hostWorld.rayTraceBlocks( from, to ); //FIXME:
// https://github.com/MinecraftForge/MinecraftForge/pull/6708
if (hit != null && !BAD_BLOCKS.contains(directedBlock)) {
if (hit.getPos().equals(directedTile.getPos())) {
final ItemStack g = directedBlock.getPickBlock(directedBlockState, hit, hostWorld,
directedTile.getPos(), null);
if (!g.isEmpty()) {
what = g;
}
}
}
} catch (final Throwable t) {
BAD_BLOCKS.add(directedBlock); // nope!
}
if (what.getItem() != Items.AIR) {
return new TranslatableText(what.getTranslationKey());
}
final Item item = Item.getItemFromBlock(directedBlock);
if (item == Items.AIR) {
return new TranslatableText(directedBlock.getTranslationKey());
}
}
}
return new LiteralText("Nothing");
}
public long getSortValue() {
final BlockEntity te = this.iHost.getBlockEntity();
return (te.getPos().getZ() << 24) ^ (te.getPos().getX() << 8) ^ te.getPos().getY();
}
public void initialize() {
this.updateCraftingList();
}
public int getPriority() {
return this.priority;
}
public void setPriority(final int newValue) {
this.priority = newValue;
this.iHost.saveChanges();
try {
this.gridProxy.getGrid().postEvent(new MENetworkCraftingPatternChange(this, this.gridProxy.getNode()));
} catch (final GridAccessException e) {
// :P
}
}
public boolean hasCapability(Capability<?> capabilityClass, Direction facing) {
return capabilityClass == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY
|| capabilityClass == Capabilities.STORAGE_MONITORABLE_ACCESSOR;
}
@SuppressWarnings("unchecked")
public <T> LazyOptional<T> getCapability(Capability<T> capabilityClass, Direction facing) {
if (capabilityClass == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) {
return (LazyOptional<T>) LazyOptional.of(() -> this.storage);
} else if (capabilityClass == Capabilities.STORAGE_MONITORABLE_ACCESSOR) {
return (LazyOptional<T>) LazyOptional.of(() -> this.accessor);
}
return LazyOptional.empty();
}
private class InterfaceRequestSource extends MachineSource {
private final InterfaceRequestContext context;
public 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(DualityInterface.this.priority, o);
}
}
private class InterfaceInventory extends MEMonitorIInventory {
public InterfaceInventory(final DualityInterface tileInterface) {
super(new AdaptorFixedInv(tileInterface.storage));
this.setActionSource(new MachineSource(DualityInterface.this.iHost));
}
@Override
public IAEItemStack injectItems(final IAEItemStack 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 IAEItemStack extractItems(final IAEItemStack request, final Actionable type, final IActionSource src) {
final Optional<InterfaceRequestContext> context = src.context(InterfaceRequestContext.class);
final boolean hasLowerOrEqualPriority = context.map(c -> c.compareTo(DualityInterface.this.priority) <= 0)
.orElse(false);
if (hasLowerOrEqualPriority) {
return null;
}
return super.extractItems(request, type, src);
}
}
private class Accessor implements IStorageMonitorableAccessor {
@Nullable
@Override
public IStorageMonitorable getInventory(IActionSource src) {
return DualityInterface.this.getMonitorable(src, DualityInterface.this);
}
}
}