2018-08-08 03:16:53 +00:00
/ *
* This file is part of Baritone .
*
* Baritone is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
2018-08-08 04:15:22 +00:00
* Baritone is distributed in the hope that it will be useful ,
2018-08-08 03:16:53 +00:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with Baritone . If not , see < https : //www.gnu.org/licenses/>.
* /
2018-08-22 20:15:56 +00:00
package baritone.pathing.path ;
2018-08-02 14:01:34 +00:00
2018-08-22 20:15:56 +00:00
import baritone.Baritone ;
import baritone.event.events.TickEvent ;
import baritone.pathing.movement.ActionCosts ;
import baritone.pathing.movement.Movement ;
import baritone.pathing.movement.MovementState ;
2018-08-26 15:12:57 +00:00
import baritone.pathing.movement.movements.MovementDescend ;
import baritone.pathing.movement.movements.MovementDiagonal ;
import baritone.pathing.movement.movements.MovementTraverse ;
2018-08-22 20:15:56 +00:00
import baritone.utils.BlockStateInterface ;
import baritone.utils.Helper ;
2018-08-04 19:53:50 +00:00
import net.minecraft.client.entity.EntityPlayerSP ;
import net.minecraft.init.Blocks ;
2018-08-02 14:01:34 +00:00
import net.minecraft.util.Tuple ;
import net.minecraft.util.math.BlockPos ;
2018-08-09 23:37:08 +00:00
import java.util.Collections ;
import java.util.HashSet ;
import java.util.Set ;
2018-08-22 20:15:56 +00:00
import static baritone.pathing.movement.MovementState.MovementStatus.* ;
2018-08-04 22:28:36 +00:00
2018-08-02 14:01:34 +00:00
/ * *
2018-08-04 19:53:50 +00:00
* Behavior to execute a precomputed path . Does not ( yet ) deal with path segmentation or stitching
* or cutting ( jumping onto the next path if it starts with a backtrack of this path ' s ending )
*
* @author leijurv
2018-08-02 14:01:34 +00:00
* /
2018-08-13 19:35:44 +00:00
public class PathExecutor implements Helper {
2018-08-02 14:01:34 +00:00
private static final double MAX_DIST_FROM_PATH = 2 ;
2018-08-04 19:53:50 +00:00
private static final double MAX_TICKS_AWAY = 200 ; // ten seconds
2018-08-02 14:01:34 +00:00
private final IPath path ;
2018-08-04 19:53:50 +00:00
private int pathPosition ;
private int ticksAway ;
private int ticksOnCurrent ;
2018-08-13 18:49:36 +00:00
private Double currentMovementInitialCostEstimate ;
private Integer costEstimateIndex ;
2018-08-04 19:53:50 +00:00
private boolean failed ;
2018-08-09 23:37:08 +00:00
private boolean recalcBP = true ;
private HashSet < BlockPos > toBreak = new HashSet < > ( ) ;
private HashSet < BlockPos > toPlace = new HashSet < > ( ) ;
2018-08-12 15:40:44 +00:00
private HashSet < BlockPos > toWalkInto = new HashSet < > ( ) ;
2018-08-02 14:01:34 +00:00
2018-08-05 03:25:05 +00:00
public PathExecutor ( IPath path ) {
2018-08-02 14:01:34 +00:00
this . path = path ;
2018-08-04 19:53:50 +00:00
this . pathPosition = 0 ;
2018-08-02 14:01:34 +00:00
}
2018-08-13 19:35:44 +00:00
/ * *
* Tick this executor
*
* @param event
* @return True if a movement just finished ( and the player is therefore in a " stable " state , like ,
* not sneaking out over lava ) , false otherwise
* /
public boolean onTick ( TickEvent event ) {
2018-08-06 23:09:28 +00:00
if ( event . getType ( ) = = TickEvent . Type . OUT ) {
2018-08-13 19:35:44 +00:00
throw new IllegalStateException ( ) ;
2018-08-06 20:07:30 +00:00
}
2018-08-04 19:53:50 +00:00
if ( pathPosition > = path . length ( ) ) {
//stop bugging me, I'm done
//TODO Baritone.INSTANCE.behaviors.remove(this)
2018-08-13 19:35:44 +00:00
return true ;
2018-08-04 19:53:50 +00:00
}
BlockPos whereShouldIBe = path . positions ( ) . get ( pathPosition ) ;
EntityPlayerSP thePlayer = mc . player ;
BlockPos whereAmI = playerFeet ( ) ;
if ( pathPosition = = path . length ( ) - 1 ) {
2018-08-06 23:09:28 +00:00
pathPosition + + ;
2018-08-13 19:35:44 +00:00
return true ;
2018-08-04 19:53:50 +00:00
}
if ( ! whereShouldIBe . equals ( whereAmI ) ) {
2018-08-20 23:23:32 +00:00
//System.out.println("Should be at " + whereShouldIBe + " actually am at " + whereAmI);
2018-08-04 19:53:50 +00:00
if ( ! Blocks . AIR . equals ( BlockStateInterface . getBlock ( whereAmI . down ( ) ) ) ) { //do not skip if standing on air, because our position isn't stable to skip
for ( int i = 0 ; i < pathPosition - 2 & & i < path . length ( ) ; i + + ) { //this happens for example when you lag out and get teleported back a couple blocks
if ( whereAmI . equals ( path . positions ( ) . get ( i ) ) ) {
2018-08-09 00:04:01 +00:00
displayChatMessageRaw ( " Skipping back " + ( pathPosition - i ) + " steps, to " + i ) ;
2018-08-23 18:14:37 +00:00
int previousPos = pathPosition ;
2018-08-04 19:53:50 +00:00
pathPosition = Math . max ( i - 1 , 0 ) ; // previous step might not actually be done
2018-08-23 18:14:37 +00:00
for ( int j = pathPosition ; j < = previousPos ; j + + ) {
path . movements ( ) . get ( j ) . reset ( ) ;
}
2018-08-13 23:41:08 +00:00
Baritone . INSTANCE . getInputOverrideHandler ( ) . clearAllKeys ( ) ;
2018-08-13 19:35:44 +00:00
return false ;
2018-08-04 19:53:50 +00:00
}
}
for ( int i = pathPosition + 2 ; i < path . length ( ) ; i + + ) { //dont check pathPosition+1. the movement tells us when it's done (e.g. sneak placing)
if ( whereAmI . equals ( path . positions ( ) . get ( i ) ) ) {
2018-08-11 20:08:16 +00:00
if ( i - pathPosition > 2 ) {
displayChatMessageRaw ( " Skipping forward " + ( i - pathPosition ) + " steps, to " + i ) ;
}
2018-08-20 23:23:32 +00:00
System . out . println ( " Double skip sundae " ) ;
2018-08-04 19:53:50 +00:00
pathPosition = i - 1 ;
2018-08-13 23:41:08 +00:00
Baritone . INSTANCE . getInputOverrideHandler ( ) . clearAllKeys ( ) ;
2018-08-13 19:35:44 +00:00
return false ;
2018-08-04 19:53:50 +00:00
}
}
}
}
Tuple < Double , BlockPos > status = path . closestPathPos ( thePlayer . posX , thePlayer . posY , thePlayer . posZ ) ;
double distanceFromPath = status . getFirst ( ) ;
if ( distanceFromPath > MAX_DIST_FROM_PATH ) {
ticksAway + + ;
System . out . println ( " FAR AWAY FROM PATH FOR " + ticksAway + " TICKS. Current distance: " + distanceFromPath + " . Threshold: " + MAX_DIST_FROM_PATH ) ;
if ( ticksAway > MAX_TICKS_AWAY ) {
2018-08-09 00:04:01 +00:00
displayChatMessageRaw ( " Too far away from path for too long, cancelling path " ) ;
2018-08-04 19:53:50 +00:00
System . out . println ( " Too many ticks " ) ;
pathPosition = path . length ( ) + 3 ;
2018-08-09 00:04:01 +00:00
Baritone . INSTANCE . getInputOverrideHandler ( ) . clearAllKeys ( ) ;
2018-08-04 19:53:50 +00:00
failed = true ;
2018-08-13 19:35:44 +00:00
return false ;
2018-08-04 19:53:50 +00:00
}
2018-08-02 14:01:34 +00:00
} else {
2018-08-04 19:53:50 +00:00
ticksAway = 0 ;
}
//this commented block is literally cursed.
/ * Out . log ( actions . get ( pathPosition ) ) ;
if ( pathPosition < actions . size ( ) - 1 ) { //if there are two ActionBridges in a row and they are at right angles, walk diagonally. This makes it so you walk at 45 degrees along a zigzag path instead of doing inefficient zigging and zagging
if ( ( actions . get ( pathPosition ) instanceof ActionBridge ) & & ( actions . get ( pathPosition + 1 ) instanceof ActionBridge ) ) {
ActionBridge curr = ( ActionBridge ) actions . get ( pathPosition ) ;
ActionBridge next = ( ActionBridge ) actions . get ( pathPosition + 1 ) ;
if ( curr . dx ( ) ! = next . dx ( ) | | curr . dz ( ) ! = next . dz ( ) ) { //two movement are not parallel, so this is a right angle
if ( curr . amIGood ( ) & & next . amIGood ( ) ) { //nothing in the way
BlockPos cornerToCut1 = new BlockPos ( next . to . getX ( ) - next . from . getX ( ) + curr . from . getX ( ) , next . to . getY ( ) , next . to . getZ ( ) - next . from . getZ ( ) + curr . from . getZ ( ) ) ;
BlockPos cornerToCut2 = cornerToCut1 . up ( ) ;
//Block corner1 = Baritone.get(cornerToCut1).getBlock();
//Block corner2 = Baritone.get(cornerToCut2).getBlock();
//Out.gui("Cutting conner " + cornerToCut1 + " " + corner1, Out.Mode.Debug);
if ( ! Action . avoidWalkingInto ( cornerToCut1 ) & & ! Action . avoidWalkingInto ( cornerToCut2 ) ) {
double x = ( next . from . getX ( ) + next . to . getX ( ) + 1 . 0D ) * 0 . 5D ;
double z = ( next . from . getZ ( ) + next . to . getZ ( ) + 1 . 0D ) * 0 . 5D ;
MovementManager . clearMovement ( ) ;
if ( ! MovementManager . forward & & curr . oneInTen ! = null & & curr . oneInTen ) {
MovementManager . clearMovement ( ) ;
MovementManager . forward = LookManager . lookAtCoords ( x , 0 , z , false ) ;
} else {
MovementManager . moveTowardsCoords ( x , 0 , z ) ;
}
if ( MovementManager . forward & & ! MovementManager . backward ) {
thePlayer . setSprinting ( true ) ;
}
return false ;
}
}
}
2018-08-02 14:01:34 +00:00
}
2018-08-04 19:53:50 +00:00
} * /
2018-08-09 23:37:08 +00:00
long start = System . currentTimeMillis ( ) ;
2018-08-06 23:09:28 +00:00
for ( int i = pathPosition - 10 ; i < pathPosition + 10 ; i + + ) {
if ( i > = 0 & & i < path . movements ( ) . size ( ) ) {
2018-08-09 23:37:08 +00:00
Movement m = path . movements ( ) . get ( i ) ;
HashSet < BlockPos > prevBreak = new HashSet < > ( m . toBreak ( ) ) ;
HashSet < BlockPos > prevPlace = new HashSet < > ( m . toPlace ( ) ) ;
2018-08-12 15:40:44 +00:00
HashSet < BlockPos > prevWalkInto = new HashSet < > ( m . toWalkInto ( ) ) ;
2018-08-09 23:37:08 +00:00
m . toBreakCached = null ;
m . toPlaceCached = null ;
2018-08-12 15:40:44 +00:00
m . toWalkIntoCached = null ;
2018-08-09 23:37:08 +00:00
if ( ! prevBreak . equals ( new HashSet < > ( m . toBreak ( ) ) ) ) {
recalcBP = true ;
}
if ( ! prevPlace . equals ( new HashSet < > ( m . toPlace ( ) ) ) ) {
recalcBP = true ;
}
2018-08-12 15:40:44 +00:00
if ( ! prevWalkInto . equals ( new HashSet < > ( m . toWalkInto ( ) ) ) ) {
recalcBP = true ;
}
2018-08-09 23:37:08 +00:00
}
}
if ( recalcBP ) {
HashSet < BlockPos > newBreak = new HashSet < > ( ) ;
HashSet < BlockPos > newPlace = new HashSet < > ( ) ;
2018-08-12 15:40:44 +00:00
HashSet < BlockPos > newWalkInto = new HashSet < > ( ) ;
2018-08-13 18:49:36 +00:00
for ( int i = pathPosition ; i < path . movements ( ) . size ( ) ; i + + ) {
2018-08-09 23:37:08 +00:00
newBreak . addAll ( path . movements ( ) . get ( i ) . toBreak ( ) ) ;
newPlace . addAll ( path . movements ( ) . get ( i ) . toPlace ( ) ) ;
2018-08-12 15:40:44 +00:00
newWalkInto . addAll ( path . movements ( ) . get ( i ) . toWalkInto ( ) ) ;
2018-08-06 23:09:28 +00:00
}
2018-08-09 23:37:08 +00:00
toBreak = newBreak ;
toPlace = newPlace ;
2018-08-12 15:40:44 +00:00
toWalkInto = newWalkInto ;
2018-08-09 23:37:08 +00:00
recalcBP = false ;
}
long end = System . currentTimeMillis ( ) ;
if ( end - start > 0 ) {
2018-08-10 17:45:18 +00:00
//displayChatMessageRaw("Recalculating break and place took " + (end - start) + "ms");
2018-08-06 23:09:28 +00:00
}
2018-08-04 19:53:50 +00:00
Movement movement = path . movements ( ) . get ( pathPosition ) ;
2018-08-13 18:49:36 +00:00
double currentCost = movement . recalculateCost ( ) ;
if ( currentCost > = ActionCosts . COST_INF ) {
2018-08-09 00:04:01 +00:00
displayChatMessageRaw ( " Something has changed in the world and this movement has become impossible. Cancelling. " ) ;
2018-08-04 19:53:50 +00:00
pathPosition = path . length ( ) + 3 ;
failed = true ;
2018-08-09 00:04:01 +00:00
Baritone . INSTANCE . getInputOverrideHandler ( ) . clearAllKeys ( ) ;
2018-08-13 19:35:44 +00:00
return true ;
2018-08-02 14:01:34 +00:00
}
2018-08-13 18:49:36 +00:00
if ( costEstimateIndex = = null | | costEstimateIndex ! = pathPosition ) {
costEstimateIndex = pathPosition ;
currentMovementInitialCostEstimate = currentCost ; // do this only once, when the movement starts
2018-08-26 15:49:56 +00:00
for ( int i = 1 ; i < Baritone . settings ( ) . costVerificationLookahead . get ( ) & & pathPosition + i < path . length ( ) - 1 ; i + + ) {
if ( path . movements ( ) . get ( pathPosition + i ) . recalculateCost ( ) > = ActionCosts . COST_INF ) {
displayChatMessageRaw ( " Something has changed in the world and a future movement has become impossible. Cancelling. " ) ;
pathPosition = path . length ( ) + 3 ;
failed = true ;
Baritone . INSTANCE . getInputOverrideHandler ( ) . clearAllKeys ( ) ;
return true ;
}
}
2018-08-13 18:49:36 +00:00
}
2018-08-04 22:28:36 +00:00
MovementState . MovementStatus movementStatus = movement . update ( ) ;
if ( movementStatus = = UNREACHABLE | | movementStatus = = FAILED ) {
2018-08-09 00:04:01 +00:00
displayChatMessageRaw ( " Movement returns status " + movementStatus ) ;
2018-08-04 22:28:36 +00:00
pathPosition = path . length ( ) + 3 ;
failed = true ;
2018-08-09 00:04:01 +00:00
Baritone . INSTANCE . getInputOverrideHandler ( ) . clearAllKeys ( ) ;
2018-08-13 19:35:44 +00:00
return true ;
2018-08-04 22:28:36 +00:00
}
if ( movementStatus = = SUCCESS ) {
2018-08-21 03:17:55 +00:00
//System.out.println("Movement done, next path");
2018-08-04 19:53:50 +00:00
pathPosition + + ;
ticksOnCurrent = 0 ;
2018-08-07 15:05:18 +00:00
Baritone . INSTANCE . getInputOverrideHandler ( ) . clearAllKeys ( ) ;
2018-08-07 15:01:26 +00:00
onTick ( event ) ;
2018-08-13 19:35:44 +00:00
return true ;
2018-08-04 19:53:50 +00:00
} else {
2018-08-26 15:12:57 +00:00
sprintIfRequested ( ) ;
2018-08-04 19:53:50 +00:00
ticksOnCurrent + + ;
2018-08-20 23:23:32 +00:00
if ( ticksOnCurrent > currentMovementInitialCostEstimate + Baritone . settings ( ) . movementTimeoutTicks . get ( ) ) {
2018-08-13 18:49:36 +00:00
// only fail if the total time has exceeded the initial estimate
// as you break the blocks required, the remaining cost goes down, to the point where
2018-08-20 23:23:32 +00:00
// ticksOnCurrent is greater than recalculateCost + 100
2018-08-13 18:49:36 +00:00
// this is why we cache cost at the beginning, and don't recalculate for this comparison every tick
2018-08-20 23:23:32 +00:00
displayChatMessageRaw ( " This movement has taken too long ( " + ticksOnCurrent + " ticks, expected " + currentMovementInitialCostEstimate + " ). Cancelling. " ) ;
2018-08-06 02:08:23 +00:00
movement . cancel ( ) ;
2018-08-09 00:04:01 +00:00
Baritone . INSTANCE . getInputOverrideHandler ( ) . clearAllKeys ( ) ;
2018-08-04 19:53:50 +00:00
pathPosition = path . length ( ) + 3 ;
failed = true ;
2018-08-13 19:35:44 +00:00
return true ;
2018-08-04 19:53:50 +00:00
}
}
2018-08-13 19:35:44 +00:00
return false ; // movement is in progress
2018-08-04 19:53:50 +00:00
}
2018-08-26 15:12:57 +00:00
private void sprintIfRequested ( ) {
if ( ! Baritone . settings ( ) . allowSprint . get ( ) ) {
player ( ) . setSprinting ( false ) ;
return ;
}
if ( Baritone . INSTANCE . getInputOverrideHandler ( ) . isInputForcedDown ( mc . gameSettings . keyBindSprint ) ) {
if ( ! player ( ) . isSprinting ( ) ) {
player ( ) . setSprinting ( true ) ;
}
return ;
}
Movement movement = path . movements ( ) . get ( pathPosition ) ;
if ( movement instanceof MovementDescend & & pathPosition < path . length ( ) - 2 ) {
Movement next = path . movements ( ) . get ( pathPosition + 1 ) ;
if ( next instanceof MovementDescend ) {
if ( next . getDirection ( ) . equals ( movement . getDirection ( ) ) ) {
if ( playerFeet ( ) . equals ( movement . getDest ( ) ) ) {
pathPosition + + ;
Baritone . INSTANCE . getInputOverrideHandler ( ) . clearAllKeys ( ) ;
}
if ( ! player ( ) . isSprinting ( ) ) {
player ( ) . setSprinting ( true ) ;
}
return ;
}
}
if ( next instanceof MovementTraverse ) {
if ( next . getDirection ( ) . down ( ) . equals ( movement . getDirection ( ) ) ) {
if ( playerFeet ( ) . equals ( movement . getDest ( ) ) ) {
pathPosition + + ;
Baritone . INSTANCE . getInputOverrideHandler ( ) . clearAllKeys ( ) ;
}
if ( ! player ( ) . isSprinting ( ) ) {
player ( ) . setSprinting ( true ) ;
}
return ;
}
}
if ( next instanceof MovementDiagonal & & Baritone . settings ( ) . allowOvershootDiagonalDescend . get ( ) ) {
if ( playerFeet ( ) . equals ( movement . getDest ( ) ) ) {
pathPosition + + ;
Baritone . INSTANCE . getInputOverrideHandler ( ) . clearAllKeys ( ) ;
}
if ( ! player ( ) . isSprinting ( ) ) {
player ( ) . setSprinting ( true ) ;
}
return ;
}
2018-08-26 20:51:50 +00:00
//displayChatMessageRaw("Turning off sprinting " + movement + " " + next + " " + movement.getDirection() + " " + next.getDirection().down() + " " + next.getDirection().down().equals(movement.getDirection()));
2018-08-26 15:12:57 +00:00
}
player ( ) . setSprinting ( false ) ;
}
2018-08-09 20:08:56 +00:00
public int getPosition ( ) {
return pathPosition ;
}
2018-08-04 19:53:50 +00:00
public IPath getPath ( ) {
return path ;
}
public boolean failed ( ) {
return failed ;
}
public boolean finished ( ) {
return pathPosition > = path . length ( ) ;
2018-08-02 14:01:34 +00:00
}
2018-08-09 23:37:08 +00:00
public Set < BlockPos > toBreak ( ) {
return Collections . unmodifiableSet ( toBreak ) ;
}
public Set < BlockPos > toPlace ( ) {
return Collections . unmodifiableSet ( toPlace ) ;
}
2018-08-12 15:40:44 +00:00
public Set < BlockPos > toWalkInto ( ) {
return Collections . unmodifiableSet ( toWalkInto ) ;
}
2018-08-02 14:01:34 +00:00
}