diff --git a/src/main/java/api/hbm/ntl/ISlotMonitorProvider.java b/src/main/java/api/hbm/ntl/ISlotMonitorProvider.java index fb549fee8..0dbde4ea5 100644 --- a/src/main/java/api/hbm/ntl/ISlotMonitorProvider.java +++ b/src/main/java/api/hbm/ntl/ISlotMonitorProvider.java @@ -27,6 +27,15 @@ public interface ISlotMonitorProvider { /** This allows slot monitors to find the network, and by extension all cached slots */ public PneumaticNetwork getRelevantNetwork(); + /** Runs whenever a new stack cache user (i.e. an access point) joins the network in order to grab all the stack monitors */ + public default void onNewCacheHasJoined(StackCache stackCache, PneumaticNetwork network) { + for(SlotMonitor monitor : getMonitors()) { + if(!stackCache.hasExpired && isAvailableToCache(stackCache)) { + stackCache.addToCache(monitor); + } + } + } + public default void updateMonitors() { for(SlotMonitor monitor : getMonitors()) monitor.checkUpdate(); } diff --git a/src/main/java/api/hbm/ntl/SlotMonitor.java b/src/main/java/api/hbm/ntl/SlotMonitor.java index 84b15d402..4d7b1f863 100644 --- a/src/main/java/api/hbm/ntl/SlotMonitor.java +++ b/src/main/java/api/hbm/ntl/SlotMonitor.java @@ -1,7 +1,12 @@ package api.hbm.ntl; +import java.util.Iterator; import java.util.LinkedHashSet; +import javax.annotation.Nullable; + +import com.hbm.uninos.networkproviders.PneumaticNetwork; + import api.hbm.ntl.StackCache.CacheSlot; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; @@ -26,42 +31,116 @@ public class SlotMonitor { /** If this monitor detects a change, all cache slots need to be notified */ public LinkedHashSet viewedBy = new LinkedHashSet(); - public Item item; + @Nullable public Item item; public long stacksize; public int meta; public NBTTagCompound nbt; + protected boolean hasAvailabilityChanged = false; + public SlotMonitor(int index, ISlotMonitorProvider parent) { + this.hasAvailabilityChanged = true; this.index = index; this.parent = parent; } + public ItemStack toZeroStack() { + if(item == null) return null; + ItemStack stack = new ItemStack(item, 0, meta); + stack.stackTagCompound = nbt; + return stack; + } + + /** + * Monitor providers need to keep track of whether availability has changed, i.e. compair has run out, compression setting has changed, etc + * This means that we don't have to check availability every single tick, which potentially saves a fuckton of iterations. + */ + public void availabilityHasChanged() { + this.hasAvailabilityChanged = true; + } + public void checkUpdate() { - for(CacheSlot slot : viewedBy) { // we're gonna need an iterator for that - if(!parent.isAvailableToCache(slot.getStackCache())) { - // TODO: kill from that cache + PneumaticNetwork pneumoNet = this.parent.getRelevantNetwork(); + + if(hasAvailabilityChanged) { + + if(pneumoNet != null) { + for(StackCache cache : pneumoNet.accessors) { + if(!cache.hasExpired && parent.isAvailableToCache(cache)) { + cache.addToCache(this); + } + } } + + // if this monitor is not available to some caches, remove them + Iterator iterator = viewedBy.iterator(); + while(iterator.hasNext()) { + CacheSlot slot = iterator.next(); + StackCache cache = slot.getStackCache(); + if(cache.hasExpired || !parent.isAvailableToCache(cache)) { + slot.removeMonitor(this); + iterator.remove(); + } + } + + hasAvailabilityChanged = false; } ItemStack stack = parent.getSlotAt(index); long amount = parent.getAmountAt(index); boolean hasTypeChanged = false; - if(item != stack.getItem() || meta != stack.getItemDamage()) hasTypeChanged = true; - else if(nbt == null && stack.hasTagCompound()) hasTypeChanged = true; - else if(nbt != null && !stack.hasTagCompound()) hasTypeChanged = true; - else if(nbt != null && stack.hasTagCompound() && !nbt.equals(stack.stackTagCompound)) hasTypeChanged = true; + if(stack == null || item == null) { + if(stack == null && item != null) hasTypeChanged = true; + if(stack != null && item == null) hasTypeChanged = true; + } else { + if(item != stack.getItem() || meta != stack.getItemDamage()) hasTypeChanged = true; + else if(nbt == null && stack.hasTagCompound()) hasTypeChanged = true; + else if(nbt != null && !stack.hasTagCompound()) hasTypeChanged = true; + else if(nbt != null && stack.hasTagCompound() && !nbt.equals(stack.stackTagCompound)) hasTypeChanged = true; + } if(hasTypeChanged) { - // TODO: find a bridge to the other CacheSlots, ideally over the pneumo network - // all viewing cache slots need their reference removed and a new cache slot needs to be found/created + + // remove from all existing monitors + Iterator iterator = viewedBy.iterator(); + while(iterator.hasNext()) { + CacheSlot slot = iterator.next(); + slot.removeMonitor(this); + iterator.remove(); + } + + // set updated traits + if(stack == null) { + this.item = null; + this.stacksize = 0; + this.meta = 0; + this.nbt = null; + } else { + this.item = stack.getItem(); + this.stacksize = amount; + this.meta = stack.getItemDamage(); + this.nbt = stack.hasTagCompound() ? (NBTTagCompound) stack.stackTagCompound.copy() : null; + } + + // find new monitors + if(pneumoNet != null) { + for(StackCache cache : pneumoNet.accessors) { + if(!cache.hasExpired && parent.isAvailableToCache(cache)) { + cache.addToCache(this); + } + } + } + return; } if(stacksize != amount) { long delta = amount - stacksize; for(CacheSlot slot : viewedBy) slot.changeAmounts(delta); + + this.stacksize = amount; } } } diff --git a/src/main/java/api/hbm/ntl/StackCache.java b/src/main/java/api/hbm/ntl/StackCache.java index 0f376ebb1..1739d17e0 100644 --- a/src/main/java/api/hbm/ntl/StackCache.java +++ b/src/main/java/api/hbm/ntl/StackCache.java @@ -2,6 +2,9 @@ package api.hbm.ntl; import java.util.LinkedHashMap; import java.util.LinkedHashSet; +import java.util.Map.Entry; + +import javax.annotation.Nullable; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; @@ -20,8 +23,10 @@ public class StackCache { public int y; public int z; + public boolean hasExpired = false; + /** Maps an identity number to the actual cache slot */ - public LinkedHashMap cacheSlots = new LinkedHashMap(); + public LinkedHashMap cacheSlots = new LinkedHashMap(); public StackCache(int x, int y, int z) { this.x = x; @@ -29,6 +34,26 @@ public class StackCache { this.z = z; } + public void addToCache(SlotMonitor monitor) { + long monitorIdentity = getStackIdentity(monitor.item, monitor.meta, monitor.nbt); + CacheSlot cache = cacheSlots.get(monitorIdentity); + + if(cache == null) { + cache = new CacheSlot(monitor.toZeroStack()); + cacheSlots.put(monitorIdentity, cache); + } + + cache.addMonitor(monitor); + } + + public void dissolveCache() { + for(Entry cacheEntry : cacheSlots.entrySet()) { + cacheEntry.getValue().destroy(); + } + this.cacheSlots.clear(); + this.hasExpired = true; + } + /** * A cache slot represents multiple accessible slots combined into one by type, * in essence it's a single slot with an uncapped max stack size, which references @@ -38,23 +63,53 @@ public class StackCache { */ public class CacheSlot { - public final ItemStack displayStack; + @Nullable public final ItemStack displayStack; public final int itemId; public final int meta; public final NBTTagCompound nbt; public long stacksize; + public LinkedHashSet monitors = new LinkedHashSet(); + public CacheSlot(ItemStack stack) { - this.displayStack = stack.copy(); - this.displayStack.stackSize = 1; - this.itemId = Item.getIdFromItem(stack.getItem()); - this.stacksize = stack.stackSize; - this.meta = stack.getItemDamage(); - if(stack.hasTagCompound()) - this.nbt = (NBTTagCompound) stack.stackTagCompound.copy(); - else + + if(stack != null) { + this.displayStack = stack.copy(); + this.stacksize = stack.stackSize; + this.displayStack.stackSize = 1; + this.itemId = Item.getIdFromItem(stack.getItem()); + this.meta = stack.getItemDamage(); + if(stack.hasTagCompound()) + this.nbt = (NBTTagCompound) stack.stackTagCompound.copy(); + else + this.nbt = null; + } else { + this.displayStack = null; + this.stacksize = 0; + this.itemId = 0; + this.meta = 0; this.nbt = null; + } + } + + public void addMonitor(SlotMonitor monitor) { + if(this.monitors.add(monitor)) { + this.changeAmounts(monitor.stacksize); + } + } + + public void removeMonitor(SlotMonitor monitor) { + if(this.monitors.remove(monitor)) { + this.changeAmounts(-monitor.stacksize); + } + } + + public void destroy() { + for(SlotMonitor monitor : monitors) { + monitor.viewedBy.remove(this); + } + this.stacksize = 0; } public void changeAmounts(long delta) { @@ -65,18 +120,18 @@ public class StackCache { return StackCache.this; } + // not actually used, and probably not needed. this would fix any inconsistencies with the sized, + // however we try to ensure that sizes are always correctly updated so this should never be the case. public void reCount() { this.stacksize = 0; for(SlotMonitor monitor : monitors) { this.stacksize += monitor.stacksize; } } - - public LinkedHashSet monitors = new LinkedHashSet(); } - public static int getStackIdentity(Item item, int meta, NBTTagCompound nbt) { - int identity = Item.getIdFromItem(item) * 27644437; + public static long getStackIdentity(Item item, int meta, NBTTagCompound nbt) { + long identity = Item.getIdFromItem(item) * 27644437; identity += meta * 27644437; if(nbt != null) identity += nbt.toString().hashCode(); return identity; diff --git a/src/main/java/com/hbm/inventory/container/ContainerPneumoStorageAccess.java b/src/main/java/com/hbm/inventory/container/ContainerPneumoStorageAccess.java index dfa738a37..2747981aa 100644 --- a/src/main/java/com/hbm/inventory/container/ContainerPneumoStorageAccess.java +++ b/src/main/java/com/hbm/inventory/container/ContainerPneumoStorageAccess.java @@ -40,6 +40,9 @@ public class ContainerPneumoStorageAccess extends Container { for(int i = 0; i < 9; i++) { this.addSlotToContainer(new SlotNonRetarded(invPlayer, i, 8 + i * 18, 227)); } + + inventory.updateListing(); + this.detectAndSendChanges(); } @Override @@ -64,6 +67,7 @@ public class ContainerPneumoStorageAccess extends Container { } public void updateListing() { // DEMO + if(this.cache == null) return; List cacheSlots = new ArrayList(cache.cacheSlots.size()); cacheSlots.addAll(cache.cacheSlots.values()); Collections.sort(cacheSlots, SORT_BY_STACK_SIZE); diff --git a/src/main/java/com/hbm/tileentity/network/pneumatic/TileEntityPneumoStorageAccess.java b/src/main/java/com/hbm/tileentity/network/pneumatic/TileEntityPneumoStorageAccess.java index e6396ed89..194a39380 100644 --- a/src/main/java/com/hbm/tileentity/network/pneumatic/TileEntityPneumoStorageAccess.java +++ b/src/main/java/com/hbm/tileentity/network/pneumatic/TileEntityPneumoStorageAccess.java @@ -2,23 +2,21 @@ package com.hbm.tileentity.network.pneumatic; import com.hbm.inventory.container.ContainerPneumoStorageAccess; import com.hbm.inventory.gui.GUIPneumoStorageAccess; -import com.hbm.lib.Library; import com.hbm.tileentity.IGUIProvider; +import com.hbm.tileentity.TileEntityLoadedBase; import com.hbm.tileentity.network.pneumatic.TileEntityPneumoTube.PneumaticNode; import com.hbm.uninos.UniNodespace; import com.hbm.uninos.networkproviders.PneumaticNetworkProvider; import com.hbm.util.fauxpointtwelve.BlockPos; -import com.hbm.util.fauxpointtwelve.DirPos; import api.hbm.ntl.IPneumaticConnector; import api.hbm.ntl.StackCache; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.Container; -import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; -public class TileEntityPneumoStorageAccess extends TileEntity implements IPneumaticConnector, IGUIProvider { +public class TileEntityPneumoStorageAccess extends TileEntityLoadedBase implements IPneumaticConnector, IGUIProvider { protected PneumaticNode node; public StackCache cache; @@ -26,25 +24,25 @@ public class TileEntityPneumoStorageAccess extends TileEntity implements IPneuma @Override public void updateEntity() { - if(this.cache == null) this.cache = new StackCache(xCoord, yCoord, zCoord); - if(!worldObj.isRemote) { if(this.node == null || this.node.expired) { + if(this.cache != null) this.cache.dissolveCache(); + this.node = (PneumaticNode) UniNodespace.getNode(worldObj, xCoord, yCoord, zCoord, PneumaticNetworkProvider.THE_PROVIDER); if(this.node == null || this.node.expired) { - this.node = (PneumaticNode) new PneumaticNode(new BlockPos(xCoord, yCoord, zCoord)).setConnections( - new DirPos(xCoord + 1, yCoord, zCoord, Library.POS_X), - new DirPos(xCoord - 1, yCoord, zCoord, Library.NEG_X), - new DirPos(xCoord, yCoord + 1, zCoord, Library.POS_Y), - new DirPos(xCoord, yCoord - 1, zCoord, Library.NEG_Y), - new DirPos(xCoord, yCoord, zCoord + 1, Library.POS_Z), - new DirPos(xCoord, yCoord, zCoord - 1, Library.NEG_Z) - ); + this.node = (PneumaticNode) new PneumaticNode(new BlockPos(xCoord, yCoord, zCoord)).setStandardConnections(xCoord, yCoord, zCoord); UniNodespace.createNode(worldObj, this.node); } } + + if(this.cache == null) { + this.cache = new StackCache(xCoord, yCoord, zCoord); + if(this.node != null && this.node.hasValidNet()) { + this.node.net.addStackCache(cache); + } + } } } @@ -52,11 +50,22 @@ public class TileEntityPneumoStorageAccess extends TileEntity implements IPneuma public void invalidate() { super.invalidate(); - if(!worldObj.isRemote) { - if(this.node != null) { - UniNodespace.destroyNode(worldObj, xCoord, yCoord, zCoord, PneumaticNetworkProvider.THE_PROVIDER); - } + if(!worldObj.isRemote && this.node != null) { + UniNodespace.destroyNode(worldObj, xCoord, yCoord, zCoord, PneumaticNetworkProvider.THE_PROVIDER); } + + if(this.cache != null) this.cache.dissolveCache(); + } + + @Override + public void onChunkUnload() { + super.onChunkUnload(); + + if(!worldObj.isRemote && this.node != null) { + UniNodespace.destroyNode(worldObj, xCoord, yCoord, zCoord, PneumaticNetworkProvider.THE_PROVIDER); + } + + if(this.cache != null) this.cache.dissolveCache(); } @Override diff --git a/src/main/java/com/hbm/tileentity/network/pneumatic/TileEntityPneumoStorageClutter.java b/src/main/java/com/hbm/tileentity/network/pneumatic/TileEntityPneumoStorageClutter.java index 4b907b83f..099c92df8 100644 --- a/src/main/java/com/hbm/tileentity/network/pneumatic/TileEntityPneumoStorageClutter.java +++ b/src/main/java/com/hbm/tileentity/network/pneumatic/TileEntityPneumoStorageClutter.java @@ -4,7 +4,6 @@ import com.hbm.inventory.container.ContainerPneumoStorageClutter; import com.hbm.inventory.fluid.Fluids; import com.hbm.inventory.fluid.tank.FluidTank; import com.hbm.inventory.gui.GUIPneumoStorageClutter; -import com.hbm.lib.Library; import com.hbm.tileentity.IGUIProvider; import com.hbm.tileentity.TileEntityMachineBase; import com.hbm.tileentity.network.pneumatic.TileEntityPneumoTube.PneumaticNode; @@ -12,7 +11,6 @@ import com.hbm.uninos.UniNodespace; import com.hbm.uninos.networkproviders.PneumaticNetwork; import com.hbm.uninos.networkproviders.PneumaticNetworkProvider; import com.hbm.util.fauxpointtwelve.BlockPos; -import com.hbm.util.fauxpointtwelve.DirPos; import api.hbm.fluidmk2.IFluidStandardReceiverMK2; import api.hbm.ntl.ISlotMonitorProvider; @@ -29,6 +27,7 @@ public class TileEntityPneumoStorageClutter extends TileEntityMachineBase implem public SlotMonitor[] monitors; protected PneumaticNode node; + protected boolean wasAvailable = false; public TileEntityPneumoStorageClutter() { super(6 * 9); @@ -48,30 +47,34 @@ public class TileEntityPneumoStorageClutter extends TileEntityMachineBase implem if(!worldObj.isRemote) { + boolean isAvailable = this.isAvailable(); + + if(isAvailable != wasAvailable) { + this.wasAvailable = isAvailable; + for(SlotMonitor monitor : monitors) monitor.availabilityHasChanged(); + } + if(this.node == null || this.node.expired) { this.node = (PneumaticNode) UniNodespace.getNode(worldObj, xCoord, yCoord, zCoord, PneumaticNetworkProvider.THE_PROVIDER); if(this.node == null || this.node.expired) { - this.node = (PneumaticNode) new PneumaticNode(new BlockPos(xCoord, yCoord, zCoord)).setConnections( - new DirPos(xCoord + 1, yCoord, zCoord, Library.POS_X), - new DirPos(xCoord - 1, yCoord, zCoord, Library.NEG_X), - new DirPos(xCoord, yCoord + 1, zCoord, Library.POS_Y), - new DirPos(xCoord, yCoord - 1, zCoord, Library.NEG_Y), - new DirPos(xCoord, yCoord, zCoord + 1, Library.POS_Z), - new DirPos(xCoord, yCoord, zCoord - 1, Library.NEG_Z) - ); + this.node = (PneumaticNode) new PneumaticNode(new BlockPos(xCoord, yCoord, zCoord)).setStandardConnections(xCoord, yCoord, zCoord); UniNodespace.createNode(worldObj, this.node); } } if(node != null && !node.expired && node.hasValidNet()) { - this.node.net.storages.put(this, worldObj.getTotalWorldTime()); + this.node.net.storages.add(this); } this.updateMonitors(); this.networkPackNT(15); } } + + public boolean isAvailable() { + return this.isLoaded && !this.isInvalid(); + } @Override public void invalidate() { @@ -119,6 +122,6 @@ public class TileEntityPneumoStorageClutter extends TileEntityMachineBase implem @Override public boolean isAvailableToCache(StackCache cache) { - return true; + return this.isLoaded && !this.isInvalid(); } } diff --git a/src/main/java/com/hbm/uninos/GenNode.java b/src/main/java/com/hbm/uninos/GenNode.java index 2d3ee43ab..ea389d204 100644 --- a/src/main/java/com/hbm/uninos/GenNode.java +++ b/src/main/java/com/hbm/uninos/GenNode.java @@ -1,5 +1,6 @@ package com.hbm.uninos; +import com.hbm.lib.Library; import com.hbm.util.fauxpointtwelve.BlockPos; import com.hbm.util.fauxpointtwelve.DirPos; @@ -25,6 +26,16 @@ public class GenNode { return this; } + public GenNode setStandardConnections(int xCoord, int yCoord, int zCoord) { + return this.setConnections( + new DirPos(xCoord + 1, yCoord, zCoord, Library.POS_X), + new DirPos(xCoord - 1, yCoord, zCoord, Library.NEG_X), + new DirPos(xCoord, yCoord + 1, zCoord, Library.POS_Y), + new DirPos(xCoord, yCoord - 1, zCoord, Library.NEG_Y), + new DirPos(xCoord, yCoord, zCoord + 1, Library.POS_Z), + new DirPos(xCoord, yCoord, zCoord - 1, Library.NEG_Z)); + } + public GenNode addConnection(DirPos connection) { DirPos[] newCons = new DirPos[this.connections.length + 1]; for(int i = 0; i < this.connections.length; i++) newCons[i] = this.connections[i]; diff --git a/src/main/java/com/hbm/uninos/networkproviders/PneumaticNetwork.java b/src/main/java/com/hbm/uninos/networkproviders/PneumaticNetwork.java index 80c6a47ed..cbdd9f3ab 100644 --- a/src/main/java/com/hbm/uninos/networkproviders/PneumaticNetwork.java +++ b/src/main/java/com/hbm/uninos/networkproviders/PneumaticNetwork.java @@ -5,6 +5,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map.Entry; import java.util.Random; @@ -17,6 +18,7 @@ import com.hbm.util.ItemStackUtil; import com.hbm.util.Tuple.Triplet; import api.hbm.ntl.ISlotMonitorProvider; +import api.hbm.ntl.StackCache; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.ItemStack; @@ -40,12 +42,19 @@ public class PneumaticNetwork extends NodeNet { // while the system has parts that expects IInventires to be TileEntities to work properly (mostly range checks), // it can actually handle non-TileEntities just fine. public HashMap> receivers = new HashMap(); - - public HashMap storages = new HashMap(); + + public LinkedHashSet accessors = new LinkedHashSet(); + public LinkedHashSet storages = new LinkedHashSet(); public void addReceiver(IInventory inventory, ForgeDirection pipeDir, TileEntityPneumoTube endpoint) { receivers.put(inventory, new Triplet(pipeDir, System.currentTimeMillis(), endpoint)); } + + public void addStackCache(StackCache accessor) { + if(accessors.add(accessor)) { + for(ISlotMonitorProvider storage : storages) storage.onNewCacheHasJoined(accessor, this); + } + } @Override public void update() { @@ -54,6 +63,7 @@ public class PneumaticNetwork extends NodeNet { // but we still want to reap garbage data that would otherwise accumulate long timestamp = System.currentTimeMillis(); receivers.entrySet().removeIf(x -> { return (timestamp - x.getValue().getY() > timeout) || NodeNet.isBadLink(x.getKey()); }); + accessors.removeIf(x -> { return x.hasExpired; }); } public boolean send(IInventory source, TileEntityPneumoTube tube, ForgeDirection accessDir, int sendOrder, int receiveOrder, int maxRange, int nextReceiver) {