Merge pull request #4064 from babbaj/safe-landing

find safe landing spot for elytra
This commit is contained in:
leijurv 2023-07-22 22:48:00 -07:00 committed by GitHub
commit 3ffacbf375
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 88 additions and 6 deletions

View File

@ -41,15 +41,24 @@ import baritone.process.elytra.NetherPathfinderContext;
import baritone.process.elytra.NullElytraProcess; import baritone.process.elytra.NullElytraProcess;
import baritone.utils.BaritoneProcessHelper; import baritone.utils.BaritoneProcessHelper;
import baritone.utils.PathingCommandContext; import baritone.utils.PathingCommandContext;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;
import java.util.*;
import static baritone.api.pathing.movement.ActionCosts.COST_INF; import static baritone.api.pathing.movement.ActionCosts.COST_INF;
public class ElytraProcess extends BaritoneProcessHelper implements IBaritoneProcess, IElytraProcess, AbstractGameEventListener { public class ElytraProcess extends BaritoneProcessHelper implements IBaritoneProcess, IElytraProcess, AbstractGameEventListener {
public State state; public State state;
private boolean goingToLandingSpot;
private BetterBlockPos landingSpot;
private Goal goal; private Goal goal;
private LegacyElytraBehavior behavior; private LegacyElytraBehavior behavior;
@ -93,9 +102,9 @@ public class ElytraProcess extends BaritoneProcessHelper implements IBaritonePro
return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL);
} }
if (ctx.player().isElytraFlying()) { if (ctx.player().isElytraFlying() && this.state != State.LANDING) {
final BetterBlockPos last = behavior.pathManager.path.getLast(); final BetterBlockPos last = this.behavior.pathManager.path.getLast();
if (last != null && ctx.player().getDistanceSqToCenter(last) < (5 * 5)) { if (last != null && ctx.player().getDistanceSqToCenter(last) < 1) {
if (Baritone.settings().notificationOnPathComplete.value) { if (Baritone.settings().notificationOnPathComplete.value) {
logNotification("Pathing complete", false); logNotification("Pathing complete", false);
} }
@ -105,15 +114,26 @@ public class ElytraProcess extends BaritoneProcessHelper implements IBaritonePro
ctx.world().sendQuittingDisconnectingPacket(); ctx.world().sendQuittingDisconnectingPacket();
return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL);
} }
if (!goingToLandingSpot) {
BetterBlockPos landingSpot = findSafeLandingSpot();
if (landingSpot != null) {
this.pathTo(landingSpot);
this.landingSpot = landingSpot;
this.goingToLandingSpot = true;
return this.onTick(calcFailed, isSafeToCancel);
}
// don't spam call findLandingSpot if it somehow fails (it's slow)
this.goingToLandingSpot = true;
}
this.state = State.LANDING; this.state = State.LANDING;
} }
} }
if (this.state == State.LANDING) { if (this.state == State.LANDING) {
final BetterBlockPos endPos = behavior.pathManager.path.getLast(); final BetterBlockPos endPos = this.landingSpot != null ? this.landingSpot : behavior.pathManager.path.getLast();
if (ctx.player().isElytraFlying() && endPos != null) { if (ctx.player().isElytraFlying() && endPos != null) {
Vec3d from = ctx.player().getPositionVector(); Vec3d from = ctx.player().getPositionVector();
Vec3d to = new Vec3d(endPos.x, from.y, endPos.z); Vec3d to = new Vec3d(((double) endPos.x) + 0.5, from.y, ((double) endPos.z) + 0.5);
Rotation rotation = RotationUtils.calcRotationFromVec3d(from, to, ctx.playerRotations()); Rotation rotation = RotationUtils.calcRotationFromVec3d(from, to, ctx.playerRotations());
baritone.getLookBehavior().updateTarget(rotation, false); baritone.getLookBehavior().updateTarget(rotation, false);
} else { } else {
@ -208,6 +228,7 @@ public class ElytraProcess extends BaritoneProcessHelper implements IBaritonePro
@Override @Override
public void onLostControl() { public void onLostControl() {
this.goal = null; this.goal = null;
this.goingToLandingSpot = false;
this.state = State.START_FLYING; // TODO: null state? this.state = State.START_FLYING; // TODO: null state?
if (this.behavior != null) { if (this.behavior != null) {
this.behavior.destroy(); this.behavior.destroy();
@ -234,6 +255,7 @@ public class ElytraProcess extends BaritoneProcessHelper implements IBaritonePro
@Override @Override
public void pathTo(BlockPos destination) { public void pathTo(BlockPos destination) {
this.onLostControl();
this.behavior = new LegacyElytraBehavior(this.baritone, this, destination); this.behavior = new LegacyElytraBehavior(this.baritone, this, destination);
if (ctx.world() != null) { if (ctx.world() != null) {
this.behavior.repackChunks(); this.behavior.repackChunks();
@ -253,7 +275,6 @@ public class ElytraProcess extends BaritoneProcessHelper implements IBaritonePro
public enum State { public enum State {
LOCATE_JUMP("Finding spot to jump off"), LOCATE_JUMP("Finding spot to jump off"),
VALIDATE_PATH("Validating path"),
PAUSE("Waiting for elytra path"), PAUSE("Waiting for elytra path"),
GET_TO_JUMP("Walking to takeoff"), GET_TO_JUMP("Walking to takeoff"),
START_FLYING("Begin flying"), START_FLYING("Begin flying"),
@ -329,4 +350,65 @@ public class ElytraProcess extends BaritoneProcessHelper implements IBaritonePro
return COST_INF; return COST_INF;
} }
} }
private static boolean isInBounds(BlockPos pos) {
return pos.getY() >= 0 && pos.getY() < 128;
}
private boolean isAtEdge(BlockPos pos) {
return ctx.world().isAirBlock(pos.north())
|| ctx.world().isAirBlock(pos.south())
|| ctx.world().isAirBlock(pos.east())
|| ctx.world().isAirBlock(pos.west())
// corners
|| ctx.world().isAirBlock(pos.north().west())
|| ctx.world().isAirBlock(pos.north().east())
|| ctx.world().isAirBlock(pos.south().west())
|| ctx.world().isAirBlock(pos.south().east());
}
private boolean isSafeLandingSpot(BlockPos pos, LongOpenHashSet checkedSpots) {
BlockPos.MutableBlockPos mut = new BlockPos.MutableBlockPos(pos);
checkedSpots.add(mut.toLong());
while (mut.getY() >= 0) {
IBlockState state = ctx.world().getBlockState(mut);
Block block = state.getBlock();
if (block == Blocks.NETHERRACK || block == Blocks.GRAVEL || block == Blocks.NETHER_BRICK) {
return !isAtEdge(mut);
} else if (block != Blocks.AIR) {
return false;
}
mut.setPos(mut.getX(), mut.getY() - 1, mut.getZ());
if (checkedSpots.contains(mut.toLong())) {
return false;
}
}
return false; // void
}
private BetterBlockPos findSafeLandingSpot() {
final BetterBlockPos start = ctx.playerFeet();
Queue<BetterBlockPos> queue = new PriorityQueue<>(Comparator.<BetterBlockPos>comparingInt(pos -> (pos.x-start.x)*(pos.x-start.x) + (pos.z-start.z)*(pos.z-start.z)).thenComparingInt(pos -> -pos.y));
Set<BetterBlockPos> visited = new HashSet<>();
LongOpenHashSet checkedPositions = new LongOpenHashSet();
queue.add(start);
while (!queue.isEmpty()) {
BetterBlockPos pos = queue.poll();
if (ctx.world().isBlockLoaded(pos) && isInBounds(pos) && ctx.world().getBlockState(pos).getBlock() == Blocks.AIR) {
if (isSafeLandingSpot(pos, checkedPositions)) {
return pos;
}
checkedPositions.add(pos.toLong());
if (visited.add(pos.north())) queue.add(pos.north());
if (visited.add(pos.east())) queue.add(pos.east());
if (visited.add(pos.south())) queue.add(pos.south());
if (visited.add(pos.west())) queue.add(pos.west());
if (visited.add(pos.up())) queue.add(pos.up());
if (visited.add(pos.down())) queue.add(pos.down());
}
}
return null;
}
} }