mirror of
https://github.com/GTNewHorizons/Applied-Energistics-2-Unofficial.git
synced 2026-11-13 17:04:17 +00:00
fix stale inventory access in storage buses
This commit is contained in:
11 files changed
+256
-91
No files matched your search
@@ -708,9 +708,10 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn
|
||||
|
||||
final boolean couldDoWork = this.updateStorage();
|
||||
final boolean hasWorkToDo = this.hasWorkToDo();
|
||||
return (hasWorkToDo || (sentItems && this.hasItemsToSend()))
|
||||
? (couldDoWork || sentItems ? TickRateModulation.URGENT : TickRateModulation.SLOWER)
|
||||
: TickRateModulation.SLEEP;
|
||||
if (hasWorkToDo || this.hasItemsToSend()) {
|
||||
return couldDoWork || sentItems ? TickRateModulation.URGENT : TickRateModulation.SLOWER;
|
||||
}
|
||||
return TickRateModulation.SLEEP;
|
||||
}
|
||||
|
||||
// Returns if it successfully sent some items
|
||||
@@ -724,8 +725,8 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn
|
||||
|
||||
boolean sentSomething = false;
|
||||
for (final ForgeDirection s : possibleDirections) {
|
||||
final TileEntity te = w
|
||||
.getTileEntity(tile.xCoord + s.offsetX, tile.yCoord + s.offsetY, tile.zCoord + s.offsetZ);
|
||||
final TileEntity te = Platform
|
||||
.getLoadedTileEntity(w, tile.xCoord + s.offsetX, tile.yCoord + s.offsetY, tile.zCoord + s.offsetZ);
|
||||
|
||||
if (te == null) continue;
|
||||
|
||||
@@ -1254,8 +1255,8 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn
|
||||
final ArrayList<VerifiedAcceptors> verifiedSides = new ArrayList<>();
|
||||
|
||||
for (final ForgeDirection s : possibleDirections) {
|
||||
final TileEntity te = w
|
||||
.getTileEntity(tile.xCoord + s.offsetX, tile.yCoord + s.offsetY, tile.zCoord + s.offsetZ);
|
||||
final TileEntity te = Platform
|
||||
.getLoadedTileEntity(w, tile.xCoord + s.offsetX, tile.yCoord + s.offsetY, tile.zCoord + s.offsetZ);
|
||||
|
||||
if (te == null) continue;
|
||||
|
||||
@@ -1403,8 +1404,11 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn
|
||||
boolean allAreBusy = true;
|
||||
|
||||
for (final ForgeDirection s : possibleDirections) {
|
||||
final TileEntity te = w
|
||||
.getTileEntity(tile.xCoord + s.offsetX, tile.yCoord + s.offsetY, tile.zCoord + s.offsetZ);
|
||||
final TileEntity te = Platform.getLoadedTileEntity(
|
||||
w,
|
||||
tile.xCoord + s.offsetX,
|
||||
tile.yCoord + s.offsetY,
|
||||
tile.zCoord + s.offsetZ);
|
||||
if (te != null && te.getClass().getName().equals("li.cil.oc.common.tileentity.Adapter")) continue;
|
||||
final InventoryAdaptor ad = InventoryAdaptor.getAdaptor(te, s.getOpposite());
|
||||
if (ad != null) {
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
package appeng.me.storage;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
|
||||
import appeng.api.config.AccessRestriction;
|
||||
import appeng.api.config.Actionable;
|
||||
import appeng.api.networking.security.BaseActionSource;
|
||||
import appeng.api.storage.IMEInventory;
|
||||
import appeng.api.storage.IMENetworkInventory;
|
||||
import appeng.api.storage.data.IAEStack;
|
||||
import appeng.api.storage.data.IAEStackType;
|
||||
import appeng.api.storage.data.IItemList;
|
||||
import appeng.util.Platform;
|
||||
import appeng.util.item.ItemFilterList;
|
||||
import appeng.util.item.NetworkItemList;
|
||||
import appeng.util.item.PrioritizedNetworkItemList;
|
||||
@@ -17,11 +26,49 @@ import appeng.util.item.PrioritizedNetworkItemList;
|
||||
public class StorageBusInventoryHandler<T extends IAEStack<T>> extends MEInventoryHandler<T> {
|
||||
|
||||
private final Supplier<AccessRestriction> reshuffleAccess;
|
||||
private final TileEntity target;
|
||||
private final Chunk targetChunk;
|
||||
private final TileEntity[] otherTargets;
|
||||
private final Chunk[] otherChunks;
|
||||
|
||||
public StorageBusInventoryHandler(IMEInventory<T> i, IAEStackType<T> type,
|
||||
Supplier<AccessRestriction> reshuffleAccess) {
|
||||
Supplier<AccessRestriction> reshuffleAccess, TileEntity target) {
|
||||
super(i, type);
|
||||
this.reshuffleAccess = reshuffleAccess;
|
||||
List<TileEntity> targets = Platform.getInventoryTiles(target);
|
||||
this.target = targets.isEmpty() ? null : targets.get(0);
|
||||
this.targetChunk = this.target == null ? null
|
||||
: this.target.getWorldObj().getChunkFromBlockCoords(this.target.xCoord, this.target.zCoord);
|
||||
this.otherTargets = new TileEntity[Math.max(0, targets.size() - 1)];
|
||||
this.otherChunks = new Chunk[this.otherTargets.length];
|
||||
for (int index = 1; index < targets.size(); index++) {
|
||||
TileEntity tile = targets.get(index);
|
||||
this.otherTargets[index - 1] = tile;
|
||||
this.otherChunks[index - 1] = tile.getWorldObj().getChunkFromBlockCoords(tile.xCoord, tile.zCoord);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
if (this.target == null || this.target.isInvalid() || !this.targetChunk.isChunkLoaded) return false;
|
||||
for (int index = 0; index < this.otherTargets.length; index++) {
|
||||
if (this.otherTargets[index].isInvalid() || !this.otherChunks[index].isChunkLoaded) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAccept(T input) {
|
||||
return this.isValid() && super.canAccept(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T extractItems(T request, Actionable type, BaseActionSource src) {
|
||||
return this.isValid() ? super.extractItems(request, type, src) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getAvailableItem(@Nonnull T request, int iteration) {
|
||||
return this.isValid() ? super.getAvailableItem(request, iteration) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -37,7 +84,7 @@ public class StorageBusInventoryHandler<T extends IAEStack<T>> extends MEInvento
|
||||
|
||||
@Override
|
||||
public IItemList<T> getAvailableItems(final IItemList<T> out, int iteration, Optional<Predicate<T>> filter) {
|
||||
if (!this.hasReadAccess && !isVisible()) {
|
||||
if (!this.isValid() || (!this.hasReadAccess && !isVisible())) {
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -73,6 +120,7 @@ public class StorageBusInventoryHandler<T extends IAEStack<T>> extends MEInvento
|
||||
|
||||
@Override
|
||||
public PrioritizedNetworkItemList<T> getAvailableItemsWithPriority(int iteration) {
|
||||
if (!this.isValid()) return new PrioritizedNetworkItemList<>(this.getExternalNetworkInventory());
|
||||
final Predicate<T> predicate = this.isExtractFilterActive() && !this.getExtractPartitionList().isEmpty()
|
||||
? this.getExtractFilterCondition()
|
||||
: e -> true;
|
||||
|
||||
@@ -10,11 +10,14 @@
|
||||
|
||||
package appeng.parts.automation;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.tileentity.TileEntityChest;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraft.world.World;
|
||||
@@ -51,6 +54,8 @@ public abstract class PartSharedItemBus<StackType extends IAEStack<StackType>> e
|
||||
private final IAEStackInventory config = new IAEStackInventory(this, 9);
|
||||
private int cachedAdaptorHash = 0;
|
||||
private InventoryAdaptor cachedAdaptor;
|
||||
private List<TileEntity> cachedTargets = Collections.emptyList();
|
||||
private boolean targetChunkUnloaded;
|
||||
private boolean lastRedstone = false;
|
||||
private boolean pendingStateUpdate = false;
|
||||
protected String oreFilterString = "";
|
||||
@@ -117,17 +122,25 @@ public abstract class PartSharedItemBus<StackType extends IAEStack<StackType>> e
|
||||
self.xCoord + this.getSide().offsetX,
|
||||
self.yCoord + this.getSide().offsetY,
|
||||
self.zCoord + this.getSide().offsetZ);
|
||||
this.targetChunkUnloaded = !self.getWorldObj().blockExists(
|
||||
self.xCoord + this.getSide().offsetX,
|
||||
self.yCoord + this.getSide().offsetY,
|
||||
self.zCoord + this.getSide().offsetZ)
|
||||
|| (target instanceof TileEntityChest && !Platform.isInventoryTileLoaded(target));
|
||||
|
||||
final int newAdaptorHash = Platform.generateTileHash(target);
|
||||
|
||||
// Some neighbors, such as GT tanks, expose inventories only after placement initialization.
|
||||
if (this.cachedAdaptorHash == newAdaptorHash && newAdaptorHash != 0 && this.cachedAdaptor != null) {
|
||||
if (this.cachedAdaptorHash == newAdaptorHash && newAdaptorHash != 0
|
||||
&& this.cachedAdaptor != null
|
||||
&& Platform.areInventoryTilesLoaded(this.cachedTargets)) {
|
||||
return this.cachedAdaptor;
|
||||
}
|
||||
|
||||
// noinspection MagicConstant
|
||||
this.cachedAdaptor = InventoryAdaptor.getAdaptor(target, this.getSide().getOpposite(), getAdaptorFlags());
|
||||
this.cachedAdaptorHash = this.cachedAdaptor == null ? 0 : newAdaptorHash;
|
||||
this.cachedTargets = Platform.getInventoryTiles(target);
|
||||
|
||||
return this.cachedAdaptor;
|
||||
}
|
||||
@@ -143,11 +156,9 @@ public abstract class PartSharedItemBus<StackType extends IAEStack<StackType>> e
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the bus can actually do something.
|
||||
* <p>
|
||||
* Currently this tests if the chunk for the target is actually loaded.
|
||||
* Checks whether the target and any other half of a vanilla chest can be accessed without loading chunks.
|
||||
*
|
||||
* @return true, if the the bus should do its work.
|
||||
* @return true if the target's chunks are loaded.
|
||||
*/
|
||||
protected boolean canDoBusWork() {
|
||||
final TileEntity self = this.getHost().getTile();
|
||||
@@ -155,7 +166,10 @@ public abstract class PartSharedItemBus<StackType extends IAEStack<StackType>> e
|
||||
final int xCoordinate = self.xCoord + this.getSide().offsetX;
|
||||
final int zCoordinate = self.zCoord + this.getSide().offsetZ;
|
||||
|
||||
return world != null && world.getChunkProvider().chunkExists(xCoordinate >> 4, zCoordinate >> 4);
|
||||
if (world == null || !world.getChunkProvider().chunkExists(xCoordinate >> 4, zCoordinate >> 4)) return false;
|
||||
TileEntity target = Platform
|
||||
.getLoadedTileEntity(world, xCoordinate, self.yCoord + this.getSide().offsetY, zCoordinate);
|
||||
return !(target instanceof TileEntityChest) || Platform.isInventoryTileLoaded(target);
|
||||
}
|
||||
|
||||
private void updateState() {
|
||||
@@ -190,12 +204,7 @@ public abstract class PartSharedItemBus<StackType extends IAEStack<StackType>> e
|
||||
|
||||
protected TileEntity getTileEntity(final TileEntity self, final int x, final int y, final int z) {
|
||||
final World w = self.getWorldObj();
|
||||
|
||||
if (w.getChunkProvider().chunkExists(x >> 4, z >> 4)) {
|
||||
return w.getTileEntity(x, y, z);
|
||||
}
|
||||
|
||||
return null;
|
||||
return Platform.getLoadedTileEntity(w, x, y, z);
|
||||
}
|
||||
|
||||
protected abstract TickRateModulation doBusWork();
|
||||
@@ -213,7 +222,7 @@ public abstract class PartSharedItemBus<StackType extends IAEStack<StackType>> e
|
||||
|
||||
@Override
|
||||
protected boolean isSleeping() {
|
||||
return this.getTarget() == null || super.isSleeping();
|
||||
return (this.getTarget() == null && !this.targetChunkUnloaded) || super.isSleeping();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -28,6 +28,7 @@ import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.tileentity.TileEntityChest;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.Constants.NBT;
|
||||
@@ -122,7 +123,8 @@ public class PartStorageBus extends PartUpgradeable implements IStorageBus {
|
||||
private int priority = 0;
|
||||
private boolean cached = false;
|
||||
private IStorageBusMonitor<?> monitor = null;
|
||||
private MEInventoryHandler handler = null;
|
||||
private StorageBusInventoryHandler handler = null;
|
||||
private boolean targetChunkUnloaded = false;
|
||||
private int handlerHash = 0;
|
||||
private boolean wasActive = false;
|
||||
private byte resetCacheLogic = 0;
|
||||
@@ -526,12 +528,15 @@ public class PartStorageBus extends PartUpgradeable implements IStorageBus {
|
||||
return new TickingRequest(
|
||||
TickRates.StorageBus.getMin(),
|
||||
TickRates.StorageBus.getMax(),
|
||||
this.monitor == null,
|
||||
this.monitor == null && !this.targetChunkUnloaded,
|
||||
true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TickRateModulation tickingRequest(final IGridNode node, final int ticksSinceLastCall) {
|
||||
if (this.targetChunkUnloaded || (this.handler != null && !this.handler.isValid())) {
|
||||
this.resetCacheLogic = 2;
|
||||
}
|
||||
if (this.resetCacheLogic != 0) {
|
||||
this.resetCache();
|
||||
}
|
||||
@@ -540,7 +545,7 @@ public class PartStorageBus extends PartUpgradeable implements IStorageBus {
|
||||
return this.monitor.onTick();
|
||||
}
|
||||
|
||||
return TickRateModulation.SLEEP;
|
||||
return this.targetChunkUnloaded ? TickRateModulation.SLOWER : TickRateModulation.SLEEP;
|
||||
}
|
||||
|
||||
private void resetCache() {
|
||||
@@ -605,11 +610,7 @@ public class PartStorageBus extends PartUpgradeable implements IStorageBus {
|
||||
|
||||
// Looks for interfaces for subnet
|
||||
private IMEInventory getConnectedInventory() {
|
||||
final TileEntity self = this.getHost().getTile();
|
||||
final TileEntity target = self.getWorldObj().getTileEntity(
|
||||
self.xCoord + this.getSide().offsetX,
|
||||
self.yCoord + this.getSide().offsetY,
|
||||
self.zCoord + this.getSide().offsetZ);
|
||||
final TileEntity target = this.getTarget();
|
||||
|
||||
if (target != null) {
|
||||
final IExternalStorageHandler esh = AEApi.instance().registries().externalStorage()
|
||||
@@ -621,23 +622,38 @@ public class PartStorageBus extends PartUpgradeable implements IStorageBus {
|
||||
return null;
|
||||
}
|
||||
|
||||
private TileEntity getTarget() {
|
||||
final TileEntity self = this.getHost().getTile();
|
||||
final World world = self.getWorldObj();
|
||||
final ForgeDirection side = this.getSide();
|
||||
final int x = self.xCoord + side.offsetX;
|
||||
final int y = self.yCoord + side.offsetY;
|
||||
final int z = self.zCoord + side.offsetZ;
|
||||
this.targetChunkUnloaded = !world.blockExists(x, y, z);
|
||||
if (this.targetChunkUnloaded) return null;
|
||||
final TileEntity target = Platform.getLoadedTileEntity(world, x, y, z);
|
||||
if (target instanceof TileEntityChest && !Platform.isInventoryTileLoaded(target)) {
|
||||
this.targetChunkUnloaded = true;
|
||||
return null;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
public MEInventoryHandler getInternalHandler() {
|
||||
if (this.cached) {
|
||||
if (this.cached && !this.targetChunkUnloaded && (this.handler == null || this.handler.isValid())) {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
final boolean wasSleeping = this.monitor == null;
|
||||
|
||||
this.cached = true;
|
||||
final TileEntity self = this.getHost().getTile();
|
||||
|
||||
final ForgeDirection side = this.getSide();
|
||||
final TileEntity target = self.getWorldObj()
|
||||
.getTileEntity(self.xCoord + side.offsetX, self.yCoord + side.offsetY, self.zCoord + side.offsetZ);
|
||||
final TileEntity target = this.getTarget();
|
||||
|
||||
final int newHandlerHash = Platform.generateTileHash(target);
|
||||
|
||||
if (this.handlerHash == newHandlerHash && this.handlerHash != 0 && this.handler != null) {
|
||||
if (this.handlerHash == newHandlerHash && this.handlerHash != 0
|
||||
&& this.handler != null
|
||||
&& this.handler.isValid()) {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
@@ -665,7 +681,11 @@ public class PartStorageBus extends PartUpgradeable implements IStorageBus {
|
||||
if (inv != null) {
|
||||
this.checkInterfaceVsStorageBus(target, this.getSide().getOpposite());
|
||||
|
||||
this.handler = new StorageBusInventoryHandler<>(inv, this.getStackType(), this::getReshuffleAccess);
|
||||
this.handler = new StorageBusInventoryHandler<>(
|
||||
inv,
|
||||
this.getStackType(),
|
||||
this::getReshuffleAccess,
|
||||
target);
|
||||
|
||||
AccessRestriction currentAccess = (AccessRestriction) this.getConfigManager()
|
||||
.getSetting(Settings.ACCESS);
|
||||
@@ -740,7 +760,7 @@ public class PartStorageBus extends PartUpgradeable implements IStorageBus {
|
||||
if (wasSleeping != (this.monitor == null)) {
|
||||
try {
|
||||
final ITickManager tm = this.getProxy().getTick();
|
||||
if (this.monitor == null) {
|
||||
if (this.monitor == null && !this.targetChunkUnloaded) {
|
||||
tm.sleepDevice(this.getProxy().getNode());
|
||||
} else {
|
||||
tm.wakeDevice(this.getProxy().getNode());
|
||||
|
||||
@@ -18,6 +18,7 @@ import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import appeng.core.AEConfig;
|
||||
import appeng.me.GridAccessException;
|
||||
import appeng.util.Platform;
|
||||
import cofh.api.energy.IEnergyReceiver;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
@@ -127,19 +128,18 @@ public class PartP2PGT5Power extends PartP2PTunnelNormal<PartP2PGT5Power> implem
|
||||
|
||||
@Nullable
|
||||
private TileEntity getTarget() {
|
||||
TileEntity te;
|
||||
if (this.isCachedTargetValid) {
|
||||
te = this.cachedTarget;
|
||||
if (te == null || !te.isInvalid()) {
|
||||
return te;
|
||||
}
|
||||
}
|
||||
|
||||
this.isCachedTargetValid = true;
|
||||
te = this.getTile();
|
||||
TileEntity te = this.getTile();
|
||||
ForgeDirection side = this.getSide();
|
||||
return this.cachedTarget = te.getWorldObj()
|
||||
.getTileEntity(te.xCoord + side.offsetX, te.yCoord + side.offsetY, te.zCoord + side.offsetZ);
|
||||
TileEntity target = Platform.getLoadedTileEntity(
|
||||
te.getWorldObj(),
|
||||
te.xCoord + side.offsetX,
|
||||
te.yCoord + side.offsetY,
|
||||
te.zCoord + side.offsetZ);
|
||||
if (!this.isCachedTargetValid || this.cachedTarget != target) {
|
||||
this.cachedTarget = target;
|
||||
this.isCachedTargetValid = true;
|
||||
}
|
||||
return this.cachedTarget;
|
||||
}
|
||||
|
||||
private long injectEnergy(IEnergyConnected te, ForgeDirection oppositeSide, long aVoltage, long aAmperage) {
|
||||
|
||||
@@ -59,6 +59,7 @@ public class PartP2PItems extends PartP2PTunnelNormal<PartP2PItems>
|
||||
private int oldSize = 0;
|
||||
private boolean requested;
|
||||
private IInventory cachedInv;
|
||||
private List<TileEntity> cachedTargets = new LinkedList<>();
|
||||
|
||||
public PartP2PItems(final ItemStack is) {
|
||||
super(is);
|
||||
@@ -76,11 +77,12 @@ public class PartP2PItems extends PartP2PTunnelNormal<PartP2PItems>
|
||||
private IInventory getDestination() {
|
||||
this.requested = true;
|
||||
|
||||
if (this.cachedInv != null) {
|
||||
if (this.cachedInv != null && Platform.areInventoryTilesLoaded(this.cachedTargets)) {
|
||||
return this.cachedInv;
|
||||
}
|
||||
|
||||
final List<IInventory> outs = new LinkedList<>();
|
||||
this.cachedTargets = new LinkedList<>();
|
||||
final TunnelCollection<PartP2PItems> itemTunnels;
|
||||
|
||||
try {
|
||||
@@ -93,7 +95,7 @@ public class PartP2PItems extends PartP2PTunnelNormal<PartP2PItems>
|
||||
if (t.loopsBackTo(this, new HashSet<>())) {
|
||||
continue;
|
||||
}
|
||||
final IInventory inv = t.getOutputInv();
|
||||
final IInventory inv = t.getOutputInv(this.cachedTargets);
|
||||
if (inv != null) {
|
||||
if (Platform.getRandomInt() % 2 == 0) {
|
||||
outs.add(inv);
|
||||
@@ -106,14 +108,17 @@ public class PartP2PItems extends PartP2PTunnelNormal<PartP2PItems>
|
||||
return this.cachedInv = new WrapperChainedInventory(outs);
|
||||
}
|
||||
|
||||
private IInventory getOutputInv() {
|
||||
private IInventory getOutputInv(List<TileEntity> targets) {
|
||||
IInventory output = null;
|
||||
|
||||
if (this.getProxy().isActive()) {
|
||||
final TileEntity te = this.getTile().getWorldObj().getTileEntity(
|
||||
final TileEntity te = Platform.getLoadedTileEntity(
|
||||
this.getTile().getWorldObj(),
|
||||
this.getTile().xCoord + this.getSide().offsetX,
|
||||
this.getTile().yCoord + this.getSide().offsetY,
|
||||
this.getTile().zCoord + this.getSide().offsetZ);
|
||||
final List<TileEntity> inventoryTiles = Platform.getInventoryTiles(te);
|
||||
if (inventoryTiles.isEmpty()) return null;
|
||||
|
||||
if (this.which.contains(this)) {
|
||||
return null;
|
||||
@@ -148,6 +153,10 @@ public class PartP2PItems extends PartP2PTunnelNormal<PartP2PItems>
|
||||
}
|
||||
|
||||
this.which.pop();
|
||||
if (output != null) {
|
||||
targets.add(this.getTile());
|
||||
targets.addAll(inventoryTiles);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
@@ -320,7 +329,8 @@ public class PartP2PItems extends PartP2PTunnelNormal<PartP2PItems>
|
||||
}
|
||||
|
||||
private boolean loopsBackTo(final PartP2PItems tunnel, final Set<PartP2PItems> visited) {
|
||||
final TileEntity te = this.getTile().getWorldObj().getTileEntity(
|
||||
final TileEntity te = Platform.getLoadedTileEntity(
|
||||
this.getTile().getWorldObj(),
|
||||
this.getTile().xCoord + this.getSide().offsetX,
|
||||
this.getTile().yCoord + this.getSide().offsetY,
|
||||
this.getTile().zCoord + this.getSide().offsetZ);
|
||||
|
||||
@@ -26,6 +26,7 @@ import net.minecraftforge.fluids.FluidTankInfo;
|
||||
import net.minecraftforge.fluids.IFluidHandler;
|
||||
|
||||
import appeng.me.GridAccessException;
|
||||
import appeng.util.Platform;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
@@ -187,19 +188,15 @@ public class PartP2PLiquids extends PartP2PTunnelNormal<PartP2PLiquids> implemen
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.cachedTank != null) {
|
||||
return this.cachedTank;
|
||||
}
|
||||
|
||||
final TileEntity te = this.getTile().getWorldObj().getTileEntity(
|
||||
final TileEntity te = Platform.getLoadedTileEntity(
|
||||
this.getTile().getWorldObj(),
|
||||
this.getTile().xCoord + this.getSide().offsetX,
|
||||
this.getTile().yCoord + this.getSide().offsetY,
|
||||
this.getTile().zCoord + this.getSide().offsetZ);
|
||||
if (te instanceof IFluidHandler) {
|
||||
return this.cachedTank = (IFluidHandler) te;
|
||||
if (this.cachedTank != te) {
|
||||
this.cachedTank = te instanceof IFluidHandler handler ? handler : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
return this.cachedTank;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -69,7 +69,7 @@ public final class PartP2PRFPower extends PartP2PTunnelNormal<PartP2PRFPower> im
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.isActive()) {
|
||||
if (this.getProxy().isActive()) {
|
||||
final Stack<PartP2PRFPower> stack = this.getDepth();
|
||||
|
||||
for (final PartP2PRFPower t : stack) {
|
||||
@@ -136,12 +136,13 @@ public final class PartP2PRFPower extends PartP2PTunnelNormal<PartP2PRFPower> im
|
||||
|
||||
private IEnergyReceiver getOutput() {
|
||||
if (this.isOutput()) {
|
||||
if (!this.cachedTarget) {
|
||||
final TileEntity self = this.getTile();
|
||||
final TileEntity te = self.getWorldObj().getTileEntity(
|
||||
self.xCoord + this.getSide().offsetX,
|
||||
self.yCoord + this.getSide().offsetY,
|
||||
self.zCoord + this.getSide().offsetZ);
|
||||
final TileEntity self = this.getTile();
|
||||
final TileEntity te = Platform.getLoadedTileEntity(
|
||||
self.getWorldObj(),
|
||||
self.xCoord + this.getSide().offsetX,
|
||||
self.yCoord + this.getSide().offsetY,
|
||||
self.zCoord + this.getSide().offsetZ);
|
||||
if (!this.cachedTarget || this.outputTarget != te) {
|
||||
this.outputTarget = te instanceof IEnergyReceiver ? (IEnergyReceiver) te : null;
|
||||
this.cachedTarget = true;
|
||||
}
|
||||
@@ -157,7 +158,7 @@ public final class PartP2PRFPower extends PartP2PTunnelNormal<PartP2PRFPower> im
|
||||
|
||||
@Override
|
||||
public int getEnergyStored(final ForgeDirection from) {
|
||||
if (this.isOutput() || !this.isActive()) {
|
||||
if (this.isOutput() || !this.getProxy().isActive()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -189,7 +190,7 @@ public final class PartP2PRFPower extends PartP2PTunnelNormal<PartP2PRFPower> im
|
||||
|
||||
@Override
|
||||
public int getMaxEnergyStored(final ForgeDirection from) {
|
||||
if (this.isOutput() || !this.isActive()) {
|
||||
if (this.isOutput() || !this.getProxy().isActive()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
package appeng.util;
|
||||
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
public class BlockUpdate implements IWorldCallable<Boolean> {
|
||||
|
||||
@@ -27,7 +28,14 @@ public class BlockUpdate implements IWorldCallable<Boolean> {
|
||||
@Override
|
||||
public Boolean call(final World world) throws Exception {
|
||||
if (world.blockExists(this.x, this.y, this.z)) {
|
||||
world.notifyBlocksOfNeighborChange(this.x, this.y, this.z, Platform.AIR_BLOCK);
|
||||
for (ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
|
||||
final int x = this.x + side.offsetX;
|
||||
final int y = this.y + side.offsetY;
|
||||
final int z = this.z + side.offsetZ;
|
||||
if (world.blockExists(x, y, z)) {
|
||||
world.notifyBlockOfNeighborChange(x, y, z, Platform.AIR_BLOCK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -13,6 +13,7 @@ package appeng.util;
|
||||
import static appeng.util.Platform.isEIOLoaded;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
@@ -64,11 +65,8 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
|
||||
public abstract class InventoryAdaptor implements Iterable<ItemSlot> {
|
||||
|
||||
// Two flat maps keyed by packObjectIdentity(te)|packSideAndFlags(d,flags).
|
||||
// ADAPTOR_CACHE_REFS stores the actual te reference for identity-hash collision safety.
|
||||
// ADAPTOR_CACHE stores the resolved adaptor (may be null for cached null results).
|
||||
private static final Long2ObjectOpenHashMap<Object> ADAPTOR_CACHE = new Long2ObjectOpenHashMap<>();
|
||||
private static final Long2ObjectOpenHashMap<Object> ADAPTOR_CACHE_REFS = new Long2ObjectOpenHashMap<>();
|
||||
// Entries retain the actual reference and inventory tiles to validate identity and chunk lifetime
|
||||
private static final Long2ObjectOpenHashMap<CachedAdaptor> ADAPTOR_CACHE = new Long2ObjectOpenHashMap<>();
|
||||
private static int adaptorCacheTick = Integer.MIN_VALUE;
|
||||
|
||||
private static int counter = 0;
|
||||
@@ -136,23 +134,21 @@ public abstract class InventoryAdaptor implements Iterable<ItemSlot> {
|
||||
private static void cacheInvalidateIfStale(int currentTick) {
|
||||
if (currentTick != adaptorCacheTick) {
|
||||
ADAPTOR_CACHE.clear();
|
||||
ADAPTOR_CACHE_REFS.clear();
|
||||
adaptorCacheTick = currentTick;
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearCache() {
|
||||
ADAPTOR_CACHE.clear();
|
||||
ADAPTOR_CACHE_REFS.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} iff the cache contains an entry for the given arguments and the stored reference matches the
|
||||
* supplied {@code te}.
|
||||
* Checks that a cache entry still refers to the supplied object and its current, loaded inventory tiles.
|
||||
*/
|
||||
private static boolean isCached(Object te, ForgeDirection d, int flags) {
|
||||
final long key = makeCacheKey(te, d, flags);
|
||||
return ADAPTOR_CACHE_REFS.get(key) == te;
|
||||
final CachedAdaptor entry = ADAPTOR_CACHE.get(key);
|
||||
return entry != null && entry.isValid(te);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,13 +157,29 @@ public abstract class InventoryAdaptor implements Iterable<ItemSlot> {
|
||||
*/
|
||||
private static InventoryAdaptor cacheGet(Object te, ForgeDirection d, int flags) {
|
||||
final long key = makeCacheKey(te, d, flags);
|
||||
return (InventoryAdaptor) ADAPTOR_CACHE.get(key);
|
||||
return ADAPTOR_CACHE.get(key).adaptor;
|
||||
}
|
||||
|
||||
private static void cachePut(Object te, ForgeDirection d, int flags, InventoryAdaptor adaptor) {
|
||||
final long key = makeCacheKey(te, d, flags);
|
||||
ADAPTOR_CACHE_REFS.put(key, te);
|
||||
ADAPTOR_CACHE.put(key, adaptor);
|
||||
ADAPTOR_CACHE.put(key, new CachedAdaptor(te, adaptor));
|
||||
}
|
||||
|
||||
private static final class CachedAdaptor {
|
||||
|
||||
private final Object reference;
|
||||
private final InventoryAdaptor adaptor;
|
||||
private final List<TileEntity> tiles;
|
||||
|
||||
private CachedAdaptor(Object reference, InventoryAdaptor adaptor) {
|
||||
this.reference = reference;
|
||||
this.adaptor = adaptor;
|
||||
this.tiles = reference instanceof TileEntity tile ? Platform.getInventoryTiles(tile) : null;
|
||||
}
|
||||
|
||||
private boolean isValid(Object reference) {
|
||||
return this.reference == reference && (this.tiles == null || Platform.areInventoryTilesLoaded(this.tiles));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -224,7 +236,8 @@ public abstract class InventoryAdaptor implements Iterable<ItemSlot> {
|
||||
}
|
||||
|
||||
if (invs && te instanceof TileEntityChest) {
|
||||
return new AdaptorIInventory(Platform.GetChestInv(te));
|
||||
IInventory inventory = Platform.GetChestInv(te);
|
||||
return inventory == null ? null : new AdaptorIInventory(inventory);
|
||||
}
|
||||
|
||||
if (te instanceof ISidedInventory sided) {
|
||||
|
||||
@@ -726,6 +726,7 @@ public class Platform {
|
||||
* Utility function to get the full inventory for a Double Chest in the World.
|
||||
*/
|
||||
public static IInventory GetChestInv(final Object te) {
|
||||
if (!isInventoryTileLoaded((TileEntity) te)) return null;
|
||||
TileEntityChest teA = (TileEntityChest) te;
|
||||
TileEntity teB = null;
|
||||
final Block myBlockID = teA.getWorldObj().getBlock(teA.xCoord, teA.yCoord, teA.zCoord);
|
||||
@@ -779,6 +780,59 @@ public class Platform {
|
||||
return new InventoryLargeChest("", teA, (IInventory) teB);
|
||||
}
|
||||
|
||||
/** Returns the current tile without loading its chunk. */
|
||||
@Nullable
|
||||
public static TileEntity getLoadedTileEntity(World world, int x, int y, int z) {
|
||||
if (world == null || !world.blockExists(x, y, z)) return null;
|
||||
TileEntity tile = world.getTileEntity(x, y, z);
|
||||
return tile == null || tile.isInvalid() ? null : tile;
|
||||
}
|
||||
|
||||
/** Captures the current inventory tiles, or an empty list if any required chunk is unloaded. */
|
||||
public static List<TileEntity> getInventoryTiles(TileEntity target) {
|
||||
final List<TileEntity> tiles = new ArrayList<>(2);
|
||||
if (!isInventoryTileLoaded(target)) return tiles;
|
||||
if (target instanceof TileEntityChest) {
|
||||
final World world = target.getWorldObj();
|
||||
for (ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
|
||||
if (side.offsetY != 0) continue;
|
||||
final int x = target.xCoord + side.offsetX;
|
||||
final int z = target.zCoord + side.offsetZ;
|
||||
final TileEntity neighbor = getLoadedTileEntity(world, x, target.yCoord, z);
|
||||
if (neighbor instanceof TileEntityChest && neighbor.getBlockType() == target.getBlockType()) {
|
||||
tiles.add(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
tiles.add(target);
|
||||
return tiles;
|
||||
}
|
||||
|
||||
/** Checks that the current target and neighboring chest chunks are already loaded. */
|
||||
public static boolean isInventoryTileLoaded(TileEntity target) {
|
||||
if (target == null
|
||||
|| getLoadedTileEntity(target.getWorldObj(), target.xCoord, target.yCoord, target.zCoord) != target) {
|
||||
return false;
|
||||
}
|
||||
if (target instanceof TileEntityChest) {
|
||||
for (ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) {
|
||||
if (side.offsetY == 0 && !target.getWorldObj()
|
||||
.blockExists(target.xCoord + side.offsetX, target.yCoord, target.zCoord + side.offsetZ))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Checks that every captured tile still belongs to the loaded world. */
|
||||
public static boolean areInventoryTilesLoaded(List<TileEntity> tiles) {
|
||||
if (tiles.isEmpty()) return false;
|
||||
for (TileEntity tile : tiles) {
|
||||
if (getLoadedTileEntity(tile.getWorldObj(), tile.xCoord, tile.yCoord, tile.zCoord) != tile) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isModLoaded(final String modid) {
|
||||
try {
|
||||
// if this fails for some reason, try the other method.
|
||||
@@ -1490,6 +1544,7 @@ public class Platform {
|
||||
} else if (target instanceof ITileStorageMonitorable) {
|
||||
return 0;
|
||||
} else if (target instanceof TileEntityChest chest) {
|
||||
if (!isInventoryTileLoaded(chest)) return 0;
|
||||
chest.checkForAdjacentChests();
|
||||
if (chest.adjacentChestZNeg != null) {
|
||||
hash ^= chest.adjacentChestZNeg.hashCode();
|
||||
|
||||
Reference in new issue
Block a user