/**
* Copyright (c) 2011-2015, SpaceToad and the BuildCraft Team
* http://www.mod-buildcraft.com
* <p/>
* BuildCraft is distributed under the terms of the Minecraft Mod Public
* License 1.0, or MMPL. Please check the contents of the license located in
* http://www.mod-buildcraft.com/MMPL-1.0.txt
*/
package buildcraft.core.lib.utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import net.minecraft.world.World;
import buildcraft.api.core.BlockIndex;
import buildcraft.api.core.BuildCraftAPI;
import buildcraft.api.core.IZone;
public class PathFindingSearch implements IIterableAlgorithm {
public static final int PATH_ITERATIONS = 1000;
private static final HashMap<Integer, HashSet<BlockIndex>> reservations = new HashMap<Integer, HashSet<BlockIndex>>();
private World world;
private BlockIndex start;
private List<PathFinding> pathFinders;
private IBlockFilter pathFound;
private IZone zone;
private float maxDistance;
private Iterator<BlockIndex> blockIter;
private double maxDistanceToEnd;
public PathFindingSearch(World iWorld, BlockIndex iStart,
Iterator<BlockIndex> iBlockIter, IBlockFilter iPathFound,
double iMaxDistanceToEnd, float iMaxDistance, IZone iZone) {
world = iWorld;
start = iStart;
pathFound = iPathFound;
maxDistance = iMaxDistance;
maxDistanceToEnd = iMaxDistanceToEnd;
zone = iZone;
blockIter = iBlockIter;
pathFinders = new LinkedList<PathFinding>();
}
@Override
public void iterate() {
if (pathFinders.size() < 5 && blockIter.hasNext()) {
iterateSearch(PATH_ITERATIONS * 10);
}
iteratePathFind(PATH_ITERATIONS);
}
private void iterateSearch(int itNumber) {
for (int i = 0; i < itNumber; ++i) {
if (!blockIter.hasNext()) {
return;
}
BlockIndex delta = blockIter.next();
BlockIndex block = new BlockIndex(start.x + delta.x,
((start.y + delta.y) > 0) ? start.y + delta.y : 0,
start.z + delta.z);
if (isLoadedChunk(block.x, block.z)) {
if (isTarget(block)) {
pathFinders.add(new PathFinding(world, start, block, maxDistanceToEnd, maxDistance));
}
}
if (pathFinders.size() >= 5) {
return;
}
}
}
private boolean isTarget(BlockIndex block) {
if (zone != null && !zone.contains(block.x, block.y, block.z)) {
return false;
}
if (!pathFound.matches(world, block.x, block.y, block.z)) {
return false;
}
synchronized (reservations) {
if (reservations.containsKey(world.provider.dimensionId)) {
HashSet<BlockIndex> dimReservations = reservations
.get(world.provider.dimensionId);
if (dimReservations.contains(block)) {
return false;
}
}
}
if (!BuildCraftAPI.isSoftBlock(world, block.x - 1, block.y, block.z)
&& !BuildCraftAPI.isSoftBlock(world, block.x + 1, block.y, block.z)
&& !BuildCraftAPI.isSoftBlock(world, block.x, block.y, block.z - 1)
&& !BuildCraftAPI.isSoftBlock(world, block.x, block.y, block.z + 1)
&& !BuildCraftAPI.isSoftBlock(world, block.x, block.y - 1, block.z)
&& !BuildCraftAPI.isSoftBlock(world, block.x, block.y + 1, block.z)) {
return false;
}
return true;
}
private boolean isLoadedChunk(int x, int z) {
return world.getChunkProvider().chunkExists(x >> 4, z >> 4);
}
public void iteratePathFind(int itNumber) {
for (PathFinding pathFinding : new ArrayList<PathFinding>(pathFinders)) {
pathFinding.iterate(itNumber / pathFinders.size());
if (pathFinding.isDone()) {
LinkedList<BlockIndex> path = pathFinding.getResult();
if (path != null && path.size() > 0) {
if (reserve(pathFinding.end())) {
return;
}
}
pathFinders.remove(pathFinding);
}
}
}
@Override
public boolean isDone() {
for (PathFinding pathFinding : pathFinders) {
if (pathFinding.isDone()) {
return true;
}
}
return !blockIter.hasNext();
}
public LinkedList<BlockIndex> getResult() {
for (PathFinding pathFinding : pathFinders) {
if (pathFinding.isDone()) {
return pathFinding.getResult();
}
}
return new LinkedList<BlockIndex>();
}
public BlockIndex getResultTarget() {
for (PathFinding pathFinding : pathFinders) {
if (pathFinding.isDone()) {
return pathFinding.end();
}
}
return null;
}
private boolean reserve(BlockIndex block) {
synchronized (reservations) {
if (!reservations.containsKey(world.provider.dimensionId)) {
reservations.put(world.provider.dimensionId,
new HashSet<BlockIndex>());
}
HashSet<BlockIndex> dimReservations = reservations
.get(world.provider.dimensionId);
if (dimReservations.contains(block)) {
return false;
}
dimReservations.add(block);
return true;
}
}
public void unreserve(BlockIndex block) {
synchronized (reservations) {
if (reservations.containsKey(world.provider.dimensionId)) {
reservations.get(world.provider.dimensionId).remove(block);
}
}
}
}