Lots more moved

This commit is contained in:
Sebastian Hartte
2020-07-04 21:03:30 +02:00
parent 5982f094ec
commit 1478e4c378
444 changed files with 4693 additions and 5235 deletions
+572
View File
@@ -0,0 +1,572 @@
/*
* 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 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.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import net.minecraft.world.World;
import appeng.api.AEApi;
import appeng.api.config.AccessRestriction;
import appeng.api.config.Actionable;
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.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.IActionSource;
import appeng.api.networking.storage.IStorageGrid;
import appeng.api.storage.IMEInventoryHandler;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.cells.ICellProvider;
import appeng.api.storage.channels.IItemStorageChannel;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IAEStack;
import appeng.api.storage.data.IItemList;
import appeng.crafting.CraftingJob;
import appeng.crafting.CraftingLink;
import appeng.crafting.CraftingLinkNexus;
import appeng.crafting.CraftingWatcher;
import appeng.me.cluster.implementations.CraftingCPUCluster;
import appeng.me.helpers.BaseActionSource;
import appeng.me.helpers.GenericInterestManager;
import appeng.tile.crafting.CraftingStorageBlockEntity;
import appeng.tile.crafting.CraftingBlockEntity;
public class CraftingGridCache
implements ICraftingGrid, ICraftingProviderHelper, ICellProvider, IMEInventoryHandler<IAEItemStack> {
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);
}
private final Set<CraftingCPUCluster> craftingCPUClusters = new HashSet<>();
private final Set<ICraftingProvider> craftingProviders = new HashSet<>();
private final Map<IGridNode, ICraftingWatcher> craftingWatchers = new HashMap<>();
private final IGrid grid;
private final Map<ICraftingPatternDetails, List<ICraftingMedium>> craftingMethods = new HashMap<>();
private final Map<IAEItemStack, ImmutableList<ICraftingPatternDetails>> craftableItems = new HashMap<>();
private final Set<IAEItemStack> emitableItems = new HashSet<>();
private final Map<String, CraftingLinkNexus> craftingLinks = new HashMap<>();
private final Multimap<IAEStack, CraftingWatcher> interests = HashMultimap.create();
private final GenericInterestManager<CraftingWatcher> interestManager = new GenericInterestManager<>(
this.interests);
private IStorageGrid storageGrid;
private IEnergyGrid energyGrid;
private boolean updateList = false;
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();
}
final Iterator<CraftingLinkNexus> craftingLinkIterator = this.craftingLinks.values().iterator();
while (craftingLinkIterator.hasNext()) {
if (craftingLinkIterator.next().isDead(this.grid, this)) {
craftingLinkIterator.remove();
}
}
for (final CraftingCPUCluster cpu : this.craftingCPUClusters) {
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.reset();
this.craftingWatchers.remove(machine);
}
}
if (machine instanceof ICraftingRequester) {
for (final CraftingLinkNexus link : this.craftingLinks.values()) {
if (link.isMachine(machine)) {
link.removeNode();
}
}
}
if (machine instanceof CraftingBlockEntity) {
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) {
final ICraftingWatcherHost watcherHost = (ICraftingWatcherHost) machine;
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 CraftingBlockEntity) {
this.updateList = true;
}
if (machine instanceof ICraftingProvider) {
this.craftingProviders.add((ICraftingProvider) machine);
this.updatePatterns();
}
}
@Override
public void onSplit(final IGridStorage destinationStorage) { // nothing!
}
@Override
public void onJoin(final IGridStorage sourceStorage) {
// nothing!
}
@Override
public void populateGridStorage(final IGridStorage destinationStorage) {
// nothing!
}
private void updatePatterns() {
final Map<IAEItemStack, ImmutableList<ICraftingPatternDetails>> oldItems = this.craftableItems;
// erase list.
this.craftingMethods.clear();
this.craftableItems.clear();
this.emitableItems.clear();
// update the stuff that was in the list...
this.storageGrid.postAlterationOfStoredItems(
AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class), oldItems.keySet(),
new BaseActionSource());
// re-create list..
for (final ICraftingProvider provider : this.craftingProviders) {
provider.provideCrafting(this);
}
final Map<IAEItemStack, Set<ICraftingPatternDetails>> tmpCraft = new HashMap<>();
// new craftables!
for (final ICraftingPatternDetails details : this.craftingMethods.keySet()) {
for (IAEItemStack out : details.getOutputs()) {
out = out.copy();
out.reset();
out.setCraftable(true);
Set<ICraftingPatternDetails> methods = tmpCraft.get(out);
if (methods == null) {
tmpCraft.put(out, methods = new TreeSet<>(COMPARATOR));
}
methods.add(details);
}
}
// make them immutable
for (final Entry<IAEItemStack, Set<ICraftingPatternDetails>> e : tmpCraft.entrySet()) {
this.craftableItems.put(e.getKey(), ImmutableList.copyOf(e.getValue()));
}
this.storageGrid.postAlterationOfStoredItems(
AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class), this.craftableItems.keySet(),
new BaseActionSource());
}
private void updateCPUClusters() {
this.craftingCPUClusters.clear();
for (final IGridNode cst : this.grid.getMachines(CraftingStorageBlockEntity.class)) {
final CraftingStorageBlockEntity tile = (CraftingStorageBlockEntity) 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) {
this.emitableItems.add(someItem.copy());
}
@Override
public List<IMEInventoryHandler> getCellArray(final IStorageChannel<?> channel) {
final List<IMEInventoryHandler> list = new ArrayList<>(1);
if (channel == AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class)) {
list.add(this);
}
return list;
}
@Override
public int getPriority() {
return Integer.MAX_VALUE;
}
@Override
public AccessRestriction getAccess() {
return AccessRestriction.WRITE;
}
@Override
public boolean isPrioritized(final IAEItemStack input) {
return true;
}
@Override
public boolean canAccept(final IAEItemStack 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 IAEItemStack injectItems(IAEItemStack input, final Actionable type, final IActionSource src) {
for (final CraftingCPUCluster cpu : this.craftingCPUClusters) {
input = cpu.injectItems(input, type, src);
}
return input;
}
@Override
public IAEItemStack extractItems(final IAEItemStack request, final Actionable mode, final IActionSource src) {
return null;
}
@Override
public IItemList<IAEItemStack> getAvailableItems(final IItemList<IAEItemStack> out) {
// add craftable items!
for (final IAEItemStack stack : this.craftableItems.keySet()) {
out.addCrafting(stack);
}
for (final IAEItemStack st : this.emitableItems) {
out.addCrafting(st);
}
return out;
}
@Override
public IStorageChannel<IAEItemStack> getChannel() {
return AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class);
}
@Override
public ImmutableCollection<ICraftingPatternDetails> getCraftingFor(final IAEItemStack whatToCraft,
final ICraftingPatternDetails details, final int slotIndex, final World world) {
final ImmutableList<ICraftingPatternDetails> res = this.craftableItems.get(whatToCraft);
if (res == null) {
if (details != null && details.isCraftable()) {
for (final IAEItemStack ais : this.craftableItems.keySet()) {
if (ais.getItem() == whatToCraft.getItem()
&& (!ais.getItem().isDamageable() || ais.getItemDamage() == whatToCraft.getItemDamage())) {
// TODO: check if OK
// TODO: this is slightly hacky, but fine as long as we only deal with
// itemstacks
if (details.isValidItemForSlot(slotIndex, ais.asItemStackRepresentation(), world)) {
return this.craftableItems.get(ais);
}
}
}
}
return ImmutableSet.of();
}
return res;
}
@Override
public Future<ICraftingJob> beginCraftingJob(final World world, final IGrid grid, final IActionSource actionSrc,
final IAEItemStack slotItem, final ICraftingCallback cb) {
if (world == null || grid == null || actionSrc == null || slotItem == null) {
throw new IllegalArgumentException("Invalid Crafting Job Request");
}
final CraftingJob job = new CraftingJob(world, grid, actionSrc, slotItem, cb);
return CRAFTING_POOL.submit(job, (ICraftingJob) job);
}
@Override
public ICraftingLink submitJob(final ICraftingJob job, final ICraftingRequester requestingMachine,
final ICraftingCPU target, final boolean prioritizePower, final IActionSource src) {
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) {
if (cpu.isActive() && !cpu.isBusy() && cpu.getAvailableStorage() >= job.getByteTotal()) {
validCpusClusters.add(cpu);
}
}
Collections.sort(validCpusClusters, (firstCluster, nextCluster) -> {
if (prioritizePower) {
final int comparison1 = Long.compare(nextCluster.getCoProcessors(), firstCluster.getCoProcessors());
if (comparison1 != 0) {
return comparison1;
}
return Long.compare(nextCluster.getAvailableStorage(), firstCluster.getAvailableStorage());
}
final int comparison2 = Long.compare(firstCluster.getCoProcessors(), nextCluster.getCoProcessors());
if (comparison2 != 0) {
return comparison2;
}
return Long.compare(firstCluster.getAvailableStorage(), nextCluster.getAvailableStorage());
});
if (!validCpusClusters.isEmpty()) {
cpuCluster = validCpusClusters.get(0);
}
}
if (cpuCluster != null) {
return cpuCluster.submitJob(this.grid, job, src, requestingMachine);
}
return null;
}
@Override
public ImmutableSet<ICraftingCPU> getCpus() {
return ImmutableSet.copyOf(new ActiveCpuIterator(this.craftingCPUClusters));
}
@Override
public boolean canEmitFor(final IAEItemStack someItem) {
return this.emitableItems.contains(someItem);
}
@Override
public boolean isRequesting(final IAEItemStack what) {
return this.requesting(what) > 0;
}
@Override
public long requesting(IAEItemStack what) {
long requested = 0;
for (final CraftingCPUCluster cluster : this.craftingCPUClusters) {
final IAEItemStack stack = cluster.making(what);
requested += stack != null ? stack.getStackSize() : 0;
}
return requested;
}
public List<ICraftingMedium> getMediums(final ICraftingPatternDetails key) {
List<ICraftingMedium> mediums = this.craftingMethods.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;
}
private 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;
}
private 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..
}
}
}
+183 -10
View File
@@ -1,41 +1,214 @@
/*
* 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 java.util.HashMap;
import java.util.Random;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import appeng.api.networking.GridFlags;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridCache;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.networking.IGridStorage;
import appeng.api.networking.events.MENetworkBootingStatusChange;
import appeng.api.networking.events.MENetworkEventSubscribe;
import appeng.api.networking.events.MENetworkPowerStatusChange;
import appeng.api.networking.ticking.ITickManager;
import appeng.core.AELog;
import appeng.me.cache.helpers.TunnelCollection;
import appeng.parts.p2p.MEP2PTunnelPart;
import appeng.parts.p2p.P2PTunnelPart;
import javax.annotation.Nonnull;
// FIXME FABRIC DUMMY
public class P2PCache implements IGridCache {
private static final TunnelCollection<P2PTunnelPart> NULL_COLLECTION = new TunnelCollection<P2PTunnelPart>(null,
null);
private final IGrid myGrid;
private final HashMap<Short, P2PTunnelPart> inputs = new HashMap<>();
private final Multimap<Short, P2PTunnelPart> outputs = LinkedHashMultimap.create();
private final Random frequencyGenerator;
public P2PCache(final IGrid g) {
this.myGrid = g;
this.frequencyGenerator = new Random(g.hashCode());
}
@MENetworkEventSubscribe
public void bootComplete(final MENetworkBootingStatusChange bootStatus) {
final ITickManager tm = this.myGrid.getCache(ITickManager.class);
for (final P2PTunnelPart me : this.inputs.values()) {
if (me instanceof MEP2PTunnelPart) {
tm.wakeDevice(me.getGridNode());
}
}
}
@MENetworkEventSubscribe
public void bootComplete(final MENetworkPowerStatusChange power) {
final ITickManager tm = this.myGrid.getCache(ITickManager.class);
for (final P2PTunnelPart me : this.inputs.values()) {
if (me instanceof MEP2PTunnelPart) {
tm.wakeDevice(me.getGridNode());
}
}
}
@Override
public void onUpdateTick() {
}
@Override
public void removeNode(@Nonnull IGridNode gridNode, @Nonnull IGridHost machine) {
public void removeNode(final IGridNode node, final IGridHost machine) {
if (machine instanceof P2PTunnelPart) {
if (machine instanceof MEP2PTunnelPart) {
if (!node.hasFlag(GridFlags.REQUIRE_CHANNEL)) {
return;
}
}
final P2PTunnelPart t = (P2PTunnelPart) machine;
// AELog.info( "rmv-" + (t.output ? "output: " : "input: ") + t.freq );
if (t.isOutput()) {
this.outputs.remove(t.getFrequency(), t);
} else {
this.inputs.remove(t.getFrequency());
}
this.updateTunnel(t.getFrequency(), !t.isOutput(), false);
}
}
@Override
public void addNode(final IGridNode node, final IGridHost machine) {
if (machine instanceof P2PTunnelPart) {
if (machine instanceof MEP2PTunnelPart) {
if (!node.hasFlag(GridFlags.REQUIRE_CHANNEL)) {
return;
}
}
final P2PTunnelPart t = (P2PTunnelPart) machine;
// AELog.info( "add-" + (t.output ? "output: " : "input: ") + t.freq );
if (t.isOutput()) {
this.outputs.put(t.getFrequency(), t);
} else {
this.inputs.put(t.getFrequency(), t);
}
this.updateTunnel(t.getFrequency(), !t.isOutput(), false);
}
}
@Override
public void onSplit(final IGridStorage storageB) {
}
@Override
public void addNode(@Nonnull IGridNode gridNode, @Nonnull IGridHost machine) {
public void onJoin(final IGridStorage storageB) {
}
@Override
public void onSplit(@Nonnull IGridStorage destinationStorage) {
public void populateGridStorage(final IGridStorage storage) {
}
@Override
public void onJoin(@Nonnull IGridStorage sourceStorage) {
private void updateTunnel(final short freq, final boolean updateOutputs, final boolean configChange) {
for (final P2PTunnelPart p : this.outputs.get(freq)) {
if (configChange) {
p.onTunnelConfigChange();
}
p.onTunnelNetworkChange();
}
final P2PTunnelPart in = this.inputs.get(freq);
if (in != null) {
if (configChange) {
in.onTunnelConfigChange();
}
in.onTunnelNetworkChange();
}
}
@Override
public void populateGridStorage(@Nonnull IGridStorage destinationStorage) {
public void updateFreq(final P2PTunnelPart t, final short newFrequency) {
if (this.outputs.containsValue(t)) {
this.outputs.remove(t.getFrequency(), t);
}
if (this.inputs.containsValue(t)) {
this.inputs.remove(t.getFrequency());
}
t.setFrequency(newFrequency);
if (t.isOutput()) {
this.outputs.put(t.getFrequency(), t);
} else {
this.inputs.put(t.getFrequency(), t);
}
// AELog.info( "update-" + (t.output ? "output: " : "input: ") + t.freq );
this.updateTunnel(t.getFrequency(), t.isOutput(), true);
this.updateTunnel(t.getFrequency(), !t.isOutput(), true);
}
public short newFrequency() {
short newFrequency;
int cycles = 0;
do {
newFrequency = (short) this.frequencyGenerator.nextInt(1 << 16);
cycles++;
} while (newFrequency == 0 || this.inputs.containsKey(newFrequency));
if (cycles > 25) {
AELog.debug("Generating a new P2P frequency '%1$d' took %2$d cycles", newFrequency, cycles);
}
return newFrequency;
}
public TunnelCollection<P2PTunnelPart> getOutputs(final short freq, final Class<? extends P2PTunnelPart> c) {
final P2PTunnelPart in = this.inputs.get(freq);
if (in == null) {
return NULL_COLLECTION;
}
final TunnelCollection<P2PTunnelPart> out = this.inputs.get(freq).getCollection(this.outputs.get(freq), c);
if (out == null) {
return NULL_COLLECTION;
}
return out;
}
public P2PTunnelPart getInput(final short freq) {
return this.inputs.get(freq);
}
}
+1 -1
View File
@@ -25,6 +25,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
import appeng.tile.networking.ControllerBlockEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.network.ServerPlayerEntity;
@@ -56,7 +57,6 @@ import appeng.me.pathfinding.ControllerChannelUpdater;
import appeng.me.pathfinding.ControllerValidator;
import appeng.me.pathfinding.IPathItem;
import appeng.me.pathfinding.PathSegment;
import appeng.tile.networking.ControllerBlockEntity;
public class PathGridCache implements IPathingGrid {
+231
View File
@@ -0,0 +1,231 @@
/*
* 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 java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.networking.IGridStorage;
import appeng.api.networking.events.MENetworkBootingStatusChange;
import appeng.api.networking.events.MENetworkEventSubscribe;
import appeng.api.networking.spatial.ISpatialCache;
import appeng.api.util.DimensionalCoord;
import appeng.api.util.IReadOnlyCollection;
import appeng.core.AEConfig;
import appeng.me.cluster.implementations.SpatialPylonCluster;
import appeng.tile.spatial.SpatialIOPortBlockEntity;
import appeng.tile.spatial.SpatialPylonBlockEntity;
public class SpatialPylonCache implements ISpatialCache {
private final IGrid myGrid;
private long powerRequired = 0;
private double efficiency = 0.0;
private DimensionalCoord captureMin;
private DimensionalCoord captureMax;
private boolean isValid = false;
private List<SpatialIOPortBlockEntity> ioPorts = new ArrayList<>();
private HashMap<SpatialPylonCluster, SpatialPylonCluster> clusters = new HashMap<>();
public SpatialPylonCache(final IGrid g) {
this.myGrid = g;
}
@MENetworkEventSubscribe
public void bootingRender(final MENetworkBootingStatusChange c) {
this.reset(this.myGrid);
}
private void reset(final IGrid grid) {
this.clusters = new HashMap<>();
this.ioPorts = new ArrayList<>();
for (final IGridNode gm : grid.getMachines(SpatialIOPortBlockEntity.class)) {
this.ioPorts.add((SpatialIOPortBlockEntity) gm.getMachine());
}
final IReadOnlyCollection<IGridNode> set = grid.getMachines(SpatialPylonBlockEntity.class);
for (final IGridNode gm : set) {
if (gm.meetsChannelRequirements()) {
final SpatialPylonCluster c = ((SpatialPylonBlockEntity) gm.getMachine()).getCluster();
if (c != null) {
this.clusters.put(c, c);
}
}
}
this.captureMax = null;
this.captureMin = null;
this.isValid = true;
int pylonBlocks = 0;
for (final SpatialPylonCluster cl : this.clusters.values()) {
if (this.captureMax == null) {
this.captureMax = cl.getMax().copy();
}
if (this.captureMin == null) {
this.captureMin = cl.getMin().copy();
}
pylonBlocks += cl.tileCount();
this.captureMin.x = Math.min(this.captureMin.x, cl.getMin().x);
this.captureMin.y = Math.min(this.captureMin.y, cl.getMin().y);
this.captureMin.z = Math.min(this.captureMin.z, cl.getMin().z);
this.captureMax.x = Math.max(this.captureMax.x, cl.getMax().x);
this.captureMax.y = Math.max(this.captureMax.y, cl.getMax().y);
this.captureMax.z = Math.max(this.captureMax.z, cl.getMax().z);
}
double maxPower = 0;
double minPower = 0;
if (this.hasRegion()) {
this.isValid = this.captureMax.x - this.captureMin.x > 1 && this.captureMax.y - this.captureMin.y > 1
&& this.captureMax.z - this.captureMin.z > 1;
for (final SpatialPylonCluster cl : this.clusters.values()) {
switch (cl.getCurrentAxis()) {
case X:
this.isValid = this.isValid
&& ((this.captureMax.y == cl.getMin().y || this.captureMin.y == cl.getMax().y)
|| (this.captureMax.z == cl.getMin().z || this.captureMin.z == cl.getMax().z))
&& ((this.captureMax.y == cl.getMax().y || this.captureMin.y == cl.getMin().y)
|| (this.captureMax.z == cl.getMax().z || this.captureMin.z == cl.getMin().z));
break;
case Y:
this.isValid = this.isValid
&& ((this.captureMax.x == cl.getMin().x || this.captureMin.x == cl.getMax().x)
|| (this.captureMax.z == cl.getMin().z || this.captureMin.z == cl.getMax().z))
&& ((this.captureMax.x == cl.getMax().x || this.captureMin.x == cl.getMin().x)
|| (this.captureMax.z == cl.getMax().z || this.captureMin.z == cl.getMin().z));
break;
case Z:
this.isValid = this.isValid
&& ((this.captureMax.y == cl.getMin().y || this.captureMin.y == cl.getMax().y)
|| (this.captureMax.x == cl.getMin().x || this.captureMin.x == cl.getMax().x))
&& ((this.captureMax.y == cl.getMax().y || this.captureMin.y == cl.getMin().y)
|| (this.captureMax.x == cl.getMax().x || this.captureMin.x == cl.getMin().x));
break;
case UNFORMED:
this.isValid = false;
break;
}
}
final int reqX = this.captureMax.x - this.captureMin.x;
final int reqY = this.captureMax.y - this.captureMin.y;
final int reqZ = this.captureMax.z - this.captureMin.z;
final int requirePylonBlocks = Math.max(6, ((reqX * reqZ + reqX * reqY + reqY * reqZ) * 3) / 8);
this.efficiency = (double) pylonBlocks / (double) requirePylonBlocks;
if (this.efficiency > 1.0) {
this.efficiency = 1.0;
}
if (this.efficiency < 0.0) {
this.efficiency = 0.0;
}
minPower = (double) reqX * (double) reqY * reqZ * AEConfig.instance().getSpatialPowerMultiplier();
maxPower = Math.pow(minPower, AEConfig.instance().getSpatialPowerExponent());
}
final double affective_efficiency = Math.pow(this.efficiency, 0.25);
this.powerRequired = (long) (affective_efficiency * minPower + (1.0 - affective_efficiency) * maxPower);
for (final SpatialPylonCluster cl : this.clusters.values()) {
final boolean myWasValid = cl.isValid();
cl.setValid(this.isValid);
if (myWasValid != this.isValid) {
cl.updateStatus(false);
}
}
}
@Override
public boolean hasRegion() {
return this.captureMin != null;
}
@Override
public boolean isValidRegion() {
return this.hasRegion() && this.isValid;
}
@Override
public DimensionalCoord getMin() {
return this.captureMin;
}
@Override
public DimensionalCoord getMax() {
return this.captureMax;
}
@Override
public long requiredPower() {
return this.powerRequired;
}
@Override
public float currentEfficiency() {
return (float) this.efficiency * 100;
}
@Override
public void onUpdateTick() {
}
@Override
public void removeNode(final IGridNode node, final IGridHost machine) {
}
@Override
public void addNode(final IGridNode node, final IGridHost machine) {
}
@Override
public void onSplit(final IGridStorage storageB) {
}
@Override
public void onJoin(final IGridStorage storageB) {
}
@Override
public void populateGridStorage(final IGridStorage storage) {
}
}
@@ -0,0 +1,38 @@
/*
* 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.helpers;
import appeng.api.networking.IGridConnection;
public class ConnectionWrapper {
private IGridConnection connection;
public ConnectionWrapper(final IGridConnection gc) {
this.setConnection(gc);
}
public IGridConnection getConnection() {
return this.connection;
}
public void setConnection(final IGridConnection connection) {
this.connection = connection;
}
}
+76
View File
@@ -0,0 +1,76 @@
/*
* 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.helpers;
import java.util.HashMap;
import net.minecraft.world.World;
import appeng.api.networking.IGridNode;
import appeng.parts.p2p.MEP2PTunnelPart;
import appeng.util.IWorldCallable;
public class Connections implements IWorldCallable<Void> {
private final HashMap<IGridNode, TunnelConnection> connections = new HashMap<>();
private final MEP2PTunnelPart me;
private boolean create = false;
private boolean destroy = false;
public Connections(final MEP2PTunnelPart o) {
this.me = o;
}
@Override
public Void call(final World world) throws Exception {
this.me.updateConnections(this);
return null;
}
public void markDestroy() {
this.setCreate(false);
this.setDestroy(true);
}
public void markCreate() {
this.setCreate(true);
this.setDestroy(false);
}
public HashMap<IGridNode, TunnelConnection> getConnections() {
return this.connections;
}
public boolean isCreate() {
return this.create;
}
private void setCreate(final boolean create) {
this.create = create;
}
public boolean isDestroy() {
return this.destroy;
}
private void setDestroy(final boolean destroy) {
this.destroy = destroy;
}
}
@@ -0,0 +1,65 @@
/*
* 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.helpers;
import java.util.Collection;
import java.util.Iterator;
import appeng.parts.p2p.P2PTunnelPart;
import appeng.util.iterators.NullIterator;
public class TunnelCollection<T extends P2PTunnelPart> implements Iterable<T> {
private final Class clz;
private Collection<T> tunnelSources;
public TunnelCollection(final Collection<T> src, final Class c) {
this.tunnelSources = src;
this.clz = c;
}
public void setSource(final Collection<T> c) {
this.tunnelSources = c;
}
public boolean isEmpty() {
return !this.iterator().hasNext();
}
@Override
public Iterator<T> iterator() {
if (this.tunnelSources == null) {
return new NullIterator<>();
}
return new TunnelIterator<>(this.tunnelSources, this.clz);
}
public boolean matches(final Class<? extends P2PTunnelPart> c) {
return this.clz == c;
}
public Class<? extends P2PTunnelPart> getClz() {
return this.clz;
}
public int size() {
return this.tunnelSources == null ? 0 : this.tunnelSources.size();
}
}
@@ -0,0 +1,41 @@
/*
* 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.helpers;
import appeng.api.networking.IGridConnection;
import appeng.parts.p2p.MEP2PTunnelPart;
public class TunnelConnection {
private final MEP2PTunnelPart tunnel;
private final IGridConnection c;
public TunnelConnection(final MEP2PTunnelPart t, final IGridConnection con) {
this.tunnel = t;
this.c = con;
}
public IGridConnection getConnection() {
return this.c;
}
public MEP2PTunnelPart getTunnel() {
return this.tunnel;
}
}
@@ -0,0 +1,64 @@
/*
* 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.helpers;
import java.util.Collection;
import java.util.Iterator;
import appeng.parts.p2p.P2PTunnelPart;
public class TunnelIterator<T extends P2PTunnelPart> implements Iterator<T> {
private final Iterator<T> wrapped;
private final Class targetType;
private T Next;
public TunnelIterator(final Collection<T> tunnelSources, final Class clz) {
this.wrapped = tunnelSources.iterator();
this.targetType = clz;
this.findNext();
}
private void findNext() {
while (this.Next == null && this.wrapped.hasNext()) {
this.Next = this.wrapped.next();
if (!this.targetType.isInstance(this.Next)) {
this.Next = null;
}
}
}
@Override
public boolean hasNext() {
this.findNext();
return this.Next != null;
}
@Override
public T next() {
final T tmp = this.Next;
this.Next = null;
return tmp;
}
@Override
public void remove() {
// no.
}
}
@@ -0,0 +1,207 @@
/*
* 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.cluster;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import appeng.api.util.AEPartLocation;
import appeng.api.util.WorldCoord;
import appeng.core.AELog;
import appeng.util.Platform;
public abstract class MBCalculator {
private final IAEMultiBlock target;
public MBCalculator(final IAEMultiBlock t) {
this.target = t;
}
public void calculateMultiblock(final World world, final WorldCoord loc) {
if (Platform.isClient()) {
return;
}
try {
final WorldCoord min = loc.copy();
final WorldCoord max = loc.copy();
// find size of MB structure...
while (this.isValidTileAt(world, min.x - 1, min.y, min.z)) {
min.x--;
}
while (this.isValidTileAt(world, min.x, min.y - 1, min.z)) {
min.y--;
}
while (this.isValidTileAt(world, min.x, min.y, min.z - 1)) {
min.z--;
}
while (this.isValidTileAt(world, max.x + 1, max.y, max.z)) {
max.x++;
}
while (this.isValidTileAt(world, max.x, max.y + 1, max.z)) {
max.y++;
}
while (this.isValidTileAt(world, max.x, max.y, max.z + 1)) {
max.z++;
}
if (this.checkMultiblockScale(min, max)) {
if (this.verifyUnownedRegion(world, min, max)) {
IAECluster c = this.createCluster(world, min, max);
try {
if (!this.verifyInternalStructure(world, min, max)) {
this.disconnect();
return;
}
} catch (final Exception err) {
this.disconnect();
return;
}
boolean updateGrid = false;
final IAECluster cluster = this.target.getCluster();
if (cluster == null) {
this.updateTiles(c, world, min, max);
updateGrid = true;
} else {
c = cluster;
}
c.updateStatus(updateGrid);
return;
}
}
} catch (final Throwable err) {
AELog.debug(err);
}
this.disconnect();
}
private boolean isValidTileAt(final World w, final int x, final int y, final int z) {
return this.isValidTile(w.getBlockEntity(new BlockPos(x, y, z)));
}
/**
* verify if the structure is the correct dimensions, or size
*
* @param min min world coord
* @param max max world coord
*
* @return true if structure has correct dimensions or size
*/
public abstract boolean checkMultiblockScale(WorldCoord min, WorldCoord max);
private boolean verifyUnownedRegion(final World w, final WorldCoord min, final WorldCoord max) {
for (final AEPartLocation side : AEPartLocation.SIDE_LOCATIONS) {
if (this.verifyUnownedRegionInner(w, min.x, min.y, min.z, max.x, max.y, max.z, side)) {
return false;
}
}
return true;
}
/**
* construct the correct cluster, usually very simple.
*
* @param w world
* @param min min world coord
* @param max max world coord
*
* @return created cluster
*/
public abstract IAECluster createCluster(World w, WorldCoord min, WorldCoord max);
public abstract boolean verifyInternalStructure(World world, WorldCoord min, WorldCoord max);
/**
* disassembles the multi-block.
*/
public abstract void disconnect();
/**
* configure the multi-block tiles, most of the important stuff is in here.
*
* @param c updated cluster
* @param w in world
* @param min min world coord
* @param max max world coord
*/
public abstract void updateTiles(IAECluster c, World w, WorldCoord min, WorldCoord max);
/**
* check if the block entities are correct for the structure.
*
* @param te to be checked block entity
*
* @return true if block entity is valid for structure
*/
public abstract boolean isValidTile(BlockEntity te);
private boolean verifyUnownedRegionInner(final World w, int minX, int minY, int minZ, int maxX, int maxY, int maxZ,
final AEPartLocation side) {
switch (side) {
case WEST:
minX -= 1;
maxX = minX;
break;
case EAST:
maxX += 1;
minX = maxX;
break;
case DOWN:
minY -= 1;
maxY = minY;
break;
case NORTH:
maxZ += 1;
minZ = maxZ;
break;
case SOUTH:
minZ -= 1;
maxZ = minZ;
break;
case UP:
maxY += 1;
minY = maxY;
break;
case INTERNAL:
return false;
}
for (int x = minX; x <= maxX; x++) {
for (int y = minY; y <= maxY; y++) {
for (int z = minZ; z <= maxZ; z++) {
final BlockEntity te = w.getBlockEntity(new BlockPos(x, y, z));
if (this.isValidTile(te)) {
return true;
}
}
}
}
return false;
}
}
@@ -0,0 +1,131 @@
/*
* 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.cluster.implementations;
import java.util.Iterator;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.networking.events.MENetworkCraftingCpuChange;
import appeng.api.util.AEPartLocation;
import appeng.api.util.WorldCoord;
import appeng.me.cluster.IAECluster;
import appeng.me.cluster.IAEMultiBlock;
import appeng.me.cluster.MBCalculator;
import appeng.tile.crafting.CraftingBlockEntity;
public class CraftingCPUCalculator extends MBCalculator {
private final CraftingBlockEntity tqb;
public CraftingCPUCalculator(final IAEMultiBlock t) {
super(t);
this.tqb = (CraftingBlockEntity) t;
}
@Override
public boolean checkMultiblockScale(final WorldCoord min, final WorldCoord max) {
if (max.x - min.x > 16) {
return false;
}
if (max.y - min.y > 16) {
return false;
}
if (max.z - min.z > 16) {
return false;
}
return true;
}
@Override
public IAECluster createCluster(final World w, final WorldCoord min, final WorldCoord max) {
return new CraftingCPUCluster(min, max);
}
@Override
public boolean verifyInternalStructure(final World w, final WorldCoord min, final WorldCoord max) {
boolean storage = false;
for (int x = min.x; x <= max.x; x++) {
for (int y = min.y; y <= max.y; y++) {
for (int z = min.z; z <= max.z; z++) {
final IAEMultiBlock te = (IAEMultiBlock) w.getBlockEntity(new BlockPos(x, y, z));
if (!te.isValid()) {
return false;
}
if (!storage && te instanceof CraftingBlockEntity) {
storage = ((CraftingBlockEntity) te).getStorageBytes() > 0;
}
}
}
}
return storage;
}
@Override
public void disconnect() {
this.tqb.disconnect(true);
}
@Override
public void updateTiles(final IAECluster cl, final World w, final WorldCoord min, final WorldCoord max) {
final CraftingCPUCluster c = (CraftingCPUCluster) cl;
for (int x = min.x; x <= max.x; x++) {
for (int y = min.y; y <= max.y; y++) {
for (int z = min.z; z <= max.z; z++) {
final CraftingBlockEntity te = (CraftingBlockEntity) w.getBlockEntity(new BlockPos(x, y, z));
te.updateStatus(c);
c.addTile(te);
}
}
}
c.done();
final Iterator<IGridHost> i = c.getTiles();
while (i.hasNext()) {
final IGridHost gh = i.next();
final IGridNode n = gh.getGridNode(AEPartLocation.INTERNAL);
if (n != null) {
final IGrid g = n.getGrid();
if (g != null) {
g.postEvent(new MENetworkCraftingCpuChange(n));
return;
}
}
}
}
@Override
public boolean isValidTile(final BlockEntity te) {
return te instanceof CraftingBlockEntity;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,141 @@
/*
* 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.cluster.implementations;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import appeng.api.AEApi;
import appeng.api.definitions.IBlockDefinition;
import appeng.api.definitions.IBlocks;
import appeng.api.util.WorldCoord;
import appeng.me.cluster.IAECluster;
import appeng.me.cluster.IAEMultiBlock;
import appeng.me.cluster.MBCalculator;
import appeng.tile.qnb.QuantumBridgeBlockEntity;
public class QuantumCalculator extends MBCalculator {
private final QuantumBridgeBlockEntity tqb;
public QuantumCalculator(final IAEMultiBlock t) {
super(t);
this.tqb = (QuantumBridgeBlockEntity) t;
}
@Override
public boolean checkMultiblockScale(final WorldCoord min, final WorldCoord max) {
if ((max.x - min.x + 1) * (max.y - min.y + 1) * (max.z - min.z + 1) == 9) {
final int ones = ((max.x - min.x) == 0 ? 1 : 0) + ((max.y - min.y) == 0 ? 1 : 0)
+ ((max.z - min.z) == 0 ? 1 : 0);
final int threes = ((max.x - min.x) == 2 ? 1 : 0) + ((max.y - min.y) == 2 ? 1 : 0)
+ ((max.z - min.z) == 2 ? 1 : 0);
return ones == 1 && threes == 2;
}
return false;
}
@Override
public IAECluster createCluster(final World w, final WorldCoord min, final WorldCoord max) {
return new QuantumCluster(min, max);
}
@Override
public boolean verifyInternalStructure(final World w, final WorldCoord min, final WorldCoord max) {
byte num = 0;
for (int x = min.x; x <= max.x; x++) {
for (int y = min.y; y <= max.y; y++) {
for (int z = min.z; z <= max.z; z++) {
final BlockPos p = new BlockPos(x, y, z);
final IAEMultiBlock te = (IAEMultiBlock) w.getBlockEntity(p);
if (!te.isValid()) {
return false;
}
num++;
final IBlocks blocks = AEApi.instance().definitions().blocks();
if (num == 5) {
if (!this.isBlockAtLocation(w, p, blocks.quantumLink())) {
return false;
}
} else {
if (!this.isBlockAtLocation(w, p, blocks.quantumRing())) {
return false;
}
}
}
}
}
return true;
}
@Override
public void disconnect() {
this.tqb.disconnect(true);
}
@Override
public void updateTiles(final IAECluster cl, final World w, final WorldCoord min, final WorldCoord max) {
byte num = 0;
byte ringNum = 0;
final QuantumCluster c = (QuantumCluster) cl;
for (int x = min.x; x <= max.x; x++) {
for (int y = min.y; y <= max.y; y++) {
for (int z = min.z; z <= max.z; z++) {
final QuantumBridgeBlockEntity te = (QuantumBridgeBlockEntity) w.getBlockEntity(new BlockPos(x, y, z));
num++;
final byte flags;
if (num == 5) {
flags = num;
c.setCenter(te);
} else {
if (num == 1 || num == 3 || num == 7 || num == 9) {
flags = (byte) (this.tqb.getCorner() | num);
} else {
flags = num;
}
c.getRing()[ringNum] = te;
ringNum++;
}
te.updateStatus(c, flags, true);
}
}
}
}
@Override
public boolean isValidTile(final BlockEntity te) {
return te instanceof QuantumBridgeBlockEntity;
}
private boolean isBlockAtLocation(final BlockView w, final BlockPos pos, final IBlockDefinition def) {
return def.maybeBlock().map(block -> block == w.getBlockState(pos).getBlock()).orElse(false);
}
}
@@ -0,0 +1,258 @@
/*
* 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.cluster.implementations;
import java.util.Iterator;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import appeng.api.AEApi;
import appeng.api.events.LocatableEventAnnounce;
import appeng.api.events.LocatableEventAnnounce.LocatableEvent;
import appeng.api.exceptions.FailedConnectionException;
import appeng.api.features.ILocatable;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.util.AEPartLocation;
import appeng.api.util.WorldCoord;
import appeng.core.AELog;
import appeng.me.cache.helpers.ConnectionWrapper;
import appeng.me.cluster.IAECluster;
import appeng.tile.qnb.QuantumBridgeBlockEntity;
import appeng.util.iterators.ChainedIterator;
public class QuantumCluster implements ILocatable, IAECluster {
private final WorldCoord min;
private final WorldCoord max;
private boolean isDestroyed = false;
private boolean updateStatus = true;
private QuantumBridgeBlockEntity[] Ring;
private boolean registered = false;
private ConnectionWrapper connection;
private long thisSide;
private long otherSide;
private QuantumBridgeBlockEntity center;
public QuantumCluster(final WorldCoord min, final WorldCoord max) {
this.min = min;
this.max = max;
this.setRing(new QuantumBridgeBlockEntity[8]);
}
// FIXME FABRIC No similar event available. Possibly chunk???
// FIXME FABRIC @SubscribeEvent
// FIXME FABRIC public void onUnload(final WorldEvent.Unload e) {
// FIXME FABRIC if (this.center.getWorld() == e.getWorld()) {
// FIXME FABRIC this.setUpdateStatus(false);
// FIXME FABRIC this.destroy();
// FIXME FABRIC }
// FIXME FABRIC }
@Override
public void updateStatus(final boolean updateGrid) {
final long qe = this.center.getQEFrequency();
if (this.thisSide != qe && this.thisSide != -qe) {
if (qe != 0) {
if (this.thisSide != 0) {
LocatableEventAnnounce.EVENT.invoker().onLocatableAnnounce(this, LocatableEvent.UNREGISTER);
}
if (this.canUseNode(-qe)) {
this.otherSide = qe;
this.thisSide = -qe;
} else if (this.canUseNode(qe)) {
this.thisSide = qe;
this.otherSide = -qe;
}
LocatableEventAnnounce.EVENT.invoker().onLocatableAnnounce(this, LocatableEvent.REGISTER);
} else {
LocatableEventAnnounce.EVENT.invoker().onLocatableAnnounce(this, LocatableEvent.UNREGISTER);
this.otherSide = 0;
this.thisSide = 0;
}
}
final ILocatable myOtherSide = this.otherSide == 0 ? null
: AEApi.instance().registries().locatable().getLocatableBy(this.otherSide);
boolean shutdown = false;
if (myOtherSide instanceof QuantumCluster) {
final QuantumCluster sideA = this;
final QuantumCluster sideB = (QuantumCluster) myOtherSide;
if (sideA.isActive() && sideB.isActive()) {
if (this.connection != null && this.connection.getConnection() != null) {
final IGridNode a = this.connection.getConnection().a();
final IGridNode b = this.connection.getConnection().b();
final IGridNode sa = sideA.getNode();
final IGridNode sb = sideB.getNode();
if ((a == sa || b == sa) && (a == sb || b == sb)) {
return;
}
}
try {
if (sideA.connection != null) {
if (sideA.connection.getConnection() != null) {
sideA.connection.getConnection().destroy();
sideA.connection = new ConnectionWrapper(null);
}
}
if (sideB.connection != null) {
if (sideB.connection.getConnection() != null) {
sideB.connection.getConnection().destroy();
sideB.connection = new ConnectionWrapper(null);
}
}
sideA.connection = sideB.connection = new ConnectionWrapper(
AEApi.instance().grid().createGridConnection(sideA.getNode(), sideB.getNode()));
} catch (final FailedConnectionException e) {
// :(
AELog.debug(e);
}
} else {
shutdown = true;
}
} else {
shutdown = true;
}
if (shutdown && this.connection != null) {
if (this.connection.getConnection() != null) {
this.connection.getConnection().destroy();
this.connection.setConnection(null);
this.connection = new ConnectionWrapper(null);
}
}
}
private boolean canUseNode(final long qe) {
final QuantumCluster qc = (QuantumCluster) AEApi.instance().registries().locatable().getLocatableBy(qe);
if (qc != null) {
final World theWorld = qc.center.getWorld();
if (!qc.isDestroyed) {
ChunkPos cPos = new ChunkPos(qc.center.getPos());
if (theWorld.getChunkManager().isChunkLoaded(cPos.x, cPos.z)) {
final World cur = theWorld.getServer().getWorld(theWorld.getRegistryKey());
final BlockEntity te = theWorld.getBlockEntity(qc.center.getPos());
return te != qc.center || theWorld != cur;
}
}
}
return true;
}
private boolean isActive() {
if (this.isDestroyed || !this.registered) {
return false;
}
return this.center.isPowered() && this.hasQES();
}
private IGridNode getNode() {
return this.center.getGridNode(AEPartLocation.INTERNAL);
}
private boolean hasQES() {
return this.thisSide != 0;
}
@Override
public void destroy() {
if (this.isDestroyed) {
return;
}
this.isDestroyed = true;
if (this.registered) {
// FIXME FABRIC -> onWorldUnload event
// FIXME FABRIC MinecraftForge.EVENT_BUS.unregister(this);
this.registered = false;
}
if (this.thisSide != 0) {
this.updateStatus(true);
LocatableEventAnnounce.EVENT.invoker().onLocatableAnnounce(this, LocatableEvent.UNREGISTER);
}
this.center.updateStatus(null, (byte) -1, this.isUpdateStatus());
for (final QuantumBridgeBlockEntity r : this.getRing()) {
r.updateStatus(null, (byte) -1, this.isUpdateStatus());
}
this.center = null;
this.setRing(new QuantumBridgeBlockEntity[8]);
}
@Override
public Iterator<IGridHost> getTiles() {
return new ChainedIterator<>(this.getRing()[0], this.getRing()[1], this.getRing()[2], this.getRing()[3],
this.getRing()[4], this.getRing()[5], this.getRing()[6], this.getRing()[7], this.center);
}
public boolean isCorner(final QuantumBridgeBlockEntity tileQuantumBridge) {
return this.getRing()[0] == tileQuantumBridge || this.getRing()[2] == tileQuantumBridge
|| this.getRing()[4] == tileQuantumBridge || this.getRing()[6] == tileQuantumBridge;
}
@Override
public long getLocatableSerial() {
return this.thisSide;
}
public QuantumBridgeBlockEntity getCenter() {
return this.center;
}
void setCenter(final QuantumBridgeBlockEntity c) {
this.registered = true;
// FIXME FABRIC -> onWorldUnload event
// FIXME FABRI MinecraftForge.EVENT_BUS.register(this);
this.center = c;
}
private boolean isUpdateStatus() {
return this.updateStatus;
}
public void setUpdateStatus(final boolean updateStatus) {
this.updateStatus = updateStatus;
}
QuantumBridgeBlockEntity[] getRing() {
return this.Ring;
}
private void setRing(final QuantumBridgeBlockEntity[] ring) {
this.Ring = ring;
}
}
@@ -0,0 +1,96 @@
/*
* 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.cluster.implementations;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import appeng.api.util.DimensionalCoord;
import appeng.api.util.WorldCoord;
import appeng.me.cluster.IAECluster;
import appeng.me.cluster.IAEMultiBlock;
import appeng.me.cluster.MBCalculator;
import appeng.tile.spatial.SpatialPylonBlockEntity;
public class SpatialPylonCalculator extends MBCalculator {
private final SpatialPylonBlockEntity tqb;
public SpatialPylonCalculator(final IAEMultiBlock t) {
super(t);
this.tqb = (SpatialPylonBlockEntity) t;
}
@Override
public boolean checkMultiblockScale(final WorldCoord min, final WorldCoord max) {
return (min.x == max.x && min.y == max.y && min.z != max.z)
|| (min.x == max.x && min.y != max.y && min.z == max.z)
|| (min.x != max.x && min.y == max.y && min.z == max.z);
}
@Override
public IAECluster createCluster(final World w, final WorldCoord min, final WorldCoord max) {
return new SpatialPylonCluster(new DimensionalCoord(w, min.x, min.y, min.z),
new DimensionalCoord(w, max.x, max.y, max.z));
}
@Override
public boolean verifyInternalStructure(final World w, final WorldCoord min, final WorldCoord max) {
for (int x = min.x; x <= max.x; x++) {
for (int y = min.y; y <= max.y; y++) {
for (int z = min.z; z <= max.z; z++) {
final IAEMultiBlock te = (IAEMultiBlock) w.getBlockEntity(new BlockPos(x, y, z));
if (!te.isValid()) {
return false;
}
}
}
}
return true;
}
@Override
public void disconnect() {
this.tqb.disconnect(true);
}
@Override
public void updateTiles(final IAECluster cl, final World w, final WorldCoord min, final WorldCoord max) {
final SpatialPylonCluster c = (SpatialPylonCluster) cl;
for (int x = min.x; x <= max.x; x++) {
for (int y = min.y; y <= max.y; y++) {
for (int z = min.z; z <= max.z; z++) {
final SpatialPylonBlockEntity te = (SpatialPylonBlockEntity) w.getBlockEntity(new BlockPos(x, y, z));
te.updateStatus(c);
c.getLine().add((te));
}
}
}
}
@Override
public boolean isValidTile(final BlockEntity te) {
return te instanceof SpatialPylonBlockEntity;
}
}
@@ -0,0 +1,115 @@
/*
* 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.cluster.implementations;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import appeng.api.networking.IGridHost;
import appeng.api.util.DimensionalCoord;
import appeng.me.cluster.IAECluster;
import appeng.tile.spatial.SpatialPylonBlockEntity;
public class SpatialPylonCluster implements IAECluster {
private final DimensionalCoord min;
private final DimensionalCoord max;
private final List<SpatialPylonBlockEntity> line = new ArrayList<>();
private boolean isDestroyed = false;
private Axis currentAxis = Axis.UNFORMED;
private boolean isValid;
public SpatialPylonCluster(final DimensionalCoord min, final DimensionalCoord max) {
this.min = min.copy();
this.max = max.copy();
if (this.getMin().x != this.getMax().x) {
this.setCurrentAxis(Axis.X);
} else if (this.getMin().y != this.getMax().y) {
this.setCurrentAxis(Axis.Y);
} else if (this.getMin().z != this.getMax().z) {
this.setCurrentAxis(Axis.Z);
} else {
this.setCurrentAxis(Axis.UNFORMED);
}
}
@Override
public void updateStatus(final boolean updateGrid) {
for (final SpatialPylonBlockEntity r : this.getLine()) {
r.recalculateDisplay();
}
}
@Override
public void destroy() {
if (this.isDestroyed) {
return;
}
this.isDestroyed = true;
for (final SpatialPylonBlockEntity r : this.getLine()) {
r.updateStatus(null);
}
}
@Override
public Iterator<IGridHost> getTiles() {
return (Iterator) this.getLine().iterator();
}
public int tileCount() {
return this.getLine().size();
}
public Axis getCurrentAxis() {
return this.currentAxis;
}
private void setCurrentAxis(final Axis currentAxis) {
this.currentAxis = currentAxis;
}
public boolean isValid() {
return this.isValid;
}
public void setValid(final boolean isValid) {
this.isValid = isValid;
}
public DimensionalCoord getMax() {
return this.max;
}
public DimensionalCoord getMin() {
return this.min;
}
List<SpatialPylonBlockEntity> getLine() {
return this.line;
}
public enum Axis {
X, Y, Z, UNFORMED
}
}
@@ -0,0 +1,51 @@
/*
* 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.helpers;
import java.util.Iterator;
import net.minecraft.item.ItemStack;
import appeng.api.networking.IGridMultiblock;
import appeng.api.networking.IGridNode;
import appeng.me.cluster.IAECluster;
import appeng.me.cluster.IAEMultiBlock;
import appeng.util.iterators.ChainedIterator;
import appeng.util.iterators.ProxyNodeIterator;
public class AENetworkProxyMultiblock extends AENetworkProxy implements IGridMultiblock {
public AENetworkProxyMultiblock(final IGridProxyable te, final String nbtName, final ItemStack itemStack,
final boolean inWorld) {
super(te, nbtName, itemStack, inWorld);
}
@Override
public Iterator<IGridNode> getMultiblockNodes() {
if (this.getCluster() == null) {
return new ChainedIterator<>();
}
return new ProxyNodeIterator(this.getCluster().getTiles());
}
private IAECluster getCluster() {
return ((IAEMultiBlock) this.getMachine()).getCluster();
}
}
@@ -18,12 +18,12 @@
package appeng.me.pathfinding;
import appeng.tile.networking.ControllerBlockEntity;
import net.minecraft.util.math.BlockPos;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.networking.IGridVisitor;
import appeng.tile.networking.ControllerBlockEntity;
public class ControllerValidator implements IGridVisitor {
@@ -0,0 +1,324 @@
/*
* 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.me.storage;
import alexiil.mc.lib.attributes.Simulation;
import alexiil.mc.lib.attributes.fluid.FluidVolumeUtil;
import alexiil.mc.lib.attributes.fluid.GroupedFluidInv;
import alexiil.mc.lib.attributes.fluid.amount.FluidAmount;
import alexiil.mc.lib.attributes.fluid.filter.ExactFluidFilter;
import alexiil.mc.lib.attributes.fluid.volume.FluidKey;
import alexiil.mc.lib.attributes.fluid.volume.FluidVolume;
import appeng.api.AEApi;
import appeng.api.config.AccessRestriction;
import appeng.api.config.Actionable;
import appeng.api.config.StorageFilter;
import appeng.api.networking.security.IActionSource;
import appeng.api.networking.ticking.TickRateModulation;
import appeng.api.storage.IMEMonitor;
import appeng.api.storage.IMEMonitorHandlerReceiver;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.channels.IFluidStorageChannel;
import appeng.api.storage.data.IAEFluidStack;
import appeng.api.storage.data.IItemList;
import appeng.fluids.util.AEFluidStack;
import java.math.RoundingMode;
import java.util.*;
import java.util.Map.Entry;
public class MEMonitorIFluidHandler implements IMEMonitor<IAEFluidStack>, ITickingMonitor {
private final GroupedFluidInv handler;
private final IItemList<IAEFluidStack> list = AEApi.instance().storage()
.getStorageChannel(IFluidStorageChannel.class).createList();
private final HashMap<IMEMonitorHandlerReceiver<IAEFluidStack>, Object> listeners = new HashMap<>();
private final Map<FluidKey, CachedFluidStack> memory;
private IActionSource mySource;
private StorageFilter mode = StorageFilter.EXTRACTABLE_ONLY;
public MEMonitorIFluidHandler(final GroupedFluidInv handler) {
this.handler = handler;
this.memory = new HashMap<>();
}
@Override
public void addListener(final IMEMonitorHandlerReceiver<IAEFluidStack> l, final Object verificationToken) {
this.listeners.put(l, verificationToken);
}
@Override
public void removeListener(final IMEMonitorHandlerReceiver<IAEFluidStack> l) {
this.listeners.remove(l);
}
private Simulation getFluidAction(Actionable actionable){
return actionable == Actionable.MODULATE ? Simulation.ACTION : Simulation.SIMULATE;
}
@Override
public IAEFluidStack injectItems(final IAEFluidStack input, final Actionable type, final IActionSource src) {
FluidVolume toFill = input.getFluidStack();
final FluidVolume excess = this.handler.attemptInsertion(toFill, getFluidAction(type));
if (excess.equals(toFill)) {
return input.copy();
}
if (type == Actionable.MODULATE) {
this.onTick();
}
if (excess.isEmpty()) {
return null;
}
return AEFluidStack.fromFluidVolume(excess, RoundingMode.DOWN);
}
@Override
public IAEFluidStack extractItems(final IAEFluidStack request, final Actionable type, final IActionSource src) {
FluidAmount amount = request.getAmount();
ExactFluidFilter filter = new ExactFluidFilter(request.getFluid());
final FluidVolume removed = this.handler.attemptExtraction(filter, amount, getFluidAction(type));
if (removed.isEmpty()) {
return null;
}
if (type == Actionable.MODULATE) {
this.onTick();
}
return AEFluidStack.fromFluidVolume(removed, RoundingMode.DOWN);
}
@Override
public IStorageChannel getChannel() {
return AEApi.instance().storage().getStorageChannel(IFluidStorageChannel.class);
}
private static final FluidAmount MIN_EXTRACTION_AMOUNT = FluidAmount.of(1, 1000);
@Override
public TickRateModulation onTick() {
final List<IAEFluidStack> changes = new ArrayList<>();
this.list.resetStatus();
boolean changed = false;
Set<FluidKey> storedFluids = handler.getStoredFluids();
for (FluidKey storedFluid : storedFluids) {
CachedFluidStack old = this.memory.get(storedFluid);
// FIXME FABRIC: This is doing a bunch of work that is likely unnecessary
FluidAmount newAmount = getCurrentAmount(storedFluid);
FluidAmount oldAmount = old == null ? FluidAmount.ZERO : old.volume.amount();
if (!newAmount.equals(oldAmount)) {
final CachedFluidStack cis = new CachedFluidStack(storedFluid.withAmount(newAmount));
this.memory.put(storedFluid, cis);
if (old != null && old.aeStack != null) {
old.aeStack.setStackSize(-old.aeStack.getStackSize());
changes.add(old.aeStack);
}
if (cis.aeStack != null) {
changes.add(cis.aeStack);
this.list.add(cis.aeStack);
}
changed = true;
} else {
final long newSize = newAmount.isZero() ? 0 : newAmount.asLong(1000, RoundingMode.DOWN);
final long diff = newSize - (oldAmount.isZero() ? 0 : oldAmount.asLong(1000, RoundingMode.DOWN));
IAEFluidStack stack = null;
if (!newAmount.isZero()) {
stack = (old == null || old.aeStack == null ? AEFluidStack.fromFluidVolume(storedFluid.withAmount(newAmount), RoundingMode.DOWN) : old.aeStack.copy());
}
if (stack != null) {
stack.setStackSize(newSize);
this.list.add(stack);
}
if (diff != 0 && stack != null) {
final CachedFluidStack cis = new CachedFluidStack(storedFluid.withAmount(newAmount));
this.memory.put(storedFluid, cis);
final IAEFluidStack a = stack.copy();
a.setStackSize(diff);
changes.add(a);
changed = true;
}
}
}
// detect dropped items; should fix non IISided Inventory Changes.
Set<FluidKey> toRemove = null;
for (final Entry<FluidKey, CachedFluidStack> entry : memory.entrySet()) {
if (storedFluids.contains(entry.getKey())) {
continue; // Still stored
}
if (toRemove == null) {
toRemove = new HashSet<>();
}
toRemove.add(entry.getKey());
if (entry.getValue().aeStack != null) {
final IAEFluidStack a = entry.getValue().aeStack.copy();
a.setStackSize(-a.getStackSize());
changes.add(a);
changed = true;
}
}
// Now clean up if any removed entries were found
if (toRemove != null) {
for (FluidKey fluidKey : toRemove) {
memory.remove(fluidKey);
}
}
if (!changes.isEmpty()) {
this.postDifference(changes);
}
return changed ? TickRateModulation.URGENT : TickRateModulation.SLOWER;
}
private FluidAmount getCurrentAmount(FluidKey storedFluid) {
FluidAmount newAmount = this.handler.getAmount_F(storedFluid);
if (!newAmount.isZero() && this.getMode() == StorageFilter.EXTRACTABLE_ONLY) {
// We have to actually check if we could extract _anything_
ExactFluidFilter filter = new ExactFluidFilter(storedFluid);
if (this.handler.attemptExtraction(filter, MIN_EXTRACTION_AMOUNT, Simulation.SIMULATE).isEmpty()) {
// Just to safeguard against tanks that prevent non-bucket-size extractions
if (this.handler.attemptExtraction(filter, FluidAmount.BUCKET, Simulation.SIMULATE).isEmpty()) {
newAmount = FluidAmount.ZERO;
}
}
}
return newAmount;
}
private static boolean isDifferent(FluidVolume a, FluidVolume b) {
if (a == b) {
return false;
}
if (a.isEmpty() || b.isEmpty()) {
return true;
}
return !a.getFluidKey().equals(b.getFluidKey());
}
private void postDifference(final Iterable<IAEFluidStack> a) {
if (a != null) {
final Iterator<Entry<IMEMonitorHandlerReceiver<IAEFluidStack>, Object>> i = this.listeners.entrySet()
.iterator();
while (i.hasNext()) {
final Entry<IMEMonitorHandlerReceiver<IAEFluidStack>, Object> l = i.next();
final IMEMonitorHandlerReceiver<IAEFluidStack> key = l.getKey();
if (key.isValid(l.getValue())) {
key.postChange(this, a, this.getActionSource());
} else {
i.remove();
}
}
}
}
@Override
public AccessRestriction getAccess() {
return AccessRestriction.READ_WRITE;
}
@Override
public boolean isPrioritized(final IAEFluidStack input) {
return false;
}
@Override
public boolean canAccept(final IAEFluidStack input) {
return true;
}
@Override
public int getPriority() {
return 0;
}
@Override
public int getSlot() {
return 0;
}
@Override
public boolean validForPass(final int i) {
return true;
}
@Override
public IItemList<IAEFluidStack> getAvailableItems(final IItemList out) {
for (final CachedFluidStack is : this.memory.values()) {
out.addStorage(is.aeStack);
}
return out;
}
@Override
public IItemList<IAEFluidStack> getStorageList() {
return this.list;
}
private StorageFilter getMode() {
return this.mode;
}
public void setMode(final StorageFilter mode) {
this.mode = mode;
}
private IActionSource getActionSource() {
return this.mySource;
}
@Override
public void setActionSource(final IActionSource mySource) {
this.mySource = mySource;
}
private static class CachedFluidStack {
private final FluidVolume volume;
private final IAEFluidStack aeStack;
CachedFluidStack(FluidVolume volume) {
this.aeStack = AEFluidStack.fromFluidVolume(volume, RoundingMode.DOWN);
if (aeStack != null) {
// This ensures that the amount is equal if it was rounded down
this.volume = aeStack.getFluidStack();
} else {
this.volume = FluidVolumeUtil.EMPTY;
}
}
}
}
@@ -0,0 +1,167 @@
/*
* 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.storage;
import com.mojang.authlib.GameProfile;
import appeng.api.AEApi;
import appeng.api.config.AccessRestriction;
import appeng.api.config.Actionable;
import appeng.api.config.SecurityPermissions;
import appeng.api.implementations.items.IBiometricCard;
import appeng.api.networking.security.IActionSource;
import appeng.api.storage.IMEInventoryHandler;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.channels.IItemStorageChannel;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IItemList;
import appeng.me.GridAccessException;
import appeng.tile.misc.SecurityStationBlockEntity;
public class SecurityStationInventory implements IMEInventoryHandler<IAEItemStack> {
private final IItemList<IAEItemStack> storedItems = AEApi.instance().storage()
.getStorageChannel(IItemStorageChannel.class).createList();
private final SecurityStationBlockEntity securityTile;
public SecurityStationInventory(final SecurityStationBlockEntity ts) {
this.securityTile = ts;
}
@Override
public IAEItemStack injectItems(final IAEItemStack input, final Actionable type, final IActionSource src) {
if (this.hasPermission(src)) {
if (AEApi.instance().definitions().items().biometricCard().isSameAs(input.createItemStack())) {
if (this.canAccept(input)) {
if (type == Actionable.SIMULATE) {
return null;
}
this.getStoredItems().add(input);
this.securityTile.inventoryChanged();
return null;
}
}
}
return input;
}
private boolean hasPermission(final IActionSource src) {
if (src.player().isPresent()) {
try {
return this.securityTile.getProxy().getSecurity().hasPermission(src.player().get(),
SecurityPermissions.SECURITY);
} catch (final GridAccessException e) {
// :P
}
}
return false;
}
@Override
public IAEItemStack extractItems(final IAEItemStack request, final Actionable mode, final IActionSource src) {
if (this.hasPermission(src)) {
final IAEItemStack target = this.getStoredItems().findPrecise(request);
if (target != null) {
final IAEItemStack output = target.copy();
if (mode == Actionable.SIMULATE) {
return output;
}
target.setStackSize(0);
this.securityTile.inventoryChanged();
return output;
}
}
return null;
}
@Override
public IItemList<IAEItemStack> getAvailableItems(final IItemList out) {
for (final IAEItemStack ais : this.getStoredItems()) {
out.add(ais);
}
return out;
}
@Override
public IStorageChannel getChannel() {
return AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class);
}
@Override
public AccessRestriction getAccess() {
return AccessRestriction.READ_WRITE;
}
@Override
public boolean isPrioritized(final IAEItemStack input) {
return false;
}
@Override
public boolean canAccept(final IAEItemStack input) {
if (input.getItem() instanceof IBiometricCard) {
final IBiometricCard tbc = (IBiometricCard) input.getItem();
final GameProfile newUser = tbc.getProfile(input.createItemStack());
final int PlayerID = AEApi.instance().registries().players().getID(newUser);
if (this.securityTile.getOwner() == PlayerID) {
return false;
}
for (final IAEItemStack ais : this.getStoredItems()) {
if (ais.isMeaningful()) {
final GameProfile thisUser = tbc.getProfile(ais.createItemStack());
if (thisUser == newUser) {
return false;
}
if (thisUser != null && thisUser.equals(newUser)) {
return false;
}
}
}
return true;
}
return false;
}
@Override
public int getPriority() {
return 0;
}
@Override
public int getSlot() {
return 0;
}
@Override
public boolean validForPass(final int i) {
return true;
}
public IItemList<IAEItemStack> getStoredItems() {
return this.storedItems;
}
}