From 1d092a71652f5cd7be3c1a169a3ba2b2ddee847a Mon Sep 17 00:00:00 2001 From: Brady Date: Tue, 20 Jun 2023 20:09:53 -0500 Subject: [PATCH] Compute angles on another thread between ticks --- .../baritone/api/utils/BetterBlockPos.java | 14 ++ .../baritone/behavior/ElytraBehavior.java | 167 +++++++++++++----- .../elytra/NetherPathfinderContext.java | 5 +- 3 files changed, 140 insertions(+), 46 deletions(-) diff --git a/src/api/java/baritone/api/utils/BetterBlockPos.java b/src/api/java/baritone/api/utils/BetterBlockPos.java index a9b02445b..01e53402e 100644 --- a/src/api/java/baritone/api/utils/BetterBlockPos.java +++ b/src/api/java/baritone/api/utils/BetterBlockPos.java @@ -202,6 +202,20 @@ public final class BetterBlockPos extends BlockPos { return amt == 0 ? this : new BetterBlockPos(x - amt, y, z); } + public double distanceSq(final BetterBlockPos to) { + double dx = (double) this.x - to.x; + double dy = (double) this.y - to.y; + double dz = (double) this.z - to.z; + return dx * dx + dy * dy + dz * dz; + } + + public double distanceTo(final BetterBlockPos to) { + double dx = (double) this.x - to.x; + double dy = (double) this.y - to.y; + double dz = (double) this.z - to.z; + return Math.sqrt(dx * dx + dy * dy + dz * dz); + } + @Override @Nonnull public String toString() { diff --git a/src/main/java/baritone/behavior/ElytraBehavior.java b/src/main/java/baritone/behavior/ElytraBehavior.java index 1be445b2c..59ff847d7 100644 --- a/src/main/java/baritone/behavior/ElytraBehavior.java +++ b/src/main/java/baritone/behavior/ElytraBehavior.java @@ -19,10 +19,7 @@ package baritone.behavior; import baritone.Baritone; import baritone.api.behavior.IElytraBehavior; -import baritone.api.event.events.BlockChangeEvent; -import baritone.api.event.events.ChunkEvent; -import baritone.api.event.events.PacketEvent; -import baritone.api.event.events.TickEvent; +import baritone.api.event.events.*; import baritone.api.utils.*; import baritone.behavior.elytra.NetherPathfinderContext; import baritone.behavior.elytra.UnpackedSegment; @@ -41,6 +38,8 @@ import net.minecraft.world.chunk.Chunk; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.function.IntFunction; import java.util.function.UnaryOperator; public final class ElytraBehavior extends Behavior implements IElytraBehavior, Helper { @@ -63,13 +62,16 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H private int remainingSetBackTicks; private BlockStateInterface bsi; + private Future solver; + private boolean solveNextTick; + public ElytraBehavior(Baritone baritone) { super(baritone); this.context = new NetherPathfinderContext(NETHER_SEED); this.clearLines = new ArrayList<>(); this.blockedLines = new ArrayList<>(); this.visiblePath = Collections.emptyList(); - this.pathManager = new PathManager(); + this.pathManager = this.new PathManager(); } private final class PathManager { @@ -90,11 +92,11 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H public void tick() { // Recalculate closest path node + this.updatePlayerNear(); final int prevMaxNear = this.maxPlayerNear; - this.playerNear = this.calculateNear(this.playerNear); this.maxPlayerNear = Math.max(this.maxPlayerNear, this.playerNear); - if (this.maxPlayerNear == prevMaxNear && ctx.player().isElytraFlying()) { + if (this.maxPlayerNear == prevMaxNear) { this.ticksNearUnchanged++; } else { this.ticksNearUnchanged = 0; @@ -247,7 +249,7 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H // obstacle. where do we return to pathing? // find the next valid segment final BetterBlockPos blockage = this.path.get(i); - final double distance = Math.sqrt(ctx.playerFeet().distanceSq(this.path.get(rangeEndExcl - 1))); + final double distance = ctx.playerFeet().distanceTo(this.path.get(rangeEndExcl - 1)); final long start = System.nanoTime(); this.pathRecalcSegment(rangeEndExcl - 1) @@ -276,7 +278,8 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H } } - private int calculateNear(int index) { + public void updatePlayerNear() { + int index = this.playerNear; final BetterBlockPos pos = ctx.playerFeet(); for (int i = index; i >= Math.max(index - 1000, 0); i -= 10) { if (path.get(i).distanceSq(pos) < path.get(index).distanceSq(pos)) { @@ -298,7 +301,7 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H index = i; // intentional: this changes the bound of the loop } } - return index; + this.playerNear = index; } private void removeBacktracks() { @@ -367,6 +370,18 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H return; } + // Fetch the previous solution, regardless of if it's going to be used + Solution solution = null; + if (this.solver != null) { + try { + solution = this.solver.get(); + } catch (Exception ignored) { + // it doesn't matter if get() fails since the solution can just be recalculated synchronously + } finally { + this.solver = null; + } + } + // Certified mojang employee incident if (this.remainingFireworkTicks > 0) { this.remainingFireworkTicks--; @@ -379,7 +394,7 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H this.blockedLines.clear(); final List path = this.pathManager.getPath(); - if (path.isEmpty()) { + if (path.isEmpty() || !ctx.player().isElytraFlying()) { return; } @@ -392,10 +407,6 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H Math.min(playerNear + 100, path.size()) ); - if (!ctx.player().isElytraFlying()) { - return; - } - baritone.getInputOverrideHandler().clearAllKeys(); if (ctx.player().collidedHorizontally) { @@ -405,10 +416,14 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H logDirect("vbonk"); } - final Vec3d start = ctx.playerFeetAsVec(); - final boolean currentlyBoosted = this.isFireworkActive(); + final SolverContext solverContext = this.new SolverContext(); + this.solveNextTick = true; + + // If there's no previously calculated solution to use, or the context used at the end of last tick doesn't match this tick + if (solution == null || !solution.context.equals(solverContext)) { + solution = this.solveAngles(solverContext); + } - final Solution solution = this.solveAngles(path, playerNear, start, currentlyBoosted); if (solution == null) { logDirect("no solution"); return; @@ -419,12 +434,43 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H if (!solution.solvedPitch) { logDirect("no pitch solution, probably gonna crash in a few ticks LOL!!!"); return; + } else { + this.aimPos = solution.goingTo; } - this.tickUseFireworks(start, currentlyBoosted, solution.goingTo, solution.forceUseFirework); + this.tickUseFireworks( + solution.context.start, + solution.context.currentlyBoosted, + solution.goingTo, + solution.forceUseFirework + ); } - private Solution solveAngles(final List path, final int playerNear, final Vec3d start, final boolean currentlyBoosted) { + @Override + public void onPostTick(TickEvent event) { + if (event.getType() == TickEvent.Type.IN && this.solveNextTick) { + // We're at the end of the tick, the player's position likely updated and the closest path node could've + // changed. Updating it now will avoid unnecessary recalculation on the main thread. + this.pathManager.updatePlayerNear(); + + final SolverContext context = this.new SolverContext(); + this.solver = CompletableFuture.supplyAsync(() -> this.solveAngles(context)); + this.solveNextTick = false; + } + } + + private Solution solveAngles(final SolverContext context) { + final List path = context.path; + final int playerNear = context.playerNear; + final Vec3d start = context.start; + final boolean currentlyBoosted = context.currentlyBoosted; + + final IntFunction pathAt = (i) -> new Vec3d( + path.get(i).x, + path.get(i).y, + path.get(i).z + ); + final boolean isInLava = ctx.player().isInLava(); Solution solution = null; @@ -440,10 +486,9 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H for (float interp : interps) { Vec3d dest; if (interp == 1 || i == minStep) { - dest = this.pathManager.pathAt(i); + dest = pathAt.apply(i); } else { - dest = this.pathManager.pathAt(i).scale(interp) - .add(this.pathManager.pathAt(i - 1).scale(1.0d - interp)); + dest = pathAt.apply(i).scale(interp).add(pathAt.apply(i - 1).scale(1.0d - interp)); } dest = dest.add(0, dy, 0); @@ -452,14 +497,14 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H continue; } if (start.distanceTo(dest) < 40) { - if (!clearView(dest, this.pathManager.pathAt(i + lookahead).add(0, dy, 0), false) - || !clearView(dest, this.pathManager.pathAt(i + lookahead), false)) { + if (!clearView(dest, pathAt.apply(i + lookahead).add(0, dy, 0), false) + || !clearView(dest, pathAt.apply(i + lookahead), false)) { // aka: don't go upwards if doing so would prevent us from being able to see the next position **OR** the modified next position continue; } } else { // but if it's far away, allow gaining altitude if we could lose it again by the time we get there - if (!clearView(dest, this.pathManager.pathAt(i), false)) { + if (!clearView(dest, pathAt.apply(i), false)) { continue; } } @@ -474,15 +519,18 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H final Pair pitch = this.solvePitch(dest.subtract(start), steps, relaxation, currentlyBoosted, isInLava); if (pitch.first() == null) { - solution = new Solution(new Rotation(yaw, ctx.playerRotations().getPitch()), null, false, false); + solution = new Solution(context, new Rotation(yaw, ctx.playerRotations().getPitch()), null, false, false); continue; } - final BetterBlockPos goingTo = new BetterBlockPos(dest.x, dest.y, dest.z); - this.aimPos = goingTo; - // A solution was found with yaw AND pitch, so just immediately return it. - return new Solution(new Rotation(yaw, pitch.first()), goingTo, true, pitch.second()); + return new Solution( + context, + new Rotation(yaw, pitch.first()), + new BetterBlockPos(dest.x, dest.y, dest.z), + true, + pitch.second() + ); } } } @@ -523,14 +571,47 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H } } + private final class SolverContext { + + public final List path; + public final int playerNear; + public final Vec3d start; + public final boolean currentlyBoosted; + + public SolverContext() { + this.path = ElytraBehavior.this.pathManager.getPath(); + this.playerNear = ElytraBehavior.this.pathManager.getNear(); + this.start = ElytraBehavior.this.ctx.playerFeetAsVec(); + this.currentlyBoosted = ElytraBehavior.this.isFireworkActive(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || o.getClass() != SolverContext.class) { + return false; + } + + SolverContext other = (SolverContext) o; + return this.path == other.path // Contents aren't modified, just compare by reference + && this.playerNear == other.playerNear + && Objects.equals(this.start, other.start) + && this.currentlyBoosted == other.currentlyBoosted; + } + } + private static final class Solution { + public final SolverContext context; public final Rotation rotation; public final BetterBlockPos goingTo; public final boolean solvedPitch; public final boolean forceUseFirework; - public Solution(Rotation rotation, BetterBlockPos goingTo, boolean solvedPitch, boolean forceUseFirework) { + public Solution(SolverContext context, Rotation rotation, BetterBlockPos goingTo, boolean solvedPitch, boolean forceUseFirework) { + this.context = context; this.rotation = rotation; this.goingTo = goingTo; this.solvedPitch = solvedPitch; @@ -605,10 +686,10 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H } if (clear) { - clearLines.add(new Pair<>(start, dest)); + this.clearLines.add(new Pair<>(start, dest)); return true; } else { - blockedLines.add(new Pair<>(start, dest)); + this.blockedLines.add(new Pair<>(start, dest)); return false; } } @@ -672,15 +753,6 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H return bestPitch; } - public boolean passable(int x, int y, int z, boolean ignoreLava) { - return passable(this.bsi.get0(x, y, z), ignoreLava); - } - - public static boolean passable(IBlockState state, boolean ignoreLava) { - Material mat = state.getMaterial(); - return mat == Material.AIR || (ignoreLava && mat == Material.LAVA); - } - private static Vec3d step(Vec3d motion, float rotationPitch, float rotationYaw, boolean firework) { double motionX = motion.x; double motionY = motion.y; @@ -731,5 +803,14 @@ public final class ElytraBehavior extends Behavior implements IElytraBehavior, H return new Vec3d(motionX, motionY, motionZ); } + + private boolean passable(int x, int y, int z, boolean ignoreLava) { + return passable(this.bsi.get0(x, y, z), ignoreLava); + } + + private static boolean passable(IBlockState state, boolean ignoreLava) { + Material mat = state.getMaterial(); + return mat == Material.AIR || (ignoreLava && mat == Material.LAVA); + } } diff --git a/src/main/java/baritone/behavior/elytra/NetherPathfinderContext.java b/src/main/java/baritone/behavior/elytra/NetherPathfinderContext.java index f269cd33b..8dcd87274 100644 --- a/src/main/java/baritone/behavior/elytra/NetherPathfinderContext.java +++ b/src/main/java/baritone/behavior/elytra/NetherPathfinderContext.java @@ -19,6 +19,7 @@ package baritone.behavior.elytra; import dev.babbaj.pathfinder.NetherPathfinder; import dev.babbaj.pathfinder.PathSegment; +import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; import net.minecraft.util.math.BlockPos; import net.minecraft.world.chunk.BlockStateContainer; @@ -30,8 +31,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import static baritone.behavior.ElytraBehavior.passable; - /** * @author Brady */ @@ -129,7 +128,7 @@ public final class NetherPathfinderContext { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++) { IBlockState state = bsc.get(x, y1, z); - if (!passable(state, false)) { + if (state.getMaterial() != Material.AIR) { packed[x | (z << 4) | (y << 8)] = true; } }