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:
Aidan C. Brady 2014-08-21 20:46:51 -04:00
parent 80021dd4c7
commit f87cc4a1e3
7 changed files with 117 additions and 45 deletions

View file

@ -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);

View file

@ -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()

View file

@ -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];

View file

@ -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;
}

View file

@ -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"))));

View file

@ -539,13 +539,13 @@ public abstract class PartSidedPipe extends TMultiPart implements TSlottedPart,
}
}
}
if(!world().isRemote)
{
currentTransmitterConnections = possibleTransmitters;
currentAcceptorConnections = possibleAcceptors;
}
onRefresh();
if(!world().isRemote)

View 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;
}
}