Logistical Transporter path caching.
I managed to take the average path calculation time down from 4-8 milliseconds to 100-200 microseconds. All in a day's work!
This commit is contained in:
parent
80021dd4c7
commit
f87cc4a1e3
7 changed files with 117 additions and 45 deletions
|
@ -23,8 +23,6 @@ public interface ILogisticalTransporter extends IBlockableConnection, IGridTrans
|
|||
|
||||
public void setColor(EnumColor c);
|
||||
|
||||
public TileEntity getTile();
|
||||
|
||||
public boolean canEmitTo(TileEntity tileEntity, ForgeDirection side);
|
||||
|
||||
public boolean canReceiveFrom(TileEntity tileEntity, ForgeDirection side);
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package mekanism.common.content.transporter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import mekanism.api.Coord4D;
|
||||
import mekanism.common.Mekanism;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
public class PathfinderCache
|
||||
|
@ -15,20 +16,27 @@ public class PathfinderCache
|
|||
|
||||
public static void onChanged(Coord4D location)
|
||||
{
|
||||
Set<PathData> toKill = new HashSet<PathData>();
|
||||
reset();
|
||||
}
|
||||
|
||||
public static ArrayList<Coord4D> getCache(Coord4D start, Coord4D end, EnumSet<ForgeDirection> sides)
|
||||
{
|
||||
ArrayList<Coord4D> ret = null;
|
||||
|
||||
for(Map.Entry<PathData, List<Coord4D>> entry : cachedPaths.entrySet())
|
||||
{
|
||||
if(entry.getValue().contains(entry))
|
||||
PathData data = entry.getKey();
|
||||
|
||||
if(data.startTransporter.equals(start) && data.end.equals(end) && sides.contains(data.endSide))
|
||||
{
|
||||
toKill.add(entry.getKey());
|
||||
if(ret == null || entry.getValue().size() < ret.size())
|
||||
{
|
||||
ret = (ArrayList)entry.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(PathData path : toKill)
|
||||
{
|
||||
cachedPaths.remove(path);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void reset()
|
||||
|
|
|
@ -309,11 +309,6 @@ public class TransporterManager
|
|||
|
||||
if(slots != null && slots.length != 0)
|
||||
{
|
||||
/*if(sidedInventory instanceof TileEntityBin && ForgeDirection.getOrientation(side).getOpposite().ordinal() == 0)
|
||||
{
|
||||
slots = sidedInventory.getAccessibleSlotsFromSide(1);
|
||||
}*/
|
||||
|
||||
for(int get = 0; get <= slots.length - 1; get++)
|
||||
{
|
||||
int slotID = slots[get];
|
||||
|
|
|
@ -12,10 +12,13 @@ import java.util.Set;
|
|||
import mekanism.api.Coord4D;
|
||||
import mekanism.common.InventoryNetwork;
|
||||
import mekanism.common.InventoryNetwork.AcceptorData;
|
||||
import mekanism.common.Mekanism;
|
||||
import mekanism.common.base.ILogisticalTransporter;
|
||||
import mekanism.common.content.transporter.PathfinderCache.PathData;
|
||||
import mekanism.common.content.transporter.TransporterPathfinder.Pathfinder.DestChecker;
|
||||
import mekanism.common.content.transporter.TransporterStack.Path;
|
||||
import mekanism.common.tile.TileEntityLogisticalSorter;
|
||||
import mekanism.common.util.DebugUtils;
|
||||
import mekanism.common.util.InventoryUtils;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
|
@ -55,7 +58,7 @@ public final class TransporterPathfinder
|
|||
|
||||
transportStack.idleDir = newSide;
|
||||
loopSide(ret, newSide);
|
||||
return new Destination(ret, 0, true, null).setPathType(Path.NONE);
|
||||
return new Destination(ret, true, null).setPathType(Path.NONE);
|
||||
}
|
||||
else {
|
||||
TileEntity tile = start.getFromSide(transportStack.idleDir).getTileEntity(worldObj);
|
||||
|
@ -63,7 +66,7 @@ public final class TransporterPathfinder
|
|||
if(transportStack.canInsertToTransporter(tile, transportStack.idleDir))
|
||||
{
|
||||
loopSide(ret, transportStack.idleDir);
|
||||
return new Destination(ret, 0, true, null).setPathType(Path.NONE);
|
||||
return new Destination(ret, true, null).setPathType(Path.NONE);
|
||||
}
|
||||
else {
|
||||
Destination newPath = TransporterPathfinder.getNewBasePath((ILogisticalTransporter)start.getTileEntity(worldObj), transportStack, 0);
|
||||
|
@ -84,7 +87,7 @@ public final class TransporterPathfinder
|
|||
|
||||
transportStack.idleDir = newSide;
|
||||
loopSide(ret, newSide);
|
||||
return new Destination(ret, 0, true, null).setPathType(Path.NONE);
|
||||
return new Destination(ret, true, null).setPathType(Path.NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,10 +153,9 @@ public final class TransporterPathfinder
|
|||
{
|
||||
public List<Coord4D> path = new ArrayList<Coord4D>();
|
||||
public Path pathType;
|
||||
public double score;
|
||||
public ItemStack rejected;
|
||||
|
||||
public Destination(ArrayList<Coord4D> list, double d, boolean inv, ItemStack rejects)
|
||||
public Destination(ArrayList<Coord4D> list, boolean inv, ItemStack rejects)
|
||||
{
|
||||
path = (List<Coord4D>)list.clone();
|
||||
|
||||
|
@ -162,7 +164,6 @@ public final class TransporterPathfinder
|
|||
Collections.reverse(path);
|
||||
}
|
||||
|
||||
score = d;
|
||||
rejected = rejects;
|
||||
}
|
||||
|
||||
|
@ -177,24 +178,23 @@ public final class TransporterPathfinder
|
|||
{
|
||||
int code = 1;
|
||||
code = 31 * code + path.hashCode();
|
||||
code = 31 * code + new Double(score).hashCode();
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object dest)
|
||||
{
|
||||
return dest instanceof Destination && ((Destination)dest).path.equals(path) && ((Destination)dest).score == score;
|
||||
return dest instanceof Destination && ((Destination)dest).path.equals(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Destination dest)
|
||||
{
|
||||
if(score < dest.score)
|
||||
if(path.size() < dest.path.size())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if(score > dest.score)
|
||||
else if(path.size() > dest.path.size())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
@ -205,30 +205,27 @@ public final class TransporterPathfinder
|
|||
}
|
||||
|
||||
public static List<Destination> getPaths(ILogisticalTransporter start, TransporterStack stack, int min)
|
||||
{
|
||||
DestChecker checker = new DestChecker()
|
||||
{
|
||||
@Override
|
||||
public boolean isValid(TransporterStack stack, int side, TileEntity tile)
|
||||
{
|
||||
return InventoryUtils.canInsert(tile, stack.color, stack.itemStack, side, false);
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
InventoryNetwork network = start.getTransmitterNetwork();
|
||||
List<AcceptorData> acceptors = network.calculateAcceptors(stack.itemStack, stack.color);
|
||||
List<Destination> paths = new ArrayList<Destination>();
|
||||
|
||||
for(AcceptorData entry : acceptors)
|
||||
{
|
||||
Pathfinder p = new Pathfinder(checker, start.getTile().getWorldObj(), entry.location, Coord4D.get(start.getTile()), stack);
|
||||
|
||||
if(p.getPath().size() >= 2)
|
||||
DestChecker checker = new DestChecker()
|
||||
{
|
||||
if(TransporterManager.getToUse(stack.itemStack, entry.rejected).stackSize >= min)
|
||||
@Override
|
||||
public boolean isValid(TransporterStack stack, int dir, TileEntity tile)
|
||||
{
|
||||
paths.add(new Destination(p.getPath(), p.finalScore, false, entry.rejected));
|
||||
return InventoryUtils.canInsert(tile, stack.color, stack.itemStack, dir, false);
|
||||
}
|
||||
};
|
||||
|
||||
Destination d = getPath(checker, entry.sides, start, entry.location, stack, entry.rejected, min);
|
||||
|
||||
if(d != null)
|
||||
{
|
||||
paths.add(d);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,7 +233,31 @@ public final class TransporterPathfinder
|
|||
|
||||
return paths;
|
||||
}
|
||||
|
||||
|
||||
public static Destination getPath(DestChecker checker, EnumSet<ForgeDirection> sides, ILogisticalTransporter start, Coord4D dest, TransporterStack stack, ItemStack rejects, int min)
|
||||
{
|
||||
ArrayList<Coord4D> test = PathfinderCache.getCache(Coord4D.get(start.getTile()), dest, sides);
|
||||
|
||||
if(test != null)
|
||||
{
|
||||
return new Destination(test, false, rejects);
|
||||
}
|
||||
|
||||
Pathfinder p = new Pathfinder(checker, start.getTile().getWorldObj(), dest, Coord4D.get(start.getTile()), stack);
|
||||
|
||||
if(p.getPath().size() >= 2)
|
||||
{
|
||||
if(TransporterManager.getToUse(stack.itemStack, rejects).stackSize >= min)
|
||||
{
|
||||
PathfinderCache.cachedPaths.put(new PathData(Coord4D.get(start.getTile()), dest, p.side), p.getPath());
|
||||
|
||||
return new Destination(p.getPath(), false, rejects);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Destination getNewBasePath(ILogisticalTransporter start, TransporterStack stack, int min)
|
||||
{
|
||||
List<Destination> paths = getPaths(start, stack, min);
|
||||
|
@ -245,6 +266,11 @@ public final class TransporterPathfinder
|
|||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for(Destination d : paths)
|
||||
{
|
||||
Mekanism.logger.info(d.path.size());
|
||||
}
|
||||
|
||||
return paths.get(0);
|
||||
}
|
||||
|
@ -257,7 +283,7 @@ public final class TransporterPathfinder
|
|||
|
||||
for(Destination d : paths)
|
||||
{
|
||||
if(destPaths.get(d.path.get(0)) == null || destPaths.get(d.path.get(0)).score < d.score)
|
||||
if(destPaths.get(d.path.get(0)) == null || destPaths.get(d.path.get(0)).path.size() < d.path.size())
|
||||
{
|
||||
destPaths.put(d.path.get(0), d);
|
||||
}
|
||||
|
@ -316,6 +342,8 @@ public final class TransporterPathfinder
|
|||
public final DestChecker destChecker;
|
||||
|
||||
public double finalScore;
|
||||
|
||||
public ForgeDirection side;
|
||||
|
||||
public ArrayList<Coord4D> results;
|
||||
|
||||
|
@ -424,6 +452,7 @@ public final class TransporterPathfinder
|
|||
}
|
||||
else if(neighbor.equals(finalNode) && destChecker.isValid(transportStack, i, neighbor.getTileEntity(worldObj)))
|
||||
{
|
||||
side = direction;
|
||||
results = reconstructPath(navMap, currentNode);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import mekanism.common.InventoryNetwork;
|
|||
import mekanism.common.Mekanism;
|
||||
import mekanism.common.base.ILogisticalTransporter;
|
||||
import mekanism.common.content.transporter.InvStack;
|
||||
import mekanism.common.content.transporter.PathfinderCache;
|
||||
import mekanism.common.content.transporter.TransporterManager;
|
||||
import mekanism.common.content.transporter.TransporterStack;
|
||||
import mekanism.common.content.transporter.TransporterStack.Path;
|
||||
|
@ -94,6 +95,17 @@ public class PartLogisticalTransporter extends PartTransmitter<InventoryNetwork>
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWorldSeparate()
|
||||
{
|
||||
super.onWorldSeparate();
|
||||
|
||||
if(!world().isRemote)
|
||||
{
|
||||
PathfinderCache.onChanged(Coord4D.get(tile()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidTransmitter(TileEntity tileEntity)
|
||||
{
|
||||
|
@ -479,6 +491,9 @@ public class PartLogisticalTransporter extends PartTransmitter<InventoryNetwork>
|
|||
{
|
||||
Mekanism.packetHandler.sendToServer(new DataRequestMessage(Coord4D.get(tile())));
|
||||
}
|
||||
else {
|
||||
PathfinderCache.onChanged(Coord4D.get(tile()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -660,6 +675,7 @@ public class PartLogisticalTransporter extends PartTransmitter<InventoryNetwork>
|
|||
TransporterUtils.incrementColor(this);
|
||||
refreshConnections();
|
||||
tile().notifyTileChange();
|
||||
PathfinderCache.onChanged(Coord4D.get(tile()));
|
||||
Mekanism.packetHandler.sendToReceivers(new TileEntityMessage(Coord4D.get(tile()), getNetworkedData(new ArrayList())), new Range4D(Coord4D.get(tile())));
|
||||
player.addChatMessage(new ChatComponentText(EnumColor.DARK_BLUE + "[Mekanism]" + EnumColor.GREY + " " + MekanismUtils.localize("tooltip.configurator.toggleColor") + ": " + (color != null ? color.getName() : EnumColor.BLACK + MekanismUtils.localize("gui.none"))));
|
||||
|
||||
|
|
|
@ -539,13 +539,13 @@ public abstract class PartSidedPipe extends TMultiPart implements TSlottedPart,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(!world().isRemote)
|
||||
{
|
||||
currentTransmitterConnections = possibleTransmitters;
|
||||
currentAcceptorConnections = possibleAcceptors;
|
||||
}
|
||||
|
||||
|
||||
onRefresh();
|
||||
|
||||
if(!world().isRemote)
|
||||
|
|
26
src/main/java/mekanism/common/util/DebugUtils.java
Normal file
26
src/main/java/mekanism/common/util/DebugUtils.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
package mekanism.common.util;
|
||||
|
||||
public class DebugUtils
|
||||
{
|
||||
private static long prevNanoTime;
|
||||
|
||||
public static void startTracking()
|
||||
{
|
||||
prevNanoTime = System.nanoTime();
|
||||
}
|
||||
|
||||
public static long getTrackedNanos()
|
||||
{
|
||||
return System.nanoTime()-prevNanoTime;
|
||||
}
|
||||
|
||||
public static long getTrackedMicros()
|
||||
{
|
||||
return getTrackedNanos()/1000;
|
||||
}
|
||||
|
||||
public static long getTrackedMillis()
|
||||
{
|
||||
return getTrackedMicros()/1000;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue