2018-12-26 05:07:17 +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/>.
* /
package baritone.process ;
import baritone.Baritone ;
import baritone.api.pathing.goals.Goal ;
import baritone.api.pathing.goals.GoalBlock ;
import baritone.api.pathing.goals.GoalComposite ;
import baritone.api.pathing.goals.GoalGetToBlock ;
2019-01-16 04:07:06 +00:00
import baritone.api.process.IBuilderProcess ;
2018-12-26 05:07:17 +00:00
import baritone.api.process.PathingCommand ;
import baritone.api.process.PathingCommandType ;
2019-02-07 00:22:40 +00:00
import baritone.api.utils.* ;
2019-01-09 04:45:02 +00:00
import baritone.api.utils.input.Input ;
2018-12-26 05:07:17 +00:00
import baritone.pathing.movement.CalculationContext ;
2019-02-07 00:22:40 +00:00
import baritone.pathing.movement.Movement ;
2019-01-09 04:45:02 +00:00
import baritone.pathing.movement.MovementHelper ;
2018-12-26 05:07:17 +00:00
import baritone.utils.BaritoneProcessHelper ;
2019-02-07 00:22:40 +00:00
import baritone.utils.BlockStateInterface ;
2018-12-26 05:07:17 +00:00
import baritone.utils.PathingCommandContext ;
2019-02-28 23:26:09 +00:00
import baritone.utils.schematic.AirSchematic ;
2019-01-16 19:45:32 +00:00
import baritone.utils.schematic.Schematic ;
2019-04-29 21:55:14 +00:00
import it.unimi.dsi.fastutil.longs.LongOpenHashSet ;
2019-04-29 23:02:44 +00:00
import net.minecraft.block.BlockAir ;
2019-05-01 21:03:36 +00:00
import net.minecraft.block.BlockLiquid ;
2018-12-26 05:07:17 +00:00
import net.minecraft.block.state.IBlockState ;
import net.minecraft.init.Blocks ;
import net.minecraft.item.ItemBlock ;
import net.minecraft.item.ItemStack ;
import net.minecraft.nbt.CompressedStreamTools ;
import net.minecraft.nbt.NBTTagCompound ;
import net.minecraft.util.EnumFacing ;
2019-01-09 04:45:02 +00:00
import net.minecraft.util.Tuple ;
2019-02-07 00:22:40 +00:00
import net.minecraft.util.math.* ;
2018-12-26 05:07:17 +00:00
import java.io.File ;
2018-12-27 18:06:10 +00:00
import java.io.FileInputStream ;
2018-12-26 05:07:17 +00:00
import java.io.IOException ;
2019-01-09 04:45:02 +00:00
import java.util.* ;
2018-12-26 05:07:17 +00:00
import static baritone.api.pathing.movement.ActionCosts.COST_INF ;
2019-01-16 04:07:06 +00:00
public class BuilderProcess extends BaritoneProcessHelper implements IBuilderProcess {
2019-04-04 06:19:32 +00:00
2018-12-26 05:07:17 +00:00
private HashSet < BetterBlockPos > incorrectPositions ;
2019-04-29 21:55:14 +00:00
private LongOpenHashSet observedCompleted ; // positions that are completed even if they're out of render distance and we can't make sure right now
2018-12-26 05:07:17 +00:00
private String name ;
2019-04-11 22:17:26 +00:00
private ISchematic realSchematic ;
2018-12-26 05:07:17 +00:00
private ISchematic schematic ;
private Vec3i origin ;
2019-02-07 00:22:40 +00:00
private int ticks ;
2019-03-28 00:32:02 +00:00
private boolean paused ;
2019-04-11 22:17:26 +00:00
private int layer ;
2018-12-26 05:07:17 +00:00
2019-04-03 05:40:33 +00:00
public BuilderProcess ( Baritone baritone ) {
super ( baritone ) ;
}
2019-01-16 04:07:06 +00:00
@Override
public void build ( String name , ISchematic schematic , Vec3i origin ) {
this . name = name ;
this . schematic = schematic ;
2019-04-11 22:17:26 +00:00
this . realSchematic = null ;
2019-01-16 04:07:06 +00:00
this . origin = origin ;
2019-03-28 00:32:02 +00:00
this . paused = false ;
2019-04-11 22:17:26 +00:00
this . layer = 0 ;
2019-04-29 21:55:14 +00:00
this . observedCompleted = new LongOpenHashSet ( ) ;
2019-03-28 00:32:02 +00:00
}
public void resume ( ) {
paused = false ;
2019-01-16 04:07:06 +00:00
}
2018-12-27 18:06:10 +00:00
2019-04-13 15:36:14 +00:00
public void pause ( ) {
paused = true ;
}
2019-01-16 04:07:06 +00:00
@Override
public boolean build ( String name , File schematic , Vec3i origin ) {
2018-12-26 05:07:17 +00:00
NBTTagCompound tag ;
2019-01-16 04:07:06 +00:00
try ( FileInputStream fileIn = new FileInputStream ( schematic ) ) {
2018-12-27 18:06:10 +00:00
tag = CompressedStreamTools . readCompressed ( fileIn ) ;
2018-12-26 05:07:17 +00:00
} catch ( IOException e ) {
e . printStackTrace ( ) ;
return false ;
}
if ( tag = = null ) {
return false ;
}
2019-01-16 04:07:06 +00:00
build ( name , parse ( tag ) , origin ) ;
2018-12-26 05:07:17 +00:00
return true ;
}
2019-02-28 23:26:09 +00:00
public void clearArea ( BlockPos corner1 , BlockPos corner2 ) {
BlockPos origin = new BlockPos ( Math . min ( corner1 . getX ( ) , corner2 . getX ( ) ) , Math . min ( corner1 . getY ( ) , corner2 . getY ( ) ) , Math . min ( corner1 . getZ ( ) , corner2 . getZ ( ) ) ) ;
int widthX = Math . abs ( corner1 . getX ( ) - corner2 . getX ( ) ) + 1 ;
int heightY = Math . abs ( corner1 . getY ( ) - corner2 . getY ( ) ) + 1 ;
int lengthZ = Math . abs ( corner1 . getZ ( ) - corner2 . getZ ( ) ) + 1 ;
build ( " clear area " , new AirSchematic ( widthX , heightY , lengthZ ) , origin ) ;
}
2018-12-26 05:07:17 +00:00
private static ISchematic parse ( NBTTagCompound schematic ) {
2019-01-09 04:45:02 +00:00
return new Schematic ( schematic ) ;
2018-12-26 05:07:17 +00:00
}
@Override
public boolean isActive ( ) {
return schematic ! = null ;
}
2019-01-15 03:49:26 +00:00
public IBlockState placeAt ( int x , int y , int z ) {
2019-01-13 03:51:21 +00:00
if ( ! isActive ( ) ) {
return null ;
}
if ( ! schematic . inSchematic ( x - origin . getX ( ) , y - origin . getY ( ) , z - origin . getZ ( ) ) ) {
return null ;
}
IBlockState state = schematic . desiredState ( x - origin . getX ( ) , y - origin . getY ( ) , z - origin . getZ ( ) ) ;
if ( state . getBlock ( ) = = Blocks . AIR ) {
return null ;
}
2019-01-15 03:49:26 +00:00
return state ;
2019-01-13 03:51:21 +00:00
}
2019-04-29 23:02:44 +00:00
2019-04-29 21:59:25 +00:00
private Optional < Tuple < BetterBlockPos , Rotation > > toBreakNearPlayer ( BuilderCalculationContext bcc ) {
2019-01-09 04:45:02 +00:00
BetterBlockPos center = ctx . playerFeet ( ) ;
2019-04-29 23:02:44 +00:00
BetterBlockPos pathStart = baritone . getPathingBehavior ( ) . pathStart ( ) ;
2019-01-09 04:45:02 +00:00
for ( int dx = - 5 ; dx < = 5 ; dx + + ) {
2019-04-29 23:02:44 +00:00
for ( int dy = Baritone . settings ( ) . breakFromAbove . value ? - 1 : 0 ; dy < = 5 ; dy + + ) {
2019-01-09 04:45:02 +00:00
for ( int dz = - 5 ; dz < = 5 ; dz + + ) {
int x = center . x + dx ;
int y = center . y + dy ;
int z = center . z + dz ;
2019-04-29 23:02:44 +00:00
if ( dy = = - 1 & & x = = pathStart . x & & z = = pathStart . z ) {
continue ; // dont mine what we're supported by, but not directly standing on
}
2019-01-09 04:45:02 +00:00
IBlockState desired = bcc . getSchematic ( x , y , z ) ;
if ( desired = = null ) {
continue ; // irrelevant
}
IBlockState curr = bcc . bsi . get0 ( x , y , z ) ;
if ( curr . getBlock ( ) ! = Blocks . AIR & & ! valid ( curr , desired ) ) {
BetterBlockPos pos = new BetterBlockPos ( x , y , z ) ;
Optional < Rotation > rot = RotationUtils . reachable ( ctx . player ( ) , pos , ctx . playerController ( ) . getBlockReachDistance ( ) ) ;
if ( rot . isPresent ( ) ) {
return Optional . of ( new Tuple < > ( pos , rot . get ( ) ) ) ;
}
}
}
}
}
return Optional . empty ( ) ;
}
2019-02-07 00:22:40 +00:00
public class Placement {
2019-04-05 04:51:02 +00:00
private final int hotbarSelection ;
private final BlockPos placeAgainst ;
private final EnumFacing side ;
private final Rotation rot ;
2019-02-07 00:22:40 +00:00
public Placement ( int hotbarSelection , BlockPos placeAgainst , EnumFacing side , Rotation rot ) {
this . hotbarSelection = hotbarSelection ;
this . placeAgainst = placeAgainst ;
this . side = side ;
this . rot = rot ;
}
}
2019-04-29 21:59:25 +00:00
private Optional < Placement > searchForPlacables ( BuilderCalculationContext bcc , List < IBlockState > desirableOnHotbar ) {
2019-02-07 00:22:40 +00:00
BetterBlockPos center = ctx . playerFeet ( ) ;
for ( int dx = - 5 ; dx < = 5 ; dx + + ) {
for ( int dy = - 5 ; dy < = 1 ; dy + + ) {
for ( int dz = - 5 ; dz < = 5 ; dz + + ) {
int x = center . x + dx ;
int y = center . y + dy ;
int z = center . z + dz ;
IBlockState desired = bcc . getSchematic ( x , y , z ) ;
if ( desired = = null ) {
continue ; // irrelevant
}
IBlockState curr = bcc . bsi . get0 ( x , y , z ) ;
if ( MovementHelper . isReplacable ( x , y , z , curr , bcc . bsi ) & & ! valid ( curr , desired ) ) {
if ( dy = = 1 & & bcc . bsi . get0 ( x , y + 1 , z ) . getBlock ( ) = = Blocks . AIR ) {
continue ;
}
2019-02-07 21:59:37 +00:00
desirableOnHotbar . add ( desired ) ;
2019-02-07 00:22:40 +00:00
Optional < Placement > opt = possibleToPlace ( desired , x , y , z , bcc . bsi ) ;
if ( opt . isPresent ( ) ) {
return opt ;
}
}
}
}
}
return Optional . empty ( ) ;
}
2019-04-29 21:59:25 +00:00
private Optional < Placement > possibleToPlace ( IBlockState toPlace , int x , int y , int z , BlockStateInterface bsi ) {
2019-02-07 00:22:40 +00:00
for ( EnumFacing against : EnumFacing . values ( ) ) {
BetterBlockPos placeAgainstPos = new BetterBlockPos ( x , y , z ) . offset ( against ) ;
IBlockState placeAgainstState = bsi . get0 ( placeAgainstPos ) ;
if ( MovementHelper . isReplacable ( placeAgainstPos . x , placeAgainstPos . y , placeAgainstPos . z , placeAgainstState , bsi ) ) {
continue ;
}
if ( ! ctx . world ( ) . mayPlace ( toPlace . getBlock ( ) , new BetterBlockPos ( x , y , z ) , false , against , null ) ) {
continue ;
}
AxisAlignedBB aabb = placeAgainstState . getBoundingBox ( ctx . world ( ) , placeAgainstPos ) ;
for ( Vec3d placementMultiplier : aabbSideMultipliers ( against ) ) {
double placeX = placeAgainstPos . x + aabb . minX * placementMultiplier . x + aabb . maxX * ( 1 - placementMultiplier . x ) ;
double placeY = placeAgainstPos . y + aabb . minY * placementMultiplier . y + aabb . maxY * ( 1 - placementMultiplier . y ) ;
double placeZ = placeAgainstPos . z + aabb . minZ * placementMultiplier . z + aabb . maxZ * ( 1 - placementMultiplier . z ) ;
2019-02-17 04:49:07 +00:00
Rotation rot = RotationUtils . calcRotationFromVec3d ( ctx . playerHead ( ) , new Vec3d ( placeX , placeY , placeZ ) , ctx . playerRotations ( ) ) ;
2019-02-07 00:22:40 +00:00
RayTraceResult result = RayTraceUtils . rayTraceTowards ( ctx . player ( ) , rot , ctx . playerController ( ) . getBlockReachDistance ( ) ) ;
if ( result ! = null & & result . typeOfHit = = RayTraceResult . Type . BLOCK & & result . getBlockPos ( ) . equals ( placeAgainstPos ) & & result . sideHit = = against . getOpposite ( ) ) {
OptionalInt hotbar = hasAnyItemThatWouldPlace ( toPlace , result , rot ) ;
if ( hotbar . isPresent ( ) ) {
return Optional . of ( new Placement ( hotbar . getAsInt ( ) , placeAgainstPos , against . getOpposite ( ) , rot ) ) ;
}
}
}
}
return Optional . empty ( ) ;
}
2019-04-29 21:59:25 +00:00
private OptionalInt hasAnyItemThatWouldPlace ( IBlockState desired , RayTraceResult result , Rotation rot ) {
2019-02-07 00:22:40 +00:00
for ( int i = 0 ; i < 9 ; i + + ) {
ItemStack stack = ctx . player ( ) . inventory . mainInventory . get ( i ) ;
if ( stack . isEmpty ( ) | | ! ( stack . getItem ( ) instanceof ItemBlock ) ) {
continue ;
}
float originalYaw = ctx . player ( ) . rotationYaw ;
float originalPitch = ctx . player ( ) . rotationPitch ;
// the state depends on the facing of the player sometimes
ctx . player ( ) . rotationYaw = rot . getYaw ( ) ;
ctx . player ( ) . rotationPitch = rot . getPitch ( ) ;
IBlockState wouldBePlaced = ( ( ItemBlock ) stack . getItem ( ) ) . getBlock ( ) . getStateForPlacement (
ctx . world ( ) ,
result . getBlockPos ( ) . offset ( result . sideHit ) ,
result . sideHit ,
( float ) result . hitVec . x - result . getBlockPos ( ) . getX ( ) , // as in PlayerControllerMP
( float ) result . hitVec . y - result . getBlockPos ( ) . getY ( ) ,
( float ) result . hitVec . z - result . getBlockPos ( ) . getZ ( ) ,
stack . getItem ( ) . getMetadata ( stack . getMetadata ( ) ) ,
ctx . player ( )
) ;
ctx . player ( ) . rotationYaw = originalYaw ;
ctx . player ( ) . rotationPitch = originalPitch ;
if ( valid ( wouldBePlaced , desired ) ) {
return OptionalInt . of ( i ) ;
}
}
return OptionalInt . empty ( ) ;
}
private static Vec3d [ ] aabbSideMultipliers ( EnumFacing side ) {
switch ( side ) {
case UP :
2019-02-21 22:24:23 +00:00
return new Vec3d [ ] { new Vec3d ( 0 . 5 , 1 , 0 . 5 ) , new Vec3d ( 0 . 1 , 1 , 0 . 5 ) , new Vec3d ( 0 . 9 , 1 , 0 . 5 ) , new Vec3d ( 0 . 5 , 1 , 0 . 1 ) , new Vec3d ( 0 . 5 , 1 , 0 . 9 ) } ;
2019-02-07 00:22:40 +00:00
case DOWN :
2019-02-21 22:24:23 +00:00
return new Vec3d [ ] { new Vec3d ( 0 . 5 , 0 , 0 . 5 ) , new Vec3d ( 0 . 1 , 0 , 0 . 5 ) , new Vec3d ( 0 . 9 , 0 , 0 . 5 ) , new Vec3d ( 0 . 5 , 0 , 0 . 1 ) , new Vec3d ( 0 . 5 , 0 , 0 . 9 ) } ;
2019-02-07 00:22:40 +00:00
case NORTH :
case SOUTH :
case EAST :
case WEST :
double x = side . getXOffset ( ) = = 0 ? 0 . 5 : ( 1 + side . getXOffset ( ) ) / 2D ;
double z = side . getZOffset ( ) = = 0 ? 0 . 5 : ( 1 + side . getZOffset ( ) ) / 2D ;
return new Vec3d [ ] { new Vec3d ( x , 0 . 25 , z ) , new Vec3d ( x , 0 . 75 , z ) } ;
default : // null
2019-04-04 06:19:32 +00:00
throw new IllegalStateException ( ) ;
2019-02-07 00:22:40 +00:00
}
}
2018-12-26 05:07:17 +00:00
@Override
public PathingCommand onTick ( boolean calcFailed , boolean isSafeToCancel ) {
2019-04-11 23:36:20 +00:00
if ( baritone . getInputOverrideHandler ( ) . isInputForcedDown ( Input . CLICK_LEFT ) ) {
ticks = 5 ;
} else {
ticks - - ;
}
2019-02-28 23:26:09 +00:00
baritone . getInputOverrideHandler ( ) . clearAllKeys ( ) ;
2019-03-28 00:32:02 +00:00
if ( paused ) {
2019-04-17 05:43:33 +00:00
return new PathingCommand ( null , PathingCommandType . CANCEL_AND_SET_GOAL ) ;
2019-03-28 00:32:02 +00:00
}
2019-04-11 22:17:26 +00:00
if ( Baritone . settings ( ) . buildInLayers . value ) {
if ( realSchematic = = null ) {
realSchematic = schematic ;
}
schematic = new ISchematic ( ) {
@Override
public IBlockState desiredState ( int x , int y , int z ) {
return realSchematic . desiredState ( x , y , z ) ;
}
@Override
public int widthX ( ) {
return realSchematic . widthX ( ) ;
}
@Override
public int heightY ( ) {
return layer ;
}
@Override
public int lengthZ ( ) {
return realSchematic . lengthZ ( ) ;
}
} ;
}
2019-01-13 03:51:21 +00:00
BuilderCalculationContext bcc = new BuilderCalculationContext ( ) ;
2019-01-09 04:45:02 +00:00
if ( ! recalc ( bcc ) ) {
2019-04-11 22:17:26 +00:00
if ( Baritone . settings ( ) . buildInLayers . value & & layer < realSchematic . heightY ( ) ) {
logDirect ( " Starting layer " + layer ) ;
layer + + ;
return onTick ( calcFailed , isSafeToCancel ) ;
}
2019-04-11 23:36:20 +00:00
int distance = Baritone . settings ( ) . buildRepeatDistance . value ;
EnumFacing direction = Baritone . settings ( ) . buildRepeatDirection . value ;
if ( distance = = 0 ) {
logDirect ( " Done building " ) ;
onLostControl ( ) ;
return null ;
}
// build repeat time
if ( distance = = - 1 ) {
distance = schematic . size ( direction . getAxis ( ) ) ;
}
layer = 0 ;
origin = new BlockPos ( origin ) . offset ( direction , distance ) ;
logDirect ( " Repeating build " + distance + " blocks to the " + direction + " , new origin is " + origin ) ;
2019-04-24 00:42:13 +00:00
return onTick ( calcFailed , isSafeToCancel ) ;
2019-01-09 04:45:02 +00:00
}
2019-02-20 06:50:03 +00:00
trim ( bcc ) ;
2019-04-11 23:36:20 +00:00
2019-01-09 04:45:02 +00:00
Optional < Tuple < BetterBlockPos , Rotation > > toBreak = toBreakNearPlayer ( bcc ) ;
if ( toBreak . isPresent ( ) & & isSafeToCancel & & ctx . player ( ) . onGround ) {
// we'd like to pause to break this block
// only change look direction if it's safe (don't want to fuck up an in progress parkour for example
Rotation rot = toBreak . get ( ) . getSecond ( ) ;
BetterBlockPos pos = toBreak . get ( ) . getFirst ( ) ;
baritone . getLookBehavior ( ) . updateTarget ( rot , true ) ;
MovementHelper . switchToBestToolFor ( ctx , bcc . get ( pos ) ) ;
2019-02-21 22:24:23 +00:00
if ( ctx . player ( ) . isSneaking ( ) ) {
// really horrible bug where a block is visible for breaking while sneaking but not otherwise
// so you can't see it, it goes to place something else, sneaks, then the next tick it tries to break
// and is unable since it's unsneaked in the intermediary tick
baritone . getInputOverrideHandler ( ) . setInputForceState ( Input . SNEAK , true ) ;
}
2019-04-16 00:12:20 +00:00
if ( ctx . isLookingAt ( pos ) | | ctx . playerRotations ( ) . isReallyCloseTo ( rot ) ) {
2019-01-09 04:45:02 +00:00
baritone . getInputOverrideHandler ( ) . setInputForceState ( Input . CLICK_LEFT , true ) ;
}
2019-04-17 06:19:55 +00:00
return new PathingCommand ( null , PathingCommandType . CANCEL_AND_SET_GOAL ) ;
2019-01-09 04:45:02 +00:00
}
2019-02-07 21:59:37 +00:00
List < IBlockState > desirableOnHotbar = new ArrayList < > ( ) ;
Optional < Placement > toPlace = searchForPlacables ( bcc , desirableOnHotbar ) ;
2019-02-07 00:22:40 +00:00
if ( toPlace . isPresent ( ) & & isSafeToCancel & & ctx . player ( ) . onGround & & ticks < = 0 ) {
Rotation rot = toPlace . get ( ) . rot ;
baritone . getLookBehavior ( ) . updateTarget ( rot , true ) ;
ctx . player ( ) . inventory . currentItem = toPlace . get ( ) . hotbarSelection ;
baritone . getInputOverrideHandler ( ) . setInputForceState ( Input . SNEAK , true ) ;
2019-04-16 00:12:20 +00:00
if ( ( ctx . isLookingAt ( toPlace . get ( ) . placeAgainst ) & & ctx . objectMouseOver ( ) . sideHit . equals ( toPlace . get ( ) . side ) ) | | ctx . playerRotations ( ) . isReallyCloseTo ( rot ) ) {
2019-02-07 00:22:40 +00:00
baritone . getInputOverrideHandler ( ) . setInputForceState ( Input . CLICK_RIGHT , true ) ;
}
2019-04-17 06:19:55 +00:00
return new PathingCommand ( null , PathingCommandType . CANCEL_AND_SET_GOAL ) ;
2019-02-07 00:22:40 +00:00
}
2019-01-09 04:45:02 +00:00
2019-02-07 21:59:37 +00:00
List < IBlockState > approxPlacable = placable ( 36 ) ;
2019-03-12 06:29:39 +00:00
if ( Baritone . settings ( ) . allowInventory . value ) {
2019-02-07 21:59:37 +00:00
ArrayList < Integer > usefulSlots = new ArrayList < > ( ) ;
List < IBlockState > noValidHotbarOption = new ArrayList < > ( ) ;
outer :
for ( IBlockState desired : desirableOnHotbar ) {
for ( int i = 0 ; i < 9 ; i + + ) {
if ( valid ( approxPlacable . get ( i ) , desired ) ) {
usefulSlots . add ( i ) ;
continue outer ;
}
}
noValidHotbarOption . add ( desired ) ;
}
outer :
for ( int i = 9 ; i < 36 ; i + + ) {
for ( IBlockState desired : noValidHotbarOption ) {
if ( valid ( approxPlacable . get ( i ) , desired ) ) {
baritone . getInventoryBehavior ( ) . attemptToPutOnHotbar ( i , usefulSlots : : contains ) ;
break outer ;
}
}
}
}
Goal goal = assemble ( bcc , approxPlacable . subList ( 0 , 9 ) ) ;
2019-01-09 23:07:06 +00:00
if ( goal = = null ) {
2019-02-07 21:59:37 +00:00
goal = assemble ( bcc , approxPlacable ) ; // we're far away, so assume that we have our whole inventory to recalculate placable properly
if ( goal = = null ) {
2019-03-28 00:32:02 +00:00
logDirect ( " Unable to do it. Pausing. resume to resume, cancel to cancel " ) ;
paused = true ;
return new PathingCommand ( null , PathingCommandType . REQUEST_PAUSE ) ;
2019-02-07 21:59:37 +00:00
}
2019-01-09 04:45:02 +00:00
}
2019-01-09 23:07:06 +00:00
return new PathingCommandContext ( goal , PathingCommandType . FORCE_REVALIDATE_GOAL_AND_PATH , bcc ) ;
2019-01-09 04:45:02 +00:00
}
2019-04-29 21:59:25 +00:00
private boolean recalc ( BuilderCalculationContext bcc ) {
2019-01-09 04:45:02 +00:00
if ( incorrectPositions = = null ) {
incorrectPositions = new HashSet < > ( ) ;
fullRecalc ( bcc ) ;
if ( incorrectPositions . isEmpty ( ) ) {
return false ;
}
}
recalcNearby ( bcc ) ;
if ( incorrectPositions . isEmpty ( ) ) {
fullRecalc ( bcc ) ;
}
return ! incorrectPositions . isEmpty ( ) ;
}
2019-04-29 21:59:25 +00:00
private void trim ( BuilderCalculationContext bcc ) {
2019-02-20 06:50:03 +00:00
HashSet < BetterBlockPos > copy = new HashSet < > ( incorrectPositions ) ;
copy . removeIf ( pos - > pos . distanceSq ( ctx . player ( ) . posX , ctx . player ( ) . posY , ctx . player ( ) . posZ ) > 200 ) ;
if ( ! copy . isEmpty ( ) ) {
incorrectPositions = copy ;
}
}
2019-04-29 21:59:25 +00:00
private void recalcNearby ( BuilderCalculationContext bcc ) {
2019-01-09 04:45:02 +00:00
BetterBlockPos center = ctx . playerFeet ( ) ;
for ( int dx = - 5 ; dx < = 5 ; dx + + ) {
for ( int dy = - 5 ; dy < = 5 ; dy + + ) {
for ( int dz = - 5 ; dz < = 5 ; dz + + ) {
int x = center . x + dx ;
int y = center . y + dy ;
int z = center . z + dz ;
IBlockState desired = bcc . getSchematic ( x , y , z ) ;
if ( desired ! = null ) {
// we care about this position
2019-04-29 21:55:14 +00:00
BetterBlockPos pos = new BetterBlockPos ( x , y , z ) ;
2019-01-09 04:45:02 +00:00
if ( valid ( bcc . bsi . get0 ( x , y , z ) , desired ) ) {
2019-04-29 21:55:14 +00:00
incorrectPositions . remove ( pos ) ;
observedCompleted . add ( BetterBlockPos . longHash ( pos ) ) ;
2019-01-09 04:45:02 +00:00
} else {
2019-04-29 21:55:14 +00:00
incorrectPositions . add ( pos ) ;
observedCompleted . rem ( BetterBlockPos . longHash ( pos ) ) ;
2019-01-09 04:45:02 +00:00
}
}
}
}
}
}
2019-04-29 21:59:25 +00:00
private void fullRecalc ( BuilderCalculationContext bcc ) {
2019-01-09 04:45:02 +00:00
incorrectPositions = new HashSet < > ( ) ;
for ( int y = 0 ; y < schematic . heightY ( ) ; y + + ) {
for ( int z = 0 ; z < schematic . lengthZ ( ) ; z + + ) {
for ( int x = 0 ; x < schematic . widthX ( ) ; x + + ) {
2019-04-29 21:55:14 +00:00
if ( ! schematic . inSchematic ( x , y , z ) ) {
continue ;
}
int blockX = x + origin . getX ( ) ;
int blockY = y + origin . getY ( ) ;
int blockZ = z + origin . getZ ( ) ;
if ( bcc . bsi . worldContainsLoadedChunk ( blockX , blockZ ) ) { // check if its in render distance, not if its in cache
// we can directly observe this block, it is in render distance
if ( valid ( bcc . bsi . get0 ( blockX , blockY , blockZ ) , schematic . desiredState ( x , y , z ) ) ) {
observedCompleted . add ( BetterBlockPos . longHash ( blockX , blockY , blockZ ) ) ;
} else {
incorrectPositions . add ( new BetterBlockPos ( blockX , blockY , blockZ ) ) ;
observedCompleted . rem ( BetterBlockPos . longHash ( blockX , blockY , blockZ ) ) ;
}
continue ;
}
// this is not in render distance
if ( ! observedCompleted . contains ( BetterBlockPos . longHash ( blockX , blockY , blockZ ) ) ) {
// and we've never seen this position be correct
// therefore mark as incorrect
incorrectPositions . add ( new BetterBlockPos ( blockX , blockY , blockZ ) ) ;
2019-01-09 04:45:02 +00:00
}
}
}
}
2018-12-26 05:07:17 +00:00
}
2019-02-07 21:59:37 +00:00
private Goal assemble ( BuilderCalculationContext bcc , List < IBlockState > approxPlacable ) {
2019-05-01 21:03:36 +00:00
List < BetterBlockPos > placable = new ArrayList < > ( ) ;
List < BetterBlockPos > breakable = new ArrayList < > ( ) ;
List < BetterBlockPos > sourceLiquids = new ArrayList < > ( ) ;
incorrectPositions . forEach ( pos - > {
IBlockState state = bcc . bsi . get0 ( pos ) ;
if ( state . getBlock ( ) instanceof BlockAir ) {
if ( approxPlacable . contains ( bcc . getSchematic ( pos . x , pos . y , pos . z ) ) ) {
placable . add ( pos ) ;
}
} else {
if ( state . getBlock ( ) instanceof BlockLiquid ) {
// if the block itself is JUST a liquid (i.e. not just a waterlogged block), we CANNOT break it
// TODO for 1.13 make sure that this only matches pure water, not waterlogged blocks
if ( ! MovementHelper . possiblyFlowing ( state ) ) {
// if it's a source block then we want to replace it with a throwaway
sourceLiquids . add ( pos ) ;
}
} else {
breakable . add ( pos ) ;
}
}
} ) ;
List < Goal > toBreak = new ArrayList < > ( ) ;
breakable . forEach ( pos - > toBreak . add ( breakGoal ( pos , bcc ) ) ) ;
List < Goal > toPlace = new ArrayList < > ( ) ;
placable . forEach ( pos - > {
if ( ! placable . contains ( pos . down ( ) ) & & ! placable . contains ( pos . down ( 2 ) ) ) {
toPlace . add ( placementGoal ( pos , bcc ) ) ;
}
} ) ;
sourceLiquids . forEach ( pos - > toPlace . add ( new GoalBlock ( pos . up ( ) ) ) ) ;
2019-01-09 23:07:06 +00:00
2019-05-01 21:03:36 +00:00
if ( ! toPlace . isEmpty ( ) ) {
return new JankyGoalComposite ( new GoalComposite ( toPlace . toArray ( new Goal [ 0 ] ) ) , new GoalComposite ( toBreak . toArray ( new Goal [ 0 ] ) ) ) ;
2019-01-09 23:07:06 +00:00
}
2019-05-01 21:03:36 +00:00
if ( toBreak . isEmpty ( ) ) {
2019-01-09 23:07:06 +00:00
return null ;
}
2019-05-01 21:03:36 +00:00
return new GoalComposite ( toBreak . toArray ( new Goal [ 0 ] ) ) ;
2019-01-09 23:07:06 +00:00
}
public static class JankyGoalComposite implements Goal {
private final Goal primary ;
private final Goal fallback ;
public JankyGoalComposite ( Goal primary , Goal fallback ) {
this . primary = primary ;
this . fallback = fallback ;
}
@Override
public boolean isInGoal ( int x , int y , int z ) {
return primary . isInGoal ( x , y , z ) | | fallback . isInGoal ( x , y , z ) ;
}
@Override
public double heuristic ( int x , int y , int z ) {
return primary . heuristic ( x , y , z ) ;
2019-01-09 04:45:02 +00:00
}
2019-02-21 22:24:23 +00:00
@Override
public String toString ( ) {
return " JankyComposite Primary: " + primary + " Fallback: " + fallback ;
}
2019-01-09 04:45:02 +00:00
}
public static class GoalBreak extends GoalGetToBlock {
public GoalBreak ( BlockPos pos ) {
super ( pos ) ;
}
@Override
public boolean isInGoal ( int x , int y , int z ) {
// can't stand right on top of a block, that might not work (what if it's unsupported, can't break then)
2019-01-09 22:36:44 +00:00
if ( y > this . y ) {
2019-01-09 04:45:02 +00:00
return false ;
}
// but any other adjacent works for breaking, including inside or below
return super . isInGoal ( x , y , z ) ;
}
2018-12-26 05:07:17 +00:00
}
2019-04-29 23:02:44 +00:00
private Goal placementGoal ( BlockPos pos , BuilderCalculationContext bcc ) {
if ( ctx . world ( ) . getBlockState ( pos ) . getBlock ( ) ! = Blocks . AIR ) { // TODO can this even happen?
2019-02-20 16:08:52 +00:00
return new GoalPlace ( pos ) ;
}
2019-02-07 00:22:40 +00:00
boolean allowSameLevel = ctx . world ( ) . getBlockState ( pos . up ( ) ) . getBlock ( ) ! = Blocks . AIR ;
for ( EnumFacing facing : Movement . HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP ) {
if ( MovementHelper . canPlaceAgainst ( ctx , pos . offset ( facing ) ) & & ctx . world ( ) . mayPlace ( bcc . getSchematic ( pos . getX ( ) , pos . getY ( ) , pos . getZ ( ) ) . getBlock ( ) , pos , false , facing , null ) ) {
return new GoalAdjacent ( pos , allowSameLevel ) ;
}
}
return new GoalPlace ( pos ) ;
}
2019-04-29 23:02:44 +00:00
private Goal breakGoal ( BlockPos pos , BuilderCalculationContext bcc ) {
if ( Baritone . settings ( ) . goalBreakFromAbove . value & & bcc . bsi . get0 ( pos . up ( ) ) . getBlock ( ) instanceof BlockAir & & bcc . bsi . get0 ( pos . up ( 2 ) ) . getBlock ( ) instanceof BlockAir ) { // TODO maybe possible without the up(2) check?
return new JankyGoalComposite ( new GoalBreak ( pos ) , new GoalGetToBlock ( pos . up ( ) ) {
@Override
public boolean isInGoal ( int x , int y , int z ) {
if ( y > this . y | | ( x = = this . x & & y = = this . y & & z = = this . z ) ) {
return false ;
}
return super . isInGoal ( x , y , z ) ;
}
} ) ;
}
return new GoalBreak ( pos ) ;
}
2019-02-07 00:22:40 +00:00
public static class GoalAdjacent extends GoalGetToBlock {
2019-04-08 00:44:23 +00:00
private boolean allowSameLevel ;
2019-02-07 00:22:40 +00:00
public GoalAdjacent ( BlockPos pos , boolean allowSameLevel ) {
super ( pos ) ;
this . allowSameLevel = allowSameLevel ;
}
public boolean isInGoal ( int x , int y , int z ) {
if ( x = = this . x & & y = = this . y & & z = = this . z ) {
return false ;
}
if ( ! allowSameLevel & & y = = this . y - 1 ) {
return false ;
}
if ( y < this . y - 1 ) {
return false ;
}
return super . isInGoal ( x , y , z ) ;
}
public double heuristic ( int x , int y , int z ) {
// prioritize lower y coordinates
return this . y * 100 + super . heuristic ( x , y , z ) ;
}
}
2019-01-09 22:36:44 +00:00
public static class GoalPlace extends GoalBlock {
public GoalPlace ( BlockPos placeAt ) {
super ( placeAt . up ( ) ) ;
}
public double heuristic ( int x , int y , int z ) {
// prioritize lower y coordinates
return this . y * 100 + super . heuristic ( x , y , z ) ;
}
}
2018-12-26 05:07:17 +00:00
@Override
public void onLostControl ( ) {
incorrectPositions = null ;
name = null ;
schematic = null ;
2019-04-11 22:17:26 +00:00
realSchematic = null ;
layer = 0 ;
2019-03-28 00:32:02 +00:00
paused = false ;
2019-04-29 21:55:14 +00:00
observedCompleted = null ;
2018-12-26 05:07:17 +00:00
}
@Override
2019-03-13 16:17:25 +00:00
public String displayName0 ( ) {
2019-03-28 00:32:02 +00:00
return paused ? " Builder Paused " : " Building " + name ;
2018-12-26 05:07:17 +00:00
}
2019-04-29 21:59:25 +00:00
private List < IBlockState > placable ( int size ) {
2018-12-26 05:07:17 +00:00
List < IBlockState > result = new ArrayList < > ( ) ;
2019-02-07 21:59:37 +00:00
for ( int i = 0 ; i < size ; i + + ) {
2018-12-26 05:07:17 +00:00
ItemStack stack = ctx . player ( ) . inventory . mainInventory . get ( i ) ;
if ( stack . isEmpty ( ) | | ! ( stack . getItem ( ) instanceof ItemBlock ) ) {
result . add ( Blocks . AIR . getDefaultState ( ) ) ;
continue ;
}
// <toxic cloud>
result . add ( ( ( ItemBlock ) stack . getItem ( ) ) . getBlock ( ) . getStateForPlacement ( ctx . world ( ) , ctx . playerFeet ( ) , EnumFacing . UP , ( float ) ctx . player ( ) . posX , ( float ) ctx . player ( ) . posY , ( float ) ctx . player ( ) . posZ , stack . getItem ( ) . getMetadata ( stack . getMetadata ( ) ) , ctx . player ( ) ) ) ;
// </toxic cloud>
}
return result ;
}
2019-04-29 21:59:25 +00:00
private boolean valid ( IBlockState current , IBlockState desired ) {
2019-01-09 04:45:02 +00:00
// TODO more complicated comparison logic I guess
return desired = = null | | current . equals ( desired ) ;
}
2018-12-26 05:07:17 +00:00
public class BuilderCalculationContext extends CalculationContext {
private final List < IBlockState > placable ;
private final ISchematic schematic ;
private final int originX ;
private final int originY ;
private final int originZ ;
2019-01-13 03:51:21 +00:00
public BuilderCalculationContext ( ) {
2018-12-26 05:07:17 +00:00
super ( BuilderProcess . this . baritone , true ) ; // wew lad
2019-02-07 21:59:37 +00:00
this . placable = placable ( 9 ) ;
2019-01-13 03:51:21 +00:00
this . schematic = BuilderProcess . this . schematic ;
this . originX = origin . getX ( ) ;
this . originY = origin . getY ( ) ;
this . originZ = origin . getZ ( ) ;
2019-01-09 22:36:44 +00:00
this . jumpPenalty + = 10 ;
2019-01-09 23:42:49 +00:00
this . backtrackCostFavoringCoefficient = 1 ;
2018-12-26 05:07:17 +00:00
}
private IBlockState getSchematic ( int x , int y , int z ) {
if ( schematic . inSchematic ( x - originX , y - originY , z - originZ ) ) {
return schematic . desiredState ( x - originX , y - originY , z - originZ ) ;
} else {
return null ;
}
}
@Override
public double costOfPlacingAt ( int x , int y , int z ) {
if ( isPossiblyProtected ( x , y , z ) | | ! worldBorder . canPlaceAt ( x , z ) ) { // make calculation fail properly if we can't build
return COST_INF ;
}
IBlockState sch = getSchematic ( x , y , z ) ;
if ( sch ! = null ) {
// TODO this can return true even when allowPlace is off.... is that an issue?
2019-01-09 04:45:02 +00:00
if ( sch . getBlock ( ) = = Blocks . AIR ) {
// we want this to be air, but they're asking if they can place here
// this won't be a schematic block, this will be a throwaway
return placeBlockCost * 2 ; // we're going to have to break it eventually
}
2018-12-26 05:07:17 +00:00
if ( placable . contains ( sch ) ) {
return 0 ; // thats right we gonna make it FREE to place a block where it should go in a structure
// no place block penalty at all 😎
// i'm such an idiot that i just tried to copy and paste the epic gamer moment emoji too
// get added to unicode when?
}
if ( ! hasThrowaway ) {
return COST_INF ;
}
2019-01-09 04:45:02 +00:00
// we want it to be something that we don't have
// even more of a pain to place something wrong
return placeBlockCost * 3 ;
2018-12-26 05:07:17 +00:00
} else {
if ( hasThrowaway ) {
return placeBlockCost ;
} else {
return COST_INF ;
}
}
}
@Override
2019-01-09 04:45:02 +00:00
public double breakCostMultiplierAt ( int x , int y , int z ) {
2018-12-26 05:07:17 +00:00
if ( ! allowBreak | | isPossiblyProtected ( x , y , z ) ) {
2019-01-09 04:45:02 +00:00
return COST_INF ;
2018-12-26 05:07:17 +00:00
}
IBlockState sch = getSchematic ( x , y , z ) ;
if ( sch ! = null ) {
if ( sch . getBlock ( ) = = Blocks . AIR ) {
// it should be air
// regardless of current contents, we can break it
2019-01-09 04:45:02 +00:00
return 1 ;
2018-12-26 05:07:17 +00:00
}
// it should be a real block
// is it already that block?
2019-01-09 04:45:02 +00:00
if ( valid ( bsi . get0 ( x , y , z ) , sch ) ) {
return 3 ;
} else {
// can break if it's wrong
// would be great to return less than 1 here, but that would actually make the cost calculation messed up
// since we're breaking a block, if we underestimate the cost, then it'll fail when it really takes the correct amount of time
return 1 ;
}
2018-12-26 05:07:17 +00:00
// TODO do blocks in render distace only?
// TODO allow breaking blocks that we have a tool to harvest and immediately place back?
} else {
2019-01-09 04:45:02 +00:00
return 1 ; // why not lol
2018-12-26 05:07:17 +00:00
}
}
}
}