Moving to source sets

This commit is contained in:
Sebastian Hartte
2020-07-01 23:36:51 +02:00
parent f2e3d81fd7
commit 2642ced86b
2924 changed files with 794 additions and 796 deletions
+252
View File
@@ -0,0 +1,252 @@
/*
* 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;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import appeng.api.AEApi;
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.IMachineSet;
import appeng.api.networking.events.MENetworkEvent;
import appeng.api.networking.events.MENetworkPostCacheConstruction;
import appeng.api.util.IReadOnlyCollection;
import appeng.core.worlddata.WorldData;
import appeng.hooks.TickHandler;
import appeng.util.ReadOnlyCollection;
public class Grid implements IGrid {
private final NetworkEventBus eventBus = new NetworkEventBus();
private final Map<Class<? extends IGridHost>, MachineSet> machines = new HashMap<>();
private final Map<Class<? extends IGridCache>, GridCacheWrapper> caches;
private GridNode pivot;
private int priority; // how import is this network?
private GridStorage myStorage;
public Grid(final GridNode center) {
this.pivot = center;
final Map<Class<? extends IGridCache>, IGridCache> myCaches = AEApi.instance().registries().gridCache()
.createCacheInstance(this);
this.caches = new HashMap<>(myCaches.size());
for (final Entry<Class<? extends IGridCache>, IGridCache> c : myCaches.entrySet()) {
final Class<? extends IGridCache> key = c.getKey();
final IGridCache value = c.getValue();
final Class<? extends IGridCache> valueClass = value.getClass();
this.eventBus.readClass(key, valueClass);
this.caches.put(key, new GridCacheWrapper(value));
}
this.postEvent(new MENetworkPostCacheConstruction());
TickHandler.INSTANCE.addNetwork(this);
center.setGrid(this);
}
int getPriority() {
return this.priority;
}
IGridStorage getMyStorage() {
return this.myStorage;
}
Map<Class<? extends IGridCache>, GridCacheWrapper> getCaches() {
return this.caches;
}
public Iterable<Class<? extends IGridHost>> getMachineClasses() {
return this.machines.keySet();
}
int size() {
int out = 0;
for (final Collection<?> x : this.machines.values()) {
out += x.size();
}
return out;
}
void remove(final GridNode gridNode) {
for (final IGridCache c : this.caches.values()) {
final IGridHost machine = gridNode.getMachine();
c.removeNode(gridNode, machine);
}
final Class<? extends IGridHost> machineClass = gridNode.getMachineClass();
final Set<IGridNode> nodes = this.machines.get(machineClass);
if (nodes != null) {
nodes.remove(gridNode);
}
gridNode.setGridStorage(null);
if (this.pivot == gridNode) {
final Iterator<IGridNode> n = this.getNodes().iterator();
if (n.hasNext()) {
this.pivot = (GridNode) n.next();
} else {
this.pivot = null;
TickHandler.INSTANCE.removeNetwork(this);
this.myStorage.remove();
}
}
}
void add(final GridNode gridNode) {
final Class<? extends IGridHost> mClass = gridNode.getMachineClass();
MachineSet nodes = this.machines.get(mClass);
if (nodes == null) {
nodes = new MachineSet(mClass);
this.machines.put(mClass, nodes);
this.eventBus.readClass(mClass, mClass);
}
// handle loading grid storages.
if (gridNode.getGridStorage() != null) {
final GridStorage gs = gridNode.getGridStorage();
final IGrid grid = gs.getGrid();
if (grid == null) {
this.myStorage = gs;
this.myStorage.setGrid(this);
for (final IGridCache gc : this.caches.values()) {
gc.onJoin(this.myStorage);
}
} else if (grid != this) {
if (this.myStorage == null) {
this.myStorage = WorldData.instance().storageData().getNewGridStorage();
this.myStorage.setGrid(this);
}
final IGridStorage tmp = new GridStorage();
if (!gs.hasDivided(this.myStorage)) {
gs.addDivided(this.myStorage);
for (final IGridCache gc : ((Grid) grid).caches.values()) {
gc.onSplit(tmp);
}
for (final IGridCache gc : this.caches.values()) {
gc.onJoin(tmp);
}
}
}
} else if (this.myStorage == null) {
this.myStorage = WorldData.instance().storageData().getNewGridStorage();
this.myStorage.setGrid(this);
}
// update grid node...
gridNode.setGridStorage(this.myStorage);
// track node.
nodes.add(gridNode);
for (final IGridCache cache : this.caches.values()) {
final IGridHost machine = gridNode.getMachine();
cache.addNode(gridNode, machine);
}
gridNode.getGridProxy().gridChanged();
// postEventTo( gridNode, networkChanged );
}
@Override
@SuppressWarnings("unchecked")
public <C extends IGridCache> C getCache(final Class<? extends IGridCache> iface) {
return (C) this.caches.get(iface).getCache();
}
@Override
public MENetworkEvent postEvent(final MENetworkEvent ev) {
return this.eventBus.postEvent(this, ev);
}
@Override
public MENetworkEvent postEventTo(final IGridNode node, final MENetworkEvent ev) {
return this.eventBus.postEventTo(this, (GridNode) node, ev);
}
@Override
public IReadOnlyCollection<Class<? extends IGridHost>> getMachinesClasses() {
final Set<Class<? extends IGridHost>> machineKeys = this.machines.keySet();
return new ReadOnlyCollection<>(machineKeys);
}
@Override
public IMachineSet getMachines(final Class<? extends IGridHost> c) {
final MachineSet s = this.machines.get(c);
if (s == null) {
return new MachineSet(c);
}
return s;
}
@Override
public IReadOnlyCollection<IGridNode> getNodes() {
return new GridNodeCollection(this.machines);
}
@Override
public boolean isEmpty() {
return this.pivot == null;
}
@Override
public IGridNode getPivot() {
return this.pivot;
}
void setPivot(final GridNode pivot) {
this.pivot = pivot;
}
public void update() {
for (final IGridCache gc : this.caches.values()) {
// are there any nodes left?
if (this.pivot != null) {
gc.onUpdateTick();
}
}
}
void saveState() {
for (final IGridCache c : this.caches.values()) {
c.populateGridStorage(this.myStorage);
}
}
public void setImportantFlag(final int i, final boolean publicHasPower) {
final int flag = 1 << i;
this.priority = (this.priority & ~flag) | (publicHasPower ? flag : 0);
}
}
@@ -0,0 +1,24 @@
/*
* 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;
public class GridAccessException extends Exception {
private static final long serialVersionUID = 3914554394866375300L;
}
@@ -0,0 +1,73 @@
/*
* 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;
import appeng.api.networking.IGridCache;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.networking.IGridStorage;
public class GridCacheWrapper implements IGridCache {
private final IGridCache myCache;
private final String name;
public GridCacheWrapper(final IGridCache gc) {
this.myCache = gc;
this.name = this.getCache().getClass().getName();
}
@Override
public void onUpdateTick() {
this.getCache().onUpdateTick();
}
@Override
public void removeNode(final IGridNode gridNode, final IGridHost machine) {
this.getCache().removeNode(gridNode, machine);
}
@Override
public void addNode(final IGridNode gridNode, final IGridHost machine) {
this.getCache().addNode(gridNode, machine);
}
@Override
public void onSplit(final IGridStorage storageB) {
this.getCache().onSplit(storageB);
}
@Override
public void onJoin(final IGridStorage storageB) {
this.getCache().onJoin(storageB);
}
@Override
public void populateGridStorage(final IGridStorage storage) {
this.getCache().populateGridStorage(storage);
}
public String getName() {
return this.name;
}
IGridCache getCache() {
return this.myCache;
}
}
+256
View File
@@ -0,0 +1,256 @@
/*
* 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;
import java.util.Arrays;
import java.util.EnumSet;
import appeng.api.exceptions.ExistingConnectionException;
import appeng.api.exceptions.FailedConnectionException;
import appeng.api.exceptions.NullNodeConnectionException;
import appeng.api.exceptions.SecurityConnectionException;
import appeng.api.features.AEFeature;
import appeng.api.networking.GridFlags;
import appeng.api.networking.IGridConnection;
import appeng.api.networking.IGridNode;
import appeng.api.networking.events.MENetworkChannelsChanged;
import appeng.api.networking.pathing.IPathingGrid;
import appeng.api.util.AEPartLocation;
import appeng.api.util.DimensionalCoord;
import appeng.api.util.IReadOnlyCollection;
import appeng.core.AEConfig;
import appeng.core.AELog;
import appeng.me.pathfinding.IPathItem;
import appeng.util.Platform;
import appeng.util.ReadOnlyCollection;
public class GridConnection implements IGridConnection, IPathItem {
private static final String EXISTING_CONNECTION_MESSAGE = "Connection between node [machine=%s, %s] and [machine=%s, %s] on [%s] already exists.";
private static final MENetworkChannelsChanged EVENT = new MENetworkChannelsChanged();
private int channelData = 0;
private Object visitorIterationNumber = null;
private GridNode sideA;
private AEPartLocation fromAtoB;
private GridNode sideB;
private GridConnection(final GridNode aNode, final GridNode bNode, final AEPartLocation fromAtoB) {
this.sideA = aNode;
this.fromAtoB = fromAtoB;
this.sideB = bNode;
}
private boolean isNetworkABetter(final GridNode a, final GridNode b) {
return a.getMyGrid().getPriority() > b.getMyGrid().getPriority() || a.getMyGrid().size() > b.getMyGrid().size();
}
@Override
public IGridNode getOtherSide(final IGridNode gridNode) {
if (gridNode == this.sideA) {
return this.sideB;
}
if (gridNode == this.sideB) {
return this.sideA;
}
throw new GridException("Invalid Side of Connection");
}
@Override
public AEPartLocation getDirection(final IGridNode side) {
if (this.fromAtoB == AEPartLocation.INTERNAL) {
return this.fromAtoB;
}
if (this.sideA == side) {
return this.fromAtoB;
} else {
return this.fromAtoB.getOpposite();
}
}
@Override
public void destroy() {
// a connection was destroyed RE-PATH!!
final IPathingGrid p = this.sideA.getInternalGrid().getCache(IPathingGrid.class);
p.repath();
this.sideA.removeConnection(this);
this.sideB.removeConnection(this);
this.sideA.validateGrid();
this.sideB.validateGrid();
}
@Override
public IGridNode a() {
return this.sideA;
}
@Override
public IGridNode b() {
return this.sideB;
}
@Override
public boolean hasDirection() {
return this.fromAtoB != AEPartLocation.INTERNAL;
}
@Override
public int getUsedChannels() {
return (this.channelData >> 8) & 0xff;
}
@Override
public IPathItem getControllerRoute() {
if (this.sideA.getFlags().contains(GridFlags.CANNOT_CARRY)) {
return null;
}
return this.sideA;
}
@Override
public void setControllerRoute(final IPathItem fast, final boolean zeroOut) {
if (zeroOut) {
this.channelData &= ~0xff;
}
if (this.sideB == fast) {
final GridNode tmp = this.sideA;
this.sideA = this.sideB;
this.sideB = tmp;
this.fromAtoB = this.fromAtoB.getOpposite();
}
}
@Override
public boolean canSupportMoreChannels() {
return this.getLastUsedChannels() < 32; // max, PERIOD.
}
@Override
public IReadOnlyCollection<IPathItem> getPossibleOptions() {
return new ReadOnlyCollection<>(Arrays.asList((IPathItem) this.a(), (IPathItem) this.b()));
}
@Override
public void incrementChannelCount(final int usedChannels) {
this.channelData += usedChannels;
}
@Override
public EnumSet<GridFlags> getFlags() {
return EnumSet.noneOf(GridFlags.class);
}
@Override
public void finalizeChannels() {
if (this.getUsedChannels() != this.getLastUsedChannels()) {
this.channelData &= 0xff;
this.channelData |= this.channelData << 8;
if (this.sideA.getInternalGrid() != null) {
this.sideA.getInternalGrid().postEventTo(this.sideA, EVENT);
}
if (this.sideB.getInternalGrid() != null) {
this.sideB.getInternalGrid().postEventTo(this.sideB, EVENT);
}
}
}
private int getLastUsedChannels() {
return this.channelData & 0xff;
}
Object getVisitorIterationNumber() {
return this.visitorIterationNumber;
}
void setVisitorIterationNumber(final Object visitorIterationNumber) {
this.visitorIterationNumber = visitorIterationNumber;
}
public static GridConnection create(final IGridNode aNode, final IGridNode bNode, final AEPartLocation fromAtoB)
throws FailedConnectionException {
if (aNode == null || bNode == null) {
throw new NullNodeConnectionException();
}
final GridNode a = (GridNode) aNode;
final GridNode b = (GridNode) bNode;
if (a.hasConnection(b) || b.hasConnection(a)) {
final String aMachineClass = a.getGridBlock().getMachine().getClass().getSimpleName();
final String bMachineClass = b.getGridBlock().getMachine().getClass().getSimpleName();
final String aCoordinates = a.getGridBlock().getLocation().toString();
final String bCoordinates = b.getGridBlock().getLocation().toString();
throw new ExistingConnectionException(String.format(EXISTING_CONNECTION_MESSAGE, aMachineClass,
aCoordinates, bMachineClass, bCoordinates, fromAtoB));
}
if (!Platform.securityCheck(a, b)) {
if (AEConfig.instance().isFeatureEnabled(AEFeature.LOG_SECURITY_AUDITS)) {
final DimensionalCoord aCoordinates = a.getGridBlock().getLocation();
final DimensionalCoord bCoordinates = b.getGridBlock().getLocation();
AELog.info("Security audit 1 failed at [%s] belonging to player [id=%d]", aCoordinates.toString(),
a.getPlayerID());
AELog.info("Security audit 2 failed at [%s] belonging to player [id=%d]", bCoordinates.toString(),
b.getPlayerID());
}
throw new SecurityConnectionException();
}
// Create the actual connection
final GridConnection connection = new GridConnection(a, b, fromAtoB);
// Update both nodes with the new connection.
if (a.getMyGrid() == null) {
b.setGrid(a.getInternalGrid());
} else {
if (a.getMyGrid() == null) {
final GridPropagator gp = new GridPropagator(b.getInternalGrid());
aNode.beginVisit(gp);
} else if (b.getMyGrid() == null) {
final GridPropagator gp = new GridPropagator(a.getInternalGrid());
bNode.beginVisit(gp);
} else if (connection.isNetworkABetter(a, b)) {
final GridPropagator gp = new GridPropagator(a.getInternalGrid());
b.beginVisit(gp);
} else {
final GridPropagator gp = new GridPropagator(b.getInternalGrid());
a.beginVisit(gp);
}
}
// a connection was destroyed RE-PATH!!
final IPathingGrid p = connection.sideA.getInternalGrid().getCache(IPathingGrid.class);
p.repath();
connection.sideA.addConnection(connection);
connection.sideB.addConnection(connection);
return connection;
}
}
@@ -16,23 +16,14 @@
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.cache.helpers;
package appeng.me;
import appeng.api.networking.IGridConnection;
public class GridException extends RuntimeException {
public class ConnectionWrapper {
private static final long serialVersionUID = -8110077032108243076L;
private IGridConnection connection;
public GridException(final String s) {
public ConnectionWrapper(final IGridConnection gc) {
this.setConnection(gc);
super(s);
}
public IGridConnection getConnection() {
return this.connection;
}
public void setConnection(final IGridConnection connection) {
this.connection = connection;
}
}
}
+617
View File
@@ -0,0 +1,617 @@
/*
* 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;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.EnumSet;
import java.util.List;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.WorldAccess;
import net.minecraft.world.World;
import appeng.api.exceptions.FailedConnectionException;
import appeng.api.exceptions.SecurityConnectionException;
import appeng.api.networking.GridFlags;
import appeng.api.networking.GridNotification;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridBlock;
import appeng.api.networking.IGridCache;
import appeng.api.networking.IGridConnection;
import appeng.api.networking.IGridConnectionVisitor;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.networking.IGridVisitor;
import appeng.api.networking.energy.IEnergyGrid;
import appeng.api.networking.events.MENetworkChannelsChanged;
import appeng.api.networking.pathing.IPathingGrid;
import appeng.api.util.AEColor;
import appeng.api.util.AEPartLocation;
import appeng.api.util.DimensionalCoord;
import appeng.api.util.IReadOnlyCollection;
import appeng.core.AELog;
import appeng.core.worlddata.WorldData;
import appeng.hooks.TickHandler;
import appeng.me.pathfinding.IPathItem;
import appeng.util.IWorldCallable;
import appeng.util.ReadOnlyCollection;
public class GridNode implements IGridNode, IPathItem {
private static final MENetworkChannelsChanged EVENT = new MENetworkChannelsChanged();
private static final int[] CHANNEL_COUNT = { 0, 8, 32 };
private final List<IGridConnection> connections = new ArrayList<>();
private final IGridBlock gridProxy;
// old power draw, used to diff
private double previousDraw = 0.0;
private long lastSecurityKey = -1;
private int playerID = -1;
private GridStorage myStorage = null;
private Grid myGrid;
private Object visitorIterationNumber = null;
// connection criteria
private int compressedData = 0;
private int usedChannels = 0;
private int lastUsedChannels = 0;
public GridNode(final IGridBlock what) {
this.gridProxy = what;
}
IGridBlock getGridProxy() {
return this.gridProxy;
}
Grid getMyGrid() {
return this.myGrid;
}
public int usedChannels() {
return this.lastUsedChannels;
}
Class<? extends IGridHost> getMachineClass() {
return this.getMachine().getClass();
}
void addConnection(final IGridConnection gridConnection) {
this.connections.add(gridConnection);
if (gridConnection.hasDirection()) {
this.gridProxy.onGridNotification(GridNotification.CONNECTIONS_CHANGED);
}
final IGridNode gn = this;
Collections.sort(this.connections, new ConnectionComparator(gn));
}
void removeConnection(final IGridConnection gridConnection) {
this.connections.remove(gridConnection);
if (gridConnection.hasDirection()) {
this.gridProxy.onGridNotification(GridNotification.CONNECTIONS_CHANGED);
}
}
boolean hasConnection(final IGridNode otherSide) {
for (final IGridConnection gc : this.connections) {
if (gc.a() == otherSide || gc.b() == otherSide) {
return true;
}
}
return false;
}
void validateGrid() {
final GridSplitDetector gsd = new GridSplitDetector(this.getInternalGrid().getPivot());
this.beginVisit(gsd);
if (!gsd.isPivotFound()) {
final IGridVisitor gp = new GridPropagator(new Grid(this));
this.beginVisit(gp);
}
}
public Grid getInternalGrid() {
if (this.myGrid == null) {
this.myGrid = new Grid(this);
}
return this.myGrid;
}
@Override
public void beginVisit(final IGridVisitor g) {
final Object tracker = new Object();
Deque<GridNode> nextRun = new ArrayDeque<>();
nextRun.add(this);
this.visitorIterationNumber = tracker;
if (g instanceof IGridConnectionVisitor) {
final Deque<IGridConnection> nextConn = new ArrayDeque<>();
final IGridConnectionVisitor gcv = (IGridConnectionVisitor) g;
while (!nextRun.isEmpty()) {
while (!nextConn.isEmpty()) {
gcv.visitConnection(nextConn.poll());
}
final Iterable<GridNode> thisRun = nextRun;
nextRun = new ArrayDeque<>();
for (final GridNode n : thisRun) {
n.visitorConnection(tracker, g, nextRun, nextConn);
}
}
} else {
while (!nextRun.isEmpty()) {
final Iterable<GridNode> thisRun = nextRun;
nextRun = new ArrayDeque<>();
for (final GridNode n : thisRun) {
n.visitorNode(tracker, g, nextRun);
}
}
}
}
@Override
public void updateState() {
final EnumSet<GridFlags> set = this.gridProxy.getFlags();
this.compressedData = set.contains(GridFlags.CANNOT_CARRY) ? 0
: (set.contains(GridFlags.DENSE_CAPACITY) ? 2 : 1);
this.compressedData |= (this.gridProxy.getGridColor().ordinal() << 3);
for (final Direction dir : this.gridProxy.getConnectableSides()) {
this.compressedData |= (1 << (dir.ordinal() + 8));
}
this.findConnections();
this.getInternalGrid();
}
@Override
public IGridHost getMachine() {
return this.gridProxy.getMachine();
}
@Override
public IGrid getGrid() {
return this.myGrid;
}
void setGrid(final Grid grid) {
if (this.myGrid == grid) {
return;
}
if (this.myGrid != null) {
this.myGrid.remove(this);
if (this.myGrid.isEmpty()) {
this.myGrid.saveState();
for (final IGridCache c : grid.getCaches().values()) {
c.onJoin(this.myGrid.getMyStorage());
}
}
}
this.myGrid = grid;
this.myGrid.add(this);
}
@Override
public void destroy() {
while (!this.connections.isEmpty()) {
// not part of this network for real anymore.
if (this.connections.size() == 1) {
this.setGridStorage(null);
}
final IGridConnection c = this.connections.listIterator().next();
final GridNode otherSide = (GridNode) c.getOtherSide(this);
otherSide.getInternalGrid().setPivot(otherSide);
c.destroy();
}
if (this.myGrid != null) {
this.myGrid.remove(this);
}
}
@Override
public WorldAccess getWorld() {
return this.gridProxy.getLocation().getWorld();
}
@Override
public EnumSet<AEPartLocation> getConnectedSides() {
final EnumSet<AEPartLocation> set = EnumSet.noneOf(AEPartLocation.class);
for (final IGridConnection gc : this.connections) {
set.add(gc.getDirection(this));
}
return set;
}
@Override
public IReadOnlyCollection<IGridConnection> getConnections() {
return new ReadOnlyCollection<>(this.connections);
}
@Override
public IGridBlock getGridBlock() {
return this.gridProxy;
}
@Override
public boolean isActive() {
final IGrid g = this.getGrid();
if (g != null) {
final IPathingGrid pg = g.getCache(IPathingGrid.class);
final IEnergyGrid eg = g.getCache(IEnergyGrid.class);
return eg.isNetworkPowered() && !pg.isNetworkBooting() && this.meetsChannelRequirements();
}
return false;
}
@Override
public void loadFromNBT(final String name, final CompoundTag nodeData) {
if (this.myGrid == null) {
final CompoundTag node = nodeData.getCompound(name);
this.playerID = node.getInt("p");
this.setLastSecurityKey(node.getLong("k"));
final long storageID = node.getLong("g");
final GridStorage gridStorage = WorldData.instance().storageData().getGridStorage(storageID);
this.setGridStorage(gridStorage);
} else {
throw new IllegalStateException("Loading data after part of a grid, this is invalid.");
}
}
@Override
public void saveToNBT(final String name, final CompoundTag nodeData) {
if (this.myStorage != null) {
final CompoundTag node = new CompoundTag();
node.putInt("p", this.playerID);
node.putLong("k", this.getLastSecurityKey());
node.putLong("g", this.myStorage.getID());
nodeData.put(name, node);
} else {
nodeData.remove(name);
}
}
@Override
public boolean meetsChannelRequirements() {
return (!this.gridProxy.getFlags().contains(GridFlags.REQUIRE_CHANNEL) || this.getUsedChannels() > 0);
}
@Override
public boolean hasFlag(final GridFlags flag) {
return this.gridProxy.getFlags().contains(flag);
}
@Override
public int getPlayerID() {
return this.playerID;
}
@Override
public void setPlayerID(final int playerID) {
if (playerID >= 0) {
this.playerID = playerID;
}
}
private int getUsedChannels() {
return this.usedChannels;
}
private void findConnections() {
if (!this.gridProxy.isWorldAccessible()) {
return;
}
final EnumSet<AEPartLocation> newSecurityConnections = EnumSet.noneOf(AEPartLocation.class);
final DimensionalCoord dc = this.gridProxy.getLocation();
for (final AEPartLocation f : AEPartLocation.SIDE_LOCATIONS) {
final IGridHost te = this.findGridHost(dc.getWorld(), dc.x + f.xOffset, dc.y + f.yOffset, dc.z + f.zOffset);
if (te != null) {
final GridNode node = (GridNode) te.getGridNode(f.getOpposite());
if (node == null) {
continue;
}
final boolean isValidConnection = this.canConnect(node, f) && node.canConnect(this, f.getOpposite());
IGridConnection con = null; // find the connection for this
// direction..
for (final IGridConnection c : this.getConnections()) {
if (c.getDirection(this) == f) {
con = c;
break;
}
}
if (con != null) {
final IGridNode os = con.getOtherSide(this);
if (os == node) {
// if this connection is no longer valid, destroy it.
if (!isValidConnection) {
con.destroy();
}
} else {
con.destroy();
// throw new GridException( "invalid state found, encountered connection to
// phantom block." );
}
} else if (isValidConnection) {
if (node.getLastSecurityKey() != -1) {
newSecurityConnections.add(f);
} else {
// construct a new connection between these two nodes.
try {
GridConnection.create(node, this, f.getOpposite());
} catch (SecurityConnectionException e) {
AELog.debug(e);
TickHandler.INSTANCE.addCallable(node.getWorld(), new MachineSecurityBreak(this));
return;
} catch (final FailedConnectionException e) {
AELog.debug(e);
return;
}
}
}
}
}
for (final AEPartLocation f : newSecurityConnections) {
final IGridHost te = this.findGridHost(dc.getWorld(), dc.x + f.xOffset, dc.y + f.yOffset, dc.z + f.zOffset);
if (te != null) {
final GridNode node = (GridNode) te.getGridNode(f.getOpposite());
if (node == null) {
continue;
}
// construct a new connection between these two nodes.
try {
GridConnection.create(node, this, f.getOpposite());
} catch (SecurityConnectionException e) {
AELog.debug(e);
TickHandler.INSTANCE.addCallable(node.getWorld(), new MachineSecurityBreak(this));
return;
} catch (final FailedConnectionException e) {
AELog.debug(e);
return;
}
}
}
}
private IGridHost findGridHost(final WorldAccess world, final int x, final int y, final int z) {
final BlockPos pos = new BlockPos(x, y, z);
if (world.isChunkLoaded(pos)) {
final BlockEntity te = world.getBlockEntity(pos);
if (te instanceof IGridHost) {
return (IGridHost) te;
}
}
return null;
}
private boolean canConnect(final GridNode from, final AEPartLocation dir) {
if (!this.isValidDirection(dir)) {
return false;
}
if (!from.getColor().matches(this.getColor())) {
return false;
}
return true;
}
private boolean isValidDirection(final AEPartLocation dir) {
return (this.compressedData & (1 << (8 + dir.ordinal()))) > 0;
}
private AEColor getColor() {
return AEColor.values()[(this.compressedData >> 3) & 0x1F];
}
private void visitorConnection(final Object tracker, final IGridVisitor g, final Deque<GridNode> nextRun,
final Deque<IGridConnection> nextConnections) {
if (g.visitNode(this)) {
for (final IGridConnection gc : this.getConnections()) {
final GridNode gn = (GridNode) gc.getOtherSide(this);
final GridConnection gcc = (GridConnection) gc;
if (gcc.getVisitorIterationNumber() != tracker) {
gcc.setVisitorIterationNumber(tracker);
nextConnections.add(gc);
}
if (tracker == gn.visitorIterationNumber) {
continue;
}
gn.visitorIterationNumber = tracker;
nextRun.add(gn);
}
}
}
private void visitorNode(final Object tracker, final IGridVisitor g, final Deque<GridNode> nextRun) {
if (g.visitNode(this)) {
for (final IGridConnection gc : this.getConnections()) {
final GridNode gn = (GridNode) gc.getOtherSide(this);
if (tracker == gn.visitorIterationNumber) {
continue;
}
gn.visitorIterationNumber = tracker;
nextRun.add(gn);
}
}
}
GridStorage getGridStorage() {
return this.myStorage;
}
void setGridStorage(final GridStorage s) {
this.myStorage = s;
this.usedChannels = 0;
this.lastUsedChannels = 0;
}
@Override
public IPathItem getControllerRoute() {
if (this.connections.isEmpty() || this.getFlags().contains(GridFlags.CANNOT_CARRY)) {
return null;
}
return (IPathItem) this.connections.get(0);
}
@Override
public void setControllerRoute(final IPathItem fast, final boolean zeroOut) {
if (zeroOut) {
this.usedChannels = 0;
}
final int idx = this.connections.indexOf(fast);
if (idx > 0) {
this.connections.remove(fast);
this.connections.add(0, (IGridConnection) fast);
}
}
@Override
public boolean canSupportMoreChannels() {
return this.getUsedChannels() < this.getMaxChannels();
}
private int getMaxChannels() {
return CHANNEL_COUNT[this.compressedData & 0x03];
}
@Override
public IReadOnlyCollection<IPathItem> getPossibleOptions() {
return (IReadOnlyCollection) this.getConnections();
}
@Override
public void incrementChannelCount(final int usedChannels) {
this.usedChannels += usedChannels;
}
@Override
public EnumSet<GridFlags> getFlags() {
return this.gridProxy.getFlags();
}
@Override
public void finalizeChannels() {
if (this.getFlags().contains(GridFlags.CANNOT_CARRY)) {
return;
}
if (this.getLastUsedChannels() != this.getUsedChannels()) {
this.lastUsedChannels = this.usedChannels;
if (this.getInternalGrid() != null) {
this.getInternalGrid().postEventTo(this, EVENT);
}
}
}
private int getLastUsedChannels() {
return this.lastUsedChannels;
}
public long getLastSecurityKey() {
return this.lastSecurityKey;
}
public void setLastSecurityKey(final long lastSecurityKey) {
this.lastSecurityKey = lastSecurityKey;
}
public double getPreviousDraw() {
return this.previousDraw;
}
public void setPreviousDraw(final double previousDraw) {
this.previousDraw = previousDraw;
}
private static class MachineSecurityBreak implements IWorldCallable<Void> {
private final GridNode node;
public MachineSecurityBreak(final GridNode node) {
this.node = node;
}
@Override
public Void call(final World world) throws Exception {
this.node.getMachine().securityBreak();
return null;
}
}
private static class ConnectionComparator implements Comparator<IGridConnection> {
private final IGridNode gn;
public ConnectionComparator(final IGridNode gn) {
this.gn = gn;
}
@Override
public int compare(final IGridConnection o1, final IGridConnection o2) {
final boolean preferredA = o1.getOtherSide(this.gn).hasFlag(GridFlags.PREFERRED);
final boolean preferredB = o2.getOtherSide(this.gn).hasFlag(GridFlags.PREFERRED);
return preferredA == preferredB ? 0 : (preferredA ? -1 : 1);
}
}
}
@@ -0,0 +1,81 @@
/*
* 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;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.util.IReadOnlyCollection;
public class GridNodeCollection implements IReadOnlyCollection<IGridNode> {
private final Map<Class<? extends IGridHost>, MachineSet> machines;
public GridNodeCollection(final Map<Class<? extends IGridHost>, MachineSet> machines) {
this.machines = machines;
}
@Override
public Iterator<IGridNode> iterator() {
return new GridNodeIterator(this.machines);
}
@Override
public int size() {
int size = 0;
for (final Set<IGridNode> o : this.machines.values()) {
size += o.size();
}
return size;
}
@Override
public boolean isEmpty() {
for (final Set<IGridNode> o : this.machines.values()) {
if (!o.isEmpty()) {
return false;
}
}
return true;
}
@Override
public boolean contains(final Object maybeGridNode) {
final boolean doesContainNode;
if (maybeGridNode instanceof IGridNode) {
final IGridNode node = (IGridNode) maybeGridNode;
final IGridHost machine = node.getMachine();
final Class<? extends IGridHost> machineClass = machine.getClass();
final MachineSet machineSet = this.machines.get(machineClass);
doesContainNode = machineSet != null && machineSet.contains(maybeGridNode);
} else {
doesContainNode = false;
}
return doesContainNode;
}
}
@@ -0,0 +1,73 @@
/*
* 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;
import java.util.Iterator;
import java.util.Map;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
/**
* Nested iterator for {@link appeng.me.MachineSet}
*
* Traverses first over the {@link appeng.me.MachineSet} and then over every
* containing {@link appeng.api.networking.IGridNode}
*/
public class GridNodeIterator implements Iterator<IGridNode> {
private final Iterator<MachineSet> outerIterator;
private Iterator<IGridNode> innerIterator;
public GridNodeIterator(final Map<Class<? extends IGridHost>, MachineSet> machines) {
this.outerIterator = machines.values().iterator();
this.innerHasNext();
}
private boolean innerHasNext() {
final boolean hasNext = this.outerIterator.hasNext();
if (hasNext) {
final MachineSet nextElem = this.outerIterator.next();
this.innerIterator = nextElem.iterator();
}
return hasNext;
}
@Override
public boolean hasNext() {
while (true) {
if (this.innerIterator.hasNext()) {
return true;
} else if (!this.innerHasNext()) {
return false;
}
}
}
@Override
public IGridNode next() {
return this.innerIterator.next();
}
@Override
public void remove() {
this.innerIterator.remove();
}
}
@@ -16,26 +16,26 @@
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.cache.helpers;
package appeng.me;
import appeng.api.networking.IGridConnection;
import appeng.parts.p2p.MEP2PTunnelPart;
import appeng.api.networking.IGridNode;
import appeng.api.networking.IGridVisitor;
public class TunnelConnection {
public class GridPropagator implements IGridVisitor {
private final Grid g;
private final MEP2PTunnelPart tunnel;
private final IGridConnection c;
public TunnelConnection(final MEP2PTunnelPart t, final IGridConnection con) {
this.tunnel = t;
this.c = con;
public GridPropagator(final Grid g) {
this.g = g;
}
public IGridConnection getConnection() {
return this.c;
}
@Override
public boolean visitNode(final IGridNode n) {
final GridNode gn = (GridNode) n;
if (gn.getMyGrid() != this.g || this.g.getPivot() == n) {
gn.setGrid(this.g);
public MEP2PTunnelPart getTunnel() {
return this.tunnel;
return true;
}
return false;
}
}
}
@@ -0,0 +1,49 @@
/*
* 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;
import appeng.api.networking.IGridNode;
import appeng.api.networking.IGridVisitor;
class GridSplitDetector implements IGridVisitor {
private final IGridNode pivot;
private boolean pivotFound;
public GridSplitDetector(final IGridNode pivot) {
this.pivot = pivot;
}
@Override
public boolean visitNode(final IGridNode n) {
if (n == this.pivot) {
this.setPivotFound(true);
}
return !this.isPivotFound();
}
public boolean isPivotFound() {
return this.pivotFound;
}
private void setPivotFound(final boolean pivotFound) {
this.pivotFound = pivotFound;
}
}
+103
View File
@@ -0,0 +1,103 @@
/*
* 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;
import java.lang.ref.WeakReference;
import java.util.WeakHashMap;
import net.minecraft.nbt.CompoundTag;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridStorage;
import appeng.core.worlddata.WorldData;
public class GridStorage implements IGridStorage {
private final long myID;
private final CompoundTag data;
private final WeakHashMap<GridStorage, Boolean> divided = new WeakHashMap<>();
private WeakReference<IGrid> internalGrid = null;
/**
* for use with world settings
*
* @param id ID of grid storage
*/
public GridStorage(final long id) {
this.myID = id;
this.data = new CompoundTag();
}
/**
* for use with world settings
*
* @param data The Grid data.
* @param id ID of grid storage
*/
public GridStorage(final long id, final CompoundTag data) {
this.myID = id;
this.data = data;
}
/**
* fake storage.
*/
public GridStorage() {
this.myID = 0;
this.data = new CompoundTag();
}
public void saveState() {
throw new IllegalStateException();
// FIXME FABRIC final Grid currentGrid = (Grid) this.getGrid();
// FIXME FABRIC if (currentGrid != null) {
// FIXME FABRIC currentGrid.saveState();
// FIXME FABRIC }
}
public IGrid getGrid() {
return this.internalGrid == null ? null : this.internalGrid.get();
}
void setGrid(final IGrid grid) {
this.internalGrid = new WeakReference<>(grid);
}
@Override
public CompoundTag dataObject() {
return this.data;
}
@Override
public long getID() {
return this.myID;
}
void addDivided(final GridStorage gs) {
this.divided.put(gs, true);
}
boolean hasDivided(final GridStorage myStorage) {
return this.divided.containsKey(myStorage);
}
void remove() {
WorldData.instance().storageData().destroyGridStorage(this.myID);
}
}
+41
View File
@@ -0,0 +1,41 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me;
import java.util.HashSet;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.networking.IMachineSet;
public class MachineSet extends HashSet<IGridNode> implements IMachineSet {
private static final long serialVersionUID = 3224660708327386933L;
private final Class<? extends IGridHost> machine;
MachineSet(final Class<? extends IGridHost> m) {
this.machine = m;
}
@Override
public Class<? extends IGridHost> getMachineClass() {
return this.machine;
}
}
@@ -0,0 +1,185 @@
/*
* 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;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import appeng.api.networking.IGridNode;
import appeng.api.networking.IMachineSet;
import appeng.api.networking.events.MENetworkEvent;
import appeng.api.networking.events.MENetworkEventSubscribe;
import appeng.core.AELog;
public class NetworkEventBus {
private static final Collection<Class> READ_CLASSES = new HashSet<>();
private static final Map<Class<? extends MENetworkEvent>, Map<Class, MENetworkEventInfo>> EVENTS = new HashMap<>();
void readClass(final Class listAs, final Class c) {
if (READ_CLASSES.contains(c)) {
return;
}
READ_CLASSES.add(c);
try {
for (final Method m : c.getMethods()) {
final MENetworkEventSubscribe s = m.getAnnotation(MENetworkEventSubscribe.class);
if (s != null) {
final Class[] types = m.getParameterTypes();
if (types.length == 1) {
if (MENetworkEvent.class.isAssignableFrom(types[0])) {
Map<Class, MENetworkEventInfo> classEvents = EVENTS.get(types[0]);
if (classEvents == null) {
EVENTS.put(types[0], classEvents = new HashMap<>());
}
MENetworkEventInfo thisEvent = classEvents.get(listAs);
if (thisEvent == null) {
thisEvent = new MENetworkEventInfo();
}
thisEvent.Add(types[0], c, m);
classEvents.put(listAs, thisEvent);
} else {
throw new IllegalStateException("Invalid ME Network Event Subscriber, " + m.getName()
+ "s Parameter must extend MENetworkEvent.");
}
} else {
throw new IllegalStateException("Invalid ME Network Event Subscriber, " + m.getName()
+ " must have exactly 1 parameter.");
}
}
}
} catch (final Throwable t) {
throw new IllegalStateException("Error while adding " + c.getName() + " to event bus", t);
}
}
MENetworkEvent postEvent(final Grid g, final MENetworkEvent e) {
final Map<Class, MENetworkEventInfo> subscribers = EVENTS.get(e.getClass());
int x = 0;
try {
if (subscribers != null) {
for (final Entry<Class, MENetworkEventInfo> subscriber : subscribers.entrySet()) {
final MENetworkEventInfo target = subscriber.getValue();
final GridCacheWrapper cache = g.getCaches().get(subscriber.getKey());
if (cache != null) {
x++;
target.invoke(cache.getCache(), e);
}
// events may create or remove grid nodes in rare cases
final IMachineSet machines = g.getMachines(subscriber.getKey());
final List<IGridNode> work = new ArrayList<>(machines.size());
machines.forEach(work::add);
for (final IGridNode obj : work) {
// stil part of grid?
if (machines.contains(obj)) {
x++;
target.invoke(obj.getMachine(), e);
}
}
}
}
} catch (final NetworkEventDone done) {
// Early out.
}
e.setVisitedObjects(x);
return e;
}
MENetworkEvent postEventTo(final Grid grid, final GridNode node, final MENetworkEvent e) {
final Map<Class, MENetworkEventInfo> subscribers = EVENTS.get(e.getClass());
int x = 0;
try {
if (subscribers != null) {
final MENetworkEventInfo target = subscribers.get(node.getMachineClass());
if (target != null) {
x++;
target.invoke(node.getMachine(), e);
}
}
} catch (final NetworkEventDone done) {
// Early out.
}
e.setVisitedObjects(x);
return e;
}
private static class NetworkEventDone extends Throwable {
private static final long serialVersionUID = -3079021487019171205L;
}
private class EventMethod {
private final Class objClass;
private final Method objMethod;
private final Class objEvent;
public EventMethod(final Class Event, final Class ObjClass, final Method ObjMethod) {
this.objClass = ObjClass;
this.objMethod = ObjMethod;
this.objEvent = Event;
}
private void invoke(final Object obj, final MENetworkEvent e) throws NetworkEventDone {
try {
this.objMethod.invoke(obj, e);
} catch (final Throwable e1) {
AELog.error("[AppEng] Network Event caused exception:");
AELog.error("Class: %1s, Object: %2s", obj.getClass().getName(), obj.toString());
AELog.info(e1);
throw new IllegalStateException(e1);
}
if (e.isCanceled()) {
throw new NetworkEventDone();
}
}
}
private class MENetworkEventInfo {
private final List<EventMethod> methods = new ArrayList<>();
private void Add(final Class Event, final Class ObjClass, final Method ObjMethod) {
this.methods.add(new EventMethod(Event, ObjClass, ObjMethod));
}
private void invoke(final Object obj, final MENetworkEvent e) throws NetworkEventDone {
for (final EventMethod em : this.methods) {
em.invoke(obj, e);
}
}
}
}
-572
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
}
-231
View File
@@ -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
View File
@@ -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;
}
}
-76
View File
@@ -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;
}
}
+119
View File
@@ -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,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.
}
}
@@ -0,0 +1,32 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.cluster;
import java.util.Iterator;
import appeng.api.networking.IGridHost;
public interface IAECluster {
void updateStatus(boolean updateGrid);
void destroy();
Iterator<IGridHost> getTiles();
}
@@ -0,0 +1,28 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.cluster;
public interface IAEMultiBlock {
void disconnect(boolean b);
IAECluster getCluster();
boolean isValid();
}
@@ -1,207 +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.cluster;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import appeng.api.util.AEPartLocation;
import appeng.api.util.WorldCoord;
import appeng.core.AELog;
import appeng.util.Platform;
public abstract class MBCalculator {
private final IAEMultiBlock target;
public MBCalculator(final IAEMultiBlock t) {
this.target = t;
}
public void calculateMultiblock(final World world, final WorldCoord loc) {
if (Platform.isClient()) {
return;
}
try {
final WorldCoord min = loc.copy();
final WorldCoord max = loc.copy();
// find size of MB structure...
while (this.isValidTileAt(world, min.x - 1, min.y, min.z)) {
min.x--;
}
while (this.isValidTileAt(world, min.x, min.y - 1, min.z)) {
min.y--;
}
while (this.isValidTileAt(world, min.x, min.y, min.z - 1)) {
min.z--;
}
while (this.isValidTileAt(world, max.x + 1, max.y, max.z)) {
max.x++;
}
while (this.isValidTileAt(world, max.x, max.y + 1, max.z)) {
max.y++;
}
while (this.isValidTileAt(world, max.x, max.y, max.z + 1)) {
max.z++;
}
if (this.checkMultiblockScale(min, max)) {
if (this.verifyUnownedRegion(world, min, max)) {
IAECluster c = this.createCluster(world, min, max);
try {
if (!this.verifyInternalStructure(world, min, max)) {
this.disconnect();
return;
}
} catch (final Exception err) {
this.disconnect();
return;
}
boolean updateGrid = false;
final IAECluster cluster = this.target.getCluster();
if (cluster == null) {
this.updateTiles(c, world, min, max);
updateGrid = true;
} else {
c = cluster;
}
c.updateStatus(updateGrid);
return;
}
}
} catch (final Throwable err) {
AELog.debug(err);
}
this.disconnect();
}
private boolean isValidTileAt(final World w, final int x, final int y, final int z) {
return this.isValidTile(w.getBlockEntity(new BlockPos(x, y, z)));
}
/**
* verify if the structure is the correct dimensions, or size
*
* @param min min world coord
* @param max max world coord
*
* @return true if structure has correct dimensions or size
*/
public abstract boolean checkMultiblockScale(WorldCoord min, WorldCoord max);
private boolean verifyUnownedRegion(final World w, final WorldCoord min, final WorldCoord max) {
for (final AEPartLocation side : AEPartLocation.SIDE_LOCATIONS) {
if (this.verifyUnownedRegionInner(w, min.x, min.y, min.z, max.x, max.y, max.z, side)) {
return false;
}
}
return true;
}
/**
* construct the correct cluster, usually very simple.
*
* @param w world
* @param min min world coord
* @param max max world coord
*
* @return created cluster
*/
public abstract IAECluster createCluster(World w, WorldCoord min, WorldCoord max);
public abstract boolean verifyInternalStructure(World world, WorldCoord min, WorldCoord max);
/**
* disassembles the multi-block.
*/
public abstract void disconnect();
/**
* configure the multi-block tiles, most of the important stuff is in here.
*
* @param c updated cluster
* @param w in world
* @param min min world coord
* @param max max world coord
*/
public abstract void updateTiles(IAECluster c, World w, WorldCoord min, WorldCoord max);
/**
* check if the block entities are correct for the structure.
*
* @param te to be checked block entity
*
* @return true if block entity is valid for structure
*/
public abstract boolean isValidTile(BlockEntity te);
private boolean verifyUnownedRegionInner(final World w, int minX, int minY, int minZ, int maxX, int maxY, int maxZ,
final AEPartLocation side) {
switch (side) {
case WEST:
minX -= 1;
maxX = minX;
break;
case EAST:
maxX += 1;
minX = maxX;
break;
case DOWN:
minY -= 1;
maxY = minY;
break;
case NORTH:
maxZ += 1;
minZ = maxZ;
break;
case SOUTH:
minZ -= 1;
maxZ = minZ;
break;
case UP:
maxY += 1;
minY = maxY;
break;
case INTERNAL:
return false;
}
for (int x = minX; x <= maxX; x++) {
for (int y = minY; y <= maxY; y++) {
for (int z = minZ; z <= maxZ; z++) {
final BlockEntity te = w.getBlockEntity(new BlockPos(x, y, z));
if (this.isValidTile(te)) {
return true;
}
}
}
}
return false;
}
}
@@ -1,131 +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.cluster.implementations;
import java.util.Iterator;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.networking.events.MENetworkCraftingCpuChange;
import appeng.api.util.AEPartLocation;
import appeng.api.util.WorldCoord;
import appeng.me.cluster.IAECluster;
import appeng.me.cluster.IAEMultiBlock;
import appeng.me.cluster.MBCalculator;
import appeng.tile.crafting.CraftingBlockEntity;
public class CraftingCPUCalculator extends MBCalculator {
private final CraftingBlockEntity tqb;
public CraftingCPUCalculator(final IAEMultiBlock t) {
super(t);
this.tqb = (CraftingBlockEntity) t;
}
@Override
public boolean checkMultiblockScale(final WorldCoord min, final WorldCoord max) {
if (max.x - min.x > 16) {
return false;
}
if (max.y - min.y > 16) {
return false;
}
if (max.z - min.z > 16) {
return false;
}
return true;
}
@Override
public IAECluster createCluster(final World w, final WorldCoord min, final WorldCoord max) {
return new CraftingCPUCluster(min, max);
}
@Override
public boolean verifyInternalStructure(final World w, final WorldCoord min, final WorldCoord max) {
boolean storage = false;
for (int x = min.x; x <= max.x; x++) {
for (int y = min.y; y <= max.y; y++) {
for (int z = min.z; z <= max.z; z++) {
final IAEMultiBlock te = (IAEMultiBlock) w.getBlockEntity(new BlockPos(x, y, z));
if (!te.isValid()) {
return false;
}
if (!storage && te instanceof CraftingBlockEntity) {
storage = ((CraftingBlockEntity) te).getStorageBytes() > 0;
}
}
}
}
return storage;
}
@Override
public void disconnect() {
this.tqb.disconnect(true);
}
@Override
public void updateTiles(final IAECluster cl, final World w, final WorldCoord min, final WorldCoord max) {
final CraftingCPUCluster c = (CraftingCPUCluster) cl;
for (int x = min.x; x <= max.x; x++) {
for (int y = min.y; y <= max.y; y++) {
for (int z = min.z; z <= max.z; z++) {
final CraftingBlockEntity te = (CraftingBlockEntity) w.getBlockEntity(new BlockPos(x, y, z));
te.updateStatus(c);
c.addTile(te);
}
}
}
c.done();
final Iterator<IGridHost> i = c.getTiles();
while (i.hasNext()) {
final IGridHost gh = i.next();
final IGridNode n = gh.getGridNode(AEPartLocation.INTERNAL);
if (n != null) {
final IGrid g = n.getGrid();
if (g != null) {
g.postEvent(new MENetworkCraftingCpuChange(n));
return;
}
}
}
}
@Override
public boolean isValidTile(final BlockEntity te) {
return te instanceof CraftingBlockEntity;
}
}
File diff suppressed because it is too large Load Diff
@@ -1,141 +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.cluster.implementations;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import appeng.api.AEApi;
import appeng.api.definitions.IBlockDefinition;
import appeng.api.definitions.IBlocks;
import appeng.api.util.WorldCoord;
import appeng.me.cluster.IAECluster;
import appeng.me.cluster.IAEMultiBlock;
import appeng.me.cluster.MBCalculator;
import appeng.tile.qnb.QuantumBridgeBlockEntity;
public class QuantumCalculator extends MBCalculator {
private final QuantumBridgeBlockEntity tqb;
public QuantumCalculator(final IAEMultiBlock t) {
super(t);
this.tqb = (QuantumBridgeBlockEntity) t;
}
@Override
public boolean checkMultiblockScale(final WorldCoord min, final WorldCoord max) {
if ((max.x - min.x + 1) * (max.y - min.y + 1) * (max.z - min.z + 1) == 9) {
final int ones = ((max.x - min.x) == 0 ? 1 : 0) + ((max.y - min.y) == 0 ? 1 : 0)
+ ((max.z - min.z) == 0 ? 1 : 0);
final int threes = ((max.x - min.x) == 2 ? 1 : 0) + ((max.y - min.y) == 2 ? 1 : 0)
+ ((max.z - min.z) == 2 ? 1 : 0);
return ones == 1 && threes == 2;
}
return false;
}
@Override
public IAECluster createCluster(final World w, final WorldCoord min, final WorldCoord max) {
return new QuantumCluster(min, max);
}
@Override
public boolean verifyInternalStructure(final World w, final WorldCoord min, final WorldCoord max) {
byte num = 0;
for (int x = min.x; x <= max.x; x++) {
for (int y = min.y; y <= max.y; y++) {
for (int z = min.z; z <= max.z; z++) {
final BlockPos p = new BlockPos(x, y, z);
final IAEMultiBlock te = (IAEMultiBlock) w.getBlockEntity(p);
if (!te.isValid()) {
return false;
}
num++;
final IBlocks blocks = AEApi.instance().definitions().blocks();
if (num == 5) {
if (!this.isBlockAtLocation(w, p, blocks.quantumLink())) {
return false;
}
} else {
if (!this.isBlockAtLocation(w, p, blocks.quantumRing())) {
return false;
}
}
}
}
}
return true;
}
@Override
public void disconnect() {
this.tqb.disconnect(true);
}
@Override
public void updateTiles(final IAECluster cl, final World w, final WorldCoord min, final WorldCoord max) {
byte num = 0;
byte ringNum = 0;
final QuantumCluster c = (QuantumCluster) cl;
for (int x = min.x; x <= max.x; x++) {
for (int y = min.y; y <= max.y; y++) {
for (int z = min.z; z <= max.z; z++) {
final QuantumBridgeBlockEntity te = (QuantumBridgeBlockEntity) w.getBlockEntity(new BlockPos(x, y, z));
num++;
final byte flags;
if (num == 5) {
flags = num;
c.setCenter(te);
} else {
if (num == 1 || num == 3 || num == 7 || num == 9) {
flags = (byte) (this.tqb.getCorner() | num);
} else {
flags = num;
}
c.getRing()[ringNum] = te;
ringNum++;
}
te.updateStatus(c, flags, true);
}
}
}
}
@Override
public boolean isValidTile(final BlockEntity te) {
return te instanceof QuantumBridgeBlockEntity;
}
private boolean isBlockAtLocation(final BlockView w, final BlockPos pos, final IBlockDefinition def) {
return def.maybeBlock().map(block -> block == w.getBlockState(pos).getBlock()).orElse(false);
}
}
@@ -1,260 +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.cluster.implementations;
import java.util.Iterator;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import net.minecraft.world.dimension.DimensionType;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import appeng.api.AEApi;
import appeng.api.events.LocatableEventAnnounce;
import appeng.api.events.LocatableEventAnnounce.LocatableEvent;
import appeng.api.exceptions.FailedConnectionException;
import appeng.api.features.ILocatable;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.util.AEPartLocation;
import appeng.api.util.WorldCoord;
import appeng.core.AELog;
import appeng.me.cache.helpers.ConnectionWrapper;
import appeng.me.cluster.IAECluster;
import appeng.tile.qnb.QuantumBridgeBlockEntity;
import appeng.util.iterators.ChainedIterator;
public class QuantumCluster implements ILocatable, IAECluster {
private final WorldCoord min;
private final WorldCoord max;
private boolean isDestroyed = false;
private boolean updateStatus = true;
private QuantumBridgeBlockEntity[] Ring;
private boolean registered = false;
private ConnectionWrapper connection;
private long thisSide;
private long otherSide;
private QuantumBridgeBlockEntity center;
public QuantumCluster(final WorldCoord min, final WorldCoord max) {
this.min = min;
this.max = max;
this.setRing(new QuantumBridgeBlockEntity[8]);
}
@SubscribeEvent
public void onUnload(final WorldEvent.Unload e) {
if (this.center.getWorld() == e.getWorld()) {
this.setUpdateStatus(false);
this.destroy();
}
}
@Override
public void updateStatus(final boolean updateGrid) {
final long qe = this.center.getQEFrequency();
if (this.thisSide != qe && this.thisSide != -qe) {
if (qe != 0) {
if (this.thisSide != 0) {
MinecraftForge.EVENT_BUS.post(new LocatableEventAnnounce(this, LocatableEvent.UNREGISTER));
}
if (this.canUseNode(-qe)) {
this.otherSide = qe;
this.thisSide = -qe;
} else if (this.canUseNode(qe)) {
this.thisSide = qe;
this.otherSide = -qe;
}
MinecraftForge.EVENT_BUS.post(new LocatableEventAnnounce(this, LocatableEvent.REGISTER));
} else {
MinecraftForge.EVENT_BUS.post(new LocatableEventAnnounce(this, LocatableEvent.UNREGISTER));
this.otherSide = 0;
this.thisSide = 0;
}
}
final ILocatable myOtherSide = this.otherSide == 0 ? null
: AEApi.instance().registries().locatable().getLocatableBy(this.otherSide);
boolean shutdown = false;
if (myOtherSide instanceof QuantumCluster) {
final QuantumCluster sideA = this;
final QuantumCluster sideB = (QuantumCluster) myOtherSide;
if (sideA.isActive() && sideB.isActive()) {
if (this.connection != null && this.connection.getConnection() != null) {
final IGridNode a = this.connection.getConnection().a();
final IGridNode b = this.connection.getConnection().b();
final IGridNode sa = sideA.getNode();
final IGridNode sb = sideB.getNode();
if ((a == sa || b == sa) && (a == sb || b == sb)) {
return;
}
}
try {
if (sideA.connection != null) {
if (sideA.connection.getConnection() != null) {
sideA.connection.getConnection().destroy();
sideA.connection = new ConnectionWrapper(null);
}
}
if (sideB.connection != null) {
if (sideB.connection.getConnection() != null) {
sideB.connection.getConnection().destroy();
sideB.connection = new ConnectionWrapper(null);
}
}
sideA.connection = sideB.connection = new ConnectionWrapper(
AEApi.instance().grid().createGridConnection(sideA.getNode(), sideB.getNode()));
} catch (final FailedConnectionException e) {
// :(
AELog.debug(e);
}
} else {
shutdown = true;
}
} else {
shutdown = true;
}
if (shutdown && this.connection != null) {
if (this.connection.getConnection() != null) {
this.connection.getConnection().destroy();
this.connection.setConnection(null);
this.connection = new ConnectionWrapper(null);
}
}
}
private boolean canUseNode(final long qe) {
final QuantumCluster qc = (QuantumCluster) AEApi.instance().registries().locatable().getLocatableBy(qe);
if (qc != null) {
final World theWorld = qc.center.getWorld();
if (!qc.isDestroyed) {
ChunkPos cPos = new ChunkPos(qc.center.getPos());
if (theWorld.getChunkManager().isChunkLoaded(cPos)) {
final DimensionType id = theWorld.dimension.getType();
final World cur = theWorld.getServer().getWorld(id);
final BlockEntity te = theWorld.getBlockEntity(qc.center.getPos());
return te != qc.center || theWorld != cur;
}
}
}
return true;
}
private boolean isActive() {
if (this.isDestroyed || !this.registered) {
return false;
}
return this.center.isPowered() && this.hasQES();
}
private IGridNode getNode() {
return this.center.getGridNode(AEPartLocation.INTERNAL);
}
private boolean hasQES() {
return this.thisSide != 0;
}
@Override
public void destroy() {
if (this.isDestroyed) {
return;
}
this.isDestroyed = true;
if (this.registered) {
MinecraftForge.EVENT_BUS.unregister(this);
this.registered = false;
}
if (this.thisSide != 0) {
this.updateStatus(true);
MinecraftForge.EVENT_BUS.post(new LocatableEventAnnounce(this, LocatableEvent.UNREGISTER));
}
this.center.updateStatus(null, (byte) -1, this.isUpdateStatus());
for (final QuantumBridgeBlockEntity r : this.getRing()) {
r.updateStatus(null, (byte) -1, this.isUpdateStatus());
}
this.center = null;
this.setRing(new QuantumBridgeBlockEntity[8]);
}
@Override
public Iterator<IGridHost> getTiles() {
return new ChainedIterator<>(this.getRing()[0], this.getRing()[1], this.getRing()[2], this.getRing()[3],
this.getRing()[4], this.getRing()[5], this.getRing()[6], this.getRing()[7], this.center);
}
public boolean isCorner(final QuantumBridgeBlockEntity tileQuantumBridge) {
return this.getRing()[0] == tileQuantumBridge || this.getRing()[2] == tileQuantumBridge
|| this.getRing()[4] == tileQuantumBridge || this.getRing()[6] == tileQuantumBridge;
}
@Override
public long getLocatableSerial() {
return this.thisSide;
}
public QuantumBridgeBlockEntity getCenter() {
return this.center;
}
void setCenter(final QuantumBridgeBlockEntity c) {
this.registered = true;
MinecraftForge.EVENT_BUS.register(this);
this.center = c;
}
private boolean isUpdateStatus() {
return this.updateStatus;
}
public void setUpdateStatus(final boolean updateStatus) {
this.updateStatus = updateStatus;
}
QuantumBridgeBlockEntity[] getRing() {
return this.Ring;
}
private void setRing(final QuantumBridgeBlockEntity[] ring) {
this.Ring = ring;
}
}
@@ -1,96 +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.cluster.implementations;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import appeng.api.util.DimensionalCoord;
import appeng.api.util.WorldCoord;
import appeng.me.cluster.IAECluster;
import appeng.me.cluster.IAEMultiBlock;
import appeng.me.cluster.MBCalculator;
import appeng.tile.spatial.SpatialPylonBlockEntity;
public class SpatialPylonCalculator extends MBCalculator {
private final SpatialPylonBlockEntity tqb;
public SpatialPylonCalculator(final IAEMultiBlock t) {
super(t);
this.tqb = (SpatialPylonBlockEntity) t;
}
@Override
public boolean checkMultiblockScale(final WorldCoord min, final WorldCoord max) {
return (min.x == max.x && min.y == max.y && min.z != max.z)
|| (min.x == max.x && min.y != max.y && min.z == max.z)
|| (min.x != max.x && min.y == max.y && min.z == max.z);
}
@Override
public IAECluster createCluster(final World w, final WorldCoord min, final WorldCoord max) {
return new SpatialPylonCluster(new DimensionalCoord(w, min.x, min.y, min.z),
new DimensionalCoord(w, max.x, max.y, max.z));
}
@Override
public boolean verifyInternalStructure(final World w, final WorldCoord min, final WorldCoord max) {
for (int x = min.x; x <= max.x; x++) {
for (int y = min.y; y <= max.y; y++) {
for (int z = min.z; z <= max.z; z++) {
final IAEMultiBlock te = (IAEMultiBlock) w.getBlockEntity(new BlockPos(x, y, z));
if (!te.isValid()) {
return false;
}
}
}
}
return true;
}
@Override
public void disconnect() {
this.tqb.disconnect(true);
}
@Override
public void updateTiles(final IAECluster cl, final World w, final WorldCoord min, final WorldCoord max) {
final SpatialPylonCluster c = (SpatialPylonCluster) cl;
for (int x = min.x; x <= max.x; x++) {
for (int y = min.y; y <= max.y; y++) {
for (int z = min.z; z <= max.z; z++) {
final SpatialPylonBlockEntity te = (SpatialPylonBlockEntity) w.getBlockEntity(new BlockPos(x, y, z));
te.updateStatus(c);
c.getLine().add((te));
}
}
}
}
@Override
public boolean isValidTile(final BlockEntity te) {
return te instanceof SpatialPylonBlockEntity;
}
}
@@ -1,115 +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.cluster.implementations;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import appeng.api.networking.IGridHost;
import appeng.api.util.DimensionalCoord;
import appeng.me.cluster.IAECluster;
import appeng.tile.spatial.SpatialPylonBlockEntity;
public class SpatialPylonCluster implements IAECluster {
private final DimensionalCoord min;
private final DimensionalCoord max;
private final List<SpatialPylonBlockEntity> line = new ArrayList<>();
private boolean isDestroyed = false;
private Axis currentAxis = Axis.UNFORMED;
private boolean isValid;
public SpatialPylonCluster(final DimensionalCoord min, final DimensionalCoord max) {
this.min = min.copy();
this.max = max.copy();
if (this.getMin().x != this.getMax().x) {
this.setCurrentAxis(Axis.X);
} else if (this.getMin().y != this.getMax().y) {
this.setCurrentAxis(Axis.Y);
} else if (this.getMin().z != this.getMax().z) {
this.setCurrentAxis(Axis.Z);
} else {
this.setCurrentAxis(Axis.UNFORMED);
}
}
@Override
public void updateStatus(final boolean updateGrid) {
for (final SpatialPylonBlockEntity r : this.getLine()) {
r.recalculateDisplay();
}
}
@Override
public void destroy() {
if (this.isDestroyed) {
return;
}
this.isDestroyed = true;
for (final SpatialPylonBlockEntity r : this.getLine()) {
r.updateStatus(null);
}
}
@Override
public Iterator<IGridHost> getTiles() {
return (Iterator) this.getLine().iterator();
}
public int tileCount() {
return this.getLine().size();
}
public Axis getCurrentAxis() {
return this.currentAxis;
}
private void setCurrentAxis(final Axis currentAxis) {
this.currentAxis = currentAxis;
}
public boolean isValid() {
return this.isValid;
}
public void setValid(final boolean isValid) {
this.isValid = isValid;
}
public DimensionalCoord getMax() {
return this.max;
}
public DimensionalCoord getMin() {
return this.min;
}
List<SpatialPylonBlockEntity> getLine() {
return this.line;
}
public enum Axis {
X, Y, Z, UNFORMED
}
}
@@ -0,0 +1,99 @@
/*
* 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.energy;
import appeng.api.networking.energy.IEnergyWatcher;
public class EnergyThreshold implements Comparable<EnergyThreshold> {
private final double threshold;
private final IEnergyWatcher watcher;
private final int watcherHash;
public EnergyThreshold(final double lim, final IEnergyWatcher watcher) {
this.threshold = lim;
this.watcher = watcher;
this.watcherHash = watcher.hashCode();
}
/**
* Special constructor to allow querying a for a subset of thresholds.
*
* @param lim
* @param bound
*/
public EnergyThreshold(final double lim, final int bound) {
this.threshold = lim;
this.watcher = null;
this.watcherHash = bound;
}
public IEnergyWatcher getEnergyWatcher() {
return this.watcher;
}
@Override
public int compareTo(EnergyThreshold o) {
int a = Double.compare(this.threshold, o.threshold);
if (a == 0) {
return Integer.compare(this.watcherHash, o.watcherHash);
}
return a;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(this.threshold);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + ((this.watcher == null) ? 0 : this.watcher.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
EnergyThreshold other = (EnergyThreshold) obj;
if (Double.doubleToLongBits(this.threshold) != Double.doubleToLongBits(other.threshold)) {
return false;
}
if (this.watcher == null) {
if (other.watcher != null) {
return false;
}
} else if (!this.watcher.equals(other.watcher)) {
return false;
}
return true;
}
}
@@ -0,0 +1,82 @@
/*
* 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.energy;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import appeng.api.networking.energy.IEnergyWatcher;
import appeng.api.networking.energy.IEnergyWatcherHost;
import appeng.me.cache.EnergyGridCache;
/**
* Maintain my interests, and a global watch list, they should always be fully
* synchronized.
*/
public class EnergyWatcher implements IEnergyWatcher {
private final EnergyGridCache gsc;
private final IEnergyWatcherHost watcherHost;
private final Set<EnergyThreshold> myInterests = new HashSet<>();
public EnergyWatcher(final EnergyGridCache cache, final IEnergyWatcherHost host) {
this.gsc = cache;
this.watcherHost = host;
}
public void post(final EnergyGridCache energyGridCache) {
this.watcherHost.onThresholdPass(energyGridCache);
}
public IEnergyWatcherHost getHost() {
return this.watcherHost;
}
@Override
public boolean add(final double amount) {
final EnergyThreshold eh = new EnergyThreshold(amount, this);
if (this.myInterests.contains(eh))
{
return false;
}
return this.gsc.registerEnergyInterest(eh) && this.myInterests.add(eh);
}
@Override
public boolean remove(final double amount) {
final EnergyThreshold eh = new EnergyThreshold(amount, this);
return this.myInterests.remove(eh) && this.gsc.unregisterEnergyInterest(eh);
}
@Override
public void reset() {
for (Iterator<EnergyThreshold> iterator = this.myInterests.iterator(); iterator.hasNext();) {
final EnergyThreshold threshold = iterator.next();
this.gsc.unregisterEnergyInterest(threshold);
iterator.remove();
}
}
}
@@ -0,0 +1,377 @@
/*
* 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.helpers;
import java.util.Collections;
import java.util.EnumSet;
import com.mojang.authlib.GameProfile;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.math.Direction;
import appeng.api.AEApi;
import appeng.api.networking.GridFlags;
import appeng.api.networking.GridNotification;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridBlock;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.networking.crafting.ICraftingGrid;
import appeng.api.networking.energy.IEnergyGrid;
import appeng.api.networking.events.MENetworkPowerIdleChange;
import appeng.api.networking.pathing.IPathingGrid;
import appeng.api.networking.security.ISecurityGrid;
import appeng.api.networking.storage.IStorageGrid;
import appeng.api.networking.ticking.ITickManager;
import appeng.api.util.AEColor;
import appeng.api.util.DimensionalCoord;
import appeng.api.util.IOrientable;
import appeng.core.worlddata.WorldData;
import appeng.hooks.TickHandler;
import appeng.me.GridAccessException;
import appeng.me.cache.P2PCache;
import appeng.parts.networking.CablePart;
import appeng.tile.AEBaseBlockEntity;
import appeng.util.Platform;
public class AENetworkProxy implements IGridBlock {
private final IGridProxyable gp;
private final boolean worldNode;
private final String nbtName; // name
private AEColor myColor = AEColor.TRANSPARENT;
private CompoundTag data = null; // input
private ItemStack myRepInstance = ItemStack.EMPTY;
private boolean isReady = false;
private IGridNode node = null;
private EnumSet<Direction> validSides;
private EnumSet<GridFlags> flags = EnumSet.noneOf(GridFlags.class);
private double idleDraw = 1.0;
private PlayerEntity owner;
public AENetworkProxy(final IGridProxyable te, final String nbtName, final ItemStack visual,
final boolean inWorld) {
this.gp = te;
this.nbtName = nbtName;
this.worldNode = inWorld;
this.myRepInstance = visual;
this.validSides = EnumSet.allOf(Direction.class);
}
public void setVisualRepresentation(final ItemStack is) {
this.myRepInstance = is;
}
public void writeToNBT(final CompoundTag tag) {
if (this.node != null) {
this.node.saveToNBT(this.nbtName, tag);
}
}
public void setValidSides(final EnumSet<Direction> validSides) {
this.validSides = validSides;
if (this.node != null) {
this.node.updateState();
}
}
public void validate() {
if (this.gp instanceof AEBaseBlockEntity) {
TickHandler.INSTANCE.addInit((AEBaseBlockEntity) this.gp);
}
}
public void onChunkUnloaded() {
this.isReady = false;
this.remove();
}
public void remove() {
this.isReady = false;
if (this.node != null) {
this.node.destroy();
this.node = null;
}
}
public void onReady() {
this.isReady = true;
// send orientation based directionality to the node.
if (this.gp instanceof IOrientable) {
final IOrientable ori = (IOrientable) this.gp;
if (ori.canBeRotated()) {
ori.setOrientation(ori.getForward(), ori.getUp());
}
}
this.getNode();
}
public IGridNode getNode() {
if (this.node == null && Platform.isServer() && this.isReady) {
this.node = AEApi.instance().grid().createGridNode(this);
this.readFromNBT(this.data);
this.node.updateState();
}
return this.node;
}
public void readFromNBT(final CompoundTag tag) {
this.data = tag;
if (this.node != null && this.data != null) {
this.node.loadFromNBT(this.nbtName, this.data);
this.data = null;
} else if (this.node != null && this.owner != null) {
final GameProfile profile = this.owner.getGameProfile();
final int playerID = WorldData.instance().playerData().getMePlayerId(profile);
this.node.setPlayerID(playerID);
this.owner = null;
}
}
public IPathingGrid getPath() throws GridAccessException {
final IGrid grid = this.getGrid();
if (grid == null) {
throw new GridAccessException();
}
final IPathingGrid pg = grid.getCache(IPathingGrid.class);
if (pg == null) {
throw new GridAccessException();
}
return pg;
}
/**
* short cut!
*
* @return grid of node
*
* @throws GridAccessException of node or grid is null
*/
public IGrid getGrid() throws GridAccessException {
if (this.node == null) {
throw new GridAccessException();
}
final IGrid grid = this.node.getGrid();
if (grid == null) {
throw new GridAccessException();
}
return grid;
}
public ITickManager getTick() throws GridAccessException {
final IGrid grid = this.getGrid();
if (grid == null) {
throw new GridAccessException();
}
final ITickManager pg = grid.getCache(ITickManager.class);
if (pg == null) {
throw new GridAccessException();
}
return pg;
}
public IStorageGrid getStorage() throws GridAccessException {
final IGrid grid = this.getGrid();
if (grid == null) {
throw new GridAccessException();
}
final IStorageGrid pg = grid.getCache(IStorageGrid.class);
if (pg == null) {
throw new GridAccessException();
}
return pg;
}
public P2PCache getP2P() throws GridAccessException {
final IGrid grid = this.getGrid();
if (grid == null) {
throw new GridAccessException();
}
final P2PCache pg = grid.getCache(P2PCache.class);
if (pg == null) {
throw new GridAccessException();
}
return pg;
}
public ISecurityGrid getSecurity() throws GridAccessException {
final IGrid grid = this.getGrid();
if (grid == null) {
throw new GridAccessException();
}
final ISecurityGrid sg = grid.getCache(ISecurityGrid.class);
if (sg == null) {
throw new GridAccessException();
}
return sg;
}
public ICraftingGrid getCrafting() throws GridAccessException {
final IGrid grid = this.getGrid();
if (grid == null) {
throw new GridAccessException();
}
final ICraftingGrid sg = grid.getCache(ICraftingGrid.class);
if (sg == null) {
throw new GridAccessException();
}
return sg;
}
@Override
public double getIdlePowerUsage() {
return this.idleDraw;
}
@Override
public EnumSet<GridFlags> getFlags() {
return this.flags;
}
@Override
public boolean isWorldAccessible() {
return this.worldNode;
}
@Override
public DimensionalCoord getLocation() {
return this.gp.getLocation();
}
@Override
public AEColor getGridColor() {
return this.getColor();
}
@Override
public void onGridNotification(final GridNotification notification) {
if (this.gp instanceof CablePart) {
((CablePart) this.gp).markForUpdate();
}
}
@Override
public void setNetworkStatus(final IGrid grid, final int channelsInUse) {
}
@Override
public EnumSet<Direction> getConnectableSides() {
return this.validSides;
}
@Override
public IGridHost getMachine() {
return this.gp;
}
@Override
public void gridChanged() {
this.gp.gridChanged();
}
@Override
public ItemStack getMachineRepresentation() {
return this.myRepInstance;
}
public void setFlags(final GridFlags... requireChannel) {
final EnumSet<GridFlags> flags = EnumSet.noneOf(GridFlags.class);
Collections.addAll(flags, requireChannel);
this.flags = flags;
}
public void setIdlePowerUsage(final double idle) {
this.idleDraw = idle;
if (this.node != null) {
try {
final IGrid g = this.getGrid();
g.postEvent(new MENetworkPowerIdleChange(this.node));
} catch (final GridAccessException e) {
// not ready for this yet..
}
}
}
public boolean isReady() {
return this.isReady;
}
public boolean isActive() {
if (this.node == null) {
return false;
}
return this.node.isActive();
}
public boolean isPowered() {
try {
return this.getEnergy().isNetworkPowered();
} catch (final GridAccessException e) {
return false;
}
}
public IEnergyGrid getEnergy() throws GridAccessException {
final IGrid grid = this.getGrid();
if (grid == null) {
throw new GridAccessException();
}
final IEnergyGrid eg = grid.getCache(IEnergyGrid.class);
if (eg == null) {
throw new GridAccessException();
}
return eg;
}
public void setOwner(final PlayerEntity player) {
this.owner = player;
}
public AEColor getColor() {
return this.myColor;
}
public void setColor(final AEColor myColor) {
this.myColor = myColor;
}
}
@@ -0,0 +1,44 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.helpers;
import java.util.Optional;
import net.minecraft.entity.player.PlayerEntity;
import appeng.api.networking.security.IActionHost;
import appeng.api.networking.security.IActionSource;
public class BaseActionSource implements IActionSource {
@Override
public Optional<PlayerEntity> player() {
return Optional.empty();
}
@Override
public Optional<IActionHost> machine() {
return Optional.empty();
}
@Override
public <T> Optional<T> context(Class<T> key) {
return Optional.empty();
}
}
@@ -18,34 +18,26 @@
package appeng.me.helpers;
import java.util.Iterator;
import net.minecraft.item.ItemStack;
import appeng.api.networking.IGridMultiblock;
import appeng.api.config.Actionable;
import appeng.api.config.PowerMultiplier;
import appeng.api.networking.IGridNode;
import appeng.me.cluster.IAECluster;
import appeng.me.cluster.IAEMultiBlock;
import appeng.util.iterators.ChainedIterator;
import appeng.util.iterators.ProxyNodeIterator;
import appeng.api.networking.energy.IEnergySource;
public class AENetworkProxyMultiblock extends AENetworkProxy implements IGridMultiblock {
public class ChannelPowerSrc implements IEnergySource {
public AENetworkProxyMultiblock(final IGridProxyable te, final String nbtName, final ItemStack itemStack,
final boolean inWorld) {
super(te, nbtName, itemStack, inWorld);
private final IGridNode node;
private final IEnergySource realSrc;
public ChannelPowerSrc(final IGridNode networkNode, final IEnergySource src) {
this.node = networkNode;
this.realSrc = src;
}
@Override
public Iterator<IGridNode> getMultiblockNodes() {
if (this.getCluster() == null) {
return new ChainedIterator<>();
public double extractAEPower(final double amt, final Actionable mode, final PowerMultiplier usePowerMultiplier) {
if (this.node.isActive()) {
return this.realSrc.extractAEPower(amt, mode, usePowerMultiplier);
}
return new ProxyNodeIterator(this.getCluster().getTiles());
}
private IAECluster getCluster() {
return ((IAEMultiBlock) this.getMachine()).getCluster();
return 0.0;
}
}
@@ -0,0 +1,102 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.helpers;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.google.common.collect.Multimap;
import appeng.api.storage.data.IAEStack;
public class GenericInterestManager<T> {
private final Multimap<IAEStack, T> container;
private List<SavedTransactions> transactions = null;
private int transDepth = 0;
public GenericInterestManager(final Multimap<IAEStack, T> interests) {
this.container = interests;
}
public void enableTransactions() {
if (this.transDepth == 0) {
this.transactions = new ArrayList<>();
}
this.transDepth++;
}
public void disableTransactions() {
this.transDepth--;
if (this.transDepth == 0) {
final List<SavedTransactions> myActions = this.transactions;
this.transactions = null;
for (final SavedTransactions t : myActions) {
if (t.put) {
this.put(t.stack, t.iw);
} else {
this.remove(t.stack, t.iw);
}
}
}
}
public boolean put(final IAEStack stack, final T iw) {
if (this.transactions != null) {
this.transactions.add(new SavedTransactions(true, stack, iw));
return true;
} else {
return this.container.put(stack, iw);
}
}
public boolean remove(final IAEStack stack, final T iw) {
if (this.transactions != null) {
this.transactions.add(new SavedTransactions(false, stack, iw));
return true;
} else {
return this.container.remove(stack, iw);
}
}
public boolean containsKey(final IAEStack stack) {
return this.container.containsKey(stack);
}
public Collection<T> get(final IAEStack stack) {
return this.container.get(stack);
}
private class SavedTransactions {
private final boolean put;
private final IAEStack stack;
private final T iw;
public SavedTransactions(final boolean putOperation, final IAEStack myStack, final T watcher) {
this.put = putOperation;
this.stack = myStack;
this.iw = watcher;
}
}
}
@@ -0,0 +1,32 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.helpers;
import appeng.api.networking.IGridBlock;
import appeng.api.networking.IGridHost;
import appeng.api.util.DimensionalCoord;
public interface IGridProxyable extends IGridHost {
IGridBlock getProxy(); // FIXME AENetworkProxy return type
DimensionalCoord getLocation();
void gridChanged();
}
@@ -0,0 +1,188 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 AlgorithmX2
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package appeng.me.helpers;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import com.google.common.collect.ImmutableList;
import appeng.api.config.AccessRestriction;
import appeng.api.config.Actionable;
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;
/**
* Common implementation of a simple class that monitors injection/extraction of
* a inventory to send events to a list of listeners.
*
* @param <T>
*
* TODO: Needs to be redesigned to solve performance issues.
*/
public class MEMonitorHandler<T extends IAEStack<T>> implements IMEMonitor<T> {
private final IMEInventoryHandler<T> internalHandler;
private final IItemList<T> cachedList;
private final HashMap<IMEMonitorHandlerReceiver<T>, Object> listeners = new HashMap<>();
protected boolean hasChanged = true;
public MEMonitorHandler(final IMEInventoryHandler<T> t) {
this.internalHandler = t;
this.cachedList = t.getChannel().createList();
}
public MEMonitorHandler(final IMEInventoryHandler<T> t, final IStorageChannel<T> chan) {
this.internalHandler = t;
this.cachedList = chan.createList();
}
@Override
public void addListener(final IMEMonitorHandlerReceiver<T> l, final Object verificationToken) {
this.listeners.put(l, verificationToken);
}
@Override
public void removeListener(final IMEMonitorHandlerReceiver<T> l) {
this.listeners.remove(l);
}
@Override
public T injectItems(final T input, final Actionable mode, final IActionSource src) {
if (mode == Actionable.SIMULATE) {
return this.getHandler().injectItems(input, mode, src);
}
return this.monitorDifference(input.copy(), this.getHandler().injectItems(input, mode, src), false, src);
}
protected IMEInventoryHandler<T> getHandler() {
return this.internalHandler;
}
private T monitorDifference(final 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;
}
protected void postChangesToListeners(final Iterable<T> changes, final IActionSource src) {
this.notifyListenersOfChange(changes, src);
}
protected void notifyListenersOfChange(final Iterable<T> diff, final IActionSource src) {
this.hasChanged = true;// need to update the cache.
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();
}
}
}
protected Iterator<Entry<IMEMonitorHandlerReceiver<T>, Object>> getListeners() {
return this.listeners.entrySet().iterator();
}
@Override
public T extractItems(final T request, final Actionable mode, final IActionSource src) {
if (mode == Actionable.SIMULATE) {
return this.getHandler().extractItems(request, mode, src);
}
return this.monitorDifference(request.copy(), this.getHandler().extractItems(request, mode, src), true, src);
}
@Override
public IStorageChannel<T> getChannel() {
return this.getHandler().getChannel();
}
@Override
public AccessRestriction getAccess() {
return this.getHandler().getAccess();
}
@Override
public IItemList<T> getStorageList() {
if (this.hasChanged) {
this.hasChanged = false;
this.cachedList.resetStatus();
return this.getAvailableItems(this.cachedList);
}
return this.cachedList;
}
@Override
public boolean isPrioritized(final T input) {
return this.getHandler().isPrioritized(input);
}
@Override
public boolean canAccept(final T input) {
return this.getHandler().canAccept(input);
}
@Override
public IItemList<T> getAvailableItems(final IItemList<T> out) {
return this.getHandler().getAvailableItems(out);
}
@Override
public int getPriority() {
return this.getHandler().getPriority();
}
@Override
public int getSlot() {
return this.getHandler().getSlot();
}
@Override
public boolean validForPass(final int i) {
return this.getHandler().validForPass(i);
}
}
@@ -0,0 +1,51 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.helpers;
import java.util.Optional;
import net.minecraft.entity.player.PlayerEntity;
import appeng.api.networking.security.IActionHost;
import appeng.api.networking.security.IActionSource;
public class MachineSource implements IActionSource {
private final IActionHost via;
public MachineSource(final IActionHost v) {
this.via = v;
}
@Override
public Optional<PlayerEntity> player() {
return Optional.empty();
}
@Override
public Optional<IActionHost> machine() {
return Optional.of(this.via);
}
@Override
public <T> Optional<T> context(Class<T> key) {
return Optional.empty();
}
}
@@ -0,0 +1,55 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.helpers;
import java.util.Optional;
import com.google.common.base.Preconditions;
import net.minecraft.entity.player.PlayerEntity;
import appeng.api.networking.security.IActionHost;
import appeng.api.networking.security.IActionSource;
public class PlayerSource implements IActionSource {
private final PlayerEntity player;
private final IActionHost via;
public PlayerSource(final PlayerEntity p, final IActionHost v) {
Preconditions.checkNotNull(p);
this.player = p;
this.via = v;
}
@Override
public Optional<PlayerEntity> player() {
return Optional.of(this.player);
}
@Override
public Optional<IActionHost> machine() {
return Optional.ofNullable(this.via);
}
@Override
public <T> Optional<T> context(Class<T> key) {
return Optional.empty();
}
}
@@ -0,0 +1,51 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.pathfinding;
import appeng.api.networking.IGridConnection;
import appeng.api.networking.IGridConnectionVisitor;
import appeng.api.networking.IGridNode;
import appeng.me.GridConnection;
import appeng.me.GridNode;
public class AdHocChannelUpdater implements IGridConnectionVisitor {
private final int usedChannels;
public AdHocChannelUpdater(final int used) {
this.usedChannels = used;
}
@Override
public boolean visitNode(final IGridNode n) {
final GridNode gn = (GridNode) n;
gn.setControllerRoute(null, true);
gn.incrementChannelCount(this.usedChannels);
gn.finalizeChannels();
return true;
}
@Override
public void visitConnection(final IGridConnection gcc) {
final GridConnection gc = (GridConnection) gcc;
gc.setControllerRoute(null, true);
gc.incrementChannelCount(this.usedChannels);
gc.finalizeChannels();
}
}
@@ -0,0 +1,41 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.pathfinding;
import appeng.api.networking.IGridConnection;
import appeng.api.networking.IGridConnectionVisitor;
import appeng.api.networking.IGridNode;
import appeng.me.GridConnection;
import appeng.me.GridNode;
public class ControllerChannelUpdater implements IGridConnectionVisitor {
@Override
public boolean visitNode(final IGridNode n) {
final GridNode gn = (GridNode) n;
gn.finalizeChannels();
return true;
}
@Override
public void visitConnection(final IGridConnection gcc) {
final GridConnection gc = (GridConnection) gcc;
gc.finalizeChannels();
}
}
@@ -0,0 +1,91 @@
/*
* 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.pathfinding;
import net.minecraft.util.math.BlockPos;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.networking.IGridVisitor;
import appeng.tile.networking.ControllerBlockEntity;
public class ControllerValidator implements IGridVisitor {
private boolean isValid = true;
private int found = 0;
private int minX;
private int minY;
private int minZ;
private int maxX;
private int maxY;
private int maxZ;
public ControllerValidator(final int x, final int y, final int z) {
this.minX = x;
this.maxX = x;
this.minY = y;
this.maxY = y;
this.minZ = z;
this.maxZ = z;
}
@Override
public boolean visitNode(final IGridNode n) {
final IGridHost host = n.getMachine();
if (this.isValid() && host instanceof ControllerBlockEntity) {
final ControllerBlockEntity c = (ControllerBlockEntity) host;
final BlockPos pos = c.getPos();
this.minX = Math.min(pos.getX(), this.minX);
this.maxX = Math.max(pos.getX(), this.maxX);
this.minY = Math.min(pos.getY(), this.minY);
this.maxY = Math.max(pos.getY(), this.maxY);
this.minZ = Math.min(pos.getZ(), this.minZ);
this.maxZ = Math.max(pos.getZ(), this.maxZ);
if (this.maxX - this.minX < 7 && this.maxY - this.minY < 7 && this.maxZ - this.minZ < 7) {
this.setFound(this.getFound() + 1);
return true;
}
this.setValid(false);
} else {
return false;
}
return this.isValid();
}
public boolean isValid() {
return this.isValid;
}
private void setValid(final boolean isValid) {
this.isValid = isValid;
}
public int getFound() {
return this.found;
}
private void setFound(final int found) {
this.found = found;
}
}
@@ -0,0 +1,58 @@
/*
* 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.pathfinding;
import java.util.EnumSet;
import appeng.api.networking.GridFlags;
import appeng.api.util.IReadOnlyCollection;
public interface IPathItem {
IPathItem getControllerRoute();
void setControllerRoute(IPathItem fast, boolean zeroOut);
/**
* used to determine if the finder can continue.
*/
boolean canSupportMoreChannels();
/**
* find possible choices for other pathing.
*/
IReadOnlyCollection<IPathItem> getPossibleOptions();
/**
* add one to the channel count, this is mostly for cables.
*/
void incrementChannelCount(int usedChannels);
/**
* get the grid flags for this IPathItem.
*
* @return the flag set.
*/
EnumSet<GridFlags> getFlags();
/**
* channels are done, wrap it up.
*/
void finalizeChannels();
}
@@ -0,0 +1,145 @@
/*
* 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.pathfinding;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import appeng.api.networking.GridFlags;
import appeng.api.networking.IGridMultiblock;
import appeng.api.networking.IGridNode;
import appeng.me.cache.PathGridCache;
public class PathSegment {
private final PathGridCache pgc;
private final Set<IPathItem> semiOpen;
private final Set<IPathItem> closed;
private boolean isDead;
private List<IPathItem> open;
public PathSegment(final PathGridCache myPGC, final List<IPathItem> open, final Set<IPathItem> semiOpen,
final Set<IPathItem> closed) {
this.open = open;
this.semiOpen = semiOpen;
this.closed = closed;
this.pgc = myPGC;
this.setDead(false);
}
public boolean step() {
final List<IPathItem> oldOpen = this.open;
this.open = new ArrayList<>();
for (final IPathItem i : oldOpen) {
for (final IPathItem pi : i.getPossibleOptions()) {
final EnumSet<GridFlags> flags = pi.getFlags();
if (!this.closed.contains(pi)) {
pi.setControllerRoute(i, true);
if (flags.contains(GridFlags.REQUIRE_CHANNEL)) {
// close the semi open.
if (!this.semiOpen.contains(pi)) {
final boolean worked;
if (flags.contains(GridFlags.COMPRESSED_CHANNEL)) {
worked = this.useDenseChannel(pi);
} else {
worked = this.useChannel(pi);
}
if (worked && flags.contains(GridFlags.MULTIBLOCK)) {
final Iterator<IGridNode> oni = ((IGridMultiblock) ((IGridNode) pi).getGridBlock())
.getMultiblockNodes();
while (oni.hasNext()) {
final IGridNode otherNodes = oni.next();
if (otherNodes != pi) {
this.semiOpen.add((IPathItem) otherNodes);
}
}
}
} else {
pi.incrementChannelCount(1); // give a channel.
this.semiOpen.remove(pi);
}
}
this.closed.add(pi);
this.open.add(pi);
}
}
}
return this.open.isEmpty();
}
private boolean useDenseChannel(final IPathItem start) {
IPathItem pi = start;
while (pi != null) {
if (!pi.canSupportMoreChannels() || pi.getFlags().contains(GridFlags.CANNOT_CARRY_COMPRESSED)) {
return false;
}
pi = pi.getControllerRoute();
}
pi = start;
while (pi != null) {
this.pgc.setChannelsByBlocks(this.pgc.getChannelsByBlocks() + 1);
pi.incrementChannelCount(1);
pi = pi.getControllerRoute();
}
this.pgc.setChannelsInUse(this.pgc.getChannelsInUse() + 1);
return true;
}
private boolean useChannel(final IPathItem start) {
IPathItem pi = start;
while (pi != null) {
if (!pi.canSupportMoreChannels()) {
return false;
}
pi = pi.getControllerRoute();
}
pi = start;
while (pi != null) {
this.pgc.setChannelsByBlocks(this.pgc.getChannelsByBlocks() + 1);
pi.incrementChannelCount(1);
pi = pi.getControllerRoute();
}
this.pgc.setChannelsInUse(this.pgc.getChannelsInUse() + 1);
return true;
}
public boolean isDead() {
return this.isDead;
}
public void setDead(final boolean isDead) {
this.isDead = isDead;
}
}
@@ -0,0 +1,306 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2018, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.storage;
import alexiil.mc.lib.attributes.item.FixedItemInv;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import appeng.api.config.FuzzyMode;
import appeng.api.implementations.items.IStorageCell;
import appeng.api.storage.cells.CellState;
import appeng.api.storage.cells.ICellInventory;
import appeng.api.storage.cells.ISaveProvider;
import appeng.api.storage.data.IAEStack;
import appeng.api.storage.data.IItemList;
/**
* @author DrummerMC
* @version rv6 - 2018-01-17
* @since rv6 2018-01-17
*/
public abstract class AbstractCellInventory<T extends IAEStack<T>> implements ICellInventory<T> {
private static final int MAX_ITEM_TYPES = 63;
private static final String ITEM_TYPE_TAG = "it";
private static final String ITEM_COUNT_TAG = "ic";
private static final String ITEM_SLOT = "#";
private static final String ITEM_SLOT_COUNT = "@";
protected static final String ITEM_PRE_FORMATTED_COUNT = "PF";
protected static final String ITEM_PRE_FORMATTED_SLOT = "PF#";
protected static final String ITEM_PRE_FORMATTED_NAME = "PN";
protected static final String ITEM_PRE_FORMATTED_FUZZY = "FP";
private static final String[] ITEM_SLOT_KEYS = new String[MAX_ITEM_TYPES];
private static final String[] ITEM_SLOT_COUNT_KEYS = new String[MAX_ITEM_TYPES];
private final CompoundTag tagCompound;
protected final ISaveProvider container;
private int maxItemTypes = MAX_ITEM_TYPES;
private short storedItems = 0;
private int storedItemCount = 0;
protected IItemList<T> cellItems;
private final ItemStack i;
protected final IStorageCell<T> cellType;
protected final int itemsPerByte;
private boolean isPersisted = true;
static {
for (int x = 0; x < MAX_ITEM_TYPES; x++) {
ITEM_SLOT_KEYS[x] = ITEM_SLOT + x;
ITEM_SLOT_COUNT_KEYS[x] = ITEM_SLOT_COUNT + x;
}
}
protected AbstractCellInventory(final IStorageCell<T> cellType, final ItemStack o, final ISaveProvider container) {
this.i = o;
this.cellType = cellType;
this.itemsPerByte = this.cellType.getChannel().getUnitsPerByte();
this.maxItemTypes = this.cellType.getTotalTypes(this.i);
if (this.maxItemTypes > MAX_ITEM_TYPES) {
this.maxItemTypes = MAX_ITEM_TYPES;
}
if (this.maxItemTypes < 1) {
this.maxItemTypes = 1;
}
this.container = container;
this.tagCompound = o.getOrCreateTag();
this.storedItems = this.tagCompound.getShort(ITEM_TYPE_TAG);
this.storedItemCount = this.tagCompound.getInt(ITEM_COUNT_TAG);
this.cellItems = null;
}
protected IItemList<T> getCellItems() {
if (this.cellItems == null) {
this.cellItems = this.getChannel().createList();
this.loadCellItems();
}
return this.cellItems;
}
@Override
public void persist() {
if (this.isPersisted) {
return;
}
int itemCount = 0;
// add new pretty stuff...
int x = 0;
for (final T v : this.cellItems) {
itemCount += v.getStackSize();
final CompoundTag g = new CompoundTag();
v.writeToNBT(g);
this.tagCompound.put(ITEM_SLOT_KEYS[x], g);
this.tagCompound.putInt(ITEM_SLOT_COUNT_KEYS[x], (int) v.getStackSize());
x++;
}
final short oldStoredItems = this.storedItems;
this.storedItems = (short) this.cellItems.size();
if (this.cellItems.isEmpty()) {
this.tagCompound.remove(ITEM_TYPE_TAG);
} else {
this.tagCompound.putShort(ITEM_TYPE_TAG, this.storedItems);
}
this.storedItemCount = itemCount;
if (itemCount == 0) {
this.tagCompound.remove(ITEM_COUNT_TAG);
} else {
this.tagCompound.putInt(ITEM_COUNT_TAG, itemCount);
}
// clean any old crusty stuff...
for (; x < oldStoredItems && x < this.maxItemTypes; x++) {
this.tagCompound.remove(ITEM_SLOT_KEYS[x]);
this.tagCompound.remove(ITEM_SLOT_COUNT_KEYS[x]);
}
this.isPersisted = true;
}
protected void saveChanges() {
// recalculate values
this.storedItems = (short) this.cellItems.size();
this.storedItemCount = 0;
for (final T v : this.cellItems) {
this.storedItemCount += v.getStackSize();
}
this.isPersisted = false;
if (this.container != null) {
this.container.saveChanges(this);
} else {
// if there is no ISaveProvider, store to NBT immediately
this.persist();
}
}
private void loadCellItems() {
if (this.cellItems == null) {
this.cellItems = this.getChannel().createList();
}
this.cellItems.resetStatus(); // clears totals and stuff.
final int types = (int) this.getStoredItemTypes();
boolean needsUpdate = false;
for (int slot = 0; slot < types; slot++) {
CompoundTag compoundTag = this.tagCompound.getCompound(ITEM_SLOT_KEYS[slot]);
int stackSize = this.tagCompound.getInt(ITEM_SLOT_COUNT_KEYS[slot]);
needsUpdate |= !this.loadCellItem(compoundTag, stackSize);
}
if (needsUpdate) {
this.saveChanges();
}
}
/**
* Load a single item.
*
* @param compoundTag
* @param stackSize
* @return true when successfully loaded
*/
protected abstract boolean loadCellItem(CompoundTag compoundTag, int stackSize);
@Override
public IItemList<T> getAvailableItems(final IItemList<T> out) {
for (final T item : this.getCellItems()) {
out.add(item);
}
return out;
}
@Override
public ItemStack getItemStack() {
return this.i;
}
@Override
public double getIdleDrain() {
return this.cellType.getIdleDrain();
}
@Override
public FuzzyMode getFuzzyMode() {
return this.cellType.getFuzzyMode(this.i);
}
@Override
public FixedItemInv getConfigInventory() {
return this.cellType.getConfigInventory(this.i);
}
@Override
public FixedItemInv getUpgradesInventory() {
return this.cellType.getUpgradesInventory(this.i);
}
@Override
public int getBytesPerType() {
return this.cellType.getBytesPerType(this.i);
}
@Override
public boolean canHoldNewItem() {
final long bytesFree = this.getFreeBytes();
return (bytesFree > this.getBytesPerType()
|| (bytesFree == this.getBytesPerType() && this.getUnusedItemCount() > 0))
&& this.getRemainingItemTypes() > 0;
}
@Override
public long getTotalBytes() {
return this.cellType.getBytes(this.i);
}
@Override
public long getFreeBytes() {
return this.getTotalBytes() - this.getUsedBytes();
}
@Override
public long getTotalItemTypes() {
return this.maxItemTypes;
}
@Override
public long getStoredItemCount() {
return this.storedItemCount;
}
@Override
public long getStoredItemTypes() {
return this.storedItems;
}
@Override
public long getRemainingItemTypes() {
final long basedOnStorage = this.getFreeBytes() / this.getBytesPerType();
final long baseOnTotal = this.getTotalItemTypes() - this.getStoredItemTypes();
return basedOnStorage > baseOnTotal ? baseOnTotal : basedOnStorage;
}
@Override
public long getUsedBytes() {
final long bytesForItemCount = (this.getStoredItemCount() + this.getUnusedItemCount()) / this.itemsPerByte;
return this.getStoredItemTypes() * this.getBytesPerType() + bytesForItemCount;
}
@Override
public long getRemainingItemCount() {
final long remaining = this.getFreeBytes() * this.itemsPerByte + this.getUnusedItemCount();
return remaining > 0 ? remaining : 0;
}
@Override
public int getUnusedItemCount() {
final int div = (int) (this.getStoredItemCount() % 8);
if (div == 0) {
return 0;
}
return this.itemsPerByte - div;
}
@Override
public CellState getStatusForCell() {
if (this.getStoredItemTypes() == 0) {
return CellState.EMPTY;
}
if (this.canHoldNewItem()) {
return CellState.NOT_EMPTY;
}
if (this.getRemainingItemCount() > 0) {
return CellState.TYPES_FULL;
}
return CellState.FULL;
}
}
@@ -0,0 +1,238 @@
package appeng.me.storage;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import appeng.api.config.Actionable;
import appeng.api.exceptions.AppEngException;
import appeng.api.implementations.items.IStorageCell;
import appeng.api.networking.security.IActionSource;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.cells.ICellInventory;
import appeng.api.storage.cells.ISaveProvider;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IAEStack;
import appeng.core.AEConfig;
import appeng.core.AELog;
import appeng.util.item.AEStack;
public class BasicCellInventory<T extends IAEStack<T>> extends AbstractCellInventory<T> {
private final IStorageChannel<T> channel;
private BasicCellInventory(final IStorageCell<T> cellType, final ItemStack o, final ISaveProvider container) {
super(cellType, o, container);
this.channel = cellType.getChannel();
}
public static <T extends IAEStack<T>> ICellInventory<T> createInventory(final ItemStack o,
final ISaveProvider container) {
try {
if (o == null) {
throw new AppEngException("ItemStack was used as a cell, but was not a cell!");
}
final Item type = o.getItem();
final IStorageCell<T> cellType;
if (type instanceof IStorageCell) {
cellType = (IStorageCell<T>) type;
} else {
throw new AppEngException("ItemStack was used as a cell, but was not a cell!");
}
if (!cellType.isStorageCell(o)) {
throw new AppEngException("ItemStack was used as a cell, but was not a cell!");
}
return new BasicCellInventory<T>(cellType, o, container);
} catch (final AppEngException e) {
AELog.error(e);
return null;
}
}
public static <T extends AEStack<T>> boolean isCellOfType(final ItemStack input, IStorageChannel<?> channel) {
final IStorageCell<?> type = getStorageCell(input);
return type != null && type.getChannel() == channel;
}
public static boolean isCell(final ItemStack input) {
return getStorageCell(input) != null;
}
private boolean isStorageCell(final T input) {
if (input instanceof IAEItemStack) {
final IAEItemStack stack = (IAEItemStack) input;
final IStorageCell<?> type = getStorageCell(stack.getDefinition());
return type != null && !type.storableInStorageCell();
}
return false;
}
private static IStorageCell<?> getStorageCell(final ItemStack input) {
if (input != null) {
final Item type = input.getItem();
if (type instanceof IStorageCell) {
return (IStorageCell<?>) type;
}
}
return null;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static boolean isCellEmpty(ICellInventory inv) {
if (inv != null) {
return inv.getAvailableItems(inv.getChannel().createList()).isEmpty();
}
return true;
}
@Override
public T injectItems(T input, Actionable mode, IActionSource src) {
if (input == null) {
return null;
}
if (input.getStackSize() == 0) {
return null;
}
if (this.cellType.isBlackListed(this.getItemStack(), input)) {
return input;
}
// This is slightly hacky as it expects a read-only access, but fine for now.
// TODO: Guarantee a read-only access. E.g. provide an isEmpty() method and
// ensure CellInventory does not write
// any NBT data for empty cells instead of relying on an empty IItemContainer
if (this.isStorageCell(input)) {
final ICellInventory<?> meInventory = createInventory(((IAEItemStack) input).createItemStack(), null);
if (!isCellEmpty(meInventory)) {
return input;
}
}
final T l = this.getCellItems().findPrecise(input);
if (l != null) {
final long remainingItemCount = this.getRemainingItemCount();
if (remainingItemCount <= 0) {
return input;
}
if (input.getStackSize() > remainingItemCount) {
final T r = input.copy();
r.setStackSize(r.getStackSize() - remainingItemCount);
if (mode == Actionable.MODULATE) {
l.setStackSize(l.getStackSize() + remainingItemCount);
this.saveChanges();
}
return r;
} else {
if (mode == Actionable.MODULATE) {
l.setStackSize(l.getStackSize() + input.getStackSize());
this.saveChanges();
}
return null;
}
}
if (this.canHoldNewItem()) // room for new type, and for at least one item!
{
final int remainingItemCount = (int) this.getRemainingItemCount()
- this.getBytesPerType() * this.itemsPerByte;
if (remainingItemCount > 0) {
if (input.getStackSize() > remainingItemCount) {
final T toReturn = input.copy();
toReturn.setStackSize(input.getStackSize() - remainingItemCount);
if (mode == Actionable.MODULATE) {
final T toWrite = input.copy();
toWrite.setStackSize(remainingItemCount);
this.cellItems.add(toWrite);
this.saveChanges();
}
return toReturn;
}
if (mode == Actionable.MODULATE) {
this.cellItems.add(input);
this.saveChanges();
}
return null;
}
}
return input;
}
@Override
public T extractItems(T request, Actionable mode, IActionSource src) {
if (request == null) {
return null;
}
final long size = Math.min(Integer.MAX_VALUE, request.getStackSize());
T Results = null;
final T l = this.getCellItems().findPrecise(request);
if (l != null) {
Results = l.copy();
if (l.getStackSize() <= size) {
Results.setStackSize(l.getStackSize());
if (mode == Actionable.MODULATE) {
l.setStackSize(0);
this.saveChanges();
}
} else {
Results.setStackSize(size);
if (mode == Actionable.MODULATE) {
l.setStackSize(l.getStackSize() - size);
this.saveChanges();
}
}
}
return Results;
}
@Override
public IStorageChannel<T> getChannel() {
return this.channel;
}
@Override
protected boolean loadCellItem(CompoundTag compoundTag, int stackSize) {
// Now load the item stack
final T t;
try {
t = this.getChannel().createFromNBT(compoundTag);
if (t == null) {
AELog.warn("Removing item " + compoundTag
+ " from storage cell because the associated item type couldn't be found.");
return false;
}
} catch (Throwable ex) {
if (AEConfig.instance().isRemoveCrashingItemsOnLoad()) {
AELog.warn(ex,
"Removing item " + compoundTag + " from storage cell because loading the ItemStack crashed.");
return false;
}
throw ex;
}
t.setStackSize(stackSize);
if (stackSize > 0) {
this.cellItems.add(t);
}
return true;
}
}
@@ -0,0 +1,128 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2018, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.storage;
import alexiil.mc.lib.attributes.item.FixedItemInv;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import appeng.api.config.FuzzyMode;
import appeng.api.config.IncludeExclude;
import appeng.api.config.Upgrades;
import appeng.api.implementations.items.IUpgradeModule;
import appeng.api.storage.IMEInventory;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.cells.ICellInventory;
import appeng.api.storage.cells.ICellInventoryHandler;
import appeng.api.storage.data.IAEStack;
import appeng.api.storage.data.IItemList;
import appeng.util.prioritylist.FuzzyPriorityList;
import appeng.util.prioritylist.PrecisePriorityList;
/**
* @author DrummerMC
* @version rv6 - 2018-01-23
* @since rv6 2018-01-23
*/
public class BasicCellInventoryHandler<T extends IAEStack<T>> extends MEInventoryHandler<T>
implements ICellInventoryHandler<T> {
public BasicCellInventoryHandler(final IMEInventory c, final IStorageChannel<T> channel) {
super(c, channel);
final ICellInventory ci = this.getCellInv();
if (ci != null) {
final IItemList<T> priorityList = channel.createList();
final FixedItemInv upgrades = ci.getUpgradesInventory();
final FixedItemInv config = ci.getConfigInventory();
final FuzzyMode fzMode = ci.getFuzzyMode();
boolean hasInverter = false;
boolean hasFuzzy = false;
for (int x = 0; x < upgrades.getSlotCount(); x++) {
final ItemStack is = upgrades.getInvStack(x);
if (!is.isEmpty() && is.getItem() instanceof IUpgradeModule) {
final Upgrades u = ((IUpgradeModule) is.getItem()).getType(is);
if (u != null) {
switch (u) {
case FUZZY:
hasFuzzy = true;
break;
case INVERTER:
hasInverter = true;
break;
default:
}
}
}
}
for (int x = 0; x < config.getSlotCount(); x++) {
final ItemStack is = config.getInvStack(x);
if (!is.isEmpty()) {
final T configItem = channel.createStack(is);
if (configItem != null) {
priorityList.add(configItem);
}
}
}
this.setWhitelist(hasInverter ? IncludeExclude.BLACKLIST : IncludeExclude.WHITELIST);
if (!priorityList.isEmpty()) {
if (hasFuzzy) {
this.setPartitionList(new FuzzyPriorityList<>(priorityList, fzMode));
} else {
this.setPartitionList(new PrecisePriorityList<>(priorityList));
}
}
}
}
@Override
public ICellInventory getCellInv() {
Object o = this.getInternal();
if (o instanceof MEPassThrough) {
o = ((MEPassThrough) o).getInternal();
}
return (ICellInventory) (o instanceof ICellInventory ? o : null);
}
@Override
public boolean isPreformatted() {
return !this.getPartitionList().isEmpty();
}
@Override
public boolean isFuzzy() {
return this.getPartitionList() instanceof FuzzyPriorityList;
}
@Override
public IncludeExclude getIncludeExcludeMode() {
return this.getWhitelist();
}
CompoundTag openNbtData() {
return this.getCellInv().getItemStack().getOrCreateTag();
}
}
@@ -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.storage;
import net.minecraft.item.ItemStack;
import appeng.api.AEApi;
import appeng.api.config.AccessRestriction;
import appeng.api.config.Actionable;
import appeng.api.networking.security.IActionSource;
import appeng.api.storage.IMEInventoryHandler;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.cells.ICellInventoryHandler;
import appeng.api.storage.channels.IItemStorageChannel;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IItemList;
import appeng.items.contents.CellConfig;
import appeng.util.item.AEItemStack;
public class CreativeCellInventory implements IMEInventoryHandler<IAEItemStack> {
private final IItemList<IAEItemStack> itemListCache = AEApi.instance().storage()
.getStorageChannel(IItemStorageChannel.class).createList();
protected CreativeCellInventory(final ItemStack o) {
final CellConfig cc = new CellConfig(o);
for (final ItemStack is : cc) {
if (!is.isEmpty()) {
final IAEItemStack i = AEItemStack.fromItemStack(is);
i.setStackSize(Integer.MAX_VALUE);
this.itemListCache.add(i);
}
}
}
public static ICellInventoryHandler getCell(final ItemStack o) {
return new BasicCellInventoryHandler(new CreativeCellInventory(o),
AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class));
}
@Override
public IAEItemStack injectItems(final IAEItemStack input, final Actionable mode, final IActionSource src) {
final IAEItemStack local = this.itemListCache.findPrecise(input);
if (local == null) {
return input;
}
return null;
}
@Override
public IAEItemStack extractItems(final IAEItemStack request, final Actionable mode, final IActionSource src) {
final IAEItemStack local = this.itemListCache.findPrecise(request);
if (local == null) {
return null;
}
return request.copy();
}
@Override
public IItemList<IAEItemStack> getAvailableItems(final IItemList out) {
for (final IAEItemStack ais : this.itemListCache) {
out.add(ais);
}
return out;
}
@Override
public IStorageChannel getChannel() {
return AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class);
}
@Override
public AccessRestriction getAccess() {
return AccessRestriction.READ_WRITE;
}
@Override
public boolean isPrioritized(final IAEItemStack input) {
return this.itemListCache.findPrecise(input) != null;
}
@Override
public boolean canAccept(final IAEItemStack input) {
return this.itemListCache.findPrecise(input) != null;
}
@Override
public int getPriority() {
return 0;
}
@Override
public int getSlot() {
return 0;
}
@Override
public boolean validForPass(final int i) {
return true;
}
}
@@ -0,0 +1,83 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.storage;
import net.minecraft.item.ItemStack;
import appeng.api.config.Actionable;
import appeng.api.implementations.tiles.IChestOrDrive;
import appeng.api.networking.security.IActionSource;
import appeng.api.storage.cells.CellState;
import appeng.api.storage.cells.ICellHandler;
import appeng.api.storage.cells.ICellInventoryHandler;
import appeng.api.storage.data.IAEStack;
public class DriveWatcher<T extends IAEStack<T>> extends MEInventoryHandler<T> {
private CellState oldStatus = CellState.EMPTY;
private final ItemStack is;
private final ICellHandler handler;
private final IChestOrDrive cord;
public DriveWatcher(final ICellInventoryHandler<T> i, final ItemStack is, final ICellHandler han,
final IChestOrDrive cod) {
super(i, i.getChannel());
this.is = is;
this.handler = han;
this.cord = cod;
}
public CellState getStatus() {
return this.handler.getStatusForCell(this.is, (ICellInventoryHandler) this.getInternal());
}
@Override
public T injectItems(final T input, final Actionable type, final IActionSource src) {
final long size = input.getStackSize();
final T a = super.injectItems(input, type, src);
if (type == Actionable.MODULATE && (a == null || a.getStackSize() != size)) {
final CellState newStatus = this.getStatus();
if (newStatus != this.oldStatus) {
this.cord.blinkCell(this.getSlot());
this.oldStatus = newStatus;
}
}
return a;
}
@Override
public T extractItems(final T request, final Actionable type, final IActionSource src) {
final T a = super.extractItems(request, type, src);
if (type == Actionable.MODULATE && a != null) {
final CellState newStatus = this.getStatus();
if (newStatus != this.oldStatus) {
this.cord.blinkCell(this.getSlot());
this.oldStatus = newStatus;
}
}
return a;
}
}
@@ -0,0 +1,30 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.storage;
import appeng.api.networking.security.IActionSource;
import appeng.api.networking.ticking.TickRateModulation;
public interface ITickingMonitor {
TickRateModulation onTick();
void setActionSource(IActionSource actionSource);
}
@@ -0,0 +1,72 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.storage;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import appeng.api.networking.storage.IStackWatcher;
import appeng.api.networking.storage.IStackWatcherHost;
import appeng.api.storage.data.IAEStack;
import appeng.me.cache.GridStorageCache;
/**
* Maintain my interests, and a global watch list, they should always be fully
* synchronized.
*/
public class ItemWatcher implements IStackWatcher {
private final GridStorageCache gsc;
private final IStackWatcherHost myObject;
private final Set<IAEStack> myInterests = new HashSet<>();
public ItemWatcher(final GridStorageCache cache, final IStackWatcherHost host) {
this.gsc = cache;
this.myObject = host;
}
public IStackWatcherHost getHost() {
return this.myObject;
}
@Override
public boolean add(final IAEStack e) {
if (this.myInterests.contains(e)) {
return false;
}
return this.myInterests.add(e.copy()) && this.gsc.getInterestManager().put(e, this);
}
@Override
public boolean remove(final IAEStack o) {
return this.myInterests.remove(o) && this.gsc.getInterestManager().remove(o, this);
}
@Override
public void reset() {
final Iterator<IAEStack> i = this.myInterests.iterator();
while (i.hasNext()) {
this.gsc.getInterestManager().remove(i.next(), this);
i.remove();
}
}
}
@@ -0,0 +1,167 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.storage;
import appeng.api.config.AccessRestriction;
import appeng.api.config.Actionable;
import appeng.api.config.IncludeExclude;
import appeng.api.networking.security.IActionSource;
import appeng.api.storage.IMEInventory;
import appeng.api.storage.IMEInventoryHandler;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.data.IAEStack;
import appeng.api.storage.data.IItemList;
import appeng.util.prioritylist.DefaultPriorityList;
import appeng.util.prioritylist.IPartitionList;
public class MEInventoryHandler<T extends IAEStack<T>> implements IMEInventoryHandler<T> {
private final IMEInventoryHandler<T> internal;
private int myPriority;
private IncludeExclude myWhitelist;
private AccessRestriction myAccess;
private IPartitionList<T> myPartitionList;
private AccessRestriction cachedAccessRestriction;
private boolean hasReadAccess;
private boolean hasWriteAccess;
public MEInventoryHandler(final IMEInventory<T> i, final IStorageChannel<T> channel) {
if (i instanceof IMEInventoryHandler) {
this.internal = (IMEInventoryHandler<T>) i;
} else {
this.internal = new MEPassThrough<>(i, channel);
}
this.myPriority = 0;
this.myWhitelist = IncludeExclude.WHITELIST;
this.setBaseAccess(AccessRestriction.READ_WRITE);
this.myPartitionList = new DefaultPriorityList<>();
}
IncludeExclude getWhitelist() {
return this.myWhitelist;
}
public void setWhitelist(final IncludeExclude myWhitelist) {
this.myWhitelist = myWhitelist;
}
public AccessRestriction getBaseAccess() {
return this.myAccess;
}
public void setBaseAccess(final AccessRestriction myAccess) {
this.myAccess = myAccess;
this.cachedAccessRestriction = this.myAccess.restrictPermissions(this.internal.getAccess());
this.hasReadAccess = this.cachedAccessRestriction.hasPermission(AccessRestriction.READ);
this.hasWriteAccess = this.cachedAccessRestriction.hasPermission(AccessRestriction.WRITE);
}
IPartitionList<T> getPartitionList() {
return this.myPartitionList;
}
public void setPartitionList(final IPartitionList<T> myPartitionList) {
this.myPartitionList = myPartitionList;
}
@Override
public T injectItems(final T input, final Actionable type, final IActionSource src) {
if (!this.canAccept(input)) {
return input;
}
return this.internal.injectItems(input, type, src);
}
@Override
public T extractItems(final T request, final Actionable type, final IActionSource src) {
if (!this.hasReadAccess) {
return null;
}
return this.internal.extractItems(request, type, src);
}
@Override
public IItemList<T> getAvailableItems(final IItemList<T> out) {
if (!this.hasReadAccess) {
return out;
}
return this.internal.getAvailableItems(out);
}
@Override
public IStorageChannel<T> getChannel() {
return this.internal.getChannel();
}
@Override
public AccessRestriction getAccess() {
return this.cachedAccessRestriction;
}
@Override
public boolean isPrioritized(final T input) {
if (this.myWhitelist == IncludeExclude.WHITELIST) {
return this.myPartitionList.isListed(input) || this.internal.isPrioritized(input);
}
return false;
}
@Override
public boolean canAccept(final T input) {
if (!this.hasWriteAccess) {
return false;
}
if (this.myWhitelist == IncludeExclude.BLACKLIST && this.myPartitionList.isListed(input)) {
return false;
}
if (this.myPartitionList.isEmpty() || this.myWhitelist == IncludeExclude.BLACKLIST) {
return this.internal.canAccept(input);
}
return this.myPartitionList.isListed(input) && this.internal.canAccept(input);
}
@Override
public int getPriority() {
return this.myPriority;
}
public void setPriority(final int myPriority) {
this.myPriority = myPriority;
}
@Override
public int getSlot() {
return this.internal.getSlot();
}
@Override
public boolean validForPass(final int i) {
return true;
}
public IMEInventory<T> getInternal() {
return this.internal;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2018, AlgorithmX2, All rights reserved.
* 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
@@ -26,9 +26,7 @@ import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import alexiil.mc.lib.attributes.fluid.volume.FluidVolume;
import net.minecraftforge.fluids.FluidAttributes;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraft.item.ItemStack;
import appeng.api.AEApi;
import appeng.api.config.AccessRestriction;
@@ -39,106 +37,112 @@ import appeng.api.networking.ticking.TickRateModulation;
import appeng.api.storage.IMEMonitor;
import appeng.api.storage.IMEMonitorHandlerReceiver;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.channels.IFluidStorageChannel;
import appeng.api.storage.data.IAEFluidStack;
import appeng.api.storage.channels.IItemStorageChannel;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IItemList;
import appeng.util.InventoryAdaptor;
import appeng.util.Platform;
import appeng.util.inv.ItemSlot;
public class MEMonitorIFluidHandler implements IMEMonitor<IAEFluidStack>, ITickingMonitor {
private final IFluidHandler handler;
private final IItemList<IAEFluidStack> list = AEApi.instance().storage()
.getStorageChannel(IFluidStorageChannel.class).createList();
private final HashMap<IMEMonitorHandlerReceiver<IAEFluidStack>, Object> listeners = new HashMap<>();
private final NavigableMap<Integer, CachedFluidStack> memory;
public class MEMonitorIInventory implements IMEMonitor<IAEItemStack>, ITickingMonitor {
private final InventoryAdaptor adaptor;
private final IItemList<IAEItemStack> list = AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class)
.createList();
private final HashMap<IMEMonitorHandlerReceiver<IAEItemStack>, Object> listeners = new HashMap<>();
private final NavigableMap<Integer, CachedItemStack> memory;
private IActionSource mySource;
private StorageFilter mode = StorageFilter.EXTRACTABLE_ONLY;
public MEMonitorIFluidHandler(final IFluidHandler handler) {
this.handler = handler;
public MEMonitorIInventory(final InventoryAdaptor adaptor) {
this.adaptor = adaptor;
this.memory = new ConcurrentSkipListMap<>();
}
@Override
public void addListener(final IMEMonitorHandlerReceiver<IAEFluidStack> l, final Object verificationToken) {
public void addListener(final IMEMonitorHandlerReceiver<IAEItemStack> l, final Object verificationToken) {
this.listeners.put(l, verificationToken);
}
@Override
public void removeListener(final IMEMonitorHandlerReceiver<IAEFluidStack> l) {
public void removeListener(final IMEMonitorHandlerReceiver<IAEItemStack> l) {
this.listeners.remove(l);
}
@Override
public IAEFluidStack injectItems(final IAEFluidStack input, final Actionable type, final IActionSource src) {
final int filled = this.handler.fill(input.getFluidStack(), type.getFluidAction());
public IAEItemStack injectItems(final IAEItemStack input, final Actionable type, final IActionSource src) {
ItemStack out = ItemStack.EMPTY;
if (filled == 0) {
return input.copy();
if (type == Actionable.SIMULATE) {
out = this.adaptor.simulateAdd(input.createItemStack());
} else {
out = this.adaptor.addItems(input.createItemStack());
}
if (type == Actionable.MODULATE) {
this.onTick();
}
if (filled == input.getStackSize()) {
if (out.isEmpty()) {
return null;
}
final IAEFluidStack o = input.copy();
o.setStackSize(input.getStackSize() - filled);
// better then doing construction from scratch :3
final IAEItemStack o = input.copy();
o.setStackSize(out.getCount());
return o;
}
@Override
public IAEFluidStack extractItems(final IAEFluidStack request, final Actionable type, final IActionSource src) {
final FluidVolume removed = this.handler.drain(request.getFluidStack(), type.getFluidAction());
public IAEItemStack extractItems(final IAEItemStack request, final Actionable type, final IActionSource src) {
ItemStack out = ItemStack.EMPTY;
if (removed.isEmpty() || removed.getAmount() == 0) {
if (type == Actionable.SIMULATE) {
out = this.adaptor.simulateRemove((int) request.getStackSize(), request.getDefinition(), null);
} else {
out = this.adaptor.removeItems((int) request.getStackSize(), request.getDefinition(), null);
}
if (out.isEmpty()) {
return null;
}
// better then doing construction from scratch :3
final IAEItemStack o = request.copy();
o.setStackSize(out.getCount());
if (type == Actionable.MODULATE) {
this.onTick();
}
final IAEFluidStack o = request.copy();
o.setStackSize(removed.getAmount());
return o;
}
@Override
public IStorageChannel getChannel() {
return AEApi.instance().storage().getStorageChannel(IFluidStorageChannel.class);
return AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class);
}
@Override
public TickRateModulation onTick() {
final List<IAEFluidStack> changes = new ArrayList<>();
final List<IAEItemStack> changes = new ArrayList<>();
this.list.resetStatus();
int high = 0;
boolean changed = false;
for (final ItemSlot is : this.adaptor) {
final CachedItemStack old = this.memory.get(is.getSlot());
high = Math.max(high, is.getSlot());
int tankCount = this.handler.getTanks();
for (int tank = 0; tank < tankCount; ++tank) {
final CachedFluidStack old = this.memory.get(tank);
high = Math.max(high, tank);
final ItemStack newIS = !is.isExtractable() && this.getMode() == StorageFilter.EXTRACTABLE_ONLY
? ItemStack.EMPTY
: is.getItemStack();
final ItemStack oldIS = old == null ? ItemStack.EMPTY : old.itemStack;
FluidVolume newIS = this.handler.getFluidInTank(tank);
if (!newIS.isEmpty() && this.getMode() == StorageFilter.EXTRACTABLE_ONLY) {
// We have to actually check if we could extract _anything_
if (this.handler.drain(1, IFluidHandler.FluidAction.SIMULATE).isEmpty()) {
// Just to safeguard against tanks that prevent non-bucket-size extractions
if (this.handler.drain(FluidAttributes.BUCKET_VOLUME, IFluidHandler.FluidAction.SIMULATE)
.isEmpty()) {
newIS = FluidVolumeUtil.EMPTY;
}
}
}
final FluidVolume oldIS = old == null ? FluidVolumeUtil.EMPTY : old.fluidStack;
if (isDifferent(newIS, oldIS)) {
final CachedFluidStack cis = new CachedFluidStack(newIS);
this.memory.put(tank, cis);
if (this.isDifferent(newIS, oldIS)) {
final CachedItemStack cis = new CachedItemStack(is.getItemStack());
this.memory.put(is.getSlot(), cis);
if (old != null && old.aeStack != null) {
old.aeStack.setStackSize(-old.aeStack.getStackSize());
@@ -152,25 +156,22 @@ public class MEMonitorIFluidHandler implements IMEMonitor<IAEFluidStack>, ITicki
changed = true;
} else {
final int newSize = newIS.isEmpty() ? 0 : newIS.getAmount();
final int diff = newSize - (oldIS.isEmpty() ? 0 : oldIS.getAmount());
final int newSize = (newIS.isEmpty() ? 0 : newIS.getCount());
final int diff = newSize - (oldIS.isEmpty() ? 0 : oldIS.getCount());
IAEFluidStack stack = null;
if (!newIS.isEmpty()) {
stack = (old == null || old.aeStack == null ? AEApi.instance().storage()
.getStorageChannel(IFluidStorageChannel.class).createStack(newIS) : old.aeStack.copy());
}
final IAEItemStack stack = (old == null || old.aeStack == null
? AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class).createStack(newIS)
: old.aeStack.copy());
if (stack != null) {
stack.setStackSize(newSize);
this.list.add(stack);
}
if (diff != 0 && stack != null) {
final CachedFluidStack cis = new CachedFluidStack(newIS);
this.memory.put(tank, cis);
final CachedItemStack cis = new CachedItemStack(is.getItemStack());
this.memory.put(is.getSlot(), cis);
final IAEFluidStack a = stack.copy();
final IAEItemStack a = stack.copy();
a.setStackSize(diff);
changes.add(a);
changed = true;
@@ -179,11 +180,11 @@ public class MEMonitorIFluidHandler implements IMEMonitor<IAEFluidStack>, ITicki
}
// detect dropped items; should fix non IISided Inventory Changes.
final NavigableMap<Integer, CachedFluidStack> end = this.memory.tailMap(high, false);
final NavigableMap<Integer, CachedItemStack> end = this.memory.tailMap(high, false);
if (!end.isEmpty()) {
for (final CachedFluidStack cis : end.values()) {
for (final CachedItemStack cis : end.values()) {
if (cis != null && cis.aeStack != null) {
final IAEFluidStack a = cis.aeStack.copy();
final IAEItemStack a = cis.aeStack.copy();
a.setStackSize(-a.getStackSize());
changes.add(a);
changed = true;
@@ -199,23 +200,27 @@ public class MEMonitorIFluidHandler implements IMEMonitor<IAEFluidStack>, ITicki
return changed ? TickRateModulation.URGENT : TickRateModulation.SLOWER;
}
private static boolean isDifferent(FluidVolume a, FluidVolume b) {
if (a == b) {
private boolean isDifferent(final ItemStack a, final ItemStack b) {
if (a == b && b.isEmpty()) {
return false;
}
if (a.isEmpty() || b.isEmpty()) {
if ((a.isEmpty() && !b.isEmpty()) || (!a.isEmpty() && b.isEmpty())) {
return true;
}
return !a.getFluidKey().equals(b.getFluidKey());
return !Platform.itemComparisons().isSameItem(a, b);
}
private void postDifference(final Iterable<IAEFluidStack> a) {
private void postDifference(final Iterable<IAEItemStack> a) {
// AELog.info( a.getItemStack().getTranslationKey() + " @ " + a.getStackSize()
// );
if (a != null) {
final Iterator<Entry<IMEMonitorHandlerReceiver<IAEFluidStack>, Object>> i = this.listeners.entrySet()
final Iterator<Entry<IMEMonitorHandlerReceiver<IAEItemStack>, Object>> i = this.listeners.entrySet()
.iterator();
while (i.hasNext()) {
final Entry<IMEMonitorHandlerReceiver<IAEFluidStack>, Object> l = i.next();
final IMEMonitorHandlerReceiver<IAEFluidStack> key = l.getKey();
final Entry<IMEMonitorHandlerReceiver<IAEItemStack>, Object> l = i.next();
final IMEMonitorHandlerReceiver<IAEItemStack> key = l.getKey();
if (key.isValid(l.getValue())) {
key.postChange(this, a, this.getActionSource());
} else {
@@ -231,12 +236,12 @@ public class MEMonitorIFluidHandler implements IMEMonitor<IAEFluidStack>, ITicki
}
@Override
public boolean isPrioritized(final IAEFluidStack input) {
public boolean isPrioritized(final IAEItemStack input) {
return false;
}
@Override
public boolean canAccept(final IAEFluidStack input) {
public boolean canAccept(final IAEItemStack input) {
return true;
}
@@ -256,8 +261,8 @@ public class MEMonitorIFluidHandler implements IMEMonitor<IAEFluidStack>, ITicki
}
@Override
public IItemList<IAEFluidStack> getAvailableItems(final IItemList out) {
for (final CachedFluidStack is : this.memory.values()) {
public IItemList<IAEItemStack> getAvailableItems(final IItemList out) {
for (final CachedItemStack is : this.memory.values()) {
out.addStorage(is.aeStack);
}
@@ -265,7 +270,7 @@ public class MEMonitorIFluidHandler implements IMEMonitor<IAEFluidStack>, ITicki
}
@Override
public IItemList<IAEFluidStack> getStorageList() {
public IItemList<IAEItemStack> getStorageList() {
return this.list;
}
@@ -286,18 +291,18 @@ public class MEMonitorIFluidHandler implements IMEMonitor<IAEFluidStack>, ITicki
this.mySource = mySource;
}
private static class CachedFluidStack {
private static class CachedItemStack {
private final FluidVolume fluidStack;
private final IAEFluidStack aeStack;
private final ItemStack itemStack;
private final IAEItemStack aeStack;
CachedFluidStack(final FluidVolume is) {
public CachedItemStack(final ItemStack is) {
if (is.isEmpty()) {
this.fluidStack = FluidVolumeUtil.EMPTY;
this.itemStack = ItemStack.EMPTY;
this.aeStack = null;
} else {
this.fluidStack = is.copy();
this.aeStack = AEApi.instance().storage().getStorageChannel(IFluidStorageChannel.class).createStack(is);
this.itemStack = is.copy();
this.aeStack = AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class).createStack(is);
}
}
}
@@ -0,0 +1,143 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.storage;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import appeng.api.networking.security.IActionSource;
import appeng.api.networking.storage.IBaseMonitor;
import appeng.api.storage.IMEInventory;
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.util.Platform;
import appeng.util.inv.ItemListIgnoreCrafting;
public class MEMonitorPassThrough<T extends IAEStack<T>> extends MEPassThrough<T>
implements IMEMonitor<T>, IMEMonitorHandlerReceiver<T> {
private final HashMap<IMEMonitorHandlerReceiver<T>, Object> listeners = new HashMap<>();
private IActionSource changeSource;
private IMEMonitor<T> monitor;
public MEMonitorPassThrough(final IMEInventory<T> i, final IStorageChannel channel) {
super(i, channel);
if (i instanceof IMEMonitor) {
this.monitor = (IMEMonitor<T>) i;
}
}
@Override
public void setInternal(final IMEInventory<T> i) {
if (this.monitor != null) {
this.monitor.removeListener(this);
}
this.monitor = null;
final IItemList<T> before = this.getInternal() == null ? this.getWrappedChannel().createList()
: this.getInternal()
.getAvailableItems(new ItemListIgnoreCrafting(this.getWrappedChannel().createList()));
super.setInternal(i);
if (i instanceof IMEMonitor) {
this.monitor = (IMEMonitor<T>) i;
}
final IItemList<T> after = this.getInternal() == null ? this.getWrappedChannel().createList()
: this.getInternal()
.getAvailableItems(new ItemListIgnoreCrafting(this.getWrappedChannel().createList()));
if (this.monitor != null) {
this.monitor.addListener(this, this.monitor);
}
Platform.postListChanges(before, after, this, this.getChangeSource());
}
@Override
public IItemList<T> getAvailableItems(final IItemList out) {
super.getAvailableItems(new ItemListIgnoreCrafting(out));
return out;
}
@Override
public void addListener(final IMEMonitorHandlerReceiver<T> l, final Object verificationToken) {
this.listeners.put(l, verificationToken);
}
@Override
public void removeListener(final IMEMonitorHandlerReceiver<T> l) {
this.listeners.remove(l);
}
@Override
public IItemList<T> getStorageList() {
if (this.monitor == null) {
final IItemList<T> out = this.getWrappedChannel().createList();
this.getInternal().getAvailableItems(new ItemListIgnoreCrafting(out));
return out;
}
return this.monitor.getStorageList();
}
@Override
public boolean isValid(final Object verificationToken) {
return verificationToken == this.monitor;
}
@Override
public void postChange(final IBaseMonitor<T> monitor, final Iterable<T> change, final IActionSource source) {
final Iterator<Entry<IMEMonitorHandlerReceiver<T>, Object>> i = this.listeners.entrySet().iterator();
while (i.hasNext()) {
final Entry<IMEMonitorHandlerReceiver<T>, Object> e = i.next();
final IMEMonitorHandlerReceiver<T> receiver = e.getKey();
if (receiver.isValid(e.getValue())) {
receiver.postChange(this, change, source);
} else {
i.remove();
}
}
}
@Override
public void onListUpdate() {
final Iterator<Entry<IMEMonitorHandlerReceiver<T>, Object>> i = this.listeners.entrySet().iterator();
while (i.hasNext()) {
final Entry<IMEMonitorHandlerReceiver<T>, Object> e = i.next();
final IMEMonitorHandlerReceiver<T> receiver = e.getKey();
if (receiver.isValid(e.getValue())) {
receiver.onListUpdate();
} else {
i.remove();
}
}
}
private IActionSource getChangeSource() {
return this.changeSource;
}
public void setChangeSource(final IActionSource changeSource) {
this.changeSource = changeSource;
}
}
@@ -0,0 +1,101 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.storage;
import appeng.api.config.AccessRestriction;
import appeng.api.config.Actionable;
import appeng.api.networking.security.IActionSource;
import appeng.api.storage.IMEInventory;
import appeng.api.storage.IMEInventoryHandler;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.data.IAEStack;
import appeng.api.storage.data.IItemList;
public class MEPassThrough<T extends IAEStack<T>> implements IMEInventoryHandler<T> {
private final IStorageChannel wrappedChannel;
private IMEInventory<T> internal;
public MEPassThrough(final IMEInventory<T> i, final IStorageChannel channel) {
this.wrappedChannel = channel;
this.setInternal(i);
}
protected IMEInventory<T> getInternal() {
return this.internal;
}
public void setInternal(final IMEInventory<T> i) {
this.internal = i;
}
@Override
public T injectItems(final T input, final Actionable type, final IActionSource src) {
return this.internal.injectItems(input, type, src);
}
@Override
public T extractItems(final T request, final Actionable type, final IActionSource src) {
return this.internal.extractItems(request, type, src);
}
@Override
public IItemList<T> getAvailableItems(final IItemList out) {
return this.internal.getAvailableItems(out);
}
@Override
public IStorageChannel getChannel() {
return this.internal.getChannel();
}
@Override
public AccessRestriction getAccess() {
return AccessRestriction.READ_WRITE;
}
@Override
public boolean isPrioritized(final T input) {
return false;
}
@Override
public boolean canAccept(final T input) {
return true;
}
@Override
public int getPriority() {
return 0;
}
@Override
public int getSlot() {
return 0;
}
@Override
public boolean validForPass(final int i) {
return true;
}
IStorageChannel getWrappedChannel() {
return this.wrappedChannel;
}
}
@@ -0,0 +1,278 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.storage;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.NavigableMap;
import java.util.TreeMap;
import appeng.api.config.AccessRestriction;
import appeng.api.config.Actionable;
import appeng.api.config.SecurityPermissions;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridNode;
import appeng.api.networking.security.IActionSource;
import appeng.api.networking.security.ISecurityGrid;
import appeng.api.storage.IMEInventoryHandler;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.data.IAEStack;
import appeng.api.storage.data.IItemList;
import appeng.me.cache.SecurityCache;
public class NetworkInventoryHandler<T extends IAEStack<T>> implements IMEInventoryHandler<T> {
private static final ThreadLocal<Deque> DEPTH_MOD = new ThreadLocal<>();
private static final ThreadLocal<Deque> DEPTH_SIM = new ThreadLocal<>();
private static final Comparator<Integer> PRIORITY_SORTER = (o1, o2) -> Integer.compare(o2, o1);
private static int currentPass = 0;
private final IStorageChannel<T> myChannel;
private final SecurityCache security;
private final NavigableMap<Integer, List<IMEInventoryHandler<T>>> priorityInventory;
private int myPass = 0;
public NetworkInventoryHandler(final IStorageChannel<T> chan, final SecurityCache security) {
this.myChannel = chan;
this.security = security;
this.priorityInventory = new TreeMap<>(PRIORITY_SORTER);
}
public void addNewStorage(final IMEInventoryHandler<T> h) {
final int priority = h.getPriority();
List<IMEInventoryHandler<T>> list = this.priorityInventory.get(priority);
if (list == null) {
this.priorityInventory.put(priority, list = new ArrayList<>());
}
list.add(h);
}
@Override
public T injectItems(T input, final Actionable type, final IActionSource src) {
if (this.diveList(this, type)) {
return input;
}
if (this.testPermission(src, SecurityPermissions.INJECT)) {
this.surface(this, type);
return input;
}
for (final List<IMEInventoryHandler<T>> invList : this.priorityInventory.values()) {
Iterator<IMEInventoryHandler<T>> ii = invList.iterator();
while (ii.hasNext() && input != null) {
final IMEInventoryHandler<T> inv = ii.next();
if (inv.validForPass(1) && inv.canAccept(input)
&& (inv.isPrioritized(input) || inv.extractItems(input, Actionable.SIMULATE, src) != null)) {
input = inv.injectItems(input, type, src);
}
}
// We need to ignore prioritized inventories in the second pass. If they were
// not able to store everything
// during the first pass, they will do so in the second, but as this is
// stateless we will just report twice
// the amount of storable items.
// ignores craftingcache on the second pass.
ii = invList.iterator();
while (ii.hasNext() && input != null) {
final IMEInventoryHandler<T> inv = ii.next();
if (inv.validForPass(2) && inv.canAccept(input) && !inv.isPrioritized(input)) {
input = inv.injectItems(input, type, src);
}
}
}
this.surface(this, type);
return input;
}
private boolean diveList(final NetworkInventoryHandler<T> networkInventoryHandler, final Actionable type) {
final Deque cDepth = this.getDepth(type);
if (cDepth.contains(networkInventoryHandler)) {
return true;
}
cDepth.push(this);
return false;
}
private boolean testPermission(final IActionSource src, final SecurityPermissions permission) {
if (src.player().isPresent()) {
if (!this.security.hasPermission(src.player().get(), permission)) {
return true;
}
} else if (src.machine().isPresent()) {
if (this.security.isAvailable()) {
final IGridNode n = src.machine().get().getActionableNode();
if (n == null) {
return true;
}
final IGrid gn = n.getGrid();
if (gn != this.security.getGrid()) {
final ISecurityGrid sg = gn.getCache(ISecurityGrid.class);
final int playerID = sg.getOwner();
if (!this.security.hasPermission(playerID, permission)) {
return true;
}
}
}
}
return false;
}
private void surface(final NetworkInventoryHandler<T> networkInventoryHandler, final Actionable type) {
if (this.getDepth(type).pop() != this) {
throw new IllegalStateException("Invalid Access to Networked Storage API detected.");
}
}
private Deque getDepth(final Actionable type) {
final ThreadLocal<Deque> depth = type == Actionable.MODULATE ? DEPTH_MOD : DEPTH_SIM;
Deque s = depth.get();
if (s == null) {
depth.set(s = new ArrayDeque<>());
}
return s;
}
@Override
public T extractItems(T request, final Actionable mode, final IActionSource src) {
if (this.diveList(this, mode)) {
return null;
}
if (this.testPermission(src, SecurityPermissions.EXTRACT)) {
this.surface(this, mode);
return null;
}
final Iterator<List<IMEInventoryHandler<T>>> i = this.priorityInventory.descendingMap().values().iterator();// priorityInventory.asMap().descendingMap().entrySet().iterator();
final T output = request.copy();
request = request.copy();
output.setStackSize(0);
final long req = request.getStackSize();
while (i.hasNext()) {
final List<IMEInventoryHandler<T>> invList = i.next();
final Iterator<IMEInventoryHandler<T>> ii = invList.iterator();
while (ii.hasNext() && output.getStackSize() < req) {
final IMEInventoryHandler<T> inv = ii.next();
request.setStackSize(req - output.getStackSize());
output.add(inv.extractItems(request, mode, src));
}
}
this.surface(this, mode);
if (output.getStackSize() <= 0) {
return null;
}
return output;
}
@Override
public IItemList<T> getAvailableItems(IItemList<T> out) {
if (this.diveIteration(this, Actionable.SIMULATE)) {
return out;
}
// for (Entry<Integer, IMEInventoryHandler<T>> h : priorityInventory.entries())
for (final List<IMEInventoryHandler<T>> i : this.priorityInventory.values()) {
for (final IMEInventoryHandler<T> j : i) {
out = j.getAvailableItems(out);
}
}
this.surface(this, Actionable.SIMULATE);
return out;
}
private boolean diveIteration(final NetworkInventoryHandler<T> networkInventoryHandler, final Actionable type) {
final Deque cDepth = this.getDepth(type);
if (cDepth.isEmpty()) {
currentPass++;
this.myPass = currentPass;
} else {
if (currentPass == this.myPass) {
return true;
} else {
this.myPass = currentPass;
}
}
cDepth.push(this);
return false;
}
@Override
public IStorageChannel<T> getChannel() {
return this.myChannel;
}
@Override
public AccessRestriction getAccess() {
return AccessRestriction.READ_WRITE;
}
@Override
public boolean isPrioritized(final T input) {
return false;
}
@Override
public boolean canAccept(final T input) {
return true;
}
@Override
public int getPriority() {
return 0;
}
@Override
public int getSlot() {
return 0;
}
@Override
public boolean validForPass(final int i) {
return true;
}
}
@@ -0,0 +1,82 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.me.storage;
import appeng.api.AEApi;
import appeng.api.config.AccessRestriction;
import appeng.api.config.Actionable;
import appeng.api.networking.security.IActionSource;
import appeng.api.storage.IMEInventoryHandler;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.channels.IItemStorageChannel;
import appeng.api.storage.data.IAEStack;
import appeng.api.storage.data.IItemList;
public class NullInventory<T extends IAEStack<T>> implements IMEInventoryHandler<T> {
@Override
public T injectItems(final T input, final Actionable mode, final IActionSource src) {
return input;
}
@Override
public T extractItems(final T request, final Actionable mode, final IActionSource src) {
return null;
}
@Override
public IItemList<T> getAvailableItems(final IItemList out) {
return out;
}
@Override
public IStorageChannel getChannel() {
return AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class);
}
@Override
public AccessRestriction getAccess() {
return AccessRestriction.READ;
}
@Override
public boolean isPrioritized(final T input) {
return false;
}
@Override
public boolean canAccept(final T input) {
return false;
}
@Override
public int getPriority() {
return 0;
}
@Override
public int getSlot() {
return 0;
}
@Override
public boolean validForPass(final int i) {
return i == 2;
}
}
@@ -1,167 +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.storage;
import com.mojang.authlib.GameProfile;
import appeng.api.AEApi;
import appeng.api.config.AccessRestriction;
import appeng.api.config.Actionable;
import appeng.api.config.SecurityPermissions;
import appeng.api.implementations.items.IBiometricCard;
import appeng.api.networking.security.IActionSource;
import appeng.api.storage.IMEInventoryHandler;
import appeng.api.storage.IStorageChannel;
import appeng.api.storage.channels.IItemStorageChannel;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.storage.data.IItemList;
import appeng.me.GridAccessException;
import appeng.tile.misc.SecurityStationBlockEntity;
public class SecurityStationInventory implements IMEInventoryHandler<IAEItemStack> {
private final IItemList<IAEItemStack> storedItems = AEApi.instance().storage()
.getStorageChannel(IItemStorageChannel.class).createList();
private final SecurityStationBlockEntity securityTile;
public SecurityStationInventory(final SecurityStationBlockEntity ts) {
this.securityTile = ts;
}
@Override
public IAEItemStack injectItems(final IAEItemStack input, final Actionable type, final IActionSource src) {
if (this.hasPermission(src)) {
if (AEApi.instance().definitions().items().biometricCard().isSameAs(input.createItemStack())) {
if (this.canAccept(input)) {
if (type == Actionable.SIMULATE) {
return null;
}
this.getStoredItems().add(input);
this.securityTile.inventoryChanged();
return null;
}
}
}
return input;
}
private boolean hasPermission(final IActionSource src) {
if (src.player().isPresent()) {
try {
return this.securityTile.getProxy().getSecurity().hasPermission(src.player().get(),
SecurityPermissions.SECURITY);
} catch (final GridAccessException e) {
// :P
}
}
return false;
}
@Override
public IAEItemStack extractItems(final IAEItemStack request, final Actionable mode, final IActionSource src) {
if (this.hasPermission(src)) {
final IAEItemStack target = this.getStoredItems().findPrecise(request);
if (target != null) {
final IAEItemStack output = target.copy();
if (mode == Actionable.SIMULATE) {
return output;
}
target.setStackSize(0);
this.securityTile.inventoryChanged();
return output;
}
}
return null;
}
@Override
public IItemList<IAEItemStack> getAvailableItems(final IItemList out) {
for (final IAEItemStack ais : this.getStoredItems()) {
out.add(ais);
}
return out;
}
@Override
public IStorageChannel getChannel() {
return AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class);
}
@Override
public AccessRestriction getAccess() {
return AccessRestriction.READ_WRITE;
}
@Override
public boolean isPrioritized(final IAEItemStack input) {
return false;
}
@Override
public boolean canAccept(final IAEItemStack input) {
if (input.getItem() instanceof IBiometricCard) {
final IBiometricCard tbc = (IBiometricCard) input.getItem();
final GameProfile newUser = tbc.getProfile(input.createItemStack());
final int PlayerID = AEApi.instance().registries().players().getID(newUser);
if (this.securityTile.getOwner() == PlayerID) {
return false;
}
for (final IAEItemStack ais : this.getStoredItems()) {
if (ais.isMeaningful()) {
final GameProfile thisUser = tbc.getProfile(ais.createItemStack());
if (thisUser == newUser) {
return false;
}
if (thisUser != null && thisUser.equals(newUser)) {
return false;
}
}
}
return true;
}
return false;
}
@Override
public int getPriority() {
return 0;
}
@Override
public int getSlot() {
return 0;
}
@Override
public boolean validForPass(final int i) {
return true;
}
public IItemList<IAEItemStack> getStoredItems() {
return this.storedItems;
}
}