2023-06-11 07:33:35 +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 Lesser General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* Baritone is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public License
* along with Baritone . If not , see < https : //www.gnu.org/licenses/>.
* /
2023-07-17 02:56:17 +00:00
package baritone.process.elytra ;
2023-06-11 07:33:35 +00:00
2023-06-17 06:55:07 +00:00
import baritone.Baritone ;
2023-07-01 01:42:03 +00:00
import baritone.api.IBaritone ;
2023-07-12 03:47:09 +00:00
import baritone.api.Settings ;
2023-06-25 04:44:08 +00:00
import baritone.api.behavior.look.IAimProcessor ;
import baritone.api.behavior.look.ITickableAimProcessor ;
2023-06-21 01:09:53 +00:00
import baritone.api.event.events.* ;
2023-07-07 18:19:18 +00:00
import baritone.api.event.events.type.EventState ;
2023-07-12 03:47:09 +00:00
import baritone.api.pathing.goals.GoalBlock ;
2023-06-18 15:57:13 +00:00
import baritone.api.utils.* ;
2023-07-01 01:42:03 +00:00
import baritone.pathing.movement.CalculationContext ;
2023-07-17 02:56:17 +00:00
import baritone.process.ElytraProcess ;
2023-06-11 07:33:35 +00:00
import baritone.utils.BlockStateInterface ;
2023-07-12 03:47:09 +00:00
import baritone.utils.IRenderer ;
import baritone.utils.PathRenderer ;
2023-07-08 02:44:59 +00:00
import baritone.utils.accessor.IChunkProviderClient ;
2023-06-18 05:24:10 +00:00
import baritone.utils.accessor.IEntityFireworkRocket ;
2023-06-28 22:47:01 +00:00
import it.unimi.dsi.fastutil.floats.FloatArrayList ;
import it.unimi.dsi.fastutil.floats.FloatIterator ;
2023-07-03 18:27:23 +00:00
import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap ;
2023-06-11 07:33:35 +00:00
import net.minecraft.block.material.Material ;
2023-06-13 04:43:10 +00:00
import net.minecraft.block.state.IBlockState ;
2023-06-11 07:33:35 +00:00
import net.minecraft.entity.item.EntityFireworkRocket ;
2023-06-18 05:24:10 +00:00
import net.minecraft.init.Items ;
2023-07-12 20:42:14 +00:00
import net.minecraft.inventory.ClickType ;
2023-06-18 05:24:10 +00:00
import net.minecraft.item.ItemStack ;
import net.minecraft.nbt.NBTTagCompound ;
2023-06-20 02:22:57 +00:00
import net.minecraft.network.play.server.SPacketPlayerPosLook ;
2023-06-11 07:33:35 +00:00
import net.minecraft.util.EnumHand ;
2023-07-12 20:42:14 +00:00
import net.minecraft.util.NonNullList ;
2023-07-10 07:12:10 +00:00
import net.minecraft.util.math.AxisAlignedBB ;
import net.minecraft.util.math.BlockPos ;
import net.minecraft.util.math.MathHelper ;
import net.minecraft.util.math.Vec3d ;
2023-07-17 04:34:48 +00:00
import net.minecraft.world.World ;
2023-06-17 03:31:53 +00:00
import net.minecraft.world.chunk.Chunk ;
2023-06-11 07:33:35 +00:00
2023-07-12 03:47:09 +00:00
import java.awt.* ;
2023-06-13 04:43:10 +00:00
import java.util.* ;
2023-07-12 03:47:09 +00:00
import java.util.List ;
2023-06-30 04:35:12 +00:00
import java.util.concurrent.* ;
2023-06-17 19:15:57 +00:00
import java.util.function.UnaryOperator ;
2023-06-11 07:33:35 +00:00
2023-07-01 01:42:03 +00:00
import static baritone.api.pathing.movement.ActionCosts.COST_INF ;
2023-07-03 18:27:23 +00:00
import static baritone.utils.BaritoneMath.fastCeil ;
import static baritone.utils.BaritoneMath.fastFloor ;
2023-07-01 01:42:03 +00:00
2023-07-17 04:34:48 +00:00
public final class LegacyElytraBehavior implements Helper {
2023-07-17 02:56:17 +00:00
private final Baritone baritone ;
private final IPlayerContext ctx ;
2023-06-11 07:33:35 +00:00
2023-06-17 01:38:29 +00:00
// Used exclusively for PathRenderer
2023-06-19 01:06:32 +00:00
public List < Pair < Vec3d , Vec3d > > clearLines ;
public List < Pair < Vec3d , Vec3d > > blockedLines ;
2023-06-25 05:27:38 +00:00
public List < Vec3d > simulationLine ;
2023-06-17 03:31:53 +00:00
public BlockPos aimPos ;
2023-06-17 01:53:53 +00:00
public List < BetterBlockPos > visiblePath ;
2023-06-17 01:38:29 +00:00
2023-06-17 06:39:21 +00:00
// :sunglasses:
2023-07-17 04:34:48 +00:00
private NetherPathfinderContext context ; // TODO: make this final
2023-07-08 02:44:59 +00:00
private CompletableFuture < Void > forceResetContext ;
2023-07-17 02:56:17 +00:00
public final PathManager pathManager ;
2023-06-28 05:46:14 +00:00
private final ElytraProcess process ;
2023-06-23 23:30:10 +00:00
/ * *
* Remaining cool - down ticks between firework usage
* /
2023-06-20 04:12:46 +00:00
private int remainingFireworkTicks ;
2023-06-23 23:30:10 +00:00
2023-06-26 20:35:40 +00:00
/ * *
* Remaining cool - down ticks after the player ' s position and rotation are reset by the server
* /
private int remainingSetBackTicks ;
2023-06-23 23:30:10 +00:00
/ * *
* The most recent minimum number of firework boost ticks , equivalent to { @code 10 * ( 1 + Flight ) }
* < p >
* Updated every time a firework is automatically used
* /
private int minimumBoostTicks ;
2023-06-30 04:35:12 +00:00
private boolean deployedFireworkLastTick ;
private final int [ ] nextTickBoostCounter ;
2023-06-19 20:29:30 +00:00
private BlockStateInterface bsi ;
2023-07-13 19:28:51 +00:00
private BlockStateOctreeInterface boi ;
2023-07-17 02:56:17 +00:00
public BlockPos destination ;
2023-06-17 00:54:37 +00:00
2023-06-30 04:35:12 +00:00
private final ExecutorService solverExecutor ;
2023-06-21 01:09:53 +00:00
private Future < Solution > solver ;
2023-06-28 05:46:14 +00:00
private Solution pendingSolution ;
2023-06-21 01:09:53 +00:00
private boolean solveNextTick ;
2023-07-16 05:19:51 +00:00
private long timeLastCacheCull = 0L ;
2023-07-12 20:42:14 +00:00
// auto swap
2023-07-13 18:21:15 +00:00
private int invTickCountdown = 0 ;
2023-07-16 05:19:51 +00:00
private final Queue < Runnable > invTransactionQueue = new LinkedList < > ( ) ;
2023-07-12 20:42:14 +00:00
2023-07-17 02:56:17 +00:00
public LegacyElytraBehavior ( Baritone baritone , ElytraProcess process ) {
this . baritone = baritone ;
this . ctx = baritone . getPlayerContext ( ) ;
2023-06-21 03:36:27 +00:00
this . clearLines = new CopyOnWriteArrayList < > ( ) ;
this . blockedLines = new CopyOnWriteArrayList < > ( ) ;
2023-06-21 01:09:53 +00:00
this . pathManager = this . new PathManager ( ) ;
2023-07-17 02:56:17 +00:00
this . process = process ;
2023-06-30 04:35:12 +00:00
this . solverExecutor = Executors . newSingleThreadExecutor ( ) ;
2023-06-29 19:46:04 +00:00
this . nextTickBoostCounter = new int [ 2 ] ;
2023-07-17 04:34:48 +00:00
this . context = new NetherPathfinderContext ( Baritone . settings ( ) . elytraNetherSeed . value ) ;
2023-06-28 05:46:14 +00:00
}
2023-07-17 02:56:17 +00:00
public final class PathManager {
2023-06-17 00:54:37 +00:00
2023-07-17 02:56:17 +00:00
public NetherPath path ;
2023-06-17 19:15:57 +00:00
private boolean completePath ;
private boolean recalculating ;
2023-06-20 18:43:14 +00:00
private int maxPlayerNear ;
private int ticksNearUnchanged ;
2023-06-19 20:16:11 +00:00
private int playerNear ;
2023-06-17 19:15:57 +00:00
public PathManager ( ) {
// lol imagine initializing fields normally
this . clear ( ) ;
2023-06-17 04:35:56 +00:00
}
2023-06-16 23:59:57 +00:00
2023-06-17 19:15:57 +00:00
public void tick ( ) {
// Recalculate closest path node
2023-06-21 01:09:53 +00:00
this . updatePlayerNear ( ) ;
2023-06-20 18:43:14 +00:00
final int prevMaxNear = this . maxPlayerNear ;
this . maxPlayerNear = Math . max ( this . maxPlayerNear , this . playerNear ) ;
2023-06-21 05:21:37 +00:00
if ( this . maxPlayerNear = = prevMaxNear & & ctx . player ( ) . isElytraFlying ( ) ) {
2023-06-20 18:43:14 +00:00
this . ticksNearUnchanged + + ;
} else {
this . ticksNearUnchanged = 0 ;
}
2023-06-17 03:31:53 +00:00
2023-06-17 19:15:57 +00:00
// Obstacles are more important than an incomplete path, handle those first.
this . pathfindAroundObstacles ( ) ;
this . attemptNextSegment ( ) ;
2023-06-16 23:59:57 +00:00
}
2023-06-17 19:15:57 +00:00
2023-07-01 01:53:42 +00:00
public CompletableFuture < Void > pathToDestination ( ) {
return this . pathToDestination ( ctx . playerFeet ( ) ) ;
2023-06-27 07:54:53 +00:00
}
2023-07-01 01:53:42 +00:00
public CompletableFuture < Void > pathToDestination ( final BlockPos from ) {
2023-06-19 06:09:43 +00:00
final long start = System . nanoTime ( ) ;
2023-07-17 02:56:17 +00:00
return this . path0 ( from , LegacyElytraBehavior . this . destination , UnaryOperator . identity ( ) )
2023-06-18 01:16:26 +00:00
. thenRun ( ( ) - > {
2023-06-21 01:18:56 +00:00
final double distance = this . path . get ( 0 ) . distanceTo ( this . path . get ( this . path . size ( ) - 1 ) ) ;
2023-06-17 19:15:57 +00:00
if ( this . completePath ) {
2023-06-19 06:09:43 +00:00
logDirect ( String . format ( " Computed path (%.1f blocks in %.4f seconds) " , distance , ( System . nanoTime ( ) - start ) / 1e9d ) ) ;
2023-06-17 19:15:57 +00:00
} else {
2023-06-19 06:09:43 +00:00
logDirect ( String . format ( " Computed segment (Next %.1f blocks in %.4f seconds) " , distance , ( System . nanoTime ( ) - start ) / 1e9d ) ) ;
2023-06-17 19:15:57 +00:00
}
2023-06-18 01:16:26 +00:00
} )
. whenComplete ( ( result , ex ) - > {
this . recalculating = false ;
2023-06-22 23:07:11 +00:00
if ( ex ! = null ) {
final Throwable cause = ex . getCause ( ) ;
if ( cause instanceof PathCalculationException ) {
logDirect ( " Failed to compute path to destination " ) ;
} else {
logUnhandledException ( cause ) ;
}
2023-06-18 01:16:26 +00:00
}
2023-06-17 19:15:57 +00:00
} ) ;
}
2023-06-20 18:43:14 +00:00
public CompletableFuture < Void > pathRecalcSegment ( final int upToIncl ) {
2023-06-17 19:15:57 +00:00
if ( this . recalculating ) {
2023-06-20 18:43:14 +00:00
throw new IllegalStateException ( " already recalculating " ) ;
2023-06-17 04:35:56 +00:00
}
2023-06-17 19:15:57 +00:00
this . recalculating = true ;
2023-07-17 21:21:06 +00:00
final List < BetterBlockPos > after = this . path . subList ( upToIncl + 1 , this . path . size ( ) ) ;
2023-06-17 19:15:57 +00:00
final boolean complete = this . completePath ;
2023-06-20 18:43:14 +00:00
return this . path0 ( ctx . playerFeet ( ) , this . path . get ( upToIncl ) , segment - > segment . append ( after . stream ( ) , complete ) )
2023-06-18 01:16:26 +00:00
. whenComplete ( ( result , ex ) - > {
2023-06-17 19:15:57 +00:00
this . recalculating = false ;
2023-06-22 23:07:11 +00:00
if ( ex ! = null ) {
final Throwable cause = ex . getCause ( ) ;
if ( cause instanceof PathCalculationException ) {
logDirect ( " Failed to recompute segment " ) ;
} else {
logUnhandledException ( cause ) ;
}
2023-06-18 00:58:19 +00:00
}
2023-06-17 19:15:57 +00:00
} ) ;
}
2023-06-17 19:21:32 +00:00
public void pathNextSegment ( final int afterIncl ) {
2023-06-17 19:15:57 +00:00
if ( this . recalculating ) {
2023-06-17 04:35:56 +00:00
return ;
}
2023-06-17 19:15:57 +00:00
this . recalculating = true ;
final List < BetterBlockPos > before = this . path . subList ( 0 , afterIncl + 1 ) ;
2023-06-19 06:09:43 +00:00
final long start = System . nanoTime ( ) ;
2023-06-17 19:15:57 +00:00
2023-07-17 02:56:17 +00:00
this . path0 ( this . path . get ( afterIncl ) , LegacyElytraBehavior . this . destination , segment - > segment . prepend ( before . stream ( ) ) )
2023-06-18 01:16:26 +00:00
. thenRun ( ( ) - > {
2023-06-17 19:15:57 +00:00
final int recompute = this . path . size ( ) - before . size ( ) - 1 ;
2023-06-21 01:18:56 +00:00
final double distance = this . path . get ( 0 ) . distanceTo ( this . path . get ( recompute ) ) ;
2023-06-17 19:15:57 +00:00
if ( this . completePath ) {
2023-06-19 06:09:43 +00:00
logDirect ( String . format ( " Computed path (%.1f blocks in %.4f seconds) " , distance , ( System . nanoTime ( ) - start ) / 1e9d ) ) ;
2023-06-17 19:15:57 +00:00
} else {
2023-06-19 06:09:43 +00:00
logDirect ( String . format ( " Computed segment (Next %.1f blocks in %.4f seconds) " , distance , ( System . nanoTime ( ) - start ) / 1e9d ) ) ;
2023-06-17 19:15:57 +00:00
}
2023-06-18 01:16:26 +00:00
} )
. whenComplete ( ( result , ex ) - > {
this . recalculating = false ;
2023-06-22 23:07:11 +00:00
if ( ex ! = null ) {
final Throwable cause = ex . getCause ( ) ;
if ( cause instanceof PathCalculationException ) {
logDirect ( " Failed to compute next segment " ) ;
} else {
logUnhandledException ( cause ) ;
}
2023-06-18 01:16:26 +00:00
}
2023-06-17 19:15:57 +00:00
} ) ;
}
public void clear ( ) {
2023-06-21 02:07:21 +00:00
this . path = NetherPath . emptyPath ( ) ;
2023-06-17 19:15:57 +00:00
this . completePath = true ;
2023-06-19 20:15:29 +00:00
this . recalculating = false ;
2023-06-19 20:16:11 +00:00
this . playerNear = 0 ;
2023-06-20 18:43:14 +00:00
this . ticksNearUnchanged = 0 ;
this . maxPlayerNear = 0 ;
2023-06-17 19:15:57 +00:00
}
private void setPath ( final UnpackedSegment segment ) {
this . path = segment . collect ( ) ;
this . completePath = segment . isFinished ( ) ;
2023-06-20 18:43:14 +00:00
this . playerNear = 0 ;
this . ticksNearUnchanged = 0 ;
this . maxPlayerNear = 0 ;
2023-06-17 19:15:57 +00:00
}
2023-06-21 02:07:21 +00:00
public NetherPath getPath ( ) {
2023-06-17 19:15:57 +00:00
return this . path ;
}
public int getNear ( ) {
return this . playerNear ;
}
// mickey resigned
private CompletableFuture < Void > path0 ( BlockPos src , BlockPos dst , UnaryOperator < UnpackedSegment > operator ) {
2023-07-17 02:56:17 +00:00
return LegacyElytraBehavior . this . context . pathFindAsync ( src , dst )
2023-06-17 19:15:57 +00:00
. thenApply ( UnpackedSegment : : from )
. thenApply ( operator )
. thenAcceptAsync ( this : : setPath , ctx . minecraft ( ) : : addScheduledTask ) ;
}
private void pathfindAroundObstacles ( ) {
if ( this . recalculating ) {
return ;
2023-06-17 04:35:56 +00:00
}
2023-06-17 19:15:57 +00:00
2023-06-19 20:29:30 +00:00
int rangeStartIncl = playerNear ;
int rangeEndExcl = playerNear ;
while ( rangeEndExcl < path . size ( ) & & ctx . world ( ) . isBlockLoaded ( path . get ( rangeEndExcl ) , false ) ) {
rangeEndExcl + + ;
}
2023-07-17 21:21:06 +00:00
// rangeEndExcl now represents an index either not in the path, or just outside render distance
2023-06-19 20:29:30 +00:00
if ( rangeStartIncl > = rangeEndExcl ) {
// not loaded yet?
return ;
}
2023-07-13 19:28:51 +00:00
final BetterBlockPos rangeStart = path . get ( rangeStartIncl ) ;
2023-07-17 02:56:17 +00:00
if ( ! LegacyElytraBehavior . this . passable ( rangeStart . x , rangeStart . y , rangeStart . z , false ) ) {
2023-06-19 20:29:30 +00:00
// we're in a wall
return ; // previous iterations of this function SHOULD have fixed this by now :rage_cat:
}
2023-06-20 18:43:14 +00:00
2023-07-17 02:56:17 +00:00
if ( LegacyElytraBehavior . this . process . state ! = ElytraProcess . State . LANDING & & this . ticksNearUnchanged > 100 ) {
2023-06-20 18:43:14 +00:00
this . pathRecalcSegment ( rangeEndExcl - 1 )
. thenRun ( ( ) - > {
logDirect ( " Recalculating segment, no progress in last 100 ticks " ) ;
} ) ;
this . ticksNearUnchanged = 0 ;
return ;
}
2023-06-19 20:29:30 +00:00
for ( int i = rangeStartIncl ; i < rangeEndExcl - 1 ; i + + ) {
2023-07-17 02:56:17 +00:00
if ( ! LegacyElytraBehavior . this . clearView ( this . path . getVec ( i ) , this . path . getVec ( i + 1 ) , false ) ) {
2023-06-19 20:29:30 +00:00
// obstacle. where do we return to pathing?
// find the next valid segment
2023-06-20 18:43:14 +00:00
final BetterBlockPos blockage = this . path . get ( i ) ;
2023-06-21 01:09:53 +00:00
final double distance = ctx . playerFeet ( ) . distanceTo ( this . path . get ( rangeEndExcl - 1 ) ) ;
2023-06-20 18:43:14 +00:00
final long start = System . nanoTime ( ) ;
this . pathRecalcSegment ( rangeEndExcl - 1 )
. thenRun ( ( ) - > {
logDirect ( String . format ( " Recalculated segment around path blockage near %s %s %s (next %.1f blocks in %.4f seconds) " ,
SettingsUtil . maybeCensor ( blockage . x ) ,
SettingsUtil . maybeCensor ( blockage . y ) ,
SettingsUtil . maybeCensor ( blockage . z ) ,
distance ,
( System . nanoTime ( ) - start ) / 1e9d
) ) ;
} ) ;
2023-06-17 19:15:57 +00:00
return ;
}
2023-06-17 04:35:56 +00:00
}
}
2023-06-16 23:59:57 +00:00
2023-06-17 19:15:57 +00:00
private void attemptNextSegment ( ) {
if ( this . recalculating ) {
return ;
}
2023-06-17 04:35:56 +00:00
2023-06-17 19:15:57 +00:00
final int last = this . path . size ( ) - 1 ;
if ( ! this . completePath & & ctx . world ( ) . isBlockLoaded ( this . path . get ( last ) , false ) ) {
2023-06-17 19:21:32 +00:00
this . pathNextSegment ( last ) ;
2023-06-17 19:15:57 +00:00
}
}
2023-06-17 06:39:21 +00:00
2023-06-21 01:09:53 +00:00
public void updatePlayerNear ( ) {
2023-06-22 02:19:12 +00:00
if ( this . path . isEmpty ( ) ) {
return ;
}
2023-06-21 01:09:53 +00:00
int index = this . playerNear ;
2023-06-17 19:15:57 +00:00
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 ) ) {
index = i ; // intentional: this changes the bound of the loop
}
}
for ( int i = index ; i < Math . min ( index + 1000 , path . size ( ) ) ; i + = 10 ) {
if ( path . get ( i ) . distanceSq ( pos ) < path . get ( index ) . distanceSq ( pos ) ) {
index = i ; // intentional: this changes the bound of the loop
}
}
for ( int i = index ; i > = Math . max ( index - 50 , 0 ) ; i - - ) {
if ( path . get ( i ) . distanceSq ( pos ) < path . get ( index ) . distanceSq ( pos ) ) {
index = i ; // intentional: this changes the bound of the loop
}
}
for ( int i = index ; i < Math . min ( index + 50 , path . size ( ) ) ; i + + ) {
if ( path . get ( i ) . distanceSq ( pos ) < path . get ( index ) . distanceSq ( pos ) ) {
index = i ; // intentional: this changes the bound of the loop
}
}
2023-06-21 01:09:53 +00:00
this . playerNear = index ;
2023-06-17 19:15:57 +00:00
}
}
2023-07-12 03:47:09 +00:00
public void onRenderPass ( RenderEvent event ) {
final Settings settings = Baritone . settings ( ) ;
if ( this . visiblePath ! = null ) {
PathRenderer . drawPath ( this . visiblePath , 0 , Color . RED , false , 0 , 0 , 0 . 0D ) ;
}
if ( this . aimPos ! = null ) {
PathRenderer . drawGoal ( ctx . player ( ) , new GoalBlock ( this . aimPos ) , event . getPartialTicks ( ) , Color . GREEN ) ;
}
2023-07-16 22:58:42 +00:00
if ( ! this . clearLines . isEmpty ( ) & & settings . elytraRenderRaytraces . value ) {
2023-07-12 03:47:09 +00:00
IRenderer . startLines ( Color . GREEN , settings . pathRenderLineWidthPixels . value , settings . renderPathIgnoreDepth . value ) ;
for ( Pair < Vec3d , Vec3d > line : this . clearLines ) {
IRenderer . emitLine ( line . first ( ) , line . second ( ) ) ;
}
IRenderer . endLines ( settings . renderPathIgnoreDepth . value ) ;
}
2023-07-16 22:58:42 +00:00
if ( ! this . blockedLines . isEmpty ( ) & & Baritone . settings ( ) . elytraRenderRaytraces . value ) {
2023-07-12 03:47:09 +00:00
IRenderer . startLines ( Color . BLUE , settings . pathRenderLineWidthPixels . value , settings . renderPathIgnoreDepth . value ) ;
for ( Pair < Vec3d , Vec3d > line : this . blockedLines ) {
IRenderer . emitLine ( line . first ( ) , line . second ( ) ) ;
}
IRenderer . endLines ( settings . renderPathIgnoreDepth . value ) ;
}
2023-07-16 22:58:42 +00:00
if ( this . simulationLine ! = null & & Baritone . settings ( ) . elytraRenderSimulation . value ) {
2023-07-12 03:47:09 +00:00
IRenderer . startLines ( new Color ( 0x36CCDC ) , settings . pathRenderLineWidthPixels . value , settings . renderPathIgnoreDepth . value ) ;
final Vec3d offset = new Vec3d (
ctx . player ( ) . prevPosX + ( ctx . player ( ) . posX - ctx . player ( ) . prevPosX ) * event . getPartialTicks ( ) ,
ctx . player ( ) . prevPosY + ( ctx . player ( ) . posY - ctx . player ( ) . prevPosY ) * event . getPartialTicks ( ) ,
ctx . player ( ) . prevPosZ + ( ctx . player ( ) . posZ - ctx . player ( ) . prevPosZ ) * event . getPartialTicks ( )
) ;
for ( int i = 0 ; i < this . simulationLine . size ( ) - 1 ; i + + ) {
final Vec3d src = this . simulationLine . get ( i ) . add ( offset ) ;
final Vec3d dst = this . simulationLine . get ( i + 1 ) . add ( offset ) ;
IRenderer . emitLine ( src , dst ) ;
}
IRenderer . endLines ( settings . renderPathIgnoreDepth . value ) ;
}
}
2023-07-17 04:34:48 +00:00
// TODO: move this logic to ElytraProcess
2023-07-07 18:19:18 +00:00
public void onWorldEvent ( WorldEvent event ) {
if ( event . getWorld ( ) ! = null ) {
if ( event . getState ( ) = = EventState . PRE ) {
// Reset the context when it's safe to do so on the next game tick
2023-07-08 02:44:59 +00:00
this . resetContext ( ) ;
2023-07-07 18:19:18 +00:00
}
} else {
if ( event . getState ( ) = = EventState . POST ) {
// Exiting the world, just destroy and invalidate the context
if ( this . context ! = null ) {
this . context . destroy ( ) ;
this . context = null ;
}
}
}
}
2023-06-17 19:15:57 +00:00
public void onChunkEvent ( ChunkEvent event ) {
2023-07-09 16:12:21 +00:00
if ( event . isPostPopulate ( ) & & this . context ! = null ) {
2023-06-17 19:15:57 +00:00
final Chunk chunk = ctx . world ( ) . getChunk ( event . getX ( ) , event . getZ ( ) ) ;
this . context . queueForPacking ( chunk ) ;
}
}
2023-07-17 04:34:48 +00:00
public void uploadRenderDistance ( World world ) {
( ( IChunkProviderClient ) world . getChunkProvider ( ) ) . loadedChunks ( ) . forEach ( ( l , chunk ) - > {
this . context . queueForPacking ( chunk ) ;
} ) ;
}
2023-06-19 02:25:00 +00:00
public void onBlockChange ( BlockChangeEvent event ) {
2023-07-10 04:45:12 +00:00
this . context . queueBlockUpdate ( event ) ;
2023-06-19 02:25:00 +00:00
}
2023-06-20 02:22:57 +00:00
public void onReceivePacket ( PacketEvent event ) {
if ( event . getPacket ( ) instanceof SPacketPlayerPosLook ) {
2023-06-20 04:12:46 +00:00
ctx . minecraft ( ) . addScheduledTask ( ( ) - > {
this . remainingSetBackTicks = Baritone . settings ( ) . elytraFireworkSetbackUseDelay . value ;
} ) ;
2023-06-20 02:22:57 +00:00
}
}
2023-06-19 02:46:03 +00:00
public void pathTo ( BlockPos destination ) {
2023-06-27 07:54:53 +00:00
this . destination = destination ;
if ( ! Baritone . settings ( ) . elytraAutoJump . value | | ctx . player ( ) . isElytraFlying ( ) ) {
this . pathManager . pathToDestination ( ) ;
}
2023-06-11 07:33:35 +00:00
}
2023-06-16 03:59:01 +00:00
public void cancel ( ) {
2023-06-27 07:54:53 +00:00
this . destination = null ;
2023-06-17 19:15:57 +00:00
this . pathManager . clear ( ) ;
2023-06-20 04:12:46 +00:00
this . remainingFireworkTicks = 0 ;
this . remainingSetBackTicks = 0 ;
2023-06-21 04:15:45 +00:00
if ( this . solver ! = null ) {
this . solver . cancel ( true ) ;
this . solver = null ;
}
2023-06-28 06:05:22 +00:00
this . pendingSolution = null ;
2023-06-29 19:46:42 +00:00
Arrays . fill ( this . nextTickBoostCounter , 0 ) ;
2023-06-18 05:24:10 +00:00
}
2023-07-08 02:44:59 +00:00
public CompletableFuture < Void > resetContext ( ) {
if ( this . forceResetContext = = null ) {
this . forceResetContext = new CompletableFuture < > ( ) ;
}
return this . forceResetContext ;
}
public void repackChunks ( ) {
( ( IChunkProviderClient ) ctx . world ( ) . getChunkProvider ( ) ) . loadedChunks ( ) . values ( )
. forEach ( this . context : : queueForPacking ) ;
}
2023-06-18 05:24:10 +00:00
public boolean isActive ( ) {
2023-06-29 19:53:12 +00:00
return baritone . getPathingControlManager ( ) . mostRecentInControl ( )
. filter ( process - > this . process = = process ) . isPresent ( ) ;
2023-06-16 03:59:01 +00:00
}
2023-06-28 05:46:14 +00:00
public void onTick ( final TickEvent event ) {
2023-06-11 07:33:35 +00:00
if ( event . getType ( ) = = TickEvent . Type . OUT ) {
return ;
}
2023-06-19 02:46:03 +00:00
2023-06-21 01:09:53 +00:00
// Fetch the previous solution, regardless of if it's going to be used
2023-06-28 05:46:14 +00:00
this . pendingSolution = null ;
2023-06-21 01:09:53 +00:00
if ( this . solver ! = null ) {
try {
2023-06-28 05:46:14 +00:00
this . pendingSolution = this . solver . get ( ) ;
2023-06-21 01:09:53 +00:00
} catch ( Exception ignored ) {
// it doesn't matter if get() fails since the solution can just be recalculated synchronously
} finally {
this . solver = null ;
}
}
2023-07-07 17:44:56 +00:00
// Setup/reset context
final long netherSeed = Baritone . settings ( ) . elytraNetherSeed . value ;
2023-07-08 02:44:59 +00:00
if ( this . context = = null | | this . context . getSeed ( ) ! = netherSeed | | this . forceResetContext ! = null ) {
2023-07-07 17:44:56 +00:00
if ( this . context ! = null ) {
this . context . destroy ( ) ;
}
this . context = new NetherPathfinderContext ( netherSeed ) ;
2023-07-08 02:44:59 +00:00
if ( this . forceResetContext ! = null ) {
this . forceResetContext . complete ( null ) ;
this . forceResetContext = null ;
}
if ( this . context . getSeed ( ) ! = netherSeed & & this . isActive ( ) ) {
2023-07-07 17:44:56 +00:00
logDirect ( " Nether seed changed, recalculating path " ) ;
this . pathManager . pathToDestination ( ) ;
}
}
2023-07-13 18:35:33 +00:00
tickInventoryTransactions ( ) ;
2023-06-20 04:12:46 +00:00
// Certified mojang employee incident
if ( this . remainingFireworkTicks > 0 ) {
this . remainingFireworkTicks - - ;
}
if ( this . remainingSetBackTicks > 0 ) {
this . remainingSetBackTicks - - ;
}
2023-06-26 19:00:38 +00:00
if ( ! this . getAttachedFirework ( ) . isPresent ( ) ) {
this . minimumBoostTicks = 0 ;
}
2023-06-20 04:12:46 +00:00
2023-07-03 18:27:23 +00:00
// lol
MC_1_12_Collision_Fix . clear ( ) ;
2023-06-21 02:07:21 +00:00
// Reset rendered elements
2023-06-19 01:06:32 +00:00
this . clearLines . clear ( ) ;
this . blockedLines . clear ( ) ;
2023-07-01 01:42:03 +00:00
this . visiblePath = null ;
2023-06-25 05:27:38 +00:00
this . simulationLine = null ;
2023-06-21 02:07:21 +00:00
this . aimPos = null ;
2023-06-17 19:15:57 +00:00
final List < BetterBlockPos > path = this . pathManager . getPath ( ) ;
2023-06-21 05:21:37 +00:00
if ( path . isEmpty ( ) ) {
2023-06-15 07:32:26 +00:00
return ;
2023-07-01 01:42:03 +00:00
} else if ( this . destination = = null ) {
this . pathManager . clear ( ) ;
return ;
2023-06-15 07:32:26 +00:00
}
2023-06-17 01:38:29 +00:00
2023-07-13 19:28:51 +00:00
// ctx AND context???? :DDD
2023-06-19 00:57:29 +00:00
this . bsi = new BlockStateInterface ( ctx ) ;
2023-07-13 19:28:51 +00:00
this . boi = new BlockStateOctreeInterface ( context ) ;
2023-06-17 19:15:57 +00:00
this . pathManager . tick ( ) ;
final int playerNear = this . pathManager . getNear ( ) ;
this . visiblePath = path . subList (
2023-06-17 01:53:53 +00:00
Math . max ( playerNear - 30 , 0 ) ,
2023-06-18 03:03:30 +00:00
Math . min ( playerNear + 100 , path . size ( ) )
2023-06-17 01:53:53 +00:00
) ;
2023-07-17 01:21:40 +00:00
final long now = System . currentTimeMillis ( ) ;
if ( ( now - this . timeLastCacheCull ) / 1000 > Baritone . settings ( ) . elytraTimeBetweenCacheCullSecs . value ) {
this . context . queueCacheCulling ( ctx . player ( ) . chunkCoordX , ctx . player ( ) . chunkCoordZ , Baritone . settings ( ) . elytraCacheCullDistance . value , this . boi ) ;
this . timeLastCacheCull = now ;
}
2023-06-28 05:47:20 +00:00
}
2023-06-17 01:53:53 +00:00
2023-06-28 05:47:20 +00:00
/ * *
2023-07-17 02:56:17 +00:00
* Called by { @link baritone . process . ElytraProcess # onTick ( boolean , boolean ) } when the process is in control and the player is flying
2023-06-28 05:47:20 +00:00
* /
2023-07-17 02:56:17 +00:00
public void tick ( ) {
2023-06-28 06:04:08 +00:00
if ( this . pathManager . getPath ( ) . isEmpty ( ) ) {
return ;
}
2023-07-12 20:42:14 +00:00
trySwapElytra ( ) ;
2023-06-17 01:38:29 +00:00
if ( ctx . player ( ) . collidedHorizontally ) {
logDirect ( " hbonk " ) ;
}
if ( ctx . player ( ) . collidedVertically ) {
logDirect ( " vbonk " ) ;
}
2023-06-25 04:44:08 +00:00
final SolverContext solverContext = this . new SolverContext ( false ) ;
2023-06-21 01:09:53 +00:00
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
2023-06-28 05:46:14 +00:00
final Solution solution ;
if ( this . pendingSolution = = null | | ! this . pendingSolution . context . equals ( solverContext ) ) {
2023-06-21 01:09:53 +00:00
solution = this . solveAngles ( solverContext ) ;
2023-06-28 05:46:14 +00:00
} else {
solution = this . pendingSolution ;
2023-06-21 01:09:53 +00:00
}
2023-06-20 20:15:02 +00:00
2023-06-29 19:46:04 +00:00
if ( this . deployedFireworkLastTick ) {
this . nextTickBoostCounter [ solverContext . boost . isBoosted ( ) ? 1 : 0 ] + + ;
this . deployedFireworkLastTick = false ;
}
2023-06-20 20:15:02 +00:00
if ( solution = = null ) {
logDirect ( " no solution " ) ;
return ;
}
baritone . getLookBehavior ( ) . updateTarget ( solution . rotation , false ) ;
if ( ! solution . solvedPitch ) {
logDirect ( " no pitch solution, probably gonna crash in a few ticks LOL!!! " ) ;
return ;
2023-06-21 01:09:53 +00:00
} else {
2023-06-21 01:30:10 +00:00
this . aimPos = new BetterBlockPos ( solution . goingTo . x , solution . goingTo . y , solution . goingTo . z ) ;
2023-06-20 20:15:02 +00:00
}
2023-06-21 01:09:53 +00:00
this . tickUseFireworks (
solution . context . start ,
solution . goingTo ,
2023-06-26 18:55:18 +00:00
solution . context . boost . isBoosted ( ) ,
2023-06-21 01:09:53 +00:00
solution . forceUseFirework
) ;
2023-06-20 20:15:02 +00:00
}
2023-06-21 01:09:53 +00:00
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 ( ) ;
2023-06-25 04:44:08 +00:00
final SolverContext context = this . new SolverContext ( true ) ;
2023-06-30 04:35:12 +00:00
this . solver = this . solverExecutor . submit ( ( ) - > this . solveAngles ( context ) ) ;
2023-06-21 01:09:53 +00:00
this . solveNextTick = false ;
}
}
private Solution solveAngles ( final SolverContext context ) {
2023-06-21 02:07:21 +00:00
final NetherPath path = context . path ;
2023-06-21 01:09:53 +00:00
final int playerNear = context . playerNear ;
final Vec3d start = context . start ;
2023-06-20 20:05:59 +00:00
Solution solution = null ;
2023-06-18 05:11:40 +00:00
2023-06-17 01:38:29 +00:00
for ( int relaxation = 0 ; relaxation < 3 ; relaxation + + ) { // try for a strict solution first, then relax more and more (if we're in a corner or near some blocks, it will have to relax its constraints a bit)
2023-06-26 18:55:18 +00:00
int [ ] heights = context . boost . isBoosted ( ) ? new int [ ] { 20 , 10 , 5 , 0 } : new int [ ] { 0 } ; // attempt to gain height, if we can, so as not to waste the boost
2023-06-17 01:38:29 +00:00
int lookahead = relaxation = = 0 ? 2 : 3 ; // ideally this would be expressed as a distance in blocks, rather than a number of voxel steps
//int minStep = Math.max(0, playerNear - relaxation);
int minStep = playerNear ;
2023-06-28 22:47:01 +00:00
2023-06-17 01:38:29 +00:00
for ( int i = Math . min ( playerNear + 20 , path . size ( ) - 1 ) ; i > = minStep ; i - - ) {
2023-06-28 22:47:01 +00:00
final List < Pair < Vec3d , Integer > > candidates = new ArrayList < > ( ) ;
2023-06-17 01:38:29 +00:00
for ( int dy : heights ) {
2023-06-30 00:15:06 +00:00
if ( relaxation = = 0 | | i = = minStep ) {
2023-06-28 22:47:01 +00:00
// no interp
2023-06-30 00:15:06 +00:00
candidates . add ( new Pair < > ( path . getVec ( i ) , dy ) ) ;
} else if ( relaxation = = 1 ) {
final double [ ] interps = new double [ ] { 1 . 0 , 0 . 75 , 0 . 5 , 0 . 25 } ;
2023-06-29 02:47:37 +00:00
for ( double interp : interps ) {
2023-06-29 19:14:49 +00:00
final Vec3d dest = interp = = 1 . 0
? path . getVec ( i )
: path . getVec ( i ) . scale ( interp ) . add ( path . getVec ( i - 1 ) . scale ( 1 . 0 - interp ) ) ;
2023-06-30 00:15:06 +00:00
candidates . add ( new Pair < > ( dest , dy ) ) ;
2023-06-29 02:47:37 +00:00
}
} else {
2023-06-30 00:15:06 +00:00
// Create a point along the segment every block
2023-06-29 02:47:37 +00:00
final Vec3d delta = path . getVec ( i ) . subtract ( path . getVec ( i - 1 ) ) ;
2023-07-03 18:27:23 +00:00
final int steps = fastFloor ( delta . length ( ) ) ;
2023-06-30 00:15:06 +00:00
final Vec3d step = delta . normalize ( ) ;
Vec3d stepped = path . getVec ( i ) ;
2023-06-29 02:47:37 +00:00
for ( int interp = 0 ; interp < steps ; interp + + ) {
candidates . add ( new Pair < > ( stepped , dy ) ) ;
stepped = stepped . subtract ( step ) ;
2023-06-17 01:38:29 +00:00
}
2023-06-28 22:47:01 +00:00
}
}
for ( final Pair < Vec3d , Integer > candidate : candidates ) {
final Integer augment = candidate . second ( ) ;
2023-06-30 00:15:06 +00:00
final Vec3d dest = candidate . first ( ) . add ( 0 , augment , 0 ) ;
2023-06-20 02:36:09 +00:00
2023-06-28 22:47:01 +00:00
if ( augment ! = 0 ) {
if ( i + lookahead > = path . size ( ) ) {
continue ;
}
if ( start . distanceTo ( dest ) < 40 ) {
if ( ! this . clearView ( dest , path . getVec ( i + lookahead ) . add ( 0 , augment , 0 ) , false )
| | ! this . clearView ( dest , path . getVec ( 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
2023-06-11 19:49:31 +00:00
continue ;
}
2023-06-28 22:47:01 +00:00
} else {
// but if it's far away, allow gaining altitude if we could lose it again by the time we get there
if ( ! this . clearView ( dest , path . getVec ( i ) , false ) ) {
continue ;
2023-06-11 07:33:35 +00:00
}
}
2023-06-28 22:47:01 +00:00
}
2023-06-18 00:36:29 +00:00
2023-06-28 22:47:01 +00:00
final double minAvoidance = Baritone . settings ( ) . elytraMinimumAvoidance . value ;
final Double growth = relaxation = = 2 ? null
: relaxation = = 0 ? 2 * minAvoidance : minAvoidance ;
2023-06-18 18:23:41 +00:00
2023-06-30 03:59:15 +00:00
if ( this . isHitboxClear ( context , dest , growth ) ) {
2023-06-28 22:47:01 +00:00
// Yaw is trivial, just calculate the rotation required to face the destination
final float yaw = RotationUtils . calcRotationFromVec3d ( start , dest , ctx . playerRotations ( ) ) . getYaw ( ) ;
2023-06-20 20:15:02 +00:00
2023-06-30 03:59:15 +00:00
final Pair < Float , Boolean > pitch = this . solvePitch ( context , dest , relaxation ) ;
2023-06-28 22:47:01 +00:00
if ( pitch = = null ) {
solution = new Solution ( context , new Rotation ( yaw , ctx . playerRotations ( ) . getPitch ( ) ) , null , false , false ) ;
continue ;
2023-06-17 01:38:29 +00:00
}
2023-06-28 22:47:01 +00:00
// A solution was found with yaw AND pitch, so just immediately return it.
return new Solution ( context , new Rotation ( yaw , pitch . first ( ) ) , dest , true , pitch . second ( ) ) ;
2023-06-17 01:38:29 +00:00
}
2023-06-11 07:33:35 +00:00
}
}
2023-06-17 05:39:50 +00:00
}
2023-06-20 20:15:02 +00:00
return solution ;
2023-06-20 02:22:57 +00:00
}
2023-06-21 01:30:10 +00:00
private void tickUseFireworks ( final Vec3d start , final Vec3d goingTo , final boolean isBoosted , final boolean forceUseFirework ) {
2023-06-20 04:12:46 +00:00
if ( this . remainingSetBackTicks > 0 ) {
logDebug ( " waiting for elytraFireworkSetbackUseDelay: " + this . remainingSetBackTicks ) ;
2023-06-20 02:22:57 +00:00
return ;
}
2023-07-16 22:58:42 +00:00
final boolean useOnDescend = ! Baritone . settings ( ) . elytraConserveFireworks . value | | ctx . player ( ) . posY < goingTo . y + 5 ;
2023-06-18 05:13:55 +00:00
final double currentSpeed = new Vec3d (
ctx . player ( ) . motionX ,
// ignore y component if we are BOTH below where we want to be AND descending
ctx . player ( ) . posY < goingTo . y ? Math . max ( 0 , ctx . player ( ) . motionY ) : ctx . player ( ) . motionY ,
ctx . player ( ) . motionZ
2023-06-20 02:22:57 +00:00
) . lengthSquared ( ) ;
2023-06-17 19:15:57 +00:00
2023-06-20 02:22:57 +00:00
final double elytraFireworkSpeed = Baritone . settings ( ) . elytraFireworkSpeed . value ;
2023-06-21 01:30:10 +00:00
if ( this . remainingFireworkTicks < = 0 & & ( forceUseFirework | | ( ! isBoosted
2023-06-18 05:11:40 +00:00
& & useOnDescend
2023-06-17 19:15:57 +00:00
& & ( ctx . player ( ) . posY < goingTo . y - 5 | | start . distanceTo ( new Vec3d ( goingTo . x + 0 . 5 , ctx . player ( ) . posY , goingTo . z + 0 . 5 ) ) > 5 ) // UGH!!!!!!!
2023-06-20 02:22:57 +00:00
& & currentSpeed < elytraFireworkSpeed * elytraFireworkSpeed ) )
2023-06-17 05:39:50 +00:00
) {
2023-06-19 17:32:25 +00:00
// Prioritize boosting fireworks over regular ones
// TODO: Take the minimum boost time into account?
2023-07-17 02:56:17 +00:00
if ( ! baritone . getInventoryBehavior ( ) . throwaway ( true , LegacyElytraBehavior : : isBoostingFireworks ) & &
! baritone . getInventoryBehavior ( ) . throwaway ( true , LegacyElytraBehavior : : isFireworks ) ) {
2023-06-19 17:32:25 +00:00
logDirect ( " no fireworks " ) ;
return ;
2023-06-18 05:24:10 +00:00
}
2023-06-29 19:14:49 +00:00
logDirect ( " attempting to use firework " + ( forceUseFirework ? " (forced) " : " " ) ) ;
2023-06-17 05:39:50 +00:00
ctx . playerController ( ) . processRightClick ( ctx . player ( ) , ctx . world ( ) , EnumHand . MAIN_HAND ) ;
2023-06-23 23:30:10 +00:00
this . minimumBoostTicks = 10 * ( 1 + getFireworkBoost ( ctx . player ( ) . getHeldItemMainhand ( ) ) . orElse ( 0 ) ) ;
2023-06-20 04:12:46 +00:00
this . remainingFireworkTicks = 10 ;
2023-06-29 19:46:04 +00:00
this . deployedFireworkLastTick = true ;
2023-06-11 07:33:35 +00:00
}
}
2023-06-21 01:09:53 +00:00
private final class SolverContext {
2023-06-21 02:07:21 +00:00
public final NetherPath path ;
2023-06-21 01:09:53 +00:00
public final int playerNear ;
public final Vec3d start ;
2023-06-30 03:59:15 +00:00
public final boolean ignoreLava ;
2023-06-26 18:55:18 +00:00
public final FireworkBoost boost ;
2023-06-25 04:44:08 +00:00
public final IAimProcessor aimProcessor ;
2023-06-21 01:09:53 +00:00
2023-06-25 04:44:08 +00:00
public SolverContext ( boolean async ) {
2023-07-17 02:56:17 +00:00
this . path = LegacyElytraBehavior . this . pathManager . getPath ( ) ;
this . playerNear = LegacyElytraBehavior . this . pathManager . getNear ( ) ;
this . start = LegacyElytraBehavior . this . ctx . playerFeetAsVec ( ) ;
this . ignoreLava = LegacyElytraBehavior . this . ctx . player ( ) . isInLava ( ) ;
2023-06-29 19:46:04 +00:00
final Integer fireworkTicksExisted ;
2023-07-17 02:56:17 +00:00
if ( async & & LegacyElytraBehavior . this . deployedFireworkLastTick ) {
final int [ ] counter = LegacyElytraBehavior . this . nextTickBoostCounter ;
2023-06-29 19:46:04 +00:00
fireworkTicksExisted = counter [ 1 ] > counter [ 0 ] ? 0 : null ;
} else {
2023-07-17 02:56:17 +00:00
fireworkTicksExisted = LegacyElytraBehavior . this . getAttachedFirework ( ) . map ( e - > e . ticksExisted ) . orElse ( null ) ;
2023-06-29 19:46:04 +00:00
}
2023-07-17 02:56:17 +00:00
this . boost = new FireworkBoost ( fireworkTicksExisted , LegacyElytraBehavior . this . minimumBoostTicks ) ;
2023-06-25 04:44:08 +00:00
2023-07-17 02:56:17 +00:00
ITickableAimProcessor aim = LegacyElytraBehavior . this . baritone . getLookBehavior ( ) . getAimProcessor ( ) . fork ( ) ;
2023-06-25 04:44:08 +00:00
if ( async ) {
// async computation is done at the end of a tick, advance by 1 to prepare for the next tick
aim . advance ( 1 ) ;
}
this . aimProcessor = aim ;
2023-06-21 01:09:53 +00:00
}
@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 )
2023-06-30 03:59:15 +00:00
& & this . ignoreLava = = other . ignoreLava
2023-06-26 18:55:18 +00:00
& & Objects . equals ( this . boost , other . boost ) ;
}
}
private static final class FireworkBoost {
2023-06-29 19:46:04 +00:00
private final Integer fireworkTicksExisted ;
2023-06-26 18:55:18 +00:00
private final int minimumBoostTicks ;
private final int maximumBoostTicks ;
2023-06-29 19:46:04 +00:00
public FireworkBoost ( final Integer fireworkTicksExisted , final int minimumBoostTicks ) {
this . fireworkTicksExisted = fireworkTicksExisted ;
2023-06-26 18:55:18 +00:00
// this.lifetime = 10 * i + this.rand.nextInt(6) + this.rand.nextInt(7);
this . minimumBoostTicks = minimumBoostTicks ;
this . maximumBoostTicks = minimumBoostTicks + 11 ;
}
public boolean isBoosted ( ) {
2023-06-29 19:46:04 +00:00
return this . fireworkTicksExisted ! = null ;
2023-06-26 18:55:18 +00:00
}
2023-06-28 22:47:01 +00:00
/ * *
* @return The guaranteed number of remaining ticks with boost
* /
2023-06-26 18:55:18 +00:00
public int getGuaranteedBoostTicks ( ) {
2023-06-29 19:46:04 +00:00
return this . isBoosted ( ) ? Math . max ( 0 , this . minimumBoostTicks - this . fireworkTicksExisted ) : 0 ;
2023-06-26 18:55:18 +00:00
}
2023-06-28 22:47:01 +00:00
/ * *
* @return The maximum number of remaining ticks with boost
* /
2023-06-26 18:55:18 +00:00
public int getMaximumBoostTicks ( ) {
2023-06-29 19:46:04 +00:00
return this . isBoosted ( ) ? Math . max ( 0 , this . maximumBoostTicks - this . fireworkTicksExisted ) : 0 ;
2023-06-26 18:55:18 +00:00
}
@Override
public boolean equals ( Object o ) {
if ( this = = o ) {
return true ;
}
if ( o = = null | | o . getClass ( ) ! = FireworkBoost . class ) {
return false ;
}
FireworkBoost other = ( FireworkBoost ) o ;
2023-06-29 19:46:04 +00:00
if ( ! this . isBoosted ( ) & & ! other . isBoosted ( ) ) {
return true ;
}
return Objects . equals ( this . fireworkTicksExisted , other . fireworkTicksExisted )
2023-06-26 18:55:18 +00:00
& & this . minimumBoostTicks = = other . minimumBoostTicks
& & this . maximumBoostTicks = = other . maximumBoostTicks ;
2023-06-21 01:09:53 +00:00
}
}
2023-06-27 21:31:08 +00:00
private static final class PitchResult {
public final float pitch ;
public final double dot ;
public final List < Vec3d > steps ;
public PitchResult ( float pitch , double dot , List < Vec3d > steps ) {
this . pitch = pitch ;
this . dot = dot ;
this . steps = steps ;
}
}
2023-06-20 20:05:59 +00:00
private static final class Solution {
2023-06-21 01:09:53 +00:00
public final SolverContext context ;
2023-06-20 20:05:59 +00:00
public final Rotation rotation ;
2023-06-21 01:30:10 +00:00
public final Vec3d goingTo ;
2023-06-20 20:05:59 +00:00
public final boolean solvedPitch ;
public final boolean forceUseFirework ;
2023-06-21 01:30:10 +00:00
public Solution ( SolverContext context , Rotation rotation , Vec3d goingTo , boolean solvedPitch , boolean forceUseFirework ) {
2023-06-21 01:09:53 +00:00
this . context = context ;
2023-06-20 20:05:59 +00:00
this . rotation = rotation ;
2023-06-20 20:15:02 +00:00
this . goingTo = goingTo ;
2023-06-20 20:05:59 +00:00
this . solvedPitch = solvedPitch ;
this . forceUseFirework = forceUseFirework ;
}
}
2023-06-19 17:32:25 +00:00
private static boolean isFireworks ( final ItemStack itemStack ) {
if ( itemStack . getItem ( ) ! = Items . FIREWORKS ) {
return false ;
2023-06-18 05:24:10 +00:00
}
2023-06-19 17:32:25 +00:00
// If it has NBT data, make sure it won't cause us to explode.
2023-06-23 23:30:10 +00:00
final NBTTagCompound compound = itemStack . getSubCompound ( " Fireworks " ) ;
return compound = = null | | ! compound . hasKey ( " Explosions " ) ;
2023-06-18 05:24:10 +00:00
}
2023-06-19 17:32:25 +00:00
private static boolean isBoostingFireworks ( final ItemStack itemStack ) {
2023-06-23 23:30:10 +00:00
return getFireworkBoost ( itemStack ) . isPresent ( ) ;
}
private static OptionalInt getFireworkBoost ( final ItemStack itemStack ) {
if ( isFireworks ( itemStack ) ) {
final NBTTagCompound compound = itemStack . getSubCompound ( " Fireworks " ) ;
if ( compound ! = null & & compound . hasKey ( " Flight " ) ) {
return OptionalInt . of ( compound . getByte ( " Flight " ) ) ;
}
}
return OptionalInt . empty ( ) ;
2023-06-18 05:24:10 +00:00
}
2023-06-23 23:30:10 +00:00
private Optional < EntityFireworkRocket > getAttachedFirework ( ) {
2023-06-17 00:54:37 +00:00
return ctx . world ( ) . loadedEntityList . stream ( )
2023-07-10 07:12:10 +00:00
. filter ( x - > x instanceof EntityFireworkRocket )
. filter ( x - > Objects . equals ( ( ( IEntityFireworkRocket ) x ) . getBoostedEntity ( ) , ctx . player ( ) ) )
. map ( x - > ( EntityFireworkRocket ) x )
. findFirst ( ) ;
2023-06-11 07:33:35 +00:00
}
2023-06-30 03:59:15 +00:00
private boolean isHitboxClear ( final SolverContext context , final Vec3d dest , final Double growAmount ) {
final Vec3d start = context . start ;
final boolean ignoreLava = context . ignoreLava ;
2023-06-21 01:30:10 +00:00
if ( ! this . clearView ( start , dest , ignoreLava ) ) {
2023-06-18 00:36:29 +00:00
return false ;
}
if ( growAmount = = null ) {
return true ;
}
final AxisAlignedBB bb = ctx . player ( ) . getEntityBoundingBox ( ) . grow ( growAmount ) ;
2023-06-19 05:34:22 +00:00
2023-06-19 20:19:32 +00:00
final double ox = dest . x - start . x ;
final double oy = dest . y - start . y ;
final double oz = dest . z - start . z ;
final double [ ] src = new double [ ] {
bb . minX , bb . minY , bb . minZ ,
bb . minX , bb . minY , bb . maxZ ,
bb . minX , bb . maxY , bb . minZ ,
bb . minX , bb . maxY , bb . maxZ ,
bb . maxX , bb . minY , bb . minZ ,
bb . maxX , bb . minY , bb . maxZ ,
bb . maxX , bb . maxY , bb . minZ ,
bb . maxX , bb . maxY , bb . maxZ ,
} ;
final double [ ] dst = new double [ ] {
bb . minX + ox , bb . minY + oy , bb . minZ + oz ,
bb . minX + ox , bb . minY + oy , bb . maxZ + oz ,
bb . minX + ox , bb . maxY + oy , bb . minZ + oz ,
bb . minX + ox , bb . maxY + oy , bb . maxZ + oz ,
bb . maxX + ox , bb . minY + oy , bb . minZ + oz ,
bb . maxX + ox , bb . minY + oy , bb . maxZ + oz ,
bb . maxX + ox , bb . maxY + oy , bb . minZ + oz ,
bb . maxX + ox , bb . maxY + oy , bb . maxZ + oz ,
2023-06-18 00:36:29 +00:00
} ;
2023-06-25 05:11:08 +00:00
// Use non-batching method without early failure
2023-07-16 22:58:42 +00:00
if ( Baritone . settings ( ) . elytraRenderHitboxRaytraces . value ) {
2023-06-25 05:11:08 +00:00
boolean clear = true ;
for ( int i = 0 ; i < 8 ; i + + ) {
final Vec3d s = new Vec3d ( src [ i * 3 ] , src [ i * 3 + 1 ] , src [ i * 3 + 2 ] ) ;
final Vec3d d = new Vec3d ( dst [ i * 3 ] , dst [ i * 3 + 1 ] , dst [ i * 3 + 2 ] ) ;
// Don't forward ignoreLava since the batch call doesn't care about it
if ( ! this . clearView ( s , d , false ) ) {
clear = false ;
}
}
return clear ;
}
2023-06-20 02:36:09 +00:00
return this . context . raytrace ( 8 , src , dst , NetherPathfinderContext . Visibility . ALL ) ;
2023-06-11 07:33:35 +00:00
}
2023-07-17 02:56:17 +00:00
public boolean clearView ( Vec3d start , Vec3d dest , boolean ignoreLava ) {
2023-06-19 21:58:06 +00:00
final boolean clear ;
if ( ! ignoreLava ) {
2023-06-20 02:36:09 +00:00
// if start == dest then the cpp raytracer dies
2023-06-21 01:30:10 +00:00
clear = start . equals ( dest ) | | this . context . raytrace ( start , dest ) ;
2023-06-19 21:58:06 +00:00
} else {
2023-06-19 22:26:00 +00:00
clear = ctx . world ( ) . rayTraceBlocks ( start , dest , false , false , false ) = = null ;
2023-06-19 21:58:06 +00:00
}
2023-07-16 22:58:42 +00:00
if ( Baritone . settings ( ) . elytraRenderRaytraces . value ) {
2023-06-30 01:45:59 +00:00
( clear ? this . clearLines : this . blockedLines ) . add ( new Pair < > ( start , dest ) ) ;
2023-06-19 01:06:32 +00:00
}
2023-06-30 01:45:59 +00:00
return clear ;
2023-06-11 07:33:35 +00:00
}
2023-06-28 22:47:01 +00:00
private static FloatArrayList pitchesToSolveFor ( final float goodPitch , final boolean desperate ) {
final float minPitch = desperate ? - 90 : Math . max ( goodPitch - Baritone . settings ( ) . elytraPitchRange . value , - 89 ) ;
final float maxPitch = desperate ? 90 : Math . min ( goodPitch + Baritone . settings ( ) . elytraPitchRange . value , 89 ) ;
2023-07-03 18:27:23 +00:00
final FloatArrayList pitchValues = new FloatArrayList ( fastCeil ( maxPitch - minPitch ) + 1 ) ;
2023-06-28 22:47:01 +00:00
for ( float pitch = goodPitch ; pitch < = maxPitch ; pitch + + ) {
pitchValues . add ( pitch ) ;
}
for ( float pitch = goodPitch - 1 ; pitch > = minPitch ; pitch - - ) {
pitchValues . add ( pitch ) ;
}
return pitchValues ;
}
2023-06-28 22:55:47 +00:00
@FunctionalInterface
2023-06-29 19:14:49 +00:00
private interface IntTriFunction < T > {
T apply ( int first , int second , int third ) ;
2023-06-28 22:55:47 +00:00
}
2023-06-29 19:14:49 +00:00
private static final class IntTriple {
2023-06-28 22:55:47 +00:00
public final int first ;
public final int second ;
2023-06-29 19:14:49 +00:00
public final int third ;
2023-06-28 22:55:47 +00:00
2023-06-29 19:14:49 +00:00
public IntTriple ( int first , int second , int third ) {
2023-06-28 22:55:47 +00:00
this . first = first ;
this . second = second ;
2023-06-29 19:14:49 +00:00
this . third = third ;
2023-06-28 22:55:47 +00:00
}
}
2023-06-30 03:59:15 +00:00
private Pair < Float , Boolean > solvePitch ( final SolverContext context , final Vec3d goal , final int relaxation ) {
2023-06-26 21:25:21 +00:00
final boolean desperate = relaxation = = 2 ;
2023-06-28 22:47:01 +00:00
final float goodPitch = RotationUtils . calcRotationFromVec3d ( context . start , goal , ctx . playerRotations ( ) ) . getPitch ( ) ;
final FloatArrayList pitches = pitchesToSolveFor ( goodPitch , desperate ) ;
2023-06-29 19:14:49 +00:00
final IntTriFunction < PitchResult > solve = ( ticks , ticksBoosted , ticksBoostDelay ) - >
2023-06-30 03:59:15 +00:00
this . solvePitch ( context , goal , relaxation , pitches . iterator ( ) , ticks , ticksBoosted , ticksBoostDelay ) ;
2023-06-28 22:47:01 +00:00
2023-06-29 19:14:49 +00:00
final List < IntTriple > tests = new ArrayList < > ( ) ;
2023-06-28 22:47:01 +00:00
if ( context . boost . isBoosted ( ) ) {
final int guaranteed = context . boost . getGuaranteedBoostTicks ( ) ;
if ( guaranteed = = 0 ) {
// uncertain when boost will run out
final int lookahead = Math . max ( 4 , 10 - context . boost . getMaximumBoostTicks ( ) ) ;
2023-06-29 19:14:49 +00:00
tests . add ( new IntTriple ( lookahead , 1 , 0 ) ) ;
2023-06-28 22:47:01 +00:00
} else if ( guaranteed < = 5 ) {
// boost will run out within 5 ticks
2023-06-29 19:14:49 +00:00
tests . add ( new IntTriple ( guaranteed + 5 , guaranteed , 0 ) ) ;
2023-06-28 22:47:01 +00:00
} else {
// there's plenty of guaranteed boost
2023-06-29 19:14:49 +00:00
tests . add ( new IntTriple ( guaranteed + 1 , guaranteed , 0 ) ) ;
2023-06-28 22:47:01 +00:00
}
}
// Standard test, assume (not) boosted for entire duration
final int ticks = desperate ? 3 : context . boost . isBoosted ( ) ? Math . max ( 5 , context . boost . getGuaranteedBoostTicks ( ) ) : Baritone . settings ( ) . elytraSimulationTicks . value ;
2023-06-29 19:14:49 +00:00
tests . add ( new IntTriple ( ticks , context . boost . isBoosted ( ) ? ticks : 0 , 0 ) ) ;
2023-06-26 21:04:05 +00:00
2023-06-28 22:47:01 +00:00
final Optional < PitchResult > result = tests . stream ( )
2023-06-29 19:14:49 +00:00
. map ( i - > solve . apply ( i . first , i . second , i . third ) )
2023-06-28 22:47:01 +00:00
. filter ( Objects : : nonNull )
. findFirst ( ) ;
if ( result . isPresent ( ) ) {
return new Pair < > ( result . get ( ) . pitch , false ) ;
2023-06-18 18:23:41 +00:00
}
2023-06-29 19:14:49 +00:00
// If we used a firework would we be able to get out of the current situation??? perhaps
2023-06-30 00:15:06 +00:00
if ( desperate ) {
2023-06-29 19:14:49 +00:00
final List < IntTriple > testsBoost = new ArrayList < > ( ) ;
testsBoost . add ( new IntTriple ( ticks , 10 , 3 ) ) ;
testsBoost . add ( new IntTriple ( ticks , 10 , 2 ) ) ;
testsBoost . add ( new IntTriple ( ticks , 10 , 1 ) ) ;
final Optional < PitchResult > resultBoost = testsBoost . stream ( )
. map ( i - > solve . apply ( i . first , i . second , i . third ) )
. filter ( Objects : : nonNull )
. findFirst ( ) ;
if ( resultBoost . isPresent ( ) ) {
return new Pair < > ( resultBoost . get ( ) . pitch , true ) ;
2023-06-18 18:23:41 +00:00
}
}
2023-06-28 22:47:01 +00:00
return null ;
2023-06-18 18:23:41 +00:00
}
2023-06-28 22:47:01 +00:00
private PitchResult solvePitch ( final SolverContext context , final Vec3d goal , final int relaxation ,
final FloatIterator pitches , final int ticks , final int ticksBoosted ,
2023-06-30 03:59:15 +00:00
final int ticksBoostDelay ) {
2023-06-11 07:33:35 +00:00
// we are at a certain velocity, but we have a target velocity
// what pitch would get us closest to our target velocity?
// yaw is easy so we only care about pitch
2023-06-28 22:47:01 +00:00
final Vec3d goalDelta = goal . subtract ( context . start ) ;
2023-06-22 17:50:06 +00:00
final Vec3d goalDirection = goalDelta . normalize ( ) ;
2023-06-11 07:33:35 +00:00
2023-06-30 00:15:06 +00:00
final Deque < PitchResult > bestResults = new ArrayDeque < > ( ) ;
2023-06-22 17:50:06 +00:00
2023-06-28 22:47:01 +00:00
while ( pitches . hasNext ( ) ) {
final float pitch = pitches . nextFloat ( ) ;
2023-06-26 21:21:15 +00:00
final List < Vec3d > displacement = this . simulate (
context . aimProcessor . fork ( ) ,
goalDelta ,
pitch ,
2023-06-26 21:58:29 +00:00
ticks ,
ticksBoosted ,
2023-06-29 19:14:49 +00:00
ticksBoostDelay ,
2023-06-30 03:59:15 +00:00
context . ignoreLava
2023-06-26 21:21:15 +00:00
) ;
if ( displacement = = null ) {
2023-06-26 21:12:56 +00:00
continue ;
2023-06-11 07:33:35 +00:00
}
2023-06-30 00:15:06 +00:00
final Vec3d last = displacement . get ( displacement . size ( ) - 1 ) ;
2023-06-28 22:47:01 +00:00
final double goodness = goalDirection . dotProduct ( last . normalize ( ) ) ;
2023-06-30 00:15:06 +00:00
final PitchResult bestSoFar = bestResults . peek ( ) ;
if ( bestSoFar = = null | | goodness > bestSoFar . dot ) {
bestResults . push ( new PitchResult ( pitch , goodness , displacement ) ) ;
}
}
outer :
for ( final PitchResult result : bestResults ) {
if ( relaxation < 2 ) {
// Ensure that the goal is visible along the entire simulated path
// Reverse order iteration since the last position is most likely to fail
for ( int i = result . steps . size ( ) - 1 ; i > = 1 ; i - - ) {
2023-07-09 16:12:21 +00:00
if ( ! clearView ( context . start . add ( result . steps . get ( i ) ) , goal , context . ignoreLava ) ) {
2023-06-30 00:15:06 +00:00
continue outer ;
2023-06-28 22:47:01 +00:00
}
}
2023-06-30 00:15:06 +00:00
} else {
// Ensure that the goal is visible from the final position
2023-07-09 16:12:21 +00:00
if ( ! clearView ( context . start . add ( result . steps . get ( result . steps . size ( ) - 1 ) ) , goal , context . ignoreLava ) ) {
2023-06-30 00:15:06 +00:00
continue ;
}
2023-06-11 07:33:35 +00:00
}
2023-06-30 00:15:06 +00:00
2023-06-27 21:31:08 +00:00
this . simulationLine = result . steps ;
2023-06-30 00:15:06 +00:00
return result ;
2023-06-25 05:27:38 +00:00
}
2023-06-30 00:15:06 +00:00
return null ;
2023-06-11 07:33:35 +00:00
}
2023-06-26 21:21:15 +00:00
private List < Vec3d > simulate ( final ITickableAimProcessor aimProcessor , final Vec3d goalDelta , final float pitch ,
2023-07-07 02:02:13 +00:00
final int ticks , final int ticksBoosted , final int ticksBoostDelay , final boolean ignoreLava ) {
2023-06-26 21:12:56 +00:00
Vec3d delta = goalDelta ;
Vec3d motion = ctx . playerMotion ( ) ;
AxisAlignedBB hitbox = ctx . player ( ) . getEntityBoundingBox ( ) ;
2023-07-03 18:27:23 +00:00
List < Vec3d > displacement = new ArrayList < > ( ticks + 1 ) ;
2023-06-26 21:21:15 +00:00
displacement . add ( Vec3d . ZERO ) ;
2023-07-07 02:02:13 +00:00
int remainingTicksBoosted = ticksBoosted ;
2023-06-26 21:12:56 +00:00
for ( int i = 0 ; i < ticks ; i + + ) {
2023-07-03 18:27:23 +00:00
final double cx = hitbox . minX + ( hitbox . maxX - hitbox . minX ) * 0 . 5D ;
final double cz = hitbox . minZ + ( hitbox . maxZ - hitbox . minZ ) * 0 . 5D ;
if ( MC_1_12_Collision_Fix . bonk ( this . bsi , cx , hitbox . minY , cz ) ) {
2023-06-26 21:12:56 +00:00
return null ;
}
2023-06-26 23:37:06 +00:00
if ( delta . lengthSquared ( ) < 1 ) {
break ;
}
2023-06-26 21:12:56 +00:00
final Rotation rotation = aimProcessor . nextRotation (
RotationUtils . calcRotationFromVec3d ( Vec3d . ZERO , delta , ctx . playerRotations ( ) ) . withPitch ( pitch )
) ;
final Vec3d lookDirection = RotationUtils . calcLookDirectionFromRotation ( rotation ) ;
motion = step ( motion , lookDirection , rotation . getPitch ( ) ) ;
delta = delta . subtract ( motion ) ;
// Collision box while the player is in motion, with additional padding for safety
final AxisAlignedBB inMotion = hitbox . expand ( motion . x , motion . y , motion . z ) . grow ( 0 . 01 ) ;
2023-07-10 07:12:10 +00:00
int xmin = fastFloor ( inMotion . minX ) ;
int xmax = fastCeil ( inMotion . maxX ) ;
int ymin = fastFloor ( inMotion . minY ) ;
int ymax = fastCeil ( inMotion . maxY ) ;
int zmin = fastFloor ( inMotion . minZ ) ;
int zmax = fastCeil ( inMotion . maxZ ) ;
for ( int x = xmin ; x < xmax ; x + + ) {
for ( int y = ymin ; y < ymax ; y + + ) {
for ( int z = zmin ; z < zmax ; z + + ) {
2023-06-26 21:12:56 +00:00
if ( ! this . passable ( x , y , z , ignoreLava ) ) {
return null ;
}
}
}
}
2023-06-26 21:21:15 +00:00
hitbox = hitbox . offset ( motion ) ;
displacement . add ( displacement . get ( displacement . size ( ) - 1 ) . add ( motion ) ) ;
2023-06-26 21:12:56 +00:00
2023-07-07 02:02:13 +00:00
if ( i > = ticksBoostDelay & & remainingTicksBoosted - - > 0 ) {
2023-06-26 21:12:56 +00:00
// See EntityFireworkRocket
motion = motion . add (
lookDirection . x * 0 . 1 + ( lookDirection . x * 1 . 5 - motion . x ) * 0 . 5 ,
lookDirection . y * 0 . 1 + ( lookDirection . y * 1 . 5 - motion . y ) * 0 . 5 ,
lookDirection . z * 0 . 1 + ( lookDirection . z * 1 . 5 - motion . z ) * 0 . 5
) ;
}
}
2023-06-26 21:21:15 +00:00
return displacement ;
2023-06-26 21:12:56 +00:00
}
2023-06-26 20:35:40 +00:00
private static Vec3d step ( final Vec3d motion , final Vec3d lookDirection , final float pitch ) {
2023-06-11 07:33:35 +00:00
double motionX = motion . x ;
double motionY = motion . y ;
double motionZ = motion . z ;
2023-06-17 01:38:29 +00:00
2023-06-26 20:35:40 +00:00
float pitchRadians = pitch * RotationUtils . DEG_TO_RAD_F ;
2023-06-11 07:33:35 +00:00
double pitchBase2 = Math . sqrt ( lookDirection . x * lookDirection . x + lookDirection . z * lookDirection . z ) ;
double flatMotion = Math . sqrt ( motionX * motionX + motionZ * motionZ ) ;
double thisIsAlwaysOne = lookDirection . length ( ) ;
float pitchBase3 = MathHelper . cos ( pitchRadians ) ;
//System.out.println("always the same lol " + -pitchBase + " " + pitchBase3);
//System.out.println("always the same lol " + Math.abs(pitchBase3) + " " + pitchBase2);
//System.out.println("always 1 lol " + thisIsAlwaysOne);
pitchBase3 = ( float ) ( ( double ) pitchBase3 * ( double ) pitchBase3 * Math . min ( 1 , thisIsAlwaysOne / 0 . 4 ) ) ;
motionY + = - 0 . 08 + ( double ) pitchBase3 * 0 . 06 ;
if ( motionY < 0 & & pitchBase2 > 0 ) {
double speedModifier = motionY * - 0 . 1 * ( double ) pitchBase3 ;
motionY + = speedModifier ;
motionX + = lookDirection . x * speedModifier / pitchBase2 ;
motionZ + = lookDirection . z * speedModifier / pitchBase2 ;
}
if ( pitchRadians < 0 ) { // if you are looking down (below level)
double anotherSpeedModifier = flatMotion * ( double ) ( - MathHelper . sin ( pitchRadians ) ) * 0 . 04 ;
motionY + = anotherSpeedModifier * 3 . 2 ;
motionX - = lookDirection . x * anotherSpeedModifier / pitchBase2 ;
motionZ - = lookDirection . z * anotherSpeedModifier / pitchBase2 ;
}
if ( pitchBase2 > 0 ) { // this is always true unless you are looking literally straight up (let's just say the bot will never do that)
motionX + = ( lookDirection . x / pitchBase2 * flatMotion - motionX ) * 0 . 1 ;
motionZ + = ( lookDirection . z / pitchBase2 * flatMotion - motionZ ) * 0 . 1 ;
}
2023-06-21 23:59:44 +00:00
motionX * = 0 . 99f ;
motionY * = 0 . 98f ;
motionZ * = 0 . 99f ;
2023-06-11 07:33:35 +00:00
//System.out.println(motionX + " " + motionY + " " + motionZ);
2023-06-11 19:49:31 +00:00
2023-06-11 07:33:35 +00:00
return new Vec3d ( motionX , motionY , motionZ ) ;
}
2023-06-21 01:09:53 +00:00
private boolean passable ( int x , int y , int z , boolean ignoreLava ) {
2023-07-13 19:28:51 +00:00
if ( ignoreLava ) {
final Material mat = this . bsi . get0 ( x , y , z ) . getMaterial ( ) ;
return mat = = Material . AIR | | mat = = Material . LAVA ;
} else {
return ! this . boi . get0 ( x , y , z ) ;
}
2023-06-21 01:09:53 +00:00
}
2023-06-22 23:07:11 +00:00
2023-07-12 20:42:14 +00:00
private void tickInventoryTransactions ( ) {
2023-07-13 18:21:15 +00:00
if ( invTickCountdown < = 0 ) {
2023-07-16 05:19:51 +00:00
Runnable r = invTransactionQueue . poll ( ) ;
2023-07-12 20:42:14 +00:00
if ( r ! = null ) {
r . run ( ) ;
2023-07-13 18:21:15 +00:00
invTickCountdown = Baritone . settings ( ) . ticksBetweenInventoryMoves . value ;
2023-07-12 20:42:14 +00:00
}
}
2023-07-13 18:21:15 +00:00
if ( invTickCountdown > 0 ) invTickCountdown - - ;
2023-07-12 20:42:14 +00:00
}
private void queueWindowClick ( int windowId , int slotId , int button , ClickType type ) {
2023-07-16 05:19:51 +00:00
invTransactionQueue . add ( ( ) - > ctx . playerController ( ) . windowClick ( windowId , slotId , button , type , ctx . player ( ) ) ) ;
2023-07-12 20:42:14 +00:00
}
private int findGoodElytra ( ) {
NonNullList < ItemStack > invy = ctx . player ( ) . inventory . mainInventory ;
for ( int i = 0 ; i < invy . size ( ) ; i + + ) {
ItemStack slot = invy . get ( i ) ;
if ( slot . getItem ( ) = = Items . ELYTRA & & ( slot . getItem ( ) . getMaxDamage ( ) - slot . getItemDamage ( ) ) > Baritone . settings ( ) . elytraMinimumDurability . value ) {
return i ;
}
}
return - 1 ;
}
private void trySwapElytra ( ) {
2023-07-16 05:19:51 +00:00
if ( ! Baritone . settings ( ) . elytraAutoSwap . value | | ! invTransactionQueue . isEmpty ( ) ) {
2023-07-13 18:35:33 +00:00
return ;
}
2023-07-12 20:42:14 +00:00
ItemStack chest = ctx . player ( ) . inventory . armorInventory . get ( 2 ) ;
2023-07-13 18:35:33 +00:00
if ( chest . getItem ( ) ! = Items . ELYTRA
2023-07-17 02:56:17 +00:00
| | chest . getItem ( ) . getMaxDamage ( ) - chest . getItemDamage ( ) > Baritone . settings ( ) . elytraMinimumDurability . value ) {
2023-07-13 18:35:33 +00:00
return ;
}
2023-07-12 20:42:14 +00:00
int goodElytraSlot = findGoodElytra ( ) ;
if ( goodElytraSlot ! = - 1 ) {
final int CHEST_SLOT = 6 ;
final int slotId = goodElytraSlot < 9 ? goodElytraSlot + 36 : goodElytraSlot ;
queueWindowClick ( ctx . player ( ) . inventoryContainer . windowId , slotId , 0 , ClickType . PICKUP ) ;
queueWindowClick ( ctx . player ( ) . inventoryContainer . windowId , CHEST_SLOT , 0 , ClickType . PICKUP ) ;
queueWindowClick ( ctx . player ( ) . inventoryContainer . windowId , slotId , 0 , ClickType . PICKUP ) ;
}
}
2023-06-22 23:07:11 +00:00
/ * *
* Minecraft 1 . 12 ' s pushOutOfBlocks logic doesn ' t account for players being able to fit under single block spaces ,
* so whenever the edge of a ceiling is encountered while elytra flying it tries to push the player out .
* /
private static final class MC_1_12_Collision_Fix {
2023-07-03 18:27:23 +00:00
private static final Long2ReferenceOpenHashMap < Boolean > PUSH_OUT_CACHE = new Long2ReferenceOpenHashMap < > ( ) ;
private static final Long2ReferenceOpenHashMap < Boolean > IS_OPEN_CACHE = new Long2ReferenceOpenHashMap < > ( ) ;
private static final double WIDTH = 0 . 35D * 0 . 6F ;
public static void clear ( ) {
// TODO: I don't like this....
2023-07-03 18:28:53 +00:00
if ( PUSH_OUT_CACHE . size ( ) > 4096 ) {
PUSH_OUT_CACHE . clear ( ) ;
2023-07-03 18:27:23 +00:00
}
2023-07-03 18:28:53 +00:00
if ( IS_OPEN_CACHE . size ( ) > 4096 ) {
IS_OPEN_CACHE . clear ( ) ;
2023-07-03 18:27:23 +00:00
}
}
public static boolean bonk ( final BlockStateInterface bsi , final double xIn , final double yIn , final double zIn ) {
final int y = fastFloor ( yIn + 0 . 5D ) ;
final int minX = fastFloor ( xIn - WIDTH ) ;
final int minZ = fastFloor ( zIn - WIDTH ) ;
final int maxX = fastFloor ( xIn + WIDTH ) ;
final int maxZ = fastFloor ( zIn + WIDTH ) ;
if ( minX = = maxX & & minZ = = maxZ ) {
return pushOutOfBlocks ( bsi , minX , y , minZ ) ;
} else if ( minX = = maxX ) {
return pushOutOfBlocks ( bsi , minX , y , minZ ) | | pushOutOfBlocks ( bsi , minX , y , maxZ ) ;
} else if ( minZ = = maxZ ) {
return pushOutOfBlocks ( bsi , minX , y , minZ ) | | pushOutOfBlocks ( bsi , maxX , y , minZ ) ;
}
return pushOutOfBlocks ( bsi , minX , y , maxZ )
| | pushOutOfBlocks ( bsi , minX , y , minZ )
| | pushOutOfBlocks ( bsi , maxX , y , minZ )
| | pushOutOfBlocks ( bsi , maxX , y , maxZ ) ;
}
private static boolean pushOutOfBlocks ( final BlockStateInterface bsi , final int x , final int y , final int z ) {
final long hash = BetterBlockPos . serializeToLong ( x , y , z ) ;
Boolean result = PUSH_OUT_CACHE . get ( hash ) ;
if ( result = = null ) {
PUSH_OUT_CACHE . put ( hash , result = ! isOpenBlockSpace ( bsi , x , y , z ) & & (
isOpenBlockSpace ( bsi , x - 1 , y , z )
| | isOpenBlockSpace ( bsi , x + 1 , y , z )
| | isOpenBlockSpace ( bsi , x , y , z - 1 )
| | isOpenBlockSpace ( bsi , x , y , z + 1 ) )
) ;
2023-06-22 23:07:11 +00:00
}
2023-07-03 18:27:23 +00:00
return result ;
2023-06-22 23:07:11 +00:00
}
2023-07-03 18:27:23 +00:00
private static boolean isOpenBlockSpace ( final BlockStateInterface bsi , final int x , final int y , final int z ) {
final long hash = BetterBlockPos . serializeToLong ( x , y , z ) ;
Boolean result = IS_OPEN_CACHE . get ( hash ) ;
if ( result = = null ) {
IS_OPEN_CACHE . put ( hash , result = ! bsi . get0 ( x , y , z ) . isNormalCube ( ) & & ! bsi . get0 ( x , y + 1 , z ) . isNormalCube ( ) ) ;
}
return result ;
2023-06-22 23:07:11 +00:00
}
}
2023-06-27 07:54:53 +00:00
2023-07-01 01:42:03 +00:00
/ * *
* Custom calculation context which makes the player fall into lava
* /
2023-07-17 02:56:17 +00:00
public static final class WalkOffCalculationContext extends CalculationContext {
2023-06-27 07:54:53 +00:00
2023-07-01 01:42:03 +00:00
public WalkOffCalculationContext ( IBaritone baritone ) {
super ( baritone , true ) ;
this . allowFallIntoLava = true ;
2023-07-07 20:26:43 +00:00
this . minFallHeight = 8 ;
2023-07-01 01:42:03 +00:00
this . maxFallHeightNoWater = 10000 ;
}
@Override
public double costOfPlacingAt ( int x , int y , int z , IBlockState current ) {
return COST_INF ;
}
@Override
public double breakCostMultiplierAt ( int x , int y , int z , IBlockState current ) {
return COST_INF ;
}
@Override
public double placeBucketCost ( ) {
return COST_INF ;
2023-06-27 07:54:53 +00:00
}
}
2023-07-12 03:47:09 +00:00
2023-06-18 05:24:10 +00:00
}