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 ;
import baritone.api.process.PathingCommand ;
import baritone.api.process.PathingCommandType ;
import baritone.api.utils.BetterBlockPos ;
import baritone.pathing.movement.CalculationContext ;
import baritone.utils.BaritoneProcessHelper ;
import baritone.utils.BlockStateInterface ;
import baritone.utils.ISchematic ;
import baritone.utils.PathingCommandContext ;
import net.minecraft.block.state.IBlockState ;
import net.minecraft.client.Minecraft ;
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 ;
import net.minecraft.util.math.Vec3i ;
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 ;
import java.util.ArrayList ;
import java.util.HashSet ;
import java.util.List ;
import static baritone.api.pathing.movement.ActionCosts.COST_INF ;
public class BuilderProcess extends BaritoneProcessHelper {
public BuilderProcess ( Baritone baritone ) {
super ( baritone ) ;
}
private HashSet < BetterBlockPos > incorrectPositions ;
private String name ;
private ISchematic schematic ;
private Vec3i origin ;
public boolean build ( String schematicFile ) {
File file = new File ( new File ( Minecraft . getMinecraft ( ) . gameDir , " schematics " ) , schematicFile ) ;
2018-12-27 18:06:10 +00:00
System . out . println ( file + " " + file . exists ( ) ) ;
2018-12-26 05:07:17 +00:00
NBTTagCompound tag ;
2018-12-27 18:06:10 +00:00
try ( FileInputStream fileIn = new FileInputStream ( file ) ) {
tag = CompressedStreamTools . readCompressed ( fileIn ) ;
2018-12-26 05:07:17 +00:00
} catch ( IOException e ) {
e . printStackTrace ( ) ;
return false ;
}
if ( tag = = null ) {
return false ;
}
name = schematicFile ;
schematic = parse ( tag ) ;
origin = ctx . playerFeet ( ) ;
return true ;
}
private static ISchematic parse ( NBTTagCompound schematic ) {
throw new UnsupportedOperationException ( " would rather die than parse " + schematic ) ;
}
@Override
public boolean isActive ( ) {
return schematic ! = null ;
}
@Override
public PathingCommand onTick ( boolean calcFailed , boolean isSafeToCancel ) {
// TODO somehow tell inventorybehavior what we'd like to have on the hotbar
// perhaps take the 16 closest positions in incorrectPositions to ctx.playerFeet that aren't desired to be air, and then snag the top 4 most common block states, then request those on the hotbar
// this will work as is, but it'll be trashy
// need to iterate over incorrectPositions and see which ones we can "correct" from our current standing position
// considerations:
// shouldn't break blocks that are supporting our current path segment, maybe?
//
return new PathingCommandContext ( new GoalComposite ( assemble ( ) ) , PathingCommandType . FORCE_REVALIDATE_GOAL_AND_PATH , new BuilderCalculationContext ( schematic , origin ) ) ;
}
private Goal [ ] assemble ( ) {
BlockStateInterface bsi = new CalculationContext ( baritone ) . bsi ;
return incorrectPositions . stream ( ) . map ( pos - >
bsi . get0 ( pos ) . getBlock ( ) = = Blocks . AIR ?
// it's air and it shouldn't be
new GoalBlock ( pos . up ( ) )
// it's a block and it shouldn't be
2018-12-27 18:06:10 +00:00
// todo disallow right above
2018-12-26 05:07:17 +00:00
: new GoalGetToBlock ( pos ) // replace with GoalTwoBlocks to mine using pathfinding system only
) . toArray ( Goal [ ] : : new ) ;
}
@Override
public void onLostControl ( ) {
incorrectPositions = null ;
name = null ;
schematic = null ;
}
@Override
public String displayName ( ) {
return " Building " + name ;
}
/ * *
* Hotbar contents , if they were placed
* < p >
* Always length nine , empty slots become Blocks . AIR . getDefaultState ( )
*
* @return
* /
public List < IBlockState > placable ( ) {
List < IBlockState > result = new ArrayList < > ( ) ;
for ( int i = 0 ; i < 9 ; i + + ) {
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 ;
}
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 ;
public BuilderCalculationContext ( ISchematic schematic , Vec3i schematicOrigin ) {
super ( BuilderProcess . this . baritone , true ) ; // wew lad
this . placable = placable ( ) ;
this . schematic = schematic ;
this . originX = schematicOrigin . getX ( ) ;
this . originY = schematicOrigin . getY ( ) ;
this . originZ = schematicOrigin . getZ ( ) ;
}
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?
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 ;
}
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
} else {
// we want it to be something that we don't have
// even more of a pain to place something wrong
return placeBlockCost * 3 ;
}
} else {
if ( hasThrowaway ) {
return placeBlockCost ;
} else {
return COST_INF ;
}
}
}
@Override
public boolean canBreakAt ( int x , int y , int z ) {
if ( ! allowBreak | | isPossiblyProtected ( x , y , z ) ) {
return false ;
}
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
return true ;
}
// it should be a real block
// is it already that block?
return ! bsi . get0 ( x , y , z ) . equals ( sch ) ; // can break if it's wrong
// TODO do blocks in render distace only?
// TODO allow breaking blocks that we have a tool to harvest and immediately place back?
} else {
return true ; // why not lol
}
}
}
}