mirror of
https://github.com/HbmMods/Hbm-s-Nuclear-Tech-GIT.git
synced 2026-01-25 10:32:49 +00:00
Merge pull request #2641 from sunryze-git/improved-pneumatics
improved pneumatics throughput
This commit is contained in:
commit
a491b16dfe
@ -2,6 +2,7 @@ package com.hbm.uninos.networkproviders;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -34,11 +35,11 @@ public class PneumaticNetwork extends NodeNet {
|
|||||||
|
|
||||||
protected static final int timeout = 1_000;
|
protected static final int timeout = 1_000;
|
||||||
public static final int ITEMS_PER_TRANSFER = 64;
|
public static final int ITEMS_PER_TRANSFER = 64;
|
||||||
|
|
||||||
// while the system has parts that expects IInventires to be TileEntities to work properly (mostly range checks),
|
// 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.
|
// it can actually handle non-TileEntities just fine.
|
||||||
public HashMap<IInventory, Triplet<ForgeDirection, Long, TileEntityPneumoTube>> receivers = new HashMap();
|
public HashMap<IInventory, Triplet<ForgeDirection, Long, TileEntityPneumoTube>> receivers = new HashMap();
|
||||||
|
|
||||||
public void addReceiver(IInventory inventory, ForgeDirection pipeDir, TileEntityPneumoTube endpoint) {
|
public void addReceiver(IInventory inventory, ForgeDirection pipeDir, TileEntityPneumoTube endpoint) {
|
||||||
receivers.put(inventory, new Triplet(pipeDir, System.currentTimeMillis(), endpoint));
|
receivers.put(inventory, new Triplet(pipeDir, System.currentTimeMillis(), endpoint));
|
||||||
}
|
}
|
||||||
@ -51,7 +52,7 @@ public class PneumaticNetwork extends NodeNet {
|
|||||||
long timestamp = System.currentTimeMillis();
|
long timestamp = System.currentTimeMillis();
|
||||||
receivers.entrySet().removeIf(x -> { return (timestamp - x.getValue().getY() > timeout) || NodeNet.isBadLink(x.getKey()); });
|
receivers.entrySet().removeIf(x -> { return (timestamp - x.getValue().getY() > timeout) || NodeNet.isBadLink(x.getKey()); });
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean send(IInventory source, TileEntityPneumoTube tube, ForgeDirection accessDir, int sendOrder, int receiveOrder, int maxRange, int nextReceiver) {
|
public boolean send(IInventory source, TileEntityPneumoTube tube, ForgeDirection accessDir, int sendOrder, int receiveOrder, int maxRange, int nextReceiver) {
|
||||||
|
|
||||||
// turns out there may be a short time window where the cleanup hasn't happened yet, but chunkloading has already caused tiles to go invalid
|
// turns out there may be a short time window where the cleanup hasn't happened yet, but chunkloading has already caused tiles to go invalid
|
||||||
@ -67,114 +68,144 @@ public class PneumaticNetwork extends NodeNet {
|
|||||||
if(sendOrder == SEND_LAST) BobMathUtil.reverseIntArray(sourceSlotAccess);
|
if(sendOrder == SEND_LAST) BobMathUtil.reverseIntArray(sourceSlotAccess);
|
||||||
if(sendOrder == SEND_RANDOM) BobMathUtil.shuffleIntArray(sourceSlotAccess);
|
if(sendOrder == SEND_RANDOM) BobMathUtil.shuffleIntArray(sourceSlotAccess);
|
||||||
|
|
||||||
|
ISidedInventory sidedSource = source instanceof ISidedInventory ? (ISidedInventory) source : null;
|
||||||
|
boolean hasItem = false;
|
||||||
|
|
||||||
|
for(int i : sourceSlotAccess) {
|
||||||
|
ItemStack stack = source.getStackInSlot(i);
|
||||||
|
if(stack != null) {
|
||||||
|
if(sidedSource != null && !sidedSource.canExtractItem(i, stack, sourceSide)) continue;
|
||||||
|
boolean match = tube.matchesFilter(stack);
|
||||||
|
if((match && !tube.whitelist) || (!match && tube.whitelist)) continue;
|
||||||
|
hasItem = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return early if there arent any items in the source inventory, saves on some cpu usage for idle networks
|
||||||
|
if(!hasItem) return false;
|
||||||
|
|
||||||
// for round robin, receivers are ordered by proximity to the source
|
// for round robin, receivers are ordered by proximity to the source
|
||||||
ReceiverComparator comparator = new ReceiverComparator(tube);
|
ReceiverComparator comparator = new ReceiverComparator(tube);
|
||||||
List<Entry<IInventory, Triplet<ForgeDirection, Long, TileEntityPneumoTube>>> receiverList = new ArrayList(receivers.size());
|
List<Entry<IInventory, Triplet<ForgeDirection, Long, TileEntityPneumoTube>>> receiverList = new ArrayList(receivers.size());
|
||||||
receiverList.addAll(receivers.entrySet());
|
receiverList.addAll(receivers.entrySet());
|
||||||
receiverList.sort(comparator);
|
|
||||||
|
|
||||||
int index = nextReceiver % receivers.size();
|
|
||||||
Entry<IInventory, Triplet<ForgeDirection, Long, TileEntityPneumoTube>> chosenReceiverEntry = null;
|
|
||||||
|
|
||||||
if(receiveOrder == RECEIVE_ROBIN) chosenReceiverEntry = receiverList.get(index);
|
if(receiveOrder == RECEIVE_ROBIN) receiverList.sort(comparator);
|
||||||
if(receiveOrder == RECEIVE_RANDOM) chosenReceiverEntry = receiverList.get(rand.nextInt(receiverList.size()));
|
if(receiveOrder == RECEIVE_RANDOM) Collections.shuffle(receiverList);
|
||||||
|
|
||||||
if(chosenReceiverEntry == null) return false;
|
|
||||||
|
|
||||||
IInventory dest = chosenReceiverEntry.getKey();
|
|
||||||
TileEntityPneumoTube endpointTile = chosenReceiverEntry.getValue().getZ();
|
|
||||||
ISidedInventory sidedDest = dest instanceof ISidedInventory ? (ISidedInventory) dest : null;
|
|
||||||
ISidedInventory sidedSource = source instanceof ISidedInventory ? (ISidedInventory) source : null;
|
|
||||||
|
|
||||||
TileEntity tile1 = source instanceof TileEntity ? (TileEntity) source : null;
|
TileEntity tile1 = source instanceof TileEntity ? (TileEntity) source : null;
|
||||||
TileEntity tile2 = dest instanceof TileEntity ? (TileEntity) dest : null;
|
|
||||||
|
|
||||||
// range check for our compression level, skip if either source or dest aren't tile entities
|
|
||||||
if(tile1 != null && tile2 != null) {
|
|
||||||
int sq = (tile1.xCoord - tile2.xCoord) * (tile1.xCoord - tile2.xCoord) + (tile1.yCoord - tile2.yCoord) * (tile1.yCoord - tile2.yCoord) + (tile1.zCoord - tile2.zCoord) * (tile1.zCoord - tile2.zCoord);
|
|
||||||
if(sq > maxRange * maxRange) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int destSide = chosenReceiverEntry.getValue().getX().getOpposite().ordinal();
|
|
||||||
int[] destSlotAccess = getSlotAccess(dest, destSide);
|
|
||||||
int itemsLeftToSend = ITEMS_PER_TRANSFER; // not actually individual items, but rather the total "mass", based on max stack size
|
|
||||||
int itemHardCap = dest instanceof TileEntityMachineAutocrafter ? 1 : ITEMS_PER_TRANSFER;
|
|
||||||
boolean didSomething = false;
|
|
||||||
|
|
||||||
for(int sourceIndex : sourceSlotAccess) {
|
|
||||||
ItemStack sourceStack = source.getStackInSlot(sourceIndex);
|
|
||||||
if(sourceStack == null) continue;
|
|
||||||
if(sidedSource != null && !sidedSource.canExtractItem(sourceIndex, sourceStack, sourceSide)) continue;
|
|
||||||
// filter of the source
|
|
||||||
boolean match = tube.matchesFilter(sourceStack);
|
|
||||||
if((match && !tube.whitelist) || (!match && tube.whitelist)) continue;
|
|
||||||
// filter of the receiver, only if the sender and receiver aren't the same block
|
|
||||||
if(endpointTile != null && endpointTile != tube) {
|
|
||||||
match = endpointTile.matchesFilter(sourceStack);
|
|
||||||
if((match && !endpointTile.whitelist) || (!match && endpointTile.whitelist)) continue;
|
|
||||||
}
|
|
||||||
// the "mass" of an item. something that only stacks to 4 has a "mass" of 16. max transfer mass is 64, i.e. one standard stack, or one single unstackable item
|
|
||||||
int proportionalValue = MathHelper.clamp_int(64 / sourceStack.getMaxStackSize(), 1, 64);
|
|
||||||
|
|
||||||
// try to fill partial stacks first
|
|
||||||
for(int destIndex : destSlotAccess) {
|
|
||||||
ItemStack destStack = dest.getStackInSlot(destIndex);
|
|
||||||
if(destStack == null) continue;
|
|
||||||
if(!ItemStackUtil.areStacksCompatible(sourceStack, destStack)) continue;
|
|
||||||
int toMove = BobMathUtil.min(sourceStack.stackSize, destStack.getMaxStackSize() - destStack.stackSize, dest.getInventoryStackLimit() - destStack.stackSize, itemsLeftToSend / proportionalValue, itemHardCap);
|
|
||||||
if(toMove <= 0) continue;
|
|
||||||
|
|
||||||
ItemStack checkStack = destStack.copy();
|
|
||||||
checkStack.stackSize += toMove;
|
|
||||||
if(!dest.isItemValidForSlot(destIndex, checkStack)) continue;
|
|
||||||
if(sidedDest != null && !sidedDest.canInsertItem(destIndex, checkStack, destSide)) continue;
|
|
||||||
|
|
||||||
sourceStack.stackSize -= toMove;
|
|
||||||
if(sourceStack.stackSize <= 0) source.setInventorySlotContents(sourceIndex, null);
|
|
||||||
destStack.stackSize += toMove;
|
|
||||||
itemsLeftToSend -= toMove * proportionalValue;
|
|
||||||
didSomething = true;
|
|
||||||
if(itemsLeftToSend <= 0) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there's stuff left to send, occupy empty slots
|
|
||||||
if(itemsLeftToSend > 0 && sourceStack.stackSize > 0) for(int destIndex : destSlotAccess) {
|
|
||||||
if(dest.getStackInSlot(destIndex) != null) continue;
|
|
||||||
int toMove = BobMathUtil.min(sourceStack.stackSize, dest.getInventoryStackLimit(), itemsLeftToSend / proportionalValue, itemHardCap);
|
|
||||||
if(toMove <= 0) continue;
|
|
||||||
|
|
||||||
ItemStack checkStack = sourceStack.copy();
|
|
||||||
checkStack.stackSize = toMove;
|
|
||||||
if(!dest.isItemValidForSlot(destIndex, checkStack)) continue;
|
|
||||||
if(sidedDest != null && !sidedDest.canInsertItem(destIndex, checkStack, destSide)) continue;
|
|
||||||
|
|
||||||
ItemStack newStack = sourceStack.copy();
|
int attempts = 0;
|
||||||
newStack.stackSize = toMove;
|
int maxAttempts = receiverList.size();
|
||||||
sourceStack.stackSize -= toMove;
|
|
||||||
if(sourceStack.stackSize <= 0) source.setInventorySlotContents(sourceIndex, null);
|
// try all receivers for both modes, in an attempts based system.
|
||||||
dest.setInventorySlotContents(destIndex, newStack);
|
// instead of bailing out of trying after the first failure (which means you have to wait 0.25 seconds), we just try the next one.
|
||||||
itemsLeftToSend -= toMove * proportionalValue;
|
while(attempts < maxAttempts) {
|
||||||
didSomething = true;
|
int index = (receiveOrder == RECEIVE_ROBIN) ? (nextReceiver + attempts) % receiverList.size() : attempts;
|
||||||
|
|
||||||
|
Entry<IInventory, Triplet<ForgeDirection, Long, TileEntityPneumoTube>> candidate = receiverList.get(index);
|
||||||
|
|
||||||
|
if(NodeNet.isBadLink(candidate.getKey())) {
|
||||||
|
receivers.remove(candidate.getKey());
|
||||||
|
attempts++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
IInventory dest = candidate.getKey();
|
||||||
|
TileEntityPneumoTube endpointTile = candidate.getValue().getZ();
|
||||||
|
TileEntity tile2 = dest instanceof TileEntity ? (TileEntity) dest : null;
|
||||||
|
|
||||||
|
// range check for our compression level, skip if either source or dest aren't tile entities
|
||||||
|
if(tile1 != null && tile2 != null) {
|
||||||
|
int sq = (tile1.xCoord - tile2.xCoord) * (tile1.xCoord - tile2.xCoord) + (tile1.yCoord - tile2.yCoord) * (tile1.yCoord - tile2.yCoord) + (tile1.zCoord - tile2.zCoord) * (tile1.zCoord - tile2.zCoord);
|
||||||
|
if(sq > maxRange * maxRange) {
|
||||||
|
attempts++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ISidedInventory sidedDest = dest instanceof ISidedInventory ? (ISidedInventory) dest : null;
|
||||||
|
int destSide = candidate.getValue().getX().getOpposite().ordinal();
|
||||||
|
int[] destSlotAccess = getSlotAccess(dest, destSide);
|
||||||
|
int itemsLeftToSend = ITEMS_PER_TRANSFER; // not actually individual items, but rather the total "mass", based on max stack size
|
||||||
|
int itemHardCap = dest instanceof TileEntityMachineAutocrafter ? 1 : ITEMS_PER_TRANSFER;
|
||||||
|
boolean didSomething = false;
|
||||||
|
|
||||||
|
for(int sourceIndex : sourceSlotAccess) {
|
||||||
|
ItemStack sourceStack = source.getStackInSlot(sourceIndex);
|
||||||
|
if(sourceStack == null) continue;
|
||||||
|
if(sidedSource != null && !sidedSource.canExtractItem(sourceIndex, sourceStack, sourceSide)) continue;
|
||||||
|
// filter of the source
|
||||||
|
boolean match = tube.matchesFilter(sourceStack);
|
||||||
|
if((match && !tube.whitelist) || (!match && tube.whitelist)) continue;
|
||||||
|
// filter of the receiver, only if the sender and receiver aren't the same block
|
||||||
|
if(endpointTile != null && endpointTile != tube) {
|
||||||
|
match = endpointTile.matchesFilter(sourceStack);
|
||||||
|
if((match && !endpointTile.whitelist) || (!match && endpointTile.whitelist)) continue;
|
||||||
|
}
|
||||||
|
// the "mass" of an item. something that only stacks to 4 has a "mass" of 16. max transfer mass is 64, i.e. one standard stack, or one single unstackable item
|
||||||
|
int proportionalValue = MathHelper.clamp_int(64 / sourceStack.getMaxStackSize(), 1, 64);
|
||||||
|
|
||||||
|
// try to fill partial stacks first
|
||||||
|
for(int destIndex : destSlotAccess) {
|
||||||
|
ItemStack destStack = dest.getStackInSlot(destIndex);
|
||||||
|
if(destStack == null) continue;
|
||||||
|
if(!ItemStackUtil.areStacksCompatible(sourceStack, destStack)) continue;
|
||||||
|
int toMove = BobMathUtil.min(sourceStack.stackSize, destStack.getMaxStackSize() - destStack.stackSize, dest.getInventoryStackLimit() - destStack.stackSize, itemsLeftToSend / proportionalValue, itemHardCap);
|
||||||
|
if(toMove <= 0) continue;
|
||||||
|
|
||||||
|
ItemStack checkStack = destStack.copy();
|
||||||
|
checkStack.stackSize += toMove;
|
||||||
|
if(!dest.isItemValidForSlot(destIndex, checkStack)) continue;
|
||||||
|
if(sidedDest != null && !sidedDest.canInsertItem(destIndex, checkStack, destSide)) continue;
|
||||||
|
|
||||||
|
sourceStack.stackSize -= toMove;
|
||||||
|
if(sourceStack.stackSize <= 0) source.setInventorySlotContents(sourceIndex, null);
|
||||||
|
destStack.stackSize += toMove;
|
||||||
|
itemsLeftToSend -= toMove * proportionalValue;
|
||||||
|
didSomething = true;
|
||||||
|
if(itemsLeftToSend <= 0) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there's stuff left to send, occupy empty slots
|
||||||
|
if(itemsLeftToSend > 0 && sourceStack.stackSize > 0) for(int destIndex : destSlotAccess) {
|
||||||
|
if(dest.getStackInSlot(destIndex) != null) continue;
|
||||||
|
int toMove = BobMathUtil.min(sourceStack.stackSize, dest.getInventoryStackLimit(), itemsLeftToSend / proportionalValue, itemHardCap);
|
||||||
|
if(toMove <= 0) continue;
|
||||||
|
|
||||||
|
ItemStack checkStack = sourceStack.copy();
|
||||||
|
checkStack.stackSize = toMove;
|
||||||
|
if(!dest.isItemValidForSlot(destIndex, checkStack)) continue;
|
||||||
|
if(sidedDest != null && !sidedDest.canInsertItem(destIndex, checkStack, destSide)) continue;
|
||||||
|
|
||||||
|
ItemStack newStack = sourceStack.copy();
|
||||||
|
newStack.stackSize = toMove;
|
||||||
|
sourceStack.stackSize -= toMove;
|
||||||
|
if(sourceStack.stackSize <= 0) source.setInventorySlotContents(sourceIndex, null);
|
||||||
|
dest.setInventorySlotContents(destIndex, newStack);
|
||||||
|
itemsLeftToSend -= toMove * proportionalValue;
|
||||||
|
didSomething = true;
|
||||||
|
if(itemsLeftToSend <= 0) break;
|
||||||
|
}
|
||||||
|
|
||||||
if(itemsLeftToSend <= 0) break;
|
if(itemsLeftToSend <= 0) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(itemsLeftToSend <= 0) break;
|
// make sure both parties are saved to disk and increment the counter for round robin
|
||||||
|
if(didSomething) {
|
||||||
|
source.markDirty();
|
||||||
|
dest.markDirty();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
attempts++;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
// make sure both parties are saved to disk and increment the counter for round robin
|
|
||||||
if(didSomething) {
|
|
||||||
source.markDirty();
|
|
||||||
dest.markDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
return didSomething;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns an array of accessible slots from the given side of an IInventory. If it's an ISidedInventory, uses the sided restrictions instead. */
|
/** Returns an array of accessible slots from the given side of an IInventory. If it's an ISidedInventory, uses the sided restrictions instead. */
|
||||||
public static int[] getSlotAccess(IInventory inventory, int dir) {
|
public static int[] getSlotAccess(IInventory inventory, int dir) {
|
||||||
|
|
||||||
if(inventory instanceof ISidedInventory) {
|
if(inventory instanceof ISidedInventory) {
|
||||||
int[] slotAccess = ((ISidedInventory) inventory).getAccessibleSlotsFromSide(dir);
|
int[] slotAccess = ((ISidedInventory) inventory).getAccessibleSlotsFromSide(dir);
|
||||||
return Arrays.copyOf(slotAccess, slotAccess.length); //we mess with the order, so better not use the original array
|
return Arrays.copyOf(slotAccess, slotAccess.length); //we mess with the order, so better not use the original array
|
||||||
@ -184,12 +215,12 @@ public class PneumaticNetwork extends NodeNet {
|
|||||||
return slotAccess;
|
return slotAccess;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Compares IInventory by distance, going off the assumption that they are TileEntities. Uses positional data for tie-breaking if the distance is the same. */
|
/** Compares IInventory by distance, going off the assumption that they are TileEntities. Uses positional data for tie-breaking if the distance is the same. */
|
||||||
public static class ReceiverComparator implements Comparator<Entry<IInventory, Triplet<ForgeDirection, Long, TileEntityPneumoTube>>> {
|
public static class ReceiverComparator implements Comparator<Entry<IInventory, Triplet<ForgeDirection, Long, TileEntityPneumoTube>>> {
|
||||||
|
|
||||||
private TileEntityPneumoTube origin;
|
private TileEntityPneumoTube origin;
|
||||||
|
|
||||||
public ReceiverComparator(TileEntityPneumoTube origin) {
|
public ReceiverComparator(TileEntityPneumoTube origin) {
|
||||||
this.origin = origin;
|
this.origin = origin;
|
||||||
}
|
}
|
||||||
@ -204,11 +235,11 @@ public class PneumaticNetwork extends NodeNet {
|
|||||||
if(tile1 == null && tile2 != null) return 1;
|
if(tile1 == null && tile2 != null) return 1;
|
||||||
if(tile1 != null && tile2 == null) return -1;
|
if(tile1 != null && tile2 == null) return -1;
|
||||||
if(tile1 == null && tile2 == null) return 0;
|
if(tile1 == null && tile2 == null) return 0;
|
||||||
|
|
||||||
// calculate distances from origin
|
// calculate distances from origin
|
||||||
int dist1 = (tile1.xCoord - origin.xCoord) * (tile1.xCoord - origin.xCoord) + (tile1.yCoord - origin.yCoord) * (tile1.yCoord - origin.yCoord) + (tile1.zCoord - origin.zCoord) * (tile1.zCoord - origin.zCoord);
|
int dist1 = (tile1.xCoord - origin.xCoord) * (tile1.xCoord - origin.xCoord) + (tile1.yCoord - origin.yCoord) * (tile1.yCoord - origin.yCoord) + (tile1.zCoord - origin.zCoord) * (tile1.zCoord - origin.zCoord);
|
||||||
int dist2 = (tile2.xCoord - origin.xCoord) * (tile2.xCoord - origin.xCoord) + (tile2.yCoord - origin.yCoord) * (tile2.yCoord - origin.yCoord) + (tile2.zCoord - origin.zCoord) * (tile2.zCoord - origin.zCoord);
|
int dist2 = (tile2.xCoord - origin.xCoord) * (tile2.xCoord - origin.xCoord) + (tile2.yCoord - origin.yCoord) * (tile2.yCoord - origin.yCoord) + (tile2.zCoord - origin.zCoord) * (tile2.zCoord - origin.zCoord);
|
||||||
|
|
||||||
// tier-breaker: use hash value instead
|
// tier-breaker: use hash value instead
|
||||||
if(dist1 == dist2) {
|
if(dist1 == dist2) {
|
||||||
return TileEntityPneumoTube.getIdentifier(tile1.xCoord, tile1.yCoord, tile1.zCoord) - TileEntityPneumoTube.getIdentifier(tile2.xCoord, tile2.yCoord, tile2.zCoord);
|
return TileEntityPneumoTube.getIdentifier(tile1.xCoord, tile1.yCoord, tile1.zCoord) - TileEntityPneumoTube.getIdentifier(tile2.xCoord, tile2.yCoord, tile2.zCoord);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user