From 13699c24b3f1f78ed178aab36b52f5b196535325 Mon Sep 17 00:00:00 2001 From: Robert Seifert Date: Thu, 23 May 2013 02:56:49 -0400 Subject: [PATCH] Improved and randomized pathfinder Pathfinder now works better --It levels of at the correct y level for pathfinding --It pathfinder no longer in a perfect line but is randomized --Pathfinder is now limited to search with a set range preventing cross map interaction --- .../common/pump/TileEntityDrain.java | 161 +++++++++++------- .../common/pump/path/LiquidPathFinder.java | 58 +++++-- .../common/pump/path/LiquidPathFinder2D.java | 139 +++++++++++++++ 3 files changed, 275 insertions(+), 83 deletions(-) create mode 100644 src/minecraft/fluidmech/common/pump/path/LiquidPathFinder2D.java diff --git a/src/minecraft/fluidmech/common/pump/TileEntityDrain.java b/src/minecraft/fluidmech/common/pump/TileEntityDrain.java index 71d39f428..eaa36dfc3 100644 --- a/src/minecraft/fluidmech/common/pump/TileEntityDrain.java +++ b/src/minecraft/fluidmech/common/pump/TileEntityDrain.java @@ -23,6 +23,7 @@ import net.minecraftforge.liquids.ILiquidTank; import net.minecraftforge.liquids.ITankContainer; import net.minecraftforge.liquids.LiquidContainerRegistry; import net.minecraftforge.liquids.LiquidStack; +import universalelectricity.core.vector.Vector2; import universalelectricity.core.vector.Vector3; import universalelectricity.core.vector.VectorHelper; @@ -37,7 +38,16 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta private List targetSources = new ArrayList(); private List updateQue = new ArrayList(); - private LiquidPathFinder pathFinder; + private LiquidPathFinder pathLiquid; + + public LiquidPathFinder getLiquidFinder() + { + if(pathLiquid == null) + { + pathLiquid = new LiquidPathFinder(this.worldObj, false, 1000, 100); + } + return pathLiquid; + } @Override public String getMeterReading(EntityPlayer user, ForgeDirection side) @@ -49,7 +59,6 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta public void invalidate() { super.invalidate(); - pathFinder = new LiquidPathFinder(this.worldObj, false, TileEntityDrain.MAX_WORLD_EDITS_PER_PROCESS * 2); } public boolean canDrainSources() @@ -99,13 +108,17 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta { break; } + if (request.getKey() instanceof ITankContainer) { ITankContainer tank = (ITankContainer) request.getKey(); - Iterator it = this.targetSources.iterator(); - while (it.hasNext()) + + Vector3[] sortedList = this.sortedDrainList(); + + for (int i = 0; sortedList != null && i < sortedList.length; i++) { - Vector3 loc = it.next(); + Vector3 loc = sortedList[i]; + if (this.currentWorldEdits >= MAX_WORLD_EDITS_PER_PROCESS) { break; @@ -142,8 +155,6 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta /* REMOVE BLOCK */ loc.setBlock(this.worldObj, 0, 0, 2); this.currentWorldEdits++; - it.remove(); - } } } @@ -159,15 +170,12 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta */ public void getNextFluidBlock() { - if (pathFinder == null) - { - pathFinder = new LiquidPathFinder(this.worldObj, false, 1000); - } - pathFinder.reset(); - pathFinder.init(new Vector3(this.xCoord + this.getFacing().offsetX, this.yCoord + this.getFacing().offsetY, this.zCoord + this.getFacing().offsetZ)); + + getLiquidFinder().reset(); + getLiquidFinder().init(new Vector3(this.xCoord + this.getFacing().offsetX, this.yCoord + this.getFacing().offsetY, this.zCoord + this.getFacing().offsetZ)); // System.out.println("Nodes:" + pathFinder.nodes.size() + "Results:" + // pathFinder.results.size()); - for (Vector3 vec : pathFinder.nodes) + for (Vector3 vec : getLiquidFinder().nodes) { this.addVectorToQue(vec); } @@ -211,73 +219,95 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta requests.remove(); } } - /* CLEANUP TARGET LIST AND REMOVE INVALID SOURCES */ - Iterator targetIt = this.targetSources.iterator(); - while (targetIt.hasNext()) - { - Vector3 vec = targetIt.next(); - if (!FluidHelper.isSourceBlock(this.worldObj, vec)) - { - targetIt.remove(); - } - } - /* SORT RESULTS TO PUT THE HiGHEST AND FURTHEST AT THE TOP */ + + } + + public Vector3[] sortedDrainList() + { try { - List updatedList = new ArrayList(); - updatedList.addAll(this.targetSources); - - for (int i = 0; i < updatedList.size(); i++) + /* CLEANUP TARGET LIST AND REMOVE INVALID SOURCES */ + Iterator targetIt = this.targetSources.iterator(); + while (targetIt.hasNext()) { - Vector3 vec = updatedList.get(i).clone(); - if (i + 1 < updatedList.size()) + Vector3 vec = targetIt.next(); + if (!FluidHelper.isSourceBlock(this.worldObj, vec)) { - for (int b = i + 1; b < updatedList.size(); b++) + targetIt.remove(); + } + } + + Vector3[] sortedList = new Vector3[this.targetSources.size()]; + for (int b = 0; b < this.targetSources.size(); b++) + { + sortedList[b] = this.targetSources.get(b); + } + + /* SORT RESULTS TO PUT THE HiGHEST AND FURTHEST AT THE TOP */ + Vector2 machine = new Vector3(this).toVector2(); + for (int i = 0; i < sortedList.length; i++) + { + Vector3 vec = sortedList[i].clone(); + Vector2 first = vec.toVector2(); + if (i + 1 < sortedList.length) + { + Vector3 highest = vec; + int b = 0; + for (b = i + 1; b < sortedList.length; b++) { - Vector3 checkVec = updatedList.get(b).clone(); + Vector3 checkVec = sortedList[b].clone(); if (checkVec != null) { - if (checkVec.distanceTo(new Vector3(this)) > vec.distanceTo(new Vector3(this))) + Vector2 second = checkVec.toVector2(); + + if (second.distanceTo(machine) > vec.toVector2().distanceTo(machine)) { - updatedList.set(i, checkVec); - updatedList.set(b, vec); - break; + highest = checkVec.clone(); } } } - } - } - for (int i = 0; i < updatedList.size(); i++) - { - Vector3 vec = updatedList.get(i).clone(); - if (i + 1 < updatedList.size()) - { - for (int b = i + 1; b < updatedList.size(); b++) + if (b < sortedList.length) { - Vector3 checkVec = updatedList.get(b).clone(); - if (checkVec != null) - { - if (checkVec.intY() > vec.intY()) - { - updatedList.set(i, checkVec); - updatedList.set(b, vec); - break; - } - } + sortedList[i] = vec; + sortedList[b] = highest; } } } - this.targetSources.clear(); - this.targetSources.addAll(updatedList); - updatedList.clear(); + for (int i = 0; i < sortedList.length; i++) + { + Vector3 vec = sortedList[i].clone(); + if (i + 1 < sortedList.length) + { + Vector3 highest = vec; + int b = 0; + for (b = i + 1; b < sortedList.length; b++) + { + Vector3 checkVec = sortedList[b].clone(); + if (checkVec != null) + { + if (checkVec.intY() > highest.intY()) + { + highest = checkVec.clone(); + } + } + } + if (b < sortedList.length) + { + sortedList[i] = vec; + sortedList[b] = highest; + } + } + } + return sortedList; } catch (Exception e) { - System.out.println("FluidMech: Error sorting fill collection \n"); + System.out.println("FluidMech: Critical Error Processing Drain List"); e.printStackTrace(); } + return null; } @Override @@ -318,17 +348,16 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta int blocks = (resource.amount / LiquidContainerRegistry.BUCKET_VOLUME); - /* FIND ALL VALID BLOCKS ON LEVEL OR BELLOW */ - LiquidPathFinder pathFinder = new LiquidPathFinder(this.worldObj, true, this.MAX_WORLD_EDITS_PER_PROCESS * 2); + /* FIND ALL VALID BLOCKS ON LEVEL OR BELLOW */ final Vector3 faceVec = new Vector3(this.xCoord + this.getFacing().offsetX, this.yCoord + this.getFacing().offsetY, this.zCoord + this.getFacing().offsetZ); - pathFinder.init(faceVec); + getLiquidFinder().init(faceVec); /* SORT RESULTS TO PUT THE LOWEST AND CLOSEST AT THE TOP */ try { - if (pathFinder.results.size() > 1) + if (getLiquidFinder().results.size() > 1) { - Collections.sort(pathFinder.results, new Comparator() + Collections.sort(getLiquidFinder().results, new Comparator() { @Override public int compare(Object o1, Object o2) @@ -362,7 +391,7 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta } /* START FILLING IN OR CHECKING IF CAN FILL AREA */ int fillable = 0; - for (Vector3 loc : pathFinder.results) + for (Vector3 loc : getLiquidFinder().results) { if (blocks <= 0) { @@ -387,7 +416,7 @@ public class TileEntityDrain extends TileEntityFluidDevice implements ITankConta } - for (Vector3 loc : pathFinder.results) + for (Vector3 loc : getLiquidFinder().results) { if (blocks <= 0) { diff --git a/src/minecraft/fluidmech/common/pump/path/LiquidPathFinder.java b/src/minecraft/fluidmech/common/pump/path/LiquidPathFinder.java index 6e326727d..f777a8d7d 100644 --- a/src/minecraft/fluidmech/common/pump/path/LiquidPathFinder.java +++ b/src/minecraft/fluidmech/common/pump/path/LiquidPathFinder.java @@ -3,11 +3,14 @@ package fluidmech.common.pump.path; import hydraulic.helpers.FluidHelper; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Random; import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; import net.minecraftforge.common.ForgeDirection; +import universalelectricity.core.vector.Vector2; import universalelectricity.core.vector.Vector3; /** @@ -24,9 +27,14 @@ public class LiquidPathFinder private boolean fill; /* ARE WE FILLING THE PATH OR DRAINING THE PATH */ private ForgeDirection priority; /* BASED ON fill -- WHICH DIRECTION WILL THE PATH GO FIRST */ private int resultLimit = 2000; + private Vector2 Start; + private double range; + private Random random = new Random(); + List bn = new ArrayList(); - public LiquidPathFinder(final World world, final boolean fill, final int resultLimit) + public LiquidPathFinder(final World world, final boolean fill, final int resultLimit, final double range) { + this.range = range; this.world = world; this.fill = fill; if (fill) @@ -39,6 +47,10 @@ public class LiquidPathFinder } this.resultLimit = resultLimit; this.reset(); + bn.add(ForgeDirection.EAST); + bn.add(ForgeDirection.WEST); + bn.add(ForgeDirection.NORTH); + bn.add(ForgeDirection.SOUTH); } /** @@ -73,28 +85,25 @@ public class LiquidPathFinder return false; } - vec = node.clone().modifyPositionFromSide(this.priority); - if (this.isValidNode(vec) & !this.nodes.contains(vec)) + if (find(this.priority, node.clone())) { - if (this.findNodes(vec)) + return true; + } + + Collections.shuffle(bn); + Collections.shuffle(bn); + + for (ForgeDirection direction : bn) + { + if (find(direction, vec)) { return true; } } - for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) + if (find(this.priority.getOpposite(), node.clone())) { - if (direction != this.priority) - { - vec = node.clone().modifyPositionFromSide(direction); - if (this.isValidNode(vec) & !this.nodes.contains(vec)) - { - if (this.findNodes(vec)) - { - return true; - } - } - } + return true; } } catch (Exception e) @@ -105,6 +114,20 @@ public class LiquidPathFinder return false; } + public boolean find(ForgeDirection direction, Vector3 vec) + { + vec = vec.clone().modifyPositionFromSide(direction); + double distance = vec.toVector2().distanceTo(this.Start); + if (distance <= this.range && this.isValidNode(vec) & !this.nodes.contains(vec)) + { + if (this.findNodes(vec)) + { + return true; + } + } + return false; + } + public boolean isValidNode(Vector3 pos) { int blockID = pos.getBlockID(world); @@ -130,8 +153,9 @@ public class LiquidPathFinder /** * Called to execute the pathfinding operation. */ - public LiquidPathFinder init(Vector3 startNode) + public LiquidPathFinder init(final Vector3 startNode) { + this.Start = startNode.toVector2(); this.findNodes(startNode); if (this.fill && this.isValidNode(startNode)) { diff --git a/src/minecraft/fluidmech/common/pump/path/LiquidPathFinder2D.java b/src/minecraft/fluidmech/common/pump/path/LiquidPathFinder2D.java new file mode 100644 index 000000000..d9f996960 --- /dev/null +++ b/src/minecraft/fluidmech/common/pump/path/LiquidPathFinder2D.java @@ -0,0 +1,139 @@ +package fluidmech.common.pump.path; + +import hydraulic.helpers.FluidHelper; + +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; +import net.minecraftforge.common.ForgeDirection; +import universalelectricity.core.vector.Vector3; + +/** + * A simpler pathfinder based on Calclavia's PathFinder from UE api + */ +public class LiquidPathFinder2D +{ + private World world; /* MC WORLD */ + public List nodes = new ArrayList(); /* + * LOCATIONs THE PATH FINDER HAS GONE + * OVER + */ + public List results = new ArrayList();/* LOCATIONS THAT ARE VALID RESULTS */ + private boolean fill; /* ARE WE FILLING THE PATH OR DRAINING THE PATH */ + private ForgeDirection priority; /* BASED ON fill -- WHICH DIRECTION WILL THE PATH GO FIRST */ + private int resultLimit = 2000; + + public LiquidPathFinder2D(final World world, final int resultLimit) + { + this.world = world; + this.fill = fill; + if (fill) + { + priority = ForgeDirection.DOWN; + } + else + { + priority = ForgeDirection.UP; + } + this.resultLimit = resultLimit; + this.reset(); + } + + /** + * @return True on success finding, false on failure. + */ + public boolean findNodes(final Vector3 node) + { + try + { + Vector3 vec = node.clone(); + this.nodes.add(node); + Chunk chunk = this.world.getChunkFromBlockCoords(vec.intX(), vec.intZ()); + + if (chunk == null || !chunk.isChunkLoaded) + { + return true; + } + + int id = node.getBlockID(world); + int meta = node.getBlockID(world); + if (this.fill && (id == 0 || (FluidHelper.getLiquidFromBlockId(id) != null && meta != 0))) + { + this.results.add(node); + } + else if (!this.fill && FluidHelper.isSourceBlock(world, node)) + { + this.results.add(node); + } + + if (this.isDone(node)) + { + return false; + } + for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) + { + if (direction != ForgeDirection.DOWN && direction != ForgeDirection.UP) + { + vec = node.clone().modifyPositionFromSide(direction); + if (this.isValidNode(vec) & !this.nodes.contains(vec)) + { + if (this.findNodes(vec)) + { + return true; + } + } + } + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return false; + } + + public boolean isValidNode(Vector3 pos) + { + int blockID = pos.getBlockID(world); + if (!this.fill) + { + return FluidHelper.getLiquidFromBlockId(pos.getBlockID(world)) != null; + } + else + { + return FluidHelper.getLiquidFromBlockId(pos.getBlockID(world)) != null || (blockID == 0 && FluidHelper.getConnectedSources(world, pos) > 0); + } + } + + public boolean isDone(Vector3 vec) + { + if (this.results.size() >= this.resultLimit || this.nodes.size() >= 4000) + { + return true; + } + return false; + } + + /** + * Called to execute the pathfinding operation. + */ + public LiquidPathFinder2D init(Vector3 startNode) + { + this.findNodes(startNode); + if (this.fill && this.isValidNode(startNode)) + { + + } + return this; + } + + public LiquidPathFinder2D reset() + { + this.nodes.clear(); + this.results.clear(); + return this; + } +}