160 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			160 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.world.level.pathfinder;
 | |
| 
 | |
| import com.google.common.collect.ImmutableSet;
 | |
| import com.google.common.collect.Lists;
 | |
| import com.google.common.collect.Sets;
 | |
| import java.util.Comparator;
 | |
| import java.util.List;
 | |
| import java.util.Map;
 | |
| import java.util.Optional;
 | |
| import java.util.Set;
 | |
| import java.util.function.Function;
 | |
| import java.util.stream.Collectors;
 | |
| import net.minecraft.core.BlockPos;
 | |
| import net.minecraft.util.profiling.Profiler;
 | |
| import net.minecraft.util.profiling.ProfilerFiller;
 | |
| import net.minecraft.util.profiling.metrics.MetricCategory;
 | |
| import net.minecraft.world.entity.Mob;
 | |
| import net.minecraft.world.level.PathNavigationRegion;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| 
 | |
| public class PathFinder {
 | |
| 	private static final float FUDGING = 1.5F;
 | |
| 	private final Node[] neighbors = new Node[32];
 | |
| 	private int maxVisitedNodes;
 | |
| 	private final NodeEvaluator nodeEvaluator;
 | |
| 	private static final boolean DEBUG = false;
 | |
| 	private final BinaryHeap openSet = new BinaryHeap();
 | |
| 
 | |
| 	public PathFinder(NodeEvaluator nodeEvaluator, int maxVisitedNodes) {
 | |
| 		this.nodeEvaluator = nodeEvaluator;
 | |
| 		this.maxVisitedNodes = maxVisitedNodes;
 | |
| 	}
 | |
| 
 | |
| 	public void setMaxVisitedNodes(int maxVisitedNodes) {
 | |
| 		this.maxVisitedNodes = maxVisitedNodes;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Finds a path to one of the specified positions and post-processes it or returns null if no path could be found within given accuracy
 | |
| 	 */
 | |
| 	@Nullable
 | |
| 	public Path findPath(PathNavigationRegion region, Mob mob, Set<BlockPos> targetPositions, float maxRange, int accuracy, float searchDepthMultiplier) {
 | |
| 		this.openSet.clear();
 | |
| 		this.nodeEvaluator.prepare(region, mob);
 | |
| 		Node node = this.nodeEvaluator.getStart();
 | |
| 		if (node == null) {
 | |
| 			return null;
 | |
| 		} else {
 | |
| 			Map<Target, BlockPos> map = (Map<Target, BlockPos>)targetPositions.stream()
 | |
| 				.collect(Collectors.toMap(blockPos -> this.nodeEvaluator.getTarget(blockPos.getX(), blockPos.getY(), blockPos.getZ()), Function.identity()));
 | |
| 			Path path = this.findPath(node, map, maxRange, accuracy, searchDepthMultiplier);
 | |
| 			this.nodeEvaluator.done();
 | |
| 			return path;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Finds a path to one of the specified positions and post-processes it or returns null if no path could be found within given accuracy
 | |
| 	 */
 | |
| 	@Nullable
 | |
| 	private Path findPath(Node node, Map<Target, BlockPos> targetPositions, float maxRange, int accuracy, float searchDepthMultiplier) {
 | |
| 		ProfilerFiller profilerFiller = Profiler.get();
 | |
| 		profilerFiller.push("find_path");
 | |
| 		profilerFiller.markForCharting(MetricCategory.PATH_FINDING);
 | |
| 		Set<Target> set = targetPositions.keySet();
 | |
| 		node.g = 0.0F;
 | |
| 		node.h = this.getBestH(node, set);
 | |
| 		node.f = node.h;
 | |
| 		this.openSet.clear();
 | |
| 		this.openSet.insert(node);
 | |
| 		Set<Node> set2 = ImmutableSet.of();
 | |
| 		int i = 0;
 | |
| 		Set<Target> set3 = Sets.<Target>newHashSetWithExpectedSize(set.size());
 | |
| 		int j = (int)(this.maxVisitedNodes * searchDepthMultiplier);
 | |
| 
 | |
| 		while (!this.openSet.isEmpty()) {
 | |
| 			if (++i >= j) {
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			Node node2 = this.openSet.pop();
 | |
| 			node2.closed = true;
 | |
| 
 | |
| 			for (Target target : set) {
 | |
| 				if (node2.distanceManhattan(target) <= accuracy) {
 | |
| 					target.setReached();
 | |
| 					set3.add(target);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if (!set3.isEmpty()) {
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			if (!(node2.distanceTo(node) >= maxRange)) {
 | |
| 				int k = this.nodeEvaluator.getNeighbors(this.neighbors, node2);
 | |
| 
 | |
| 				for (int l = 0; l < k; l++) {
 | |
| 					Node node3 = this.neighbors[l];
 | |
| 					float f = this.distance(node2, node3);
 | |
| 					node3.walkedDistance = node2.walkedDistance + f;
 | |
| 					float g = node2.g + f + node3.costMalus;
 | |
| 					if (node3.walkedDistance < maxRange && (!node3.inOpenSet() || g < node3.g)) {
 | |
| 						node3.cameFrom = node2;
 | |
| 						node3.g = g;
 | |
| 						node3.h = this.getBestH(node3, set) * 1.5F;
 | |
| 						if (node3.inOpenSet()) {
 | |
| 							this.openSet.changeCost(node3, node3.g + node3.h);
 | |
| 						} else {
 | |
| 							node3.f = node3.g + node3.h;
 | |
| 							this.openSet.insert(node3);
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		Optional<Path> optional = !set3.isEmpty()
 | |
| 			? set3.stream()
 | |
| 				.map(targetx -> this.reconstructPath(targetx.getBestNode(), (BlockPos)targetPositions.get(targetx), true))
 | |
| 				.min(Comparator.comparingInt(Path::getNodeCount))
 | |
| 			: set.stream()
 | |
| 				.map(targetx -> this.reconstructPath(targetx.getBestNode(), (BlockPos)targetPositions.get(targetx), false))
 | |
| 				.min(Comparator.comparingDouble(Path::getDistToTarget).thenComparingInt(Path::getNodeCount));
 | |
| 		profilerFiller.pop();
 | |
| 		return optional.isEmpty() ? null : (Path)optional.get();
 | |
| 	}
 | |
| 
 | |
| 	protected float distance(Node first, Node second) {
 | |
| 		return first.distanceTo(second);
 | |
| 	}
 | |
| 
 | |
| 	private float getBestH(Node node, Set<Target> targets) {
 | |
| 		float f = Float.MAX_VALUE;
 | |
| 
 | |
| 		for (Target target : targets) {
 | |
| 			float g = node.distanceTo(target);
 | |
| 			target.updateBest(g, node);
 | |
| 			f = Math.min(g, f);
 | |
| 		}
 | |
| 
 | |
| 		return f;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Converts a recursive path point structure into a path
 | |
| 	 */
 | |
| 	private Path reconstructPath(Node point, BlockPos targetPos, boolean reachesTarget) {
 | |
| 		List<Node> list = Lists.<Node>newArrayList();
 | |
| 		Node node = point;
 | |
| 		list.add(0, point);
 | |
| 
 | |
| 		while (node.cameFrom != null) {
 | |
| 			node = node.cameFrom;
 | |
| 			list.add(0, node);
 | |
| 		}
 | |
| 
 | |
| 		return new Path(list, targetPos, reachesTarget);
 | |
| 	}
 | |
| }
 |