Files
Applied-Energistics-2-Unoff…/src/main/java/appeng/me/cache/CraftingGridCache.java
T

925 lines
34 KiB
Java

/*
* This file is part of Applied Energistics 2. Copyright (c) 2013 - 2014, 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.me.cache;
import static appeng.util.Platform.convertStack;
import static appeng.util.Platform.stackConvert;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.function.Predicate;
import java.util.stream.StreamSupport;
import javax.annotation.Nonnull;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.world.World;
import net.minecraftforge.common.util.Constants;
import org.jetbrains.annotations.NotNull;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import appeng.api.config.AccessRestriction;
import appeng.api.config.Actionable;
import appeng.api.config.CraftingAllow;
import appeng.api.config.CraftingMode;
import appeng.api.config.DiagnosticSortMode;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.networking.IGridStorage;
import appeng.api.networking.crafting.ICraftingCPU;
import appeng.api.networking.crafting.ICraftingCallback;
import appeng.api.networking.crafting.ICraftingGrid;
import appeng.api.networking.crafting.ICraftingJob;
import appeng.api.networking.crafting.ICraftingLink;
import appeng.api.networking.crafting.ICraftingMedium;
import appeng.api.networking.crafting.ICraftingPatternDetails;
import appeng.api.networking.crafting.ICraftingPostPatternChangeListener;
import appeng.api.networking.crafting.ICraftingProvider;
import appeng.api.networking.crafting.ICraftingProviderHelper;
import appeng.api.networking.crafting.ICraftingRequester;
import appeng.api.networking.crafting.ICraftingWatcher;
import appeng.api.networking.crafting.ICraftingWatcherHost;
import appeng.api.networking.energy.IEnergyGrid;
import appeng.api.networking.events.MENetworkCraftingCpuChange;
import appeng.api.networking.events.MENetworkCraftingPatternChange;
import appeng.api.networking.events.MENetworkEventSubscribe;
import appeng.api.networking.events.MENetworkPostCacheConstruction;
import appeng.api.networking.security.BaseActionSource;
import appeng.api.networking.security.PlayerSource;
import appeng.api.networking.storage.IStorageGrid;
import appeng.api.storage.ICellProvider;
import appeng.api.storage.IMEInventoryHandler;
import appeng.api.storage.StorageChannel;
import appeng.api.storage.data.AEStackTypeRegistry;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IAEStack;
import appeng.api.storage.data.IAEStackType;
import appeng.api.storage.data.IItemList;
import appeng.crafting.CraftingLink;
import appeng.crafting.CraftingLinkNexus;
import appeng.crafting.CraftingWatcher;
import appeng.crafting.fast.CraftingJobFast;
import appeng.crafting.v2.CraftingJobV2;
import appeng.me.cluster.implementations.CraftingCPUCluster;
import appeng.me.diagnostics.CraftingDiagnosticSessionId;
import appeng.me.diagnostics.CraftingNetworkDiagnostics;
import appeng.me.diagnostics.DiagnosticRowView;
import appeng.me.helpers.GenericInterestManager;
import appeng.tile.crafting.TileCraftingStorageTile;
import appeng.tile.crafting.TileCraftingTile;
import appeng.util.ItemSorters;
import appeng.util.item.OreListMultiMap;
public class CraftingGridCache
implements ICraftingGrid, ICraftingProviderHelper, ICellProvider, IMEInventoryHandler<IAEStack> {
private static final ExecutorService CRAFTING_POOL;
private static final Comparator<ICraftingPatternDetails> COMPARATOR = (firstDetail,
nextDetail) -> nextDetail.getPriority() - firstDetail.getPriority();
static {
final ThreadFactory factory = ar -> new Thread(ar, "AE Crafting Calculator");
CRAFTING_POOL = Executors.newCachedThreadPool(factory);
}
protected final Set<CraftingCPUCluster> craftingCPUClusters = new HashSet<>();
protected final Set<ICraftingProvider> craftingProviders = new HashSet<>();
protected final Map<IGridNode, ICraftingWatcher> craftingWatchers = new HashMap<>();
protected final IGrid grid;
protected final Map<ICraftingPatternDetails, List<ICraftingMedium>> craftingMethods = new HashMap<>();
protected final Map<IAEStack<?>, List<ICraftingMedium>> emitableMediums = new HashMap<>();
// Used for fuzzy lookups
protected final OreListMultiMap<ICraftingPatternDetails> craftableItemSubstitutes = new OreListMultiMap<>();
protected final Map<IAEItemStack, ImmutableList<ICraftingPatternDetails>> craftableItemsLegacy = new HashMap<>();
protected final Map<IAEStack<?>, ImmutableList<ICraftingPatternDetails>> craftableItems = new HashMap<>();
private ImmutableMap<IAEStack<?>, ImmutableList<ICraftingPatternDetails>> craftableItemsSnapshot = ImmutableMap
.of();
protected final Map<UUID, ICraftingPatternDetails> inputOnlyPatterns = new HashMap<>();
protected final Map<String, CraftingLinkNexus> craftingLinks = new HashMap<>();
protected final Multimap<IAEStack, CraftingWatcher> interests = HashMultimap.create();
protected final GenericInterestManager<CraftingWatcher> interestManager = new GenericInterestManager<>(
this.interests);
protected IStorageGrid storageGrid;
protected IEnergyGrid energyGrid;
protected boolean updateList = false;
protected static int pauseRebuilds = 0;
protected static Set<CraftingGridCache> rebuildNeeded = new HashSet<>();
protected final Set<ICraftingPostPatternChangeListener> postPatternChangeListeners = Collections
.newSetFromMap(new WeakHashMap<>());
protected final CraftingNetworkDiagnostics diagnostics = new CraftingNetworkDiagnostics();
protected final Set<Long> loadedDiagnosticStorageIds = new HashSet<>();
protected boolean diagnosticsEnabled = false;
protected long diagnosticsRevision = 0L;
public static final String DIAGNOSTICS_KEY = "CraftingDiagnostics";
public static final String DIAGNOSTICS_ENABLED_KEY = "CraftingDiagnosticsEnabled";
public static final String DIAGNOSTICS_SESSION_COUNTER_KEY = "CraftingDiagnosticsSessionCounter";
protected boolean liteCraftingDefault = false;
public static final String LITE_CRAFTING_DEFAULT_KEY = "LiteCraftingDefault";
public CraftingGridCache(final IGrid grid) {
this.grid = grid;
}
@MENetworkEventSubscribe
public void afterCacheConstruction(final MENetworkPostCacheConstruction cacheConstruction) {
this.storageGrid = this.grid.getCache(IStorageGrid.class);
this.energyGrid = this.grid.getCache(IEnergyGrid.class);
this.storageGrid.registerCellProvider(this);
}
@Override
public void onUpdateTick() {
if (this.updateList) {
this.updateList = false;
this.updateCPUClusters();
}
this.craftingLinks.values().removeIf(craftingLinkNexus -> craftingLinkNexus.isDead(this.grid, this));
for (final CraftingCPUCluster cpu : this.craftingCPUClusters) {
cpu.tryExtractItems();
cpu.updateCraftingLogic(this.grid, this.energyGrid, this);
}
}
@Override
public void removeNode(final IGridNode gridNode, final IGridHost machine) {
if (machine instanceof ICraftingWatcherHost) {
final ICraftingWatcher craftingWatcher = this.craftingWatchers.get(machine);
if (craftingWatcher != null) {
craftingWatcher.clear();
this.craftingWatchers.remove(machine);
}
}
if (machine instanceof ICraftingRequester) {
for (final CraftingLinkNexus link : this.craftingLinks.values()) {
if (link.isMachine(machine)) {
link.removeNode();
}
}
}
if (machine instanceof TileCraftingTile) {
this.updateList = true;
}
if (machine instanceof ICraftingProvider) {
this.craftingProviders.remove(machine);
this.updatePatterns();
}
}
@Override
public void addNode(final IGridNode gridNode, final IGridHost machine) {
if (machine instanceof ICraftingWatcherHost watcherHost) {
final CraftingWatcher watcher = new CraftingWatcher(this, watcherHost);
this.craftingWatchers.put(gridNode, watcher);
watcherHost.updateWatcher(watcher);
}
if (machine instanceof ICraftingRequester) {
for (final ICraftingLink link : ((ICraftingRequester) machine).getRequestedJobs()) {
if (link instanceof CraftingLink) {
this.addLink((CraftingLink) link);
}
}
}
if (machine instanceof TileCraftingTile) {
this.updateList = true;
}
if (machine instanceof ICraftingProvider) {
this.craftingProviders.add((ICraftingProvider) machine);
this.updatePatterns();
}
}
@Override
public void onSplit(final IGridStorage destinationStorage) {
destinationStorage.dataObject().removeTag(DIAGNOSTICS_KEY);
destinationStorage.dataObject().removeTag(DIAGNOSTICS_ENABLED_KEY);
destinationStorage.dataObject().removeTag(DIAGNOSTICS_SESSION_COUNTER_KEY);
destinationStorage.dataObject().removeTag(LITE_CRAFTING_DEFAULT_KEY);
}
@Override
public void onJoin(final IGridStorage sourceStorage) {
if (sourceStorage == null || sourceStorage.getID() == 0
|| !this.loadedDiagnosticStorageIds.add(sourceStorage.getID())) {
return;
}
final NBTTagCompound data = sourceStorage.dataObject();
if (data.hasKey(DIAGNOSTICS_ENABLED_KEY)) {
this.diagnosticsEnabled = this.diagnosticsEnabled || data.getBoolean(DIAGNOSTICS_ENABLED_KEY);
}
if (data.hasKey(DIAGNOSTICS_SESSION_COUNTER_KEY, Constants.NBT.TAG_LONG)) {
this.diagnostics.setSessionCounter(data.getLong(DIAGNOSTICS_SESSION_COUNTER_KEY));
}
this.readDiagnosticsFromNBT(
data.getTagList(DIAGNOSTICS_KEY, Constants.NBT.TAG_COMPOUND),
!this.diagnostics.isEmpty());
if (data.hasKey(LITE_CRAFTING_DEFAULT_KEY)) {
this.liteCraftingDefault = this.liteCraftingDefault || data.getBoolean(LITE_CRAFTING_DEFAULT_KEY);
}
}
@Override
public void populateGridStorage(final IGridStorage destinationStorage) {
destinationStorage.dataObject().setBoolean(DIAGNOSTICS_ENABLED_KEY, this.diagnosticsEnabled);
destinationStorage.dataObject().setTag(DIAGNOSTICS_KEY, this.writeDiagnosticsToNBT());
destinationStorage.dataObject().setLong(DIAGNOSTICS_SESSION_COUNTER_KEY, this.diagnostics.getSessionCounter());
destinationStorage.dataObject().setBoolean(LITE_CRAFTING_DEFAULT_KEY, this.liteCraftingDefault);
}
public static void pauseRebuilds() {
pauseRebuilds++;
}
public static void unpauseRebuilds() {
pauseRebuilds--;
if (pauseRebuilds == 0 && !rebuildNeeded.isEmpty()) {
ImmutableSet<CraftingGridCache> needed = ImmutableSet.copyOf(rebuildNeeded);
rebuildNeeded.clear();
for (CraftingGridCache cache : needed) {
cache.updatePatterns();
}
}
}
protected void updatePatterns() {
// coalesce change events during a grid traversal to a single rebuild
if (pauseRebuilds != 0) {
rebuildNeeded.add(this);
return;
}
// erase list.
this.craftingMethods.clear();
this.emitableMediums.clear();
this.craftableItems.clear();
this.craftableItemSubstitutes.clear();
this.inputOnlyPatterns.clear();
// re-create list..
for (final ICraftingProvider provider : this.craftingProviders) {
provider.provideCrafting(this);
}
setPatternsFromCraftingMethods();
for (IAEStackType<?> type : AEStackTypeRegistry.getAllTypes()) {
List<IAEStack<?>> list = new ArrayList<>();
for (IAEStack<?> craftable : this.craftableItems.keySet()) {
if (craftable.getStackType() == type) {
list.add(craftable);
}
}
this.storageGrid.postAlterationOfStoredItems(type, list, new BaseActionSource());
}
for (final ICraftingPostPatternChangeListener listener : ImmutableList
.copyOf(this.postPatternChangeListeners)) {
listener.onPostPatternChange();
}
}
/** Only for unit test usage */
public void setMockPatternsFromMethods() {
this.craftableItems.clear();
this.craftableItemSubstitutes.clear();
this.emitableMediums.clear();
this.inputOnlyPatterns.clear();
setPatternsFromCraftingMethods();
}
protected void setPatternsFromCraftingMethods() {
final Map<IAEStack<?>, Set<ICraftingPatternDetails>> tmpCraft = new HashMap<>();
// new craftables!
for (final ICraftingPatternDetails details : this.craftingMethods.keySet()) {
if (details.isInputOnly()) {
final UUID uuid = details.getInputOnlyUuid();
if (uuid != null) {
this.inputOnlyPatterns.putIfAbsent(uuid, details);
}
continue;
}
for (IAEStack<?> out : details.getCondensedAEOutputs()) {
out = out.copy();
out.reset();
out.setCraftable(true);
if (details.canBeSubstitute() && out instanceof IAEItemStack ais) {
craftableItemSubstitutes.put(ais, details);
}
Set<ICraftingPatternDetails> methods = tmpCraft.computeIfAbsent(out, k -> new LinkedHashSet<>());
methods.add(details);
}
}
craftableItemSubstitutes.freeze();
// make them immutable
for (final Entry<IAEStack<?>, Set<ICraftingPatternDetails>> e : tmpCraft.entrySet()) {
// sort a list: a set ordered by priority alone would drop patterns of equal priority
final List<ICraftingPatternDetails> patterns = new ArrayList<>(e.getValue());
patterns.sort(Comparator.comparing(ICraftingPatternDetails::getPriority).reversed());
final ImmutableList<ICraftingPatternDetails> immutable = ImmutableList.copyOf(patterns);
this.craftableItems.put(e.getKey(), immutable);
final IAEItemStack ais = stackConvert(e.getKey());
if (ais != null) craftableItemsLegacy.put(ais, immutable);
}
this.craftableItemsSnapshot = ImmutableMap.copyOf(this.craftableItems);
}
public ICraftingPatternDetails getInputOnlyPattern(final UUID uuid) {
return this.inputOnlyPatterns.get(uuid);
}
protected void updateCPUClusters() {
this.craftingCPUClusters.clear();
for (Object cls : StreamSupport.stream(grid.getMachinesClasses().spliterator(), false)
.filter(TileCraftingStorageTile.class::isAssignableFrom).toArray()) {
for (final IGridNode cst : this.grid.getMachines((Class<? extends IGridHost>) cls)) {
final TileCraftingStorageTile tile = (TileCraftingStorageTile) cst.getMachine();
final CraftingCPUCluster cluster = (CraftingCPUCluster) tile.getCluster();
if (cluster != null) {
this.craftingCPUClusters.add(cluster);
if (cluster.getLastCraftingLink() != null) {
this.addLink((CraftingLink) cluster.getLastCraftingLink());
}
}
}
}
}
public void addLink(final CraftingLink link) {
if (link.isStandalone()) {
return;
}
CraftingLinkNexus nexus = this.craftingLinks.get(link.getCraftingID());
if (nexus == null) {
this.craftingLinks.put(link.getCraftingID(), nexus = new CraftingLinkNexus(link.getCraftingID()));
}
link.setNexus(nexus);
}
@MENetworkEventSubscribe
public void updateCPUClusters(final MENetworkCraftingCpuChange c) {
this.updateList = true;
}
@MENetworkEventSubscribe
public void updateCPUClusters(final MENetworkCraftingPatternChange c) {
this.updatePatterns();
}
@Override
public void addCraftingOption(final ICraftingMedium medium, final ICraftingPatternDetails api) {
List<ICraftingMedium> details = this.craftingMethods.get(api);
if (details == null) {
details = new ArrayList<>();
details.add(medium);
this.craftingMethods.put(api, details);
} else {
details.add(medium);
}
}
@Override
public void setEmitable(final IAEItemStack someItem) {
setEmitable(null, convertStack(someItem));
}
@Override
public void setEmitable(final IAEStack<?> someItem) {
setEmitable(null, someItem);
}
@Override
public void setEmitable(ICraftingMedium medium, final IAEStack<?> someItem) {
IAEStack<?> item = someItem.copy();
item.reset();
item.setCraftable(true);
List<ICraftingMedium> mediumList = this.emitableMediums.computeIfAbsent(item, k -> new ArrayList<>());
if (medium != null) {
mediumList.add(medium);
}
}
@Override
public @NotNull List<IMEInventoryHandler> getCellArray(IAEStackType<?> type) {
return Collections.singletonList(this);
}
@Override
public int getPriority() {
return Integer.MAX_VALUE;
}
@Override
public AccessRestriction getAccess() {
return AccessRestriction.WRITE;
}
@Override
public boolean isPrioritized(final IAEStack input) {
return true;
}
@Override
public boolean canAccept(final IAEStack input) {
for (final CraftingCPUCluster cpu : this.craftingCPUClusters) {
if (cpu.canAccept(input)) {
return true;
}
}
return false;
}
@Override
public int getSlot() {
return 0;
}
@Override
public boolean validForPass(final int i) {
return i == 1;
}
@Override
public boolean isAutoCraftingInventory() {
return true;
}
@Override
public IAEStack injectItems(IAEStack input, final Actionable type, final BaseActionSource src) {
for (final CraftingCPUCluster cpu : this.craftingCPUClusters) {
if (input == null) break;
input = cpu.injectItems(input, type, src);
}
return input;
}
@Override
public IAEStack extractItems(final IAEStack request, final Actionable mode, final BaseActionSource src) {
return null;
}
@Override
public IItemList<IAEStack> getAvailableItems(final IItemList<IAEStack> out, int iteration) {
return getAvailableItems(out, iteration, Optional.empty());
}
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public IItemList<IAEStack> getAvailableItems(final IItemList<IAEStack> out, int iteration,
Optional<Predicate<IAEStack>> filter) {
final IAEStackType<?> outType = out.getStackType();
final Predicate<IAEStack> predicate = filter.orElse(null);
// add craftable items!
for (final IAEStack<?> stack : this.craftableItems.keySet()) {
if ((outType == null || outType == stack.getStackType())
&& (predicate == null || predicate.test((IAEStack) stack))) {
out.addCrafting((IAEStack) stack);
}
}
for (final IAEStack<?> stack : this.emitableMediums.keySet()) {
if ((outType == null || outType == stack.getStackType())
&& (predicate == null || predicate.test((IAEStack) stack))) {
out.addCrafting((IAEStack) stack);
}
}
return out;
}
@Override
public IAEStack getAvailableItem(@Nonnull IAEStack request, int iteration) {
return null;
}
@Override
public StorageChannel getChannel() {
return null;
}
@Override
public ImmutableMap<IAEStack<?>, ImmutableList<ICraftingPatternDetails>> getCraftingMultiPatterns() {
return this.craftableItemsSnapshot;
}
@Override
public ImmutableSet<IAEStack<?>> getEmitableItems() {
return ImmutableSet.copyOf(this.emitableMediums.keySet());
}
@Override
public ImmutableMap<IAEItemStack, ImmutableList<ICraftingPatternDetails>> getCraftingPatterns() {
return ImmutableMap.copyOf(this.craftableItemsLegacy);
}
@Override
public ImmutableCollection<ICraftingPatternDetails> getCraftingFor(final IAEItemStack whatToCraft,
final ICraftingPatternDetails details, final int slotIndex, final World world) {
return getCraftingFor(convertStack(whatToCraft), details, slotIndex, world);
}
@Override
public ImmutableCollection<ICraftingPatternDetails> getCraftingFor(final IAEStack<?> whatToCraft,
final ICraftingPatternDetails details, final int slotIndex, final World world) {
if (whatToCraft instanceof IAEItemStack wtc) {
final ImmutableList<ICraftingPatternDetails> res = this.craftableItems.get(whatToCraft);
if (res == null) {
if (details != null && details.isCraftable()) {
for (final IAEStack<?> aes : this.craftableItems.keySet()) {
if (aes instanceof IAEItemStack ais && ais.getItem() == wtc.getItem()
&& (!ais.getItem().getHasSubtypes() || ais.getItemDamage() == wtc.getItemDamage())) {
if (details.isValidItemForSlot(slotIndex, ais.getItemStack(), world)) {
return this.craftableItems.get(ais);
}
}
}
}
return ImmutableSet.of();
}
return res;
}
return ImmutableSet.of();
}
/**
* @return The task pool for executing crafting calculations.
*/
public static ExecutorService getCraftingPool() {
return CRAFTING_POOL;
}
public static Comparator<ICraftingPatternDetails> getCOMPARATOR() {
return COMPARATOR;
}
@Override
public Future<ICraftingJob> beginCraftingJob(final World world, final IGrid grid, final BaseActionSource actionSrc,
final IAEItemStack slotItem, final ICraftingCallback cb) {
return beginCraftingJob(
world,
grid,
actionSrc,
convertStack(slotItem),
CraftingMode.STANDARD,
this.liteCraftingDefault,
cb);
}
@Override
public Future<ICraftingJob> beginCraftingJob(final World world, final IGrid grid, final BaseActionSource actionSrc,
final IAEStack<?> stack, final ICraftingCallback cb) {
return beginCraftingJob(world, grid, actionSrc, stack, CraftingMode.STANDARD, this.liteCraftingDefault, cb);
}
public Future<ICraftingJob> beginCraftingJob(final World world, final IGrid grid, final BaseActionSource actionSrc,
final IAEStack<?> stack, final CraftingMode craftingMode, final boolean liteMode,
final ICraftingCallback cb) {
if (world == null || grid == null || actionSrc == null || stack == null) {
throw new IllegalArgumentException("Invalid Crafting Job Request");
}
if (liteMode) {
return new CraftingJobFast<>(world, grid, actionSrc, (IAEStack) stack, craftingMode, cb).schedule();
}
final ICraftingJob job = new CraftingJobV2<>(world, grid, actionSrc, (IAEStack) stack, craftingMode, cb);
return job.schedule();
}
@Override
public ICraftingLink submitJob(ICraftingJob job, ICraftingRequester requestingMachine, ICraftingCPU target,
boolean prioritizePower, BaseActionSource src) {
return submitJob(job, requestingMachine, target, prioritizePower, src, false);
}
@Override
public ICraftingLink submitJob(final ICraftingJob job, final ICraftingRequester requestingMachine,
final ICraftingCPU target, final boolean prioritizePower, final BaseActionSource src,
final boolean followCraft) {
if (job.isSimulation()) {
return null;
}
CraftingCPUCluster cpuCluster = null;
if (target instanceof CraftingCPUCluster) {
cpuCluster = (CraftingCPUCluster) target;
}
if (target == null) {
final List<CraftingCPUCluster> validCpusClusters = new ArrayList<>();
for (final CraftingCPUCluster cpu : this.craftingCPUClusters) {
boolean canOrder = false;
// Only player/standalone jobs may merge into an existing busy CPU.
canOrder |= (requestingMachine == null && cpu.isCraftingLinkStandalone()
&& cpu.isActive()
&& cpu.isBusy()
&& job.getOutput().isSameType((Object) cpu.getFinalMultiOutput())
&& cpu.getAvailableStorage() >= cpu.getUsedStorage() + job.getByteTotal());
// Or this cpu is idle
canOrder |= (cpu.isActive() && !cpu.isBusy() && cpu.getAvailableStorage() >= job.getByteTotal());
if (canOrder) {
if (src.isPlayer() && cpu.getCraftingAllowMode() != CraftingAllow.ONLY_NONPLAYER) {
// If is player requests and CraftingAllowMode is not ONLY_NONPLAYER
validCpusClusters.add(cpu);
} else if (!src.isPlayer() && cpu.getCraftingAllowMode() != CraftingAllow.ONLY_PLAYER) {
// If is non-player requests and CraftingAllowMode is not ONLY_PLAYER
validCpusClusters.add(cpu);
}
}
}
validCpusClusters.sort(new Comparator<>() {
private int compareInternal(CraftingCPUCluster firstCluster, CraftingCPUCluster nextCluster) {
int comparison = ItemSorters
.compareLong(nextCluster.getCoProcessors(), firstCluster.getCoProcessors());
if (comparison == 0) comparison = ItemSorters
.compareLong(nextCluster.getAvailableStorage(), firstCluster.getAvailableStorage());
if (comparison == 0) return nextCluster.getName().compareTo(firstCluster.getName());
else return comparison;
}
@Override
public int compare(final CraftingCPUCluster firstCluster, final CraftingCPUCluster nextCluster) {
if (firstCluster.isBusy() != nextCluster.isBusy()) {
return Boolean.compare(nextCluster.isBusy(), firstCluster.isBusy());
}
if (prioritizePower) return compareInternal(firstCluster, nextCluster);
else return compareInternal(nextCluster, firstCluster);
}
});
if (!validCpusClusters.isEmpty()) {
cpuCluster = validCpusClusters.get(0);
}
}
if (cpuCluster != null) {
ICraftingLink submittedJob = cpuCluster.submitJob(this.grid, job, src, requestingMachine);
if (submittedJob != null && src instanceof PlayerSource playerSource && followCraft) {
cpuCluster.togglePlayerFollowStatus(playerSource.player.getCommandSenderName());
}
return submittedJob;
}
return null;
}
@Override
public ImmutableSet<ICraftingCPU> getCpus() {
return ImmutableSet.copyOf(new ActiveCpuIterator(this.craftingCPUClusters));
}
@Override
public boolean canEmitFor(final IAEStack<?> someItem) {
return this.emitableMediums.containsKey(someItem);
}
@Override
public boolean canEmitFor(final IAEItemStack someItem) {
return this.emitableMediums.containsKey(convertStack(someItem));
}
@Override
public boolean isRequesting(final IAEStack<?> what) {
for (final CraftingCPUCluster cluster : this.craftingCPUClusters) {
if (cluster.isMaking(what)) {
return true;
}
}
return false;
}
@Override
public boolean isRequesting(final IAEItemStack what) {
for (final CraftingCPUCluster cluster : this.craftingCPUClusters) {
if (cluster.isMaking(what)) {
return true;
}
}
return false;
}
public List<ICraftingMedium> getMediums(final ICraftingPatternDetails key) {
List<ICraftingMedium> mediums = this.craftingMethods.get(key);
if (mediums == null) {
mediums = ImmutableList.of();
}
return mediums;
}
public List<ICraftingMedium> getEmitableMediums(final IAEStack<?> key) {
List<ICraftingMedium> mediums = this.emitableMediums.get(key);
if (mediums == null) {
mediums = ImmutableList.of();
}
return mediums;
}
public boolean hasCpu(final ICraftingCPU cpu) {
return this.craftingCPUClusters.contains(cpu);
}
public GenericInterestManager<CraftingWatcher> getInterestManager() {
return this.interestManager;
}
public void addPostPatternChangeListeners(final ICraftingPostPatternChangeListener listener) {
this.postPatternChangeListeners.add(listener);
}
public void removePostPatternChangeListeners(final ICraftingPostPatternChangeListener listener) {
this.postPatternChangeListeners.remove(listener);
}
public void recordDiagnosticSample(final IAEStack<?> output, final CraftingDiagnosticSessionId sessionId,
final long producedAmount, final long observedStartTick, final long observedEndTick) {
this.diagnostics.recordSample(output, sessionId, producedAmount, observedStartTick, observedEndTick);
this.diagnosticsRevision = this.diagnostics.getRevision();
}
public void completeDiagnosticSession(final CraftingDiagnosticSessionId sessionId) {
this.diagnostics.completeSession(sessionId);
this.diagnosticsRevision = this.diagnostics.getRevision();
}
public void clearDiagnosticStats() {
this.diagnostics.clear();
this.diagnosticsRevision = this.diagnostics.getRevision();
}
public void clearDiagnosticStats(final IAEStack<?> stack) {
this.diagnostics.clear(stack);
this.diagnosticsRevision = this.diagnostics.getRevision();
}
public boolean isDiagnosticsEnabled() {
return this.diagnosticsEnabled;
}
public CraftingDiagnosticSessionId nextDiagnosticSessionId() {
return this.diagnostics.nextSessionId();
}
public void setDiagnosticSessionCounter(final long diagnosticSessionCounter) {
this.diagnostics.setSessionCounter(diagnosticSessionCounter);
}
public void setDiagnosticsEnabled(final boolean enabled) {
if (this.diagnosticsEnabled == enabled) {
return;
}
this.diagnosticsEnabled = enabled;
this.diagnosticsRevision++;
}
public long getDiagnosticsRevision() {
return this.diagnosticsRevision;
}
public List<DiagnosticRowView> createDiagnosticRows(final String search, final DiagnosticSortMode sortMode,
final boolean ascending) {
return this.diagnostics.createRows(search, sortMode, ascending);
}
protected NBTTagList writeDiagnosticsToNBT() {
return this.diagnostics.writeToNBT();
}
protected void readDiagnosticsFromNBT(final NBTTagList list, final boolean merge) {
this.diagnostics.readFromNBT(list, merge);
this.diagnosticsRevision = this.diagnostics.getRevision();
}
public boolean getLiteCraftingDefault() {
return this.liteCraftingDefault;
}
public void setLiteCraftingDefault(final boolean enabled) {
if (this.liteCraftingDefault == enabled) {
return;
}
this.liteCraftingDefault = enabled;
}
public static class ActiveCpuIterator implements Iterator<ICraftingCPU> {
private final Iterator<CraftingCPUCluster> iterator;
private CraftingCPUCluster cpuCluster;
public ActiveCpuIterator(final Collection<CraftingCPUCluster> o) {
this.iterator = o.iterator();
this.cpuCluster = null;
}
@Override
public boolean hasNext() {
this.findNext();
return this.cpuCluster != null;
}
protected void findNext() {
while (this.iterator.hasNext() && this.cpuCluster == null) {
this.cpuCluster = this.iterator.next();
if (!this.cpuCluster.isActive() || this.cpuCluster.isDestroyed()) {
this.cpuCluster = null;
}
}
}
@Override
public ICraftingCPU next() {
final ICraftingCPU o = this.cpuCluster;
this.cpuCluster = null;
return o;
}
@Override
public void remove() {
// no..
}
}
}