2019-03-05 20:26:52 +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.cache.ICachedWorld ;
2019-03-14 21:52:51 +00:00
import baritone.api.pathing.goals.Goal ;
import baritone.api.pathing.goals.GoalComposite ;
2019-03-05 20:26:52 +00:00
import baritone.api.pathing.goals.GoalXZ ;
2019-04-25 20:40:33 +00:00
import baritone.api.pathing.goals.GoalYLevel ;
2019-04-18 01:10:47 +00:00
import baritone.api.process.IExploreProcess ;
2019-03-05 20:26:52 +00:00
import baritone.api.process.PathingCommand ;
import baritone.api.process.PathingCommandType ;
2019-04-22 21:34:31 +00:00
import baritone.api.utils.MyChunkPos ;
2019-03-05 20:26:52 +00:00
import baritone.cache.CachedWorld ;
import baritone.utils.BaritoneProcessHelper ;
2020-02-29 16:28:18 +00:00
import baritone.utils.NotificationHelper ;
2019-04-22 21:34:31 +00:00
import com.google.gson.Gson ;
import com.google.gson.GsonBuilder ;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet ;
2019-03-05 20:26:52 +00:00
import net.minecraft.util.math.BlockPos ;
2019-04-22 21:34:31 +00:00
import net.minecraft.util.math.ChunkPos ;
2019-03-05 20:26:52 +00:00
2019-04-22 21:34:31 +00:00
import java.io.InputStreamReader ;
import java.nio.file.Files ;
import java.nio.file.Path ;
2019-03-14 21:52:51 +00:00
import java.util.ArrayList ;
import java.util.List ;
2019-05-20 05:53:09 +00:00
public final class ExploreProcess extends BaritoneProcessHelper implements IExploreProcess {
2019-03-05 20:26:52 +00:00
private BlockPos explorationOrigin ;
2019-04-22 21:34:31 +00:00
private IChunkFilter filter ;
2019-04-27 23:16:36 +00:00
private int distanceCompleted ;
2019-03-05 20:26:52 +00:00
public ExploreProcess ( Baritone baritone ) {
2019-03-12 01:24:31 +00:00
super ( baritone ) ;
2019-03-05 20:26:52 +00:00
}
@Override
public boolean isActive ( ) {
return explorationOrigin ! = null ;
}
2019-04-18 01:10:47 +00:00
@Override
2019-03-05 20:26:52 +00:00
public void explore ( int centerX , int centerZ ) {
explorationOrigin = new BlockPos ( centerX , 0 , centerZ ) ;
2019-04-27 23:16:36 +00:00
distanceCompleted = 0 ;
2019-03-05 20:26:52 +00:00
}
2019-04-22 21:34:31 +00:00
@Override
public void applyJsonFilter ( Path path , boolean invert ) throws Exception {
filter = new JsonChunkFilter ( path , invert ) ;
}
public IChunkFilter calcFilter ( ) {
IChunkFilter filter ;
if ( this . filter ! = null ) {
filter = new EitherChunk ( this . filter , new BaritoneChunkCache ( ) ) ;
} else {
filter = new BaritoneChunkCache ( ) ;
}
return filter ;
}
2019-03-05 20:26:52 +00:00
@Override
public PathingCommand onTick ( boolean calcFailed , boolean isSafeToCancel ) {
if ( calcFailed ) {
logDirect ( " Failed " ) ;
2020-02-29 16:28:18 +00:00
if ( Baritone . settings ( ) . desktopNotifications . value & & Baritone . settings ( ) . notificationOnExploreFinished . value ) {
NotificationHelper . notify ( " Exploration failed " , true ) ;
}
2019-03-05 20:26:52 +00:00
onLostControl ( ) ;
return null ;
}
2019-04-22 21:34:31 +00:00
IChunkFilter filter = calcFilter ( ) ;
2019-04-25 19:18:25 +00:00
if ( ! Baritone . settings ( ) . disableCompletionCheck . value & & filter . countRemain ( ) = = 0 ) {
2019-04-22 21:34:31 +00:00
logDirect ( " Explored all chunks " ) ;
2020-02-29 16:28:18 +00:00
if ( Baritone . settings ( ) . desktopNotifications . value & & Baritone . settings ( ) . notificationOnExploreFinished . value ) {
NotificationHelper . notify ( " Explored all chunks " , false ) ;
}
2019-04-22 21:34:31 +00:00
onLostControl ( ) ;
return null ;
}
Goal [ ] closestUncached = closestUncachedChunks ( explorationOrigin , filter ) ;
2019-03-05 20:26:52 +00:00
if ( closestUncached = = null ) {
logDebug ( " awaiting region load from disk " ) ;
return new PathingCommand ( null , PathingCommandType . REQUEST_PAUSE ) ;
}
2019-03-14 21:52:51 +00:00
return new PathingCommand ( new GoalComposite ( closestUncached ) , PathingCommandType . FORCE_REVALIDATE_GOAL_AND_PATH ) ;
2019-03-05 20:26:52 +00:00
}
2019-04-22 21:34:31 +00:00
private Goal [ ] closestUncachedChunks ( BlockPos center , IChunkFilter filter ) {
2019-03-14 21:52:51 +00:00
int chunkX = center . getX ( ) > > 4 ;
int chunkZ = center . getZ ( ) > > 4 ;
2019-04-25 19:18:25 +00:00
int count = Math . min ( filter . countRemain ( ) , Baritone . settings ( ) . exploreChunkSetMinimumSize . value ) ;
2019-04-27 23:03:58 +00:00
List < BlockPos > centers = new ArrayList < > ( ) ;
2019-04-27 23:16:36 +00:00
int renderDistance = Baritone . settings ( ) . worldExploringChunkOffset . value ;
for ( int dist = distanceCompleted ; ; dist + + ) {
2019-03-05 20:26:52 +00:00
for ( int dx = - dist ; dx < = dist ; dx + + ) {
2019-04-27 23:16:36 +00:00
int zval = dist - Math . abs ( dx ) ;
for ( int mult = 0 ; mult < 2 ; mult + + ) {
int dz = ( mult * 2 - 1 ) * zval ; // dz can be either -zval or zval
2019-04-27 23:01:58 +00:00
int trueDist = Math . abs ( dx ) + Math . abs ( dz ) ;
2019-03-05 20:26:52 +00:00
if ( trueDist ! = dist ) {
2019-04-27 23:16:36 +00:00
throw new IllegalStateException ( ) ;
2019-03-05 20:26:52 +00:00
}
2019-04-22 21:34:31 +00:00
switch ( filter . isAlreadyExplored ( chunkX + dx , chunkZ + dz ) ) {
case UNKNOWN :
return null ; // awaiting load
case NOT_EXPLORED :
break ; // note: this breaks the switch not the for
case EXPLORED :
continue ; // note: this continues the for
2019-06-12 06:46:04 +00:00
default :
2019-04-22 21:34:31 +00:00
}
2019-04-25 19:45:53 +00:00
int centerX = ( ( chunkX + dx ) < < 4 ) + 8 ;
int centerZ = ( ( chunkZ + dz ) < < 4 ) + 8 ;
2019-04-27 23:16:36 +00:00
int offset = renderDistance < < 4 ;
2019-04-19 22:10:10 +00:00
if ( dx < 0 ) {
2019-04-22 21:34:31 +00:00
centerX - = offset ;
2019-04-19 22:10:10 +00:00
} else {
2019-04-22 21:34:31 +00:00
centerX + = offset ;
2019-04-19 22:10:10 +00:00
}
if ( dz < 0 ) {
2019-04-22 21:34:31 +00:00
centerZ - = offset ;
2019-04-19 22:10:10 +00:00
} else {
2019-04-22 21:34:31 +00:00
centerZ + = offset ;
2019-04-19 22:10:10 +00:00
}
2019-04-22 21:34:31 +00:00
centers . add ( new BlockPos ( centerX , 0 , centerZ ) ) ;
2019-03-05 20:26:52 +00:00
}
}
2019-07-30 00:35:06 +00:00
if ( dist % 10 = = 0 ) {
count = Math . min ( filter . countRemain ( ) , Baritone . settings ( ) . exploreChunkSetMinimumSize . value ) ;
}
2019-04-25 19:18:25 +00:00
if ( centers . size ( ) > = count ) {
2019-04-25 20:40:33 +00:00
return centers . stream ( ) . map ( pos - > createGoal ( pos . getX ( ) , pos . getZ ( ) ) ) . toArray ( Goal [ ] : : new ) ;
2019-03-14 21:52:51 +00:00
}
2019-04-27 23:16:36 +00:00
if ( centers . isEmpty ( ) ) {
// we have explored everything from 0 to dist inclusive
// next time we should start our check at dist+1
distanceCompleted = dist + 1 ;
}
2019-03-05 20:26:52 +00:00
}
}
2019-04-25 20:40:33 +00:00
private static Goal createGoal ( int x , int z ) {
if ( Baritone . settings ( ) . exploreMaintainY . value = = - 1 ) {
return new GoalXZ ( x , z ) ;
}
// don't use a goalblock because we still want isInGoal to return true if X and Z are correct
// we just want to try and maintain Y on the way there, not necessarily end at that specific Y
return new GoalXZ ( x , z ) {
@Override
public double heuristic ( int x , int y , int z ) {
return super . heuristic ( x , y , z ) + GoalYLevel . calculate ( Baritone . settings ( ) . exploreMaintainY . value , y ) ;
}
} ;
}
2019-04-22 21:34:31 +00:00
private enum Status {
EXPLORED , NOT_EXPLORED , UNKNOWN ;
}
private interface IChunkFilter {
2019-09-19 20:40:46 +00:00
2019-04-22 21:34:31 +00:00
Status isAlreadyExplored ( int chunkX , int chunkZ ) ;
2019-04-25 19:18:25 +00:00
int countRemain ( ) ;
2019-04-22 21:34:31 +00:00
}
private class BaritoneChunkCache implements IChunkFilter {
private final ICachedWorld cache = baritone . getWorldProvider ( ) . getCurrentWorld ( ) . getCachedWorld ( ) ;
@Override
public Status isAlreadyExplored ( int chunkX , int chunkZ ) {
int centerX = chunkX < < 4 ;
int centerZ = chunkZ < < 4 ;
if ( cache . isCached ( centerX , centerZ ) ) {
return Status . EXPLORED ;
}
if ( ! ( ( CachedWorld ) cache ) . regionLoaded ( centerX , centerZ ) ) {
Baritone . getExecutor ( ) . execute ( ( ) - > {
( ( CachedWorld ) cache ) . tryLoadFromDisk ( centerX > > 9 , centerZ > > 9 ) ;
} ) ;
return Status . UNKNOWN ; // we still need to load regions from disk in order to decide properly
}
return Status . NOT_EXPLORED ;
}
@Override
2019-04-25 19:18:25 +00:00
public int countRemain ( ) {
return Integer . MAX_VALUE ;
2019-04-22 21:34:31 +00:00
}
}
private class JsonChunkFilter implements IChunkFilter {
2019-09-19 20:40:46 +00:00
2019-04-22 21:34:31 +00:00
private final boolean invert ; // if true, the list is interpreted as a list of chunks that are NOT explored, if false, the list is interpreted as a list of chunks that ARE explored
private final LongOpenHashSet inFilter ;
private final MyChunkPos [ ] positions ;
private JsonChunkFilter ( Path path , boolean invert ) throws Exception { // ioexception, json exception, etc
this . invert = invert ;
Gson gson = new GsonBuilder ( ) . create ( ) ;
positions = gson . fromJson ( new InputStreamReader ( Files . newInputStream ( path ) ) , MyChunkPos [ ] . class ) ;
logDirect ( " Loaded " + positions . length + " positions " ) ;
inFilter = new LongOpenHashSet ( ) ;
for ( MyChunkPos mcp : positions ) {
inFilter . add ( ChunkPos . asLong ( mcp . x , mcp . z ) ) ;
}
}
@Override
public Status isAlreadyExplored ( int chunkX , int chunkZ ) {
if ( inFilter . contains ( ChunkPos . asLong ( chunkX , chunkZ ) ) ^ invert ) {
// either it's on the list of explored chunks, or it's not on the list of unexplored chunks
// either way, we have it
return Status . EXPLORED ;
} else {
// either it's not on the list of explored chunks, or it's on the list of unexplored chunks
// either way, it depends on if baritone has cached it so defer to that
return Status . UNKNOWN ;
}
}
@Override
2019-04-25 19:18:25 +00:00
public int countRemain ( ) {
2019-04-22 21:34:31 +00:00
if ( ! invert ) {
// if invert is false, anything not on the list is uncached
2019-04-25 19:18:25 +00:00
return Integer . MAX_VALUE ;
2019-04-22 21:34:31 +00:00
}
// but if invert is true, anything not on the list IS assumed cached
// so we are done if everything on our list is cached!
2019-04-25 19:18:25 +00:00
int countRemain = 0 ;
2019-04-22 21:34:31 +00:00
BaritoneChunkCache bcc = new BaritoneChunkCache ( ) ;
for ( MyChunkPos pos : positions ) {
if ( bcc . isAlreadyExplored ( pos . x , pos . z ) ! = Status . EXPLORED ) {
// either waiting for it or dont have it at all
2019-04-25 19:18:25 +00:00
countRemain + + ;
if ( countRemain > = Baritone . settings ( ) . exploreChunkSetMinimumSize . value ) {
return countRemain ;
}
2019-04-22 21:34:31 +00:00
}
}
2019-04-25 19:18:25 +00:00
return countRemain ;
2019-04-22 21:34:31 +00:00
}
}
private class EitherChunk implements IChunkFilter {
2019-09-19 20:40:46 +00:00
2019-04-22 21:34:31 +00:00
private final IChunkFilter a ;
private final IChunkFilter b ;
private EitherChunk ( IChunkFilter a , IChunkFilter b ) {
this . a = a ;
this . b = b ;
}
@Override
public Status isAlreadyExplored ( int chunkX , int chunkZ ) {
if ( a . isAlreadyExplored ( chunkX , chunkZ ) = = Status . EXPLORED ) {
return Status . EXPLORED ;
}
return b . isAlreadyExplored ( chunkX , chunkZ ) ;
}
@Override
2019-04-25 19:18:25 +00:00
public int countRemain ( ) {
return Math . min ( a . countRemain ( ) , b . countRemain ( ) ) ;
2019-04-22 21:34:31 +00:00
}
}
2019-03-05 20:26:52 +00:00
@Override
public void onLostControl ( ) {
explorationOrigin = null ;
}
@Override
2019-03-13 16:17:25 +00:00
public String displayName0 ( ) {
2019-04-27 23:16:36 +00:00
return " Exploring around " + explorationOrigin + " , distance completed " + distanceCompleted + " , currently going to " + new GoalComposite ( closestUncachedChunks ( explorationOrigin , calcFilter ( ) ) ) ;
2019-03-05 20:26:52 +00:00
}
}