Upload files to "/"
This commit is contained in:
135
PathfindingController.java
Normal file
135
PathfindingController.java
Normal file
@@ -0,0 +1,135 @@
|
||||
package xyz.nodrop.farmingtools.client.pathfinding;
|
||||
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import xyz.nodrop.farmingtools.client.pathfinding.PathInput;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Central controller for pathfinding.
|
||||
*
|
||||
* ┌─────────────────────────────────────────────────────┐
|
||||
* │ External script / command calls: │
|
||||
* │ PathfindingController.INSTANCE.goTo(x, y, z) │
|
||||
* │ PathfindingController.INSTANCE.stop() │
|
||||
* │ PathfindingController.INSTANCE.isRunning() │
|
||||
* └─────────────────────────────────────────────────────┘
|
||||
*
|
||||
* The A* search runs on a background thread so the client never freezes.
|
||||
* Once the path is ready it is handed off to PathExecutor which ticks
|
||||
* on the main client thread.
|
||||
*/
|
||||
public class PathfindingController {
|
||||
|
||||
public static final PathfindingController INSTANCE = new PathfindingController();
|
||||
|
||||
private final ExecutorService threadPool = Executors.newSingleThreadExecutor(r -> {
|
||||
Thread t = new Thread(r, "farmingtools-pathfinder");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
|
||||
private final PathExecutor executor = new PathExecutor();
|
||||
|
||||
/** Last status message — shown on HUD. */
|
||||
private volatile String status = "Idle";
|
||||
|
||||
/** Optional callback fired when path is found / fails / finishes. */
|
||||
private volatile Consumer<PathResult> onResult = null;
|
||||
|
||||
private PathfindingController() {}
|
||||
|
||||
// ── Registration ──────────────────────────────────────────────────────────
|
||||
|
||||
public void register() {
|
||||
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
||||
executor.tick(client);
|
||||
|
||||
// Auto-clear status when done
|
||||
if (!executor.isActive() && status.equals("Walking...")) {
|
||||
status = "Arrived";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ── Public API (call from any thread) ─────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Start pathfinding to the given block position.
|
||||
* Computation happens on a background thread; walking starts automatically.
|
||||
*
|
||||
* @param onResult optional callback called with the result (may be null)
|
||||
*/
|
||||
public void goTo(int x, int y, int z, boolean flying, Consumer<PathResult> onResult) {
|
||||
this.onResult = onResult;
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
ClientPlayerEntity player = client.player;
|
||||
|
||||
if (player == null || client.world == null) {
|
||||
status = "Not in world";
|
||||
return;
|
||||
}
|
||||
|
||||
BlockPos start = player.getBlockPos();
|
||||
BlockPos goal = new BlockPos(x, y, z);
|
||||
|
||||
status = "Searching...";
|
||||
executor.stop(client);
|
||||
executor.setFlying(flying);
|
||||
|
||||
threadPool.submit(() -> {
|
||||
List<BlockPos> path = AStarPathfinder.find(client.world, start, goal, flying);
|
||||
|
||||
if (path.isEmpty()) {
|
||||
status = "No path found";
|
||||
if (onResult != null) onResult.accept(PathResult.failure(goal));
|
||||
} else {
|
||||
status = "Walking...";
|
||||
executor.start(path);
|
||||
if (onResult != null) onResult.accept(PathResult.success(goal, path.size()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Convenience overload without callback. */
|
||||
public void goTo(int x, int y, int z) {
|
||||
goTo(x, y, z, false, null);
|
||||
}
|
||||
|
||||
/** Convenience overload accepting BlockPos. */
|
||||
public void goTo(BlockPos goal) {
|
||||
goTo(goal.getX(), goal.getY(), goal.getZ(), false, null);
|
||||
}
|
||||
|
||||
/** Stop walking immediately. */
|
||||
public void stop() {
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
executor.stop(client);
|
||||
status = "Stopped";
|
||||
}
|
||||
|
||||
public boolean isRunning() { return executor.isActive(); }
|
||||
public String getStatus() { return status; }
|
||||
public PathInput getCurrentInput() { return executor.getCurrentInput(); }
|
||||
public int getProgress() { return executor.getWaypointIndex(); }
|
||||
public int getPathLength() { return executor.getPathLength(); }
|
||||
public BlockPos getGoal() { return executor.getGoal(); }
|
||||
public List<BlockPos> getCurrentPath() { return executor.getPath(); }
|
||||
public int getCurrentWaypointIndex() { return executor.getWaypointIndex(); }
|
||||
// W PathfindingController:
|
||||
public PathExecutor getExecutor() { return executor; }
|
||||
|
||||
// ── Result record ─────────────────────────────────────────────────────────
|
||||
|
||||
public record PathResult(boolean success, BlockPos goal, int length) {
|
||||
static PathResult success(BlockPos g, int len) { return new PathResult(true, g, len); }
|
||||
static PathResult failure(BlockPos g) { return new PathResult(false, g, 0); }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user