Moving to source sets
This commit is contained in:
@@ -1,572 +0,0 @@
|
||||
/*
|
||||
* 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..
|
||||
}
|
||||
}
|
||||
}
|
||||
+596
@@ -0,0 +1,596 @@
|
||||
/*
|
||||
* 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.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import appeng.api.config.AccessRestriction;
|
||||
import appeng.api.config.Actionable;
|
||||
import appeng.api.config.PowerMultiplier;
|
||||
import appeng.api.networking.IGrid;
|
||||
import appeng.api.networking.IGridBlock;
|
||||
import appeng.api.networking.IGridHost;
|
||||
import appeng.api.networking.IGridNode;
|
||||
import appeng.api.networking.IGridStorage;
|
||||
import appeng.api.networking.energy.IAEPowerStorage;
|
||||
import appeng.api.networking.energy.IEnergyGrid;
|
||||
import appeng.api.networking.energy.IEnergyGridProvider;
|
||||
import appeng.api.networking.energy.IEnergyWatcher;
|
||||
import appeng.api.networking.energy.IEnergyWatcherHost;
|
||||
import appeng.api.networking.events.MENetworkEventSubscribe;
|
||||
import appeng.api.networking.events.MENetworkPostCacheConstruction;
|
||||
import appeng.api.networking.events.MENetworkPowerIdleChange;
|
||||
import appeng.api.networking.events.MENetworkPowerStatusChange;
|
||||
import appeng.api.networking.events.MENetworkPowerStorage;
|
||||
import appeng.api.networking.events.MENetworkPowerStorage.PowerEventType;
|
||||
import appeng.api.networking.pathing.IPathingGrid;
|
||||
import appeng.me.Grid;
|
||||
import appeng.me.GridNode;
|
||||
import appeng.me.energy.EnergyThreshold;
|
||||
import appeng.me.energy.EnergyWatcher;
|
||||
|
||||
public class EnergyGridCache implements IEnergyGrid {
|
||||
|
||||
private static final double MAX_BUFFER_STORAGE = 800;
|
||||
|
||||
private static final Comparator<IEnergyGridProvider> COMPARATOR_HIGHEST_AMOUNT_STORED_FIRST = (o1, o2) -> Double
|
||||
.compare(o2.getProviderStoredEnergy(), o1.getProviderStoredEnergy());
|
||||
|
||||
private static final Comparator<IEnergyGridProvider> COMPARATOR_LOWEST_PERCENTAGE_FIRST = (o1, o2) -> {
|
||||
final double percent1 = (o1.getProviderStoredEnergy() + 1) / (o1.getProviderMaxEnergy() + 1);
|
||||
final double percent2 = (o2.getProviderStoredEnergy() + 1) / (o2.getProviderMaxEnergy() + 1);
|
||||
|
||||
return Double.compare(percent1, percent2);
|
||||
};
|
||||
|
||||
private final NavigableSet<EnergyThreshold> interests = Sets.newTreeSet();
|
||||
private final double averageLength = 40.0;
|
||||
private final Set<IAEPowerStorage> providers = new LinkedHashSet<>();
|
||||
private final Set<IAEPowerStorage> requesters = new LinkedHashSet<>();
|
||||
private final Multiset<IEnergyGridProvider> energyGridProviders = HashMultiset.create();
|
||||
private final IGrid myGrid;
|
||||
private final HashMap<IGridNode, IEnergyWatcher> watchers = new HashMap<>();
|
||||
|
||||
/**
|
||||
* estimated power available.
|
||||
*/
|
||||
private int availableTicksSinceUpdate = 0;
|
||||
private double globalAvailablePower = 0;
|
||||
private double globalMaxPower = MAX_BUFFER_STORAGE;
|
||||
|
||||
/**
|
||||
* idle draw.
|
||||
*/
|
||||
private double drainPerTick = 0;
|
||||
private double avgDrainPerTick = 0;
|
||||
private double avgInjectionPerTick = 0;
|
||||
private double tickDrainPerTick = 0;
|
||||
private double tickInjectionPerTick = 0;
|
||||
|
||||
/**
|
||||
* power status
|
||||
*/
|
||||
private boolean publicHasPower = false;
|
||||
private boolean hasPower = true;
|
||||
private long ticksSinceHasPowerChange = 900;
|
||||
|
||||
private PathGridCache pgc;
|
||||
private double lastStoredPower = -1;
|
||||
|
||||
private final GridPowerStorage localStorage = new GridPowerStorage();
|
||||
|
||||
public EnergyGridCache(final IGrid g) {
|
||||
this.myGrid = g;
|
||||
this.requesters.add(this.localStorage);
|
||||
this.providers.add(this.localStorage);
|
||||
}
|
||||
|
||||
@MENetworkEventSubscribe
|
||||
public void postInit(final MENetworkPostCacheConstruction pcc) {
|
||||
this.pgc = this.myGrid.getCache(IPathingGrid.class);
|
||||
}
|
||||
|
||||
@MENetworkEventSubscribe
|
||||
public void nodeIdlePowerChangeHandler(final MENetworkPowerIdleChange ev) {
|
||||
// update power usage based on event.
|
||||
final GridNode node = (GridNode) ev.node;
|
||||
final IGridBlock gb = node.getGridBlock();
|
||||
|
||||
final double newDraw = gb.getIdlePowerUsage();
|
||||
final double diffDraw = newDraw - node.getPreviousDraw();
|
||||
node.setPreviousDraw(newDraw);
|
||||
|
||||
this.drainPerTick += diffDraw;
|
||||
}
|
||||
|
||||
@MENetworkEventSubscribe
|
||||
public void storagePowerChangeHandler(final MENetworkPowerStorage ev) {
|
||||
if (ev.storage.isAEPublicPowerStorage()) {
|
||||
switch (ev.type) {
|
||||
case PROVIDE_POWER:
|
||||
if (ev.storage.getPowerFlow() != AccessRestriction.WRITE) {
|
||||
this.providers.add(ev.storage);
|
||||
}
|
||||
break;
|
||||
case REQUEST_POWER:
|
||||
if (ev.storage.getPowerFlow() != AccessRestriction.READ) {
|
||||
this.requesters.add(ev.storage);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
(new RuntimeException("Attempt to ask the IEnergyGrid to charge a non public energy store."))
|
||||
.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdateTick() {
|
||||
if (!this.interests.isEmpty()) {
|
||||
final double oldPower = this.lastStoredPower;
|
||||
this.lastStoredPower = this.getStoredPower();
|
||||
|
||||
final EnergyThreshold low = new EnergyThreshold(Math.min(oldPower, this.lastStoredPower),
|
||||
Integer.MIN_VALUE);
|
||||
final EnergyThreshold high = new EnergyThreshold(Math.max(oldPower, this.lastStoredPower),
|
||||
Integer.MAX_VALUE);
|
||||
|
||||
for (final EnergyThreshold th : this.interests.subSet(low, true, high, true)) {
|
||||
((EnergyWatcher) th.getEnergyWatcher()).post(this);
|
||||
}
|
||||
}
|
||||
|
||||
this.avgDrainPerTick *= (this.averageLength - 1) / this.averageLength;
|
||||
this.avgInjectionPerTick *= (this.averageLength - 1) / this.averageLength;
|
||||
|
||||
this.avgDrainPerTick += this.tickDrainPerTick / this.averageLength;
|
||||
this.avgInjectionPerTick += this.tickInjectionPerTick / this.averageLength;
|
||||
|
||||
this.tickDrainPerTick = 0;
|
||||
this.tickInjectionPerTick = 0;
|
||||
|
||||
// power information.
|
||||
boolean currentlyHasPower = false;
|
||||
|
||||
if (this.drainPerTick > 0.0001) {
|
||||
final double drained = this.extractAEPower(this.getIdlePowerUsage(), Actionable.MODULATE,
|
||||
PowerMultiplier.CONFIG);
|
||||
currentlyHasPower = drained >= this.drainPerTick - 0.001;
|
||||
} else {
|
||||
currentlyHasPower = this.extractAEPower(0.1, Actionable.SIMULATE, PowerMultiplier.CONFIG) > 0;
|
||||
}
|
||||
|
||||
// ticks since change..
|
||||
if (currentlyHasPower == this.hasPower) {
|
||||
this.ticksSinceHasPowerChange++;
|
||||
} else {
|
||||
this.ticksSinceHasPowerChange = 0;
|
||||
}
|
||||
|
||||
// update status..
|
||||
this.hasPower = currentlyHasPower;
|
||||
|
||||
// update public status, this buffers power ups for 30 ticks.
|
||||
if (this.hasPower && this.ticksSinceHasPowerChange > 30) {
|
||||
this.publicPowerState(true, this.myGrid);
|
||||
} else if (!this.hasPower) {
|
||||
this.publicPowerState(false, this.myGrid);
|
||||
}
|
||||
|
||||
this.availableTicksSinceUpdate++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double extractAEPower(final double amt, final Actionable mode, final PowerMultiplier pm) {
|
||||
final double toExtract = pm.multiply(amt);
|
||||
final Queue<IEnergyGridProvider> toVisit = new PriorityQueue<>(COMPARATOR_HIGHEST_AMOUNT_STORED_FIRST);
|
||||
final Set<IEnergyGridProvider> visited = new HashSet<>();
|
||||
|
||||
double extracted = 0;
|
||||
toVisit.add(this);
|
||||
|
||||
while (!toVisit.isEmpty() && extracted < toExtract) {
|
||||
final IEnergyGridProvider next = toVisit.poll();
|
||||
visited.add(next);
|
||||
|
||||
extracted += next.extractProviderPower(toExtract - extracted, mode);
|
||||
|
||||
for (IEnergyGridProvider iEnergyGridProvider : next.providers()) {
|
||||
if (!visited.contains(iEnergyGridProvider)) {
|
||||
toVisit.add(iEnergyGridProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pm.divide(extracted);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getIdlePowerUsage() {
|
||||
return this.drainPerTick + this.pgc.getChannelPowerUsage();
|
||||
}
|
||||
|
||||
private void publicPowerState(final boolean newState, final IGrid grid) {
|
||||
if (this.publicHasPower == newState) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.publicHasPower = newState;
|
||||
((Grid) this.myGrid).setImportantFlag(0, this.publicHasPower);
|
||||
grid.postEvent(new MENetworkPowerStatusChange());
|
||||
}
|
||||
|
||||
/**
|
||||
* refresh current stored power.
|
||||
*/
|
||||
private void refreshPower() {
|
||||
this.availableTicksSinceUpdate = 0;
|
||||
this.globalAvailablePower = 0;
|
||||
for (final IAEPowerStorage p : this.providers) {
|
||||
this.globalAvailablePower += p.getAECurrentPower();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<IEnergyGridProvider> providers() {
|
||||
return this.energyGridProviders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double extractProviderPower(final double amt, final Actionable mode) {
|
||||
double extractedPower = 0;
|
||||
|
||||
final Iterator<IAEPowerStorage> it = this.providers.iterator();
|
||||
|
||||
while (extractedPower < amt && it.hasNext()) {
|
||||
final IAEPowerStorage node = it.next();
|
||||
|
||||
final double req = amt - extractedPower;
|
||||
final double newPower = node.extractAEPower(req, mode, PowerMultiplier.ONE);
|
||||
extractedPower += newPower;
|
||||
|
||||
if (newPower < req && mode == Actionable.MODULATE) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
final double result = Math.min(extractedPower, amt);
|
||||
|
||||
if (mode == Actionable.MODULATE) {
|
||||
if (extractedPower > amt) {
|
||||
this.localStorage.addCurrentAEPower(extractedPower - amt);
|
||||
}
|
||||
|
||||
this.globalAvailablePower -= result;
|
||||
this.tickDrainPerTick += result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double injectProviderPower(double amt, final Actionable mode) {
|
||||
final double originalAmount = amt;
|
||||
|
||||
final Iterator<IAEPowerStorage> it = this.requesters.iterator();
|
||||
|
||||
while (amt > 0 && it.hasNext()) {
|
||||
final IAEPowerStorage node = it.next();
|
||||
amt = node.injectAEPower(amt, mode);
|
||||
|
||||
if (amt > 0 && mode == Actionable.MODULATE) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
final double overflow = Math.max(0.0, amt);
|
||||
|
||||
if (mode == Actionable.MODULATE) {
|
||||
this.tickInjectionPerTick += originalAmount - overflow;
|
||||
}
|
||||
|
||||
return overflow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getProviderEnergyDemand(final double maxRequired) {
|
||||
double required = 0;
|
||||
|
||||
final Iterator<IAEPowerStorage> it = this.requesters.iterator();
|
||||
while (required < maxRequired && it.hasNext()) {
|
||||
final IAEPowerStorage node = it.next();
|
||||
if (node.getPowerFlow() != AccessRestriction.READ) {
|
||||
required += Math.max(0.0, node.getAEMaxPower() - node.getAECurrentPower());
|
||||
}
|
||||
}
|
||||
|
||||
return required;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getAvgPowerUsage() {
|
||||
return this.avgDrainPerTick;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getAvgPowerInjection() {
|
||||
return this.avgInjectionPerTick;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNetworkPowered() {
|
||||
return this.publicHasPower;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double injectPower(final double amt, final Actionable mode) {
|
||||
final Queue<IEnergyGridProvider> toVisit = new PriorityQueue<>(COMPARATOR_LOWEST_PERCENTAGE_FIRST);
|
||||
final Set<IEnergyGridProvider> visited = new HashSet<>();
|
||||
toVisit.add(this);
|
||||
|
||||
double leftover = amt;
|
||||
|
||||
while (!toVisit.isEmpty() && leftover > 0) {
|
||||
final IEnergyGridProvider next = toVisit.poll();
|
||||
visited.add(next);
|
||||
|
||||
leftover = next.injectProviderPower(leftover, mode);
|
||||
|
||||
for (IEnergyGridProvider iEnergyGridProvider : next.providers()) {
|
||||
if (!visited.contains(iEnergyGridProvider)) {
|
||||
toVisit.add(iEnergyGridProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return leftover;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getStoredPower() {
|
||||
if (this.availableTicksSinceUpdate > 90) {
|
||||
this.refreshPower();
|
||||
}
|
||||
|
||||
return Math.max(0.0, this.globalAvailablePower);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getMaxStoredPower() {
|
||||
return this.globalMaxPower;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getEnergyDemand(final double maxRequired) {
|
||||
final Queue<IEnergyGridProvider> toVisit = new PriorityQueue<>(COMPARATOR_LOWEST_PERCENTAGE_FIRST);
|
||||
final Set<IEnergyGridProvider> visited = new HashSet<>();
|
||||
toVisit.add(this);
|
||||
|
||||
double required = 0;
|
||||
|
||||
while (!toVisit.isEmpty() && required < maxRequired) {
|
||||
final IEnergyGridProvider next = toVisit.poll();
|
||||
visited.add(next);
|
||||
|
||||
required += next.getProviderEnergyDemand(maxRequired - required);
|
||||
|
||||
for (IEnergyGridProvider iEnergyGridProvider : next.providers()) {
|
||||
if (!visited.contains(iEnergyGridProvider)) {
|
||||
toVisit.add(iEnergyGridProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return required;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getProviderStoredEnergy() {
|
||||
return this.getStoredPower();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getProviderMaxEnergy() {
|
||||
return this.getMaxStoredPower();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNode(final IGridNode node, final IGridHost machine) {
|
||||
if (machine instanceof IEnergyGridProvider) {
|
||||
this.energyGridProviders.remove(machine);
|
||||
}
|
||||
|
||||
// idle draw.
|
||||
final GridNode gridNode = (GridNode) node;
|
||||
this.drainPerTick -= gridNode.getPreviousDraw();
|
||||
|
||||
// power storage.
|
||||
if (machine instanceof IAEPowerStorage) {
|
||||
final IAEPowerStorage ps = (IAEPowerStorage) machine;
|
||||
if (ps.isAEPublicPowerStorage()) {
|
||||
if (ps.getPowerFlow() != AccessRestriction.WRITE) {
|
||||
this.globalMaxPower -= ps.getAEMaxPower();
|
||||
this.globalAvailablePower -= ps.getAECurrentPower();
|
||||
}
|
||||
|
||||
this.providers.remove(ps);
|
||||
this.requesters.remove(ps);
|
||||
}
|
||||
}
|
||||
|
||||
if (machine instanceof IEnergyWatcherHost) {
|
||||
final IEnergyWatcher watcher = this.watchers.get(node);
|
||||
|
||||
if (watcher != null) {
|
||||
watcher.reset();
|
||||
this.watchers.remove(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNode(final IGridNode node, final IGridHost machine) {
|
||||
if (machine instanceof IEnergyGridProvider) {
|
||||
this.energyGridProviders.add((IEnergyGridProvider) machine);
|
||||
}
|
||||
|
||||
// idle draw...
|
||||
final GridNode gridNode = (GridNode) node;
|
||||
final IGridBlock gb = gridNode.getGridBlock();
|
||||
gridNode.setPreviousDraw(gb.getIdlePowerUsage());
|
||||
this.drainPerTick += gridNode.getPreviousDraw();
|
||||
|
||||
// power storage
|
||||
if (machine instanceof IAEPowerStorage) {
|
||||
final IAEPowerStorage ps = (IAEPowerStorage) machine;
|
||||
if (ps.isAEPublicPowerStorage()) {
|
||||
final double max = ps.getAEMaxPower();
|
||||
final double current = ps.getAECurrentPower();
|
||||
|
||||
if (ps.getPowerFlow() != AccessRestriction.WRITE) {
|
||||
this.globalMaxPower += ps.getAEMaxPower();
|
||||
}
|
||||
|
||||
if (current > 0 && ps.getPowerFlow() != AccessRestriction.WRITE) {
|
||||
this.globalAvailablePower += current;
|
||||
this.providers.add(ps);
|
||||
}
|
||||
|
||||
if (current < max && ps.getPowerFlow() != AccessRestriction.READ) {
|
||||
this.requesters.add(ps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (machine instanceof IEnergyWatcherHost) {
|
||||
final IEnergyWatcherHost swh = (IEnergyWatcherHost) machine;
|
||||
final EnergyWatcher iw = new EnergyWatcher(this, swh);
|
||||
|
||||
this.watchers.put(node, iw);
|
||||
swh.updateWatcher(iw);
|
||||
}
|
||||
|
||||
this.myGrid.postEventTo(node, new MENetworkPowerStatusChange());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSplit(final IGridStorage storageB) {
|
||||
final double newBuffer = this.localStorage.getAECurrentPower() / 2;
|
||||
this.localStorage.removeCurrentAEPower(newBuffer);
|
||||
storageB.dataObject().putDouble("buffer", newBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoin(final IGridStorage storageB) {
|
||||
this.localStorage.addCurrentAEPower(storageB.dataObject().getDouble("buffer"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateGridStorage(final IGridStorage storage) {
|
||||
storage.dataObject().putDouble("buffer", this.localStorage.getAECurrentPower());
|
||||
}
|
||||
|
||||
public boolean registerEnergyInterest(final EnergyThreshold threshold) {
|
||||
return this.interests.add(threshold);
|
||||
}
|
||||
|
||||
public boolean unregisterEnergyInterest(final EnergyThreshold threshold) {
|
||||
return this.interests.remove(threshold);
|
||||
}
|
||||
|
||||
private class GridPowerStorage implements IAEPowerStorage {
|
||||
private double stored = 0;
|
||||
|
||||
@Override
|
||||
public double extractAEPower(double amt, Actionable mode, PowerMultiplier usePowerMultiplier) {
|
||||
double extracted = Math.min(amt, this.stored);
|
||||
|
||||
if (mode == Actionable.MODULATE) {
|
||||
this.removeCurrentAEPower(extracted);
|
||||
}
|
||||
|
||||
return extracted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAEPublicPowerStorage() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double injectAEPower(double amt, Actionable mode) {
|
||||
double toStore = Math.min(amt, MAX_BUFFER_STORAGE - this.stored);
|
||||
|
||||
if (mode == Actionable.MODULATE) {
|
||||
this.addCurrentAEPower(toStore);
|
||||
}
|
||||
|
||||
return amt - toStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessRestriction getPowerFlow() {
|
||||
return AccessRestriction.READ_WRITE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getAEMaxPower() {
|
||||
return MAX_BUFFER_STORAGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getAECurrentPower() {
|
||||
return this.stored;
|
||||
}
|
||||
|
||||
private void addCurrentAEPower(double amount) {
|
||||
this.stored += amount;
|
||||
|
||||
if (this.stored > 0.01) {
|
||||
EnergyGridCache.this.myGrid.postEvent(new MENetworkPowerStorage(this, PowerEventType.PROVIDE_POWER));
|
||||
}
|
||||
}
|
||||
|
||||
private void removeCurrentAEPower(double amount) {
|
||||
this.stored -= amount;
|
||||
|
||||
if (this.stored < MAX_BUFFER_STORAGE - 0.001) {
|
||||
EnergyGridCache.this.myGrid.postEvent(new MENetworkPowerStorage(this, PowerEventType.REQUEST_POWER));
|
||||
}
|
||||
|
||||
if (this.stored < 0.01) {
|
||||
EnergyGridCache.this.ticksSinceHasPowerChange = 0;
|
||||
EnergyGridCache.this.publicPowerState(false, EnergyGridCache.this.myGrid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+306
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* 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.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
|
||||
import appeng.api.AEApi;
|
||||
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.MENetworkCellArrayUpdate;
|
||||
import appeng.api.networking.events.MENetworkEventSubscribe;
|
||||
import appeng.api.networking.security.IActionHost;
|
||||
import appeng.api.networking.security.IActionSource;
|
||||
import appeng.api.networking.security.ISecurityGrid;
|
||||
import appeng.api.networking.storage.IStackWatcher;
|
||||
import appeng.api.networking.storage.IStackWatcherHost;
|
||||
import appeng.api.networking.storage.IStorageGrid;
|
||||
import appeng.api.storage.IMEInventoryHandler;
|
||||
import appeng.api.storage.IMEMonitor;
|
||||
import appeng.api.storage.IStorageChannel;
|
||||
import appeng.api.storage.cells.ICellContainer;
|
||||
import appeng.api.storage.cells.ICellProvider;
|
||||
import appeng.api.storage.data.IAEItemStack;
|
||||
import appeng.api.storage.data.IAEStack;
|
||||
import appeng.api.storage.data.IItemList;
|
||||
import appeng.me.helpers.BaseActionSource;
|
||||
import appeng.me.helpers.GenericInterestManager;
|
||||
import appeng.me.helpers.MachineSource;
|
||||
import appeng.me.storage.ItemWatcher;
|
||||
import appeng.me.storage.NetworkInventoryHandler;
|
||||
|
||||
public class GridStorageCache implements IStorageGrid {
|
||||
|
||||
private final IGrid myGrid;
|
||||
private final HashSet<ICellProvider> activeCellProviders = new HashSet<>();
|
||||
private final HashSet<ICellProvider> inactiveCellProviders = new HashSet<>();
|
||||
private final SetMultimap<IAEStack, ItemWatcher> interests = HashMultimap.create();
|
||||
private final GenericInterestManager<ItemWatcher> interestManager = new GenericInterestManager<>(this.interests);
|
||||
private final HashMap<IGridNode, IStackWatcher> watchers = new HashMap<>();
|
||||
private Map<IStorageChannel<? extends IAEStack>, NetworkInventoryHandler<?>> storageNetworks;
|
||||
private Map<IStorageChannel<? extends IAEStack>, NetworkMonitor<?>> storageMonitors;
|
||||
|
||||
public GridStorageCache(final IGrid g) {
|
||||
this.myGrid = g;
|
||||
this.storageNetworks = new IdentityHashMap<>();
|
||||
this.storageMonitors = new IdentityHashMap<>();
|
||||
|
||||
AEApi.instance().storage().storageChannels()
|
||||
.forEach(channel -> this.storageMonitors.put(channel, new NetworkMonitor<>(this, channel)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdateTick() {
|
||||
this.storageMonitors.forEach((channel, monitor) -> monitor.onTick());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNode(final IGridNode node, final IGridHost machine) {
|
||||
if (machine instanceof ICellContainer) {
|
||||
final ICellContainer cc = (ICellContainer) machine;
|
||||
final CellChangeTracker tracker = new CellChangeTracker();
|
||||
|
||||
this.removeCellProvider(cc, tracker);
|
||||
this.inactiveCellProviders.remove(cc);
|
||||
this.getGrid().postEvent(new MENetworkCellArrayUpdate());
|
||||
|
||||
tracker.applyChanges();
|
||||
}
|
||||
|
||||
if (machine instanceof IStackWatcherHost) {
|
||||
final IStackWatcher myWatcher = this.watchers.get(machine);
|
||||
|
||||
if (myWatcher != null) {
|
||||
myWatcher.reset();
|
||||
this.watchers.remove(machine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNode(final IGridNode node, final IGridHost machine) {
|
||||
if (machine instanceof ICellContainer) {
|
||||
final ICellContainer cc = (ICellContainer) machine;
|
||||
this.inactiveCellProviders.add(cc);
|
||||
|
||||
this.getGrid().postEvent(new MENetworkCellArrayUpdate());
|
||||
|
||||
if (node.isActive()) {
|
||||
final CellChangeTracker tracker = new CellChangeTracker();
|
||||
|
||||
this.addCellProvider(cc, tracker);
|
||||
tracker.applyChanges();
|
||||
}
|
||||
}
|
||||
|
||||
if (machine instanceof IStackWatcherHost) {
|
||||
final IStackWatcherHost swh = (IStackWatcherHost) machine;
|
||||
final ItemWatcher iw = new ItemWatcher(this, swh);
|
||||
this.watchers.put(node, iw);
|
||||
swh.updateWatcher(iw);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSplit(final IGridStorage storageB) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoin(final IGridStorage storageB) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateGridStorage(final IGridStorage storage) {
|
||||
|
||||
}
|
||||
|
||||
public <T extends IAEStack<T>> IMEInventoryHandler<T> getInventoryHandler(IStorageChannel<T> channel) {
|
||||
return (IMEInventoryHandler<T>) this.storageNetworks.computeIfAbsent(channel, this::buildNetworkStorage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IAEStack<T>> IMEMonitor<T> getInventory(IStorageChannel<T> channel) {
|
||||
return (IMEMonitor<T>) this.storageMonitors.get(channel);
|
||||
}
|
||||
|
||||
private CellChangeTracker addCellProvider(final ICellProvider cc, final CellChangeTracker tracker) {
|
||||
if (this.inactiveCellProviders.contains(cc)) {
|
||||
this.inactiveCellProviders.remove(cc);
|
||||
this.activeCellProviders.add(cc);
|
||||
|
||||
final IActionSource actionSrc = cc instanceof IActionHost ? new MachineSource((IActionHost) cc)
|
||||
: new BaseActionSource();
|
||||
|
||||
this.storageMonitors.forEach((channel, monitor) -> {
|
||||
for (final IMEInventoryHandler<?> h : cc.getCellArray(channel)) {
|
||||
tracker.postChanges(channel, 1, h, actionSrc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return tracker;
|
||||
}
|
||||
|
||||
private CellChangeTracker removeCellProvider(final ICellProvider cc, final CellChangeTracker tracker) {
|
||||
if (this.activeCellProviders.contains(cc)) {
|
||||
this.activeCellProviders.remove(cc);
|
||||
this.inactiveCellProviders.add(cc);
|
||||
|
||||
final IActionSource actionSrc = cc instanceof IActionHost ? new MachineSource((IActionHost) cc)
|
||||
: new BaseActionSource();
|
||||
|
||||
this.storageMonitors.forEach((channel, monitor) -> {
|
||||
for (final IMEInventoryHandler<IAEItemStack> h : cc.getCellArray(channel)) {
|
||||
tracker.postChanges(channel, -1, h, actionSrc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return tracker;
|
||||
}
|
||||
|
||||
@MENetworkEventSubscribe
|
||||
public void cellUpdate(final MENetworkCellArrayUpdate ev) {
|
||||
this.storageNetworks.clear();
|
||||
|
||||
final List<ICellProvider> ll = new ArrayList<ICellProvider>();
|
||||
ll.addAll(this.inactiveCellProviders);
|
||||
ll.addAll(this.activeCellProviders);
|
||||
|
||||
final CellChangeTracker tracker = new CellChangeTracker();
|
||||
|
||||
for (final ICellProvider cc : ll) {
|
||||
boolean active = true;
|
||||
|
||||
if (cc instanceof IActionHost) {
|
||||
final IGridNode node = ((IActionHost) cc).getActionableNode();
|
||||
if (node != null && node.isActive()) {
|
||||
active = true;
|
||||
} else {
|
||||
active = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (active) {
|
||||
this.addCellProvider(cc, tracker);
|
||||
} else {
|
||||
this.removeCellProvider(cc, tracker);
|
||||
}
|
||||
}
|
||||
|
||||
this.storageMonitors.forEach((channel, monitor) -> monitor.forceUpdate());
|
||||
|
||||
tracker.applyChanges();
|
||||
}
|
||||
|
||||
private <T extends IAEStack<T>, C extends IStorageChannel<T>> void postChangesToNetwork(final C chan,
|
||||
final int upOrDown, final IItemList<T> availableItems, final IActionSource src) {
|
||||
this.storageMonitors.get(chan).postChange(upOrDown > 0, (Iterable) availableItems, src);
|
||||
}
|
||||
|
||||
private <T extends IAEStack<T>, C extends IStorageChannel<T>> NetworkInventoryHandler<T> buildNetworkStorage(
|
||||
final C chan) {
|
||||
final SecurityCache security = this.getGrid().getCache(ISecurityGrid.class);
|
||||
|
||||
final NetworkInventoryHandler<T> storageNetwork = new NetworkInventoryHandler<>(chan, security);
|
||||
|
||||
for (final ICellProvider cc : this.activeCellProviders) {
|
||||
for (final IMEInventoryHandler<T> h : cc.getCellArray(chan)) {
|
||||
storageNetwork.addNewStorage(h);
|
||||
}
|
||||
}
|
||||
|
||||
return storageNetwork;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postAlterationOfStoredItems(final IStorageChannel<?> chan, final Iterable<? extends IAEStack<?>> input,
|
||||
final IActionSource src) {
|
||||
this.storageMonitors.get(chan).postChange(true, (Iterable) input, src);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCellProvider(final ICellProvider provider) {
|
||||
this.inactiveCellProviders.add(provider);
|
||||
this.addCellProvider(provider, new CellChangeTracker()).applyChanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterCellProvider(final ICellProvider provider) {
|
||||
this.removeCellProvider(provider, new CellChangeTracker()).applyChanges();
|
||||
this.inactiveCellProviders.remove(provider);
|
||||
}
|
||||
|
||||
public GenericInterestManager<ItemWatcher> getInterestManager() {
|
||||
return this.interestManager;
|
||||
}
|
||||
|
||||
IGrid getGrid() {
|
||||
return this.myGrid;
|
||||
}
|
||||
|
||||
private class CellChangeTrackerRecord<T extends IAEStack<T>> {
|
||||
|
||||
final IStorageChannel<T> channel;
|
||||
final int up_or_down;
|
||||
final IItemList<T> list;
|
||||
final IActionSource src;
|
||||
|
||||
public CellChangeTrackerRecord(final IStorageChannel<T> channel, final int i, final IMEInventoryHandler<T> h,
|
||||
final IActionSource actionSrc) {
|
||||
this.channel = channel;
|
||||
this.up_or_down = i;
|
||||
this.src = actionSrc;
|
||||
|
||||
this.list = h.getAvailableItems(channel.createList());
|
||||
}
|
||||
|
||||
public void applyChanges() {
|
||||
GridStorageCache.this.postChangesToNetwork(this.channel, this.up_or_down, this.list, this.src);
|
||||
}
|
||||
}
|
||||
|
||||
private class CellChangeTracker<T extends IAEStack<T>> {
|
||||
|
||||
final List<CellChangeTrackerRecord<T>> data = new ArrayList<>();
|
||||
|
||||
public void postChanges(final IStorageChannel<T> channel, final int i, final IMEInventoryHandler<T> h,
|
||||
final IActionSource actionSrc) {
|
||||
this.data.add(new CellChangeTrackerRecord<T>(channel, i, h, actionSrc));
|
||||
}
|
||||
|
||||
public void applyChanges() {
|
||||
for (final CellChangeTrackerRecord<T> rec : this.data) {
|
||||
rec.applyChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* 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.Collection;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.annotation.Nonnegative;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Queues;
|
||||
|
||||
import appeng.api.config.AccessRestriction;
|
||||
import appeng.api.config.Actionable;
|
||||
import appeng.api.networking.events.MENetworkStorageEvent;
|
||||
import appeng.api.networking.security.IActionSource;
|
||||
import appeng.api.storage.IMEInventoryHandler;
|
||||
import appeng.api.storage.IMEMonitor;
|
||||
import appeng.api.storage.IMEMonitorHandlerReceiver;
|
||||
import appeng.api.storage.IStorageChannel;
|
||||
import appeng.api.storage.data.IAEStack;
|
||||
import appeng.api.storage.data.IItemList;
|
||||
import appeng.me.storage.ItemWatcher;
|
||||
|
||||
public class NetworkMonitor<T extends IAEStack<T>> implements IMEMonitor<T> {
|
||||
@Nonnull
|
||||
private static final Deque<NetworkMonitor<?>> GLOBAL_DEPTH = Queues.newArrayDeque();
|
||||
|
||||
@Nonnull
|
||||
private final GridStorageCache myGridCache;
|
||||
@Nonnull
|
||||
private final IStorageChannel<T> myChannel;
|
||||
@Nonnull
|
||||
private final IItemList<T> cachedList;
|
||||
@Nonnull
|
||||
private final Map<IMEMonitorHandlerReceiver<T>, Object> listeners;
|
||||
|
||||
private boolean sendEvent = false;
|
||||
private boolean hasChanged = false;
|
||||
@Nonnegative
|
||||
private int localDepthSemaphore = 0;
|
||||
|
||||
public NetworkMonitor(final GridStorageCache cache, final IStorageChannel<T> chan) {
|
||||
this.myGridCache = cache;
|
||||
this.myChannel = chan;
|
||||
this.cachedList = chan.createList();
|
||||
this.listeners = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(final IMEMonitorHandlerReceiver<T> l, final Object verificationToken) {
|
||||
this.listeners.put(l, verificationToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAccept(final T input) {
|
||||
return this.getHandler().canAccept(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T extractItems(final T request, final Actionable mode, final IActionSource src) {
|
||||
if (mode == Actionable.SIMULATE) {
|
||||
return this.getHandler().extractItems(request, mode, src);
|
||||
}
|
||||
|
||||
this.localDepthSemaphore++;
|
||||
final T leftover = this.getHandler().extractItems(request, mode, src);
|
||||
this.localDepthSemaphore--;
|
||||
|
||||
if (this.localDepthSemaphore == 0) {
|
||||
this.monitorDifference(request.copy(), leftover, true, src);
|
||||
}
|
||||
|
||||
return leftover;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessRestriction getAccess() {
|
||||
return this.getHandler().getAccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IItemList<T> getAvailableItems(final IItemList<T> out) {
|
||||
return this.getHandler().getAvailableItems(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IStorageChannel<T> getChannel() {
|
||||
return this.getHandler().getChannel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return this.getHandler().getPriority();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSlot() {
|
||||
return this.getHandler().getSlot();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public IItemList<T> getStorageList() {
|
||||
if (this.hasChanged) {
|
||||
this.hasChanged = false;
|
||||
this.cachedList.resetStatus();
|
||||
return this.getAvailableItems(this.cachedList);
|
||||
}
|
||||
|
||||
return this.cachedList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T injectItems(final T input, final Actionable mode, final IActionSource src) {
|
||||
if (mode == Actionable.SIMULATE) {
|
||||
return this.getHandler().injectItems(input, mode, src);
|
||||
}
|
||||
|
||||
this.localDepthSemaphore++;
|
||||
final T leftover = this.getHandler().injectItems(input, mode, src);
|
||||
this.localDepthSemaphore--;
|
||||
|
||||
if (this.localDepthSemaphore == 0) {
|
||||
this.monitorDifference(input.copy(), leftover, false, src);
|
||||
}
|
||||
|
||||
return leftover;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrioritized(final T input) {
|
||||
return this.getHandler().isPrioritized(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(final IMEMonitorHandlerReceiver<T> l) {
|
||||
this.listeners.remove(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validForPass(final int i) {
|
||||
return this.getHandler().validForPass(i);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private IMEInventoryHandler<T> getHandler() {
|
||||
return this.myGridCache.getInventoryHandler(this.myChannel);
|
||||
}
|
||||
|
||||
private Iterator<Entry<IMEMonitorHandlerReceiver<T>, Object>> getListeners() {
|
||||
return this.listeners.entrySet().iterator();
|
||||
}
|
||||
|
||||
private T monitorDifference(final IAEStack<T> original, final T leftOvers, final boolean extraction,
|
||||
final IActionSource src) {
|
||||
final T diff = original.copy();
|
||||
|
||||
if (extraction) {
|
||||
diff.setStackSize(leftOvers == null ? 0 : -leftOvers.getStackSize());
|
||||
} else if (leftOvers != null) {
|
||||
diff.decStackSize(leftOvers.getStackSize());
|
||||
}
|
||||
|
||||
if (diff.getStackSize() != 0) {
|
||||
this.postChangesToListeners(ImmutableList.of(diff), src);
|
||||
}
|
||||
|
||||
return leftOvers;
|
||||
}
|
||||
|
||||
private void notifyListenersOfChange(final Iterable<T> diff, final IActionSource src) {
|
||||
this.hasChanged = true;
|
||||
final Iterator<Entry<IMEMonitorHandlerReceiver<T>, Object>> i = this.getListeners();
|
||||
|
||||
while (i.hasNext()) {
|
||||
final Entry<IMEMonitorHandlerReceiver<T>, Object> o = i.next();
|
||||
final IMEMonitorHandlerReceiver<T> receiver = o.getKey();
|
||||
if (receiver.isValid(o.getValue())) {
|
||||
receiver.postChange(this, diff, src);
|
||||
} else {
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void postChangesToListeners(final Iterable<T> changes, final IActionSource src) {
|
||||
this.postChange(true, changes, src);
|
||||
}
|
||||
|
||||
protected void postChange(final boolean add, final Iterable<T> changes, final IActionSource src) {
|
||||
if (this.localDepthSemaphore > 0 || GLOBAL_DEPTH.contains(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLOBAL_DEPTH.push(this);
|
||||
this.localDepthSemaphore++;
|
||||
|
||||
this.sendEvent = true;
|
||||
|
||||
this.notifyListenersOfChange(changes, src);
|
||||
|
||||
for (final T changedItem : changes) {
|
||||
T difference = changedItem;
|
||||
|
||||
if (!add && changedItem != null) {
|
||||
difference = changedItem.copy();
|
||||
difference.setStackSize(-changedItem.getStackSize());
|
||||
}
|
||||
|
||||
if (this.myGridCache.getInterestManager().containsKey(changedItem)) {
|
||||
final Collection<ItemWatcher> list = this.myGridCache.getInterestManager().get(changedItem);
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
IAEStack<T> fullStack = this.getStorageList().findPrecise(changedItem);
|
||||
|
||||
if (fullStack == null) {
|
||||
fullStack = changedItem.copy();
|
||||
fullStack.setStackSize(0);
|
||||
}
|
||||
|
||||
this.myGridCache.getInterestManager().enableTransactions();
|
||||
|
||||
for (final ItemWatcher iw : list) {
|
||||
iw.getHost().onStackChange(this.getStorageList(), fullStack, difference, src,
|
||||
this.getChannel());
|
||||
}
|
||||
|
||||
this.myGridCache.getInterestManager().disableTransactions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final NetworkMonitor<?> last = GLOBAL_DEPTH.pop();
|
||||
this.localDepthSemaphore--;
|
||||
|
||||
if (last != this) {
|
||||
throw new IllegalStateException("Invalid Access to Networked Storage API detected.");
|
||||
}
|
||||
}
|
||||
|
||||
void forceUpdate() {
|
||||
this.hasChanged = true;
|
||||
|
||||
final Iterator<Entry<IMEMonitorHandlerReceiver<T>, Object>> i = this.getListeners();
|
||||
while (i.hasNext()) {
|
||||
final Entry<IMEMonitorHandlerReceiver<T>, Object> o = i.next();
|
||||
final IMEMonitorHandlerReceiver<T> receiver = o.getKey();
|
||||
|
||||
if (receiver.isValid(o.getValue())) {
|
||||
receiver.onListUpdate();
|
||||
} else {
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onTick() {
|
||||
if (this.sendEvent) {
|
||||
this.sendEvent = false;
|
||||
this.myGridCache.getGrid().postEvent(new MENetworkStorageEvent(this, this.myChannel));
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-183
@@ -1,214 +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;
|
||||
|
||||
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(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) {
|
||||
public void removeNode(@Nonnull IGridNode gridNode, @Nonnull IGridHost machine) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoin(final IGridStorage storageB) {
|
||||
public void addNode(@Nonnull IGridNode gridNode, @Nonnull IGridHost machine) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateGridStorage(final IGridStorage storage) {
|
||||
public void onSplit(@Nonnull IGridStorage destinationStorage) {
|
||||
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
@Override
|
||||
public void onJoin(@Nonnull IGridStorage sourceStorage) {
|
||||
|
||||
final P2PTunnelPart in = this.inputs.get(freq);
|
||||
if (in != null) {
|
||||
if (configChange) {
|
||||
in.onTunnelConfigChange();
|
||||
}
|
||||
in.onTunnelNetworkChange();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateFreq(final P2PTunnelPart t, final short newFrequency) {
|
||||
if (this.outputs.containsValue(t)) {
|
||||
this.outputs.remove(t.getFrequency(), t);
|
||||
}
|
||||
@Override
|
||||
public void populateGridStorage(@Nonnull IGridStorage destinationStorage) {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
+371
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* 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.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
import appeng.api.AEApi;
|
||||
import appeng.api.features.AEFeature;
|
||||
import appeng.api.networking.GridFlags;
|
||||
import appeng.api.networking.IGrid;
|
||||
import appeng.api.networking.IGridBlock;
|
||||
import appeng.api.networking.IGridConnection;
|
||||
import appeng.api.networking.IGridHost;
|
||||
import appeng.api.networking.IGridMultiblock;
|
||||
import appeng.api.networking.IGridNode;
|
||||
import appeng.api.networking.IGridStorage;
|
||||
import appeng.api.networking.events.MENetworkBootingStatusChange;
|
||||
import appeng.api.networking.events.MENetworkChannelChanged;
|
||||
import appeng.api.networking.events.MENetworkControllerChange;
|
||||
import appeng.api.networking.events.MENetworkEventSubscribe;
|
||||
import appeng.api.networking.pathing.ControllerState;
|
||||
import appeng.api.networking.pathing.IPathingGrid;
|
||||
import appeng.api.util.AEPartLocation;
|
||||
import appeng.api.util.DimensionalCoord;
|
||||
import appeng.core.AEConfig;
|
||||
import appeng.core.AppEng;
|
||||
import appeng.core.stats.IAdvancementTrigger;
|
||||
import appeng.me.GridConnection;
|
||||
import appeng.me.GridNode;
|
||||
import appeng.me.pathfinding.AdHocChannelUpdater;
|
||||
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 {
|
||||
|
||||
private final List<PathSegment> active = new ArrayList<>();
|
||||
private final Set<ControllerBlockEntity> controllers = new HashSet<>();
|
||||
private final Set<IGridNode> requireChannels = new HashSet<>();
|
||||
private final Set<IGridNode> blockDense = new HashSet<>();
|
||||
private final IGrid myGrid;
|
||||
private int channelsInUse = 0;
|
||||
private int channelsByBlocks = 0;
|
||||
private double channelPowerUsage = 0.0;
|
||||
private boolean recalculateControllerNextTick = true;
|
||||
private boolean updateNetwork = true;
|
||||
private boolean booting = false;
|
||||
private ControllerState controllerState = ControllerState.NO_CONTROLLER;
|
||||
private int ticksUntilReady = 20;
|
||||
private int lastChannels = 0;
|
||||
private HashSet<IPathItem> semiOpen = new HashSet<>();
|
||||
|
||||
public PathGridCache(final IGrid g) {
|
||||
this.myGrid = g;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdateTick() {
|
||||
if (this.recalculateControllerNextTick) {
|
||||
this.recalcController();
|
||||
}
|
||||
|
||||
if (this.updateNetwork) {
|
||||
if (!this.booting) {
|
||||
this.myGrid.postEvent(new MENetworkBootingStatusChange());
|
||||
}
|
||||
|
||||
this.booting = true;
|
||||
this.updateNetwork = false;
|
||||
this.setChannelsInUse(0);
|
||||
|
||||
if (this.controllerState == ControllerState.NO_CONTROLLER) {
|
||||
final int requiredChannels = this.calculateRequiredChannels();
|
||||
int used = requiredChannels;
|
||||
if (requiredChannels > 8) {
|
||||
used = 0;
|
||||
}
|
||||
|
||||
final int nodes = this.myGrid.getNodes().size();
|
||||
this.setChannelsInUse(used);
|
||||
|
||||
this.ticksUntilReady = 20 + Math.max(0, nodes / 100 - 20);
|
||||
this.setChannelsByBlocks(nodes * used);
|
||||
this.setChannelPowerUsage(this.getChannelsByBlocks() / 128.0);
|
||||
|
||||
this.myGrid.getPivot().beginVisit(new AdHocChannelUpdater(used));
|
||||
} else if (this.controllerState == ControllerState.CONTROLLER_CONFLICT) {
|
||||
this.ticksUntilReady = 20;
|
||||
this.myGrid.getPivot().beginVisit(new AdHocChannelUpdater(0));
|
||||
} else {
|
||||
final int nodes = this.myGrid.getNodes().size();
|
||||
this.ticksUntilReady = 20 + Math.max(0, nodes / 100 - 20);
|
||||
final HashSet<IPathItem> closedList = new HashSet<>();
|
||||
this.semiOpen = new HashSet<>();
|
||||
|
||||
// myGrid.getPivot().beginVisit( new AdHocChannelUpdater( 0 )
|
||||
// );
|
||||
for (final IGridNode node : this.myGrid.getMachines(ControllerBlockEntity.class)) {
|
||||
closedList.add((IPathItem) node);
|
||||
for (final IGridConnection gcc : node.getConnections()) {
|
||||
final GridConnection gc = (GridConnection) gcc;
|
||||
if (!(gc.getOtherSide(node).getMachine() instanceof ControllerBlockEntity)) {
|
||||
final List<IPathItem> open = new ArrayList<>();
|
||||
closedList.add(gc);
|
||||
open.add(gc);
|
||||
gc.setControllerRoute((GridNode) node, true);
|
||||
this.active.add(new PathSegment(this, open, this.semiOpen, closedList));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.active.isEmpty() || this.ticksUntilReady > 0) {
|
||||
final Iterator<PathSegment> i = this.active.iterator();
|
||||
while (i.hasNext()) {
|
||||
final PathSegment pat = i.next();
|
||||
if (pat.step()) {
|
||||
pat.setDead(true);
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
|
||||
this.ticksUntilReady--;
|
||||
|
||||
if (this.active.isEmpty() && this.ticksUntilReady <= 0) {
|
||||
if (this.controllerState == ControllerState.CONTROLLER_ONLINE) {
|
||||
final Iterator<ControllerBlockEntity> controllerIterator = this.controllers.iterator();
|
||||
if (controllerIterator.hasNext()) {
|
||||
final ControllerBlockEntity controller = controllerIterator.next();
|
||||
controller.getGridNode(AEPartLocation.INTERNAL).beginVisit(new ControllerChannelUpdater());
|
||||
}
|
||||
}
|
||||
|
||||
// check for achievements
|
||||
this.achievementPost();
|
||||
|
||||
this.booting = false;
|
||||
this.setChannelPowerUsage(this.getChannelsByBlocks() / 128.0);
|
||||
this.myGrid.postEvent(new MENetworkBootingStatusChange());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNode(final IGridNode gridNode, final IGridHost machine) {
|
||||
if (machine instanceof ControllerBlockEntity) {
|
||||
this.controllers.remove(machine);
|
||||
this.recalculateControllerNextTick = true;
|
||||
}
|
||||
|
||||
final EnumSet<GridFlags> flags = gridNode.getGridBlock().getFlags();
|
||||
|
||||
if (flags.contains(GridFlags.REQUIRE_CHANNEL)) {
|
||||
this.requireChannels.remove(gridNode);
|
||||
}
|
||||
|
||||
if (flags.contains(GridFlags.CANNOT_CARRY_COMPRESSED)) {
|
||||
this.blockDense.remove(gridNode);
|
||||
}
|
||||
|
||||
this.repath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNode(final IGridNode gridNode, final IGridHost machine) {
|
||||
if (machine instanceof ControllerBlockEntity) {
|
||||
this.controllers.add((ControllerBlockEntity) machine);
|
||||
this.recalculateControllerNextTick = true;
|
||||
}
|
||||
|
||||
final EnumSet<GridFlags> flags = gridNode.getGridBlock().getFlags();
|
||||
|
||||
if (flags.contains(GridFlags.REQUIRE_CHANNEL)) {
|
||||
this.requireChannels.add(gridNode);
|
||||
}
|
||||
|
||||
if (flags.contains(GridFlags.CANNOT_CARRY_COMPRESSED)) {
|
||||
this.blockDense.add(gridNode);
|
||||
}
|
||||
|
||||
this.repath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSplit(final IGridStorage storageB) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoin(final IGridStorage storageB) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateGridStorage(final IGridStorage storage) {
|
||||
|
||||
}
|
||||
|
||||
private void recalcController() {
|
||||
this.recalculateControllerNextTick = false;
|
||||
final ControllerState old = this.controllerState;
|
||||
|
||||
if (this.controllers.isEmpty()) {
|
||||
this.controllerState = ControllerState.NO_CONTROLLER;
|
||||
} else {
|
||||
final IGridNode startingNode = this.controllers.iterator().next().getGridNode(AEPartLocation.INTERNAL);
|
||||
if (startingNode == null) {
|
||||
this.controllerState = ControllerState.CONTROLLER_CONFLICT;
|
||||
return;
|
||||
}
|
||||
|
||||
final DimensionalCoord dc = startingNode.getGridBlock().getLocation();
|
||||
final ControllerValidator cv = new ControllerValidator(dc.x, dc.y, dc.z);
|
||||
|
||||
startingNode.beginVisit(cv);
|
||||
|
||||
if (cv.isValid() && cv.getFound() == this.controllers.size()) {
|
||||
this.controllerState = ControllerState.CONTROLLER_ONLINE;
|
||||
} else {
|
||||
this.controllerState = ControllerState.CONTROLLER_CONFLICT;
|
||||
}
|
||||
}
|
||||
|
||||
if (old != this.controllerState) {
|
||||
this.myGrid.postEvent(new MENetworkControllerChange());
|
||||
}
|
||||
}
|
||||
|
||||
private int calculateRequiredChannels() {
|
||||
this.semiOpen.clear();
|
||||
|
||||
int depth = 0;
|
||||
for (final IGridNode nodes : this.requireChannels) {
|
||||
if (!this.semiOpen.contains(nodes)) {
|
||||
final IGridBlock gb = nodes.getGridBlock();
|
||||
final EnumSet<GridFlags> flags = gb.getFlags();
|
||||
|
||||
if (flags.contains(GridFlags.COMPRESSED_CHANNEL) && !this.blockDense.isEmpty()) {
|
||||
return 9;
|
||||
}
|
||||
|
||||
depth++;
|
||||
|
||||
if (flags.contains(GridFlags.MULTIBLOCK)) {
|
||||
final IGridMultiblock gmb = (IGridMultiblock) gb;
|
||||
final Iterator<IGridNode> i = gmb.getMultiblockNodes();
|
||||
while (i.hasNext()) {
|
||||
this.semiOpen.add((IPathItem) i.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return depth;
|
||||
}
|
||||
|
||||
private void achievementPost() {
|
||||
if (this.lastChannels != this.getChannelsInUse() && AEConfig.instance().isFeatureEnabled(AEFeature.CHANNELS)) {
|
||||
final IAdvancementTrigger currentBracket = this.getAchievementBracket(this.getChannelsInUse());
|
||||
final IAdvancementTrigger lastBracket = this.getAchievementBracket(this.lastChannels);
|
||||
if (currentBracket != lastBracket && currentBracket != null) {
|
||||
for (final IGridNode n : this.requireChannels) {
|
||||
PlayerEntity player = AEApi.instance().registries().players().findPlayer(n.getPlayerID());
|
||||
if (player instanceof ServerPlayerEntity) {
|
||||
currentBracket.trigger((ServerPlayerEntity) player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.lastChannels = this.getChannelsInUse();
|
||||
}
|
||||
|
||||
private IAdvancementTrigger getAchievementBracket(final int ch) {
|
||||
if (ch < 8) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ch < 128) {
|
||||
return AppEng.instance().getAdvancementTriggers().getNetworkApprentice();
|
||||
}
|
||||
|
||||
if (ch < 2048) {
|
||||
return AppEng.instance().getAdvancementTriggers().getNetworkEngineer();
|
||||
}
|
||||
|
||||
return AppEng.instance().getAdvancementTriggers().getNetworkAdmin();
|
||||
}
|
||||
|
||||
@MENetworkEventSubscribe
|
||||
void updateNodReq(final MENetworkChannelChanged ev) {
|
||||
final IGridNode gridNode = ev.node;
|
||||
|
||||
if (gridNode.getGridBlock().getFlags().contains(GridFlags.REQUIRE_CHANNEL)) {
|
||||
this.requireChannels.add(gridNode);
|
||||
} else {
|
||||
this.requireChannels.remove(gridNode);
|
||||
}
|
||||
|
||||
this.repath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNetworkBooting() {
|
||||
return !this.booting && !this.active.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControllerState getControllerState() {
|
||||
return this.controllerState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void repath() {
|
||||
// clean up...
|
||||
this.active.clear();
|
||||
|
||||
this.setChannelsByBlocks(0);
|
||||
this.updateNetwork = true;
|
||||
}
|
||||
|
||||
double getChannelPowerUsage() {
|
||||
return this.channelPowerUsage;
|
||||
}
|
||||
|
||||
private void setChannelPowerUsage(final double channelPowerUsage) {
|
||||
this.channelPowerUsage = channelPowerUsage;
|
||||
}
|
||||
|
||||
public int getChannelsByBlocks() {
|
||||
return this.channelsByBlocks;
|
||||
}
|
||||
|
||||
public void setChannelsByBlocks(final int channelsByBlocks) {
|
||||
this.channelsByBlocks = channelsByBlocks;
|
||||
}
|
||||
|
||||
public int getChannelsInUse() {
|
||||
return this.channelsInUse;
|
||||
}
|
||||
|
||||
public void setChannelsInUse(final int channelsInUse) {
|
||||
this.channelsInUse = channelsInUse;
|
||||
}
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* This file is part of Applied Energistics 2.
|
||||
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
|
||||
*
|
||||
* Applied Energistics 2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Applied Energistics 2 is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
|
||||
*/
|
||||
|
||||
package appeng.me.cache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
|
||||
import appeng.api.config.SecurityPermissions;
|
||||
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.MENetworkEventSubscribe;
|
||||
import appeng.api.networking.events.MENetworkSecurityChange;
|
||||
import appeng.api.networking.security.ISecurityGrid;
|
||||
import appeng.api.networking.security.ISecurityProvider;
|
||||
import appeng.core.worlddata.WorldData;
|
||||
import appeng.me.GridNode;
|
||||
|
||||
public class SecurityCache implements ISecurityGrid {
|
||||
|
||||
private final IGrid myGrid;
|
||||
private final List<ISecurityProvider> securityProvider = new ArrayList<>();
|
||||
private final HashMap<Integer, EnumSet<SecurityPermissions>> playerPerms = new HashMap<>();
|
||||
private long securityKey = -1;
|
||||
|
||||
public SecurityCache(final IGrid g) {
|
||||
this.myGrid = g;
|
||||
}
|
||||
|
||||
@MENetworkEventSubscribe
|
||||
public void updatePermissions(final MENetworkSecurityChange ev) {
|
||||
this.playerPerms.clear();
|
||||
if (this.securityProvider.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.securityProvider.get(0).readPermissions(this.playerPerms);
|
||||
}
|
||||
|
||||
public long getSecurityKey() {
|
||||
return this.securityKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdateTick() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNode(final IGridNode gridNode, final IGridHost machine) {
|
||||
if (machine instanceof ISecurityProvider) {
|
||||
this.securityProvider.remove(machine);
|
||||
this.updateSecurityKey();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSecurityKey() {
|
||||
final long lastCode = this.securityKey;
|
||||
|
||||
if (this.securityProvider.size() == 1) {
|
||||
this.securityKey = this.securityProvider.get(0).getSecurityKey();
|
||||
} else {
|
||||
this.securityKey = -1;
|
||||
}
|
||||
|
||||
if (lastCode != this.securityKey) {
|
||||
this.getGrid().postEvent(new MENetworkSecurityChange());
|
||||
for (final IGridNode n : this.getGrid().getNodes()) {
|
||||
((GridNode) n).setLastSecurityKey(this.securityKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNode(final IGridNode gridNode, final IGridHost machine) {
|
||||
if (machine instanceof ISecurityProvider) {
|
||||
this.securityProvider.add((ISecurityProvider) machine);
|
||||
this.updateSecurityKey();
|
||||
} else {
|
||||
((GridNode) gridNode).setLastSecurityKey(this.securityKey);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSplit(final IGridStorage destinationStorage) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoin(final IGridStorage sourceStorage) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateGridStorage(final IGridStorage destinationStorage) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return this.securityProvider.size() == 1 && this.securityProvider.get(0).isSecurityEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(final PlayerEntity player, final SecurityPermissions perm) {
|
||||
Preconditions.checkNotNull(player);
|
||||
Preconditions.checkNotNull(perm);
|
||||
|
||||
final GameProfile profile = player.getGameProfile();
|
||||
final int playerID = WorldData.instance().playerData().getMePlayerId(profile);
|
||||
|
||||
return this.hasPermission(playerID, perm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(final int playerID, final SecurityPermissions perm) {
|
||||
if (this.isAvailable()) {
|
||||
final EnumSet<SecurityPermissions> perms = this.playerPerms.get(playerID);
|
||||
|
||||
if (perms == null) {
|
||||
if (playerID == -1) // no default?
|
||||
{
|
||||
return false;
|
||||
} else {
|
||||
return this.hasPermission(-1, perm);
|
||||
}
|
||||
}
|
||||
|
||||
return perms.contains(perm);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOwner() {
|
||||
if (this.isAvailable()) {
|
||||
return this.securityProvider.get(0).getOwner();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public IGrid getGrid() {
|
||||
return this.myGrid;
|
||||
}
|
||||
}
|
||||
@@ -1,231 +0,0 @@
|
||||
/*
|
||||
* 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) {
|
||||
|
||||
}
|
||||
}
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* 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.PriorityQueue;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import net.minecraft.util.crash.CrashException;
|
||||
import net.minecraft.util.crash.CrashReport;
|
||||
import net.minecraft.util.crash.CrashReportSection;
|
||||
import net.minecraft.util.crash.CrashException;
|
||||
|
||||
import appeng.api.networking.IGrid;
|
||||
import appeng.api.networking.IGridHost;
|
||||
import appeng.api.networking.IGridNode;
|
||||
import appeng.api.networking.IGridStorage;
|
||||
import appeng.api.networking.ticking.IGridTickable;
|
||||
import appeng.api.networking.ticking.ITickManager;
|
||||
import appeng.api.networking.ticking.TickRateModulation;
|
||||
import appeng.api.networking.ticking.TickingRequest;
|
||||
import appeng.me.cache.helpers.TickTracker;
|
||||
|
||||
public class TickManagerCache implements ITickManager {
|
||||
|
||||
private final HashMap<IGridNode, TickTracker> alertable = new HashMap<>();
|
||||
private final HashMap<IGridNode, TickTracker> sleeping = new HashMap<>();
|
||||
private final HashMap<IGridNode, TickTracker> awake = new HashMap<>();
|
||||
private final PriorityQueue<TickTracker> upcomingTicks = new PriorityQueue<>();
|
||||
|
||||
private long currentTick = 0;
|
||||
|
||||
public TickManagerCache(@SuppressWarnings("unused") final IGrid g) {
|
||||
}
|
||||
|
||||
public long getAvgNanoTime(final IGridNode node) {
|
||||
TickTracker tt = this.awake.get(node);
|
||||
|
||||
if (tt == null) {
|
||||
tt = this.sleeping.get(node);
|
||||
}
|
||||
|
||||
if (tt == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdateTick() {
|
||||
TickTracker tt = null;
|
||||
|
||||
try {
|
||||
this.currentTick++;
|
||||
|
||||
while (!this.upcomingTicks.isEmpty()) {
|
||||
tt = this.upcomingTicks.peek();
|
||||
|
||||
// Stop once it reaches a TickTracker running at a later tick
|
||||
if (tt.getNextTick() > this.currentTick) {
|
||||
break;
|
||||
}
|
||||
|
||||
this.upcomingTicks.poll();
|
||||
|
||||
final int diff = (int) (this.currentTick - tt.getLastTick());
|
||||
final TickRateModulation mod = tt.getGridTickable().tickingRequest(tt.getNode(), diff);
|
||||
|
||||
switch (mod) {
|
||||
case FASTER:
|
||||
tt.setCurrentRate(tt.getCurrentRate() - 2);
|
||||
break;
|
||||
case IDLE:
|
||||
tt.setCurrentRate(tt.getRequest().maxTickRate);
|
||||
break;
|
||||
case SAME:
|
||||
break;
|
||||
case SLEEP:
|
||||
this.sleepDevice(tt.getNode());
|
||||
break;
|
||||
case SLOWER:
|
||||
tt.setCurrentRate(tt.getCurrentRate() + 1);
|
||||
break;
|
||||
case URGENT:
|
||||
tt.setCurrentRate(0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.awake.containsKey(tt.getNode())) {
|
||||
this.addToQueue(tt);
|
||||
}
|
||||
}
|
||||
} catch (final Throwable t) {
|
||||
final CrashReport crashreport = CrashReport.create(t, "Ticking GridNode");
|
||||
final CrashReportSection section = crashreport
|
||||
.addElement(tt.getGridTickable().getClass().getSimpleName() + " being ticked.");
|
||||
tt.addEntityCrashInfo(section);
|
||||
throw new CrashException(crashreport);
|
||||
}
|
||||
}
|
||||
|
||||
private void addToQueue(final TickTracker tt) {
|
||||
tt.setLastTick(this.currentTick);
|
||||
this.upcomingTicks.add(tt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNode(final IGridNode gridNode, final IGridHost machine) {
|
||||
if (machine instanceof IGridTickable) {
|
||||
this.alertable.remove(gridNode);
|
||||
this.sleeping.remove(gridNode);
|
||||
this.awake.remove(gridNode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNode(final IGridNode gridNode, final IGridHost machine) {
|
||||
if (machine instanceof IGridTickable) {
|
||||
final IGridTickable tickable = ((IGridTickable) machine);
|
||||
final TickingRequest tr = tickable.getTickingRequest(gridNode);
|
||||
|
||||
Preconditions.checkNotNull(tr);
|
||||
|
||||
final TickTracker tt = new TickTracker(tr, gridNode, (IGridTickable) machine, this.currentTick);
|
||||
|
||||
if (tr.canBeAlerted) {
|
||||
this.alertable.put(gridNode, tt);
|
||||
}
|
||||
|
||||
if (tr.isSleeping) {
|
||||
this.sleeping.put(gridNode, tt);
|
||||
} else {
|
||||
this.awake.put(gridNode, tt);
|
||||
this.addToQueue(tt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSplit(final IGridStorage storageB) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoin(final IGridStorage storageB) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateGridStorage(final IGridStorage storage) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean alertDevice(final IGridNode node) {
|
||||
Preconditions.checkNotNull(node);
|
||||
|
||||
final TickTracker tt = this.alertable.get(node);
|
||||
if (tt == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// set to awake, this is for sanity.
|
||||
this.sleeping.remove(node);
|
||||
this.awake.put(node, tt);
|
||||
|
||||
// configure sort.
|
||||
tt.setLastTick(tt.getLastTick() - tt.getRequest().maxTickRate);
|
||||
tt.setCurrentRate(tt.getRequest().minTickRate);
|
||||
|
||||
// prevent dupes and tick build up.
|
||||
this.upcomingTicks.remove(tt);
|
||||
this.upcomingTicks.add(tt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sleepDevice(final IGridNode node) {
|
||||
Preconditions.checkNotNull(node);
|
||||
|
||||
if (this.awake.containsKey(node)) {
|
||||
final TickTracker gt = this.awake.get(node);
|
||||
this.awake.remove(node);
|
||||
this.sleeping.put(node, gt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wakeDevice(final IGridNode node) {
|
||||
Preconditions.checkNotNull(node);
|
||||
|
||||
if (this.sleeping.containsKey(node)) {
|
||||
final TickTracker gt = this.sleeping.get(node);
|
||||
this.sleeping.remove(node);
|
||||
this.awake.put(node, gt);
|
||||
this.upcomingTicks.remove(gt);
|
||||
this.addToQueue(gt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* 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,119 @@
|
||||
/*
|
||||
* 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 javax.annotation.Nonnull;
|
||||
|
||||
import appeng.api.parts.IPart;
|
||||
import net.minecraft.util.crash.CrashReportSection;
|
||||
|
||||
import appeng.api.networking.IGridNode;
|
||||
import appeng.api.networking.ticking.IGridTickable;
|
||||
import appeng.api.networking.ticking.TickingRequest;
|
||||
import appeng.api.parts.IPart;
|
||||
import appeng.api.util.DimensionalCoord;
|
||||
import appeng.me.cache.TickManagerCache;
|
||||
|
||||
public class TickTracker implements Comparable<TickTracker> {
|
||||
|
||||
private final TickingRequest request;
|
||||
private final IGridTickable gt;
|
||||
private final IGridNode node;
|
||||
|
||||
private long lastTick;
|
||||
private int currentRate;
|
||||
|
||||
public TickTracker(final TickingRequest req, final IGridNode node, final IGridTickable gt, final long currentTick) {
|
||||
this.request = req;
|
||||
this.gt = gt;
|
||||
this.node = node;
|
||||
this.setCurrentRate((req.minTickRate + req.maxTickRate) / 2);
|
||||
this.setLastTick(currentTick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@Nonnull final TickTracker t) {
|
||||
int next = Long.compare(this.getNextTick(), t.getNextTick());
|
||||
|
||||
if (next != 0) {
|
||||
return next;
|
||||
}
|
||||
|
||||
int last = Long.compare(this.getLastTick(), t.getLastTick());
|
||||
|
||||
if (last != 0) {
|
||||
return last;
|
||||
}
|
||||
|
||||
return Integer.compare(this.getCurrentRate(), t.getCurrentRate());
|
||||
|
||||
}
|
||||
|
||||
public void addEntityCrashInfo(final CrashReportSection section) {
|
||||
if (this.getGridTickable() instanceof IPart) {
|
||||
final IPart part = (IPart) this.getGridTickable();
|
||||
part.addEntityCrashInfo(section);
|
||||
}
|
||||
|
||||
section.add("CurrentTickRate", this.getCurrentRate());
|
||||
section.add("MinTickRate", this.getRequest().minTickRate);
|
||||
section.add("MaxTickRate", this.getRequest().maxTickRate);
|
||||
section.add("MachineType", this.getGridTickable().getClass().getName());
|
||||
section.add("GridBlockType", this.getNode().getGridBlock().getClass().getName());
|
||||
section.add("ConnectedSides", this.getNode().getConnectedSides());
|
||||
|
||||
final DimensionalCoord dc = this.getNode().getGridBlock().getLocation();
|
||||
if (dc != null) {
|
||||
section.add("Location", dc);
|
||||
}
|
||||
}
|
||||
|
||||
public int getCurrentRate() {
|
||||
return this.currentRate;
|
||||
}
|
||||
|
||||
public void setCurrentRate(final int currentRate) {
|
||||
this.currentRate = Math.min(this.getRequest().maxTickRate,
|
||||
Math.max(this.getRequest().minTickRate, currentRate));
|
||||
}
|
||||
|
||||
public long getNextTick() {
|
||||
return this.lastTick + this.currentRate;
|
||||
}
|
||||
|
||||
public long getLastTick() {
|
||||
return this.lastTick;
|
||||
}
|
||||
|
||||
public void setLastTick(final long lastTick) {
|
||||
this.lastTick = lastTick;
|
||||
}
|
||||
|
||||
public IGridNode getNode() {
|
||||
return this.node;
|
||||
}
|
||||
|
||||
public IGridTickable getGridTickable() {
|
||||
return this.gt;
|
||||
}
|
||||
|
||||
public TickingRequest getRequest() {
|
||||
return this.request;
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user